Adds a tracing facility for in-kernel events
authorBarret Rhoden <brho@cs.berkeley.edu>
Sat, 27 Jul 2013 00:05:59 +0000 (17:05 -0700)
committerBarret Rhoden <brho@cs.berkeley.edu>
Sat, 27 Jul 2013 00:05:59 +0000 (17:05 -0700)
Useful for debugging.  Check out k/i/trace.h for info and how to use it.

kern/include/trace.h [new file with mode: 0644]
kern/src/Kbuild
kern/src/trace.c [new file with mode: 0644]

diff --git a/kern/include/trace.h b/kern/include/trace.h
new file mode 100644 (file)
index 0000000..77c8c0a
--- /dev/null
@@ -0,0 +1,111 @@
+/* Copyright (c) 2013 The Regents of the University of California
+ * Barret Rhoden <brho@cs.berkeley.edu>
+ * See LICENSE for details.
+ *
+ * Simple ring-buffer tracing for in-kernel events.  The rings have a
+ * power-of-two number of slots, and each entry size will be rounded up to the
+ * nearest power of two.  Ring slot acquisition by default is thread-safe, but
+ * we provide racy helpers if you want a little less overhead.
+ *
+ * Users need to provide a contiguous memory buffer and the size of an event
+ * struct to init.  For example:
+ *
+ *             trace_ring_init(my_trace_ring_ptr, my_buf, buf_sz, event_sz);
+ *
+ * And then to store a trace, first get a slot, then fill it in:
+ *
+ *             struct my_trace_event *my_trace = get_trace_slot(my_trace_ring_ptr);
+ *             if (my_trace)   // only need to check if we aren't overwriting
+ *                     my_trace = whatever;
+ *
+ * Later, to process the traces, provide a function pointer to
+ * trace_ring_foreach().  This performs the func on all traces in the ring,
+ * including the unused:
+ *
+ *             void trace_handler(void *trace_event, void *data)
+ *             {
+ *                     whatever();
+ *             }
+ *             trace_ring_foreach(my_trace_ring_ptr, trace_handler, optional_blob);
+ *
+ * Rings can be racy or not, and can overwrite entries or not.  If you are not
+ * overwriting, the ring will stop giving you slots.  You need to reset the ring
+ * to get fresh slots again.  If you are overwriting, you don't need to check
+ * the return value of get_trace_slot_overwrite().
+ *
+ * Given there is overwrite, tr_next doesn't really tell us which ones were
+ * used.  So your handler should check for a flag or something.  Timestamps
+ * might help make sense of the data in these cases too. */
+
+#ifndef ROS_INC_TRACE_H
+#define ROS_INC_TRACE_H
+
+#include <ros/common.h>
+
+struct trace_ring {
+       void                                            *tr_buf;
+       size_t                                          tr_buf_sz;
+       unsigned int                            tr_event_sz_shift;
+       unsigned int                            tr_max;
+       unsigned long                           tr_next;
+};
+
+typedef void (*trace_handler_t)(void *event, void *blob);
+
+static inline void *get_trace_slot(struct trace_ring *tr);
+static inline void *get_trace_slot_overwrite(struct trace_ring *tr);
+static inline void *get_trace_slot_racy(struct trace_ring *tr);
+static inline void *get_trace_slot_overwrite_racy(struct trace_ring *tr);
+
+void trace_ring_init(struct trace_ring *tr, void *buf, size_t buf_size,
+                     size_t event_size);
+void trace_ring_reset(struct trace_ring *tr);
+void trace_ring_reset_and_clear(struct trace_ring *tr);
+void trace_ring_foreach(struct trace_ring *tr, trace_handler_t tr_func,
+                        void *data);
+
+/* Inlined funcs, declared above */
+
+/* Helper */
+static inline void *__get_tr_slot(struct trace_ring *tr, unsigned long slot)
+{
+       /* tr_max is a power of 2, we're ignoring the upper bits of my_slot */
+       slot &= tr->tr_max - 1;
+       /* event sizes are rounded up to the nearest power of 2 (sz_shift) */
+       return tr->tr_buf + (slot << tr->tr_event_sz_shift);
+}
+
+static inline void *get_trace_slot(struct trace_ring *tr)
+{
+       /* Using syncs, instead of atomics, since we access tr_next as both atomic
+        * and 'normal'. */
+       unsigned long my_slot = __sync_fetch_and_add(&tr->tr_next, 1);
+       /* We can briefly go over, so long as we subtract back down to where we were
+        * before.  This will work so long as we don't have ~2^64 threads... */
+       if (my_slot >= tr->tr_max) {
+               __sync_fetch_and_add(&tr->tr_next, -1);
+               return 0;
+       }
+       return __get_tr_slot(tr, my_slot);
+}
+
+static inline void *get_trace_slot_overwrite(struct trace_ring *tr)
+{
+       return __get_tr_slot(tr, __sync_fetch_and_add(&tr->tr_next, 1));
+}
+
+static inline void *get_trace_slot_racy(struct trace_ring *tr)
+{
+       unsigned long my_slot = tr->tr_next;
+       if (my_slot >= tr->tr_max)
+               return 0;
+       tr->tr_next++;
+       return __get_tr_slot(tr, my_slot);
+}
+
+static inline void *get_trace_slot_overwrite_racy(struct trace_ring *tr)
+{
+       return __get_tr_slot(tr, tr->tr_next++);
+}
+
+#endif /* ROS_INC_TRACE_H */
index 44221bb..6adc935 100644 (file)
@@ -40,6 +40,7 @@ obj-y                                         += syscall.o
 obj-y                                          += sysevent.o
 obj-y                                          += testing.o
 obj-y                                          += time.o
+obj-y                                          += trace.o
 obj-y                                          += trap.o
 obj-y                                          += ucq.o
 obj-y                                          += umem.o
diff --git a/kern/src/trace.c b/kern/src/trace.c
new file mode 100644 (file)
index 0000000..c1ea617
--- /dev/null
@@ -0,0 +1,39 @@
+/* Copyright (c) 2013 The Regents of the University of California
+ * Barret Rhoden <brho@cs.berkeley.edu>
+ * See LICENSE for details.
+ *
+ * Simple ring-buffer tracing for in-kernel events. */
+
+#include <trace.h>
+#include <smp.h>
+#include <string.h>
+
+void trace_ring_init(struct trace_ring *tr, void *buf, size_t buf_size,
+                     size_t event_size)
+{
+       tr->tr_buf = buf;
+       tr->tr_buf_sz = buf_size;
+       tr->tr_event_sz_shift = LOG2_UP(event_size);
+       tr->tr_max = (1 << LOG2_DOWN(buf_size / (1 << tr->tr_event_sz_shift)));
+       printd("Initializing TR with %d elements of shift %d (%d)\n",
+              tr->tr_max, tr->tr_event_sz_shift, (1 << tr->tr_event_sz_shift));
+       trace_ring_reset_and_clear(tr);
+}
+
+void trace_ring_reset(struct trace_ring *tr)
+{
+       tr->tr_next = 0;
+}
+
+void trace_ring_reset_and_clear(struct trace_ring *tr)
+{
+       memset(tr->tr_buf, 0, tr->tr_buf_sz);
+       tr->tr_next = 0;
+}
+
+void trace_ring_foreach(struct trace_ring *tr, trace_handler_t tr_func,
+                        void *data)
+{
+       for (int i = 0; i < tr->tr_max; i++)
+               tr_func(tr->tr_buf + (i << tr->tr_event_sz_shift), data);
+}