akaros/kern/include/trace.h
<<
>>
Prefs
   1/* Copyright (c) 2013 The Regents of the University of California
   2 * Barret Rhoden <brho@cs.berkeley.edu>
   3 * See LICENSE for details.
   4 *
   5 * Simple ring-buffer tracing for in-kernel events.  The rings have a
   6 * power-of-two number of slots, and each entry size will be rounded up to the
   7 * nearest power of two.  Ring slot acquisition by default is thread-safe, but
   8 * we provide racy helpers if you want a little less overhead.
   9 *
  10 * Users need to provide a contiguous memory buffer and the size of an event
  11 * struct to init.  For example:
  12 *
  13 *      trace_ring_init(my_trace_ring_ptr, my_buf, buf_sz, event_sz);
  14 *
  15 * And then to store a trace, first get a slot, then fill it in:
  16 *
  17 *      struct my_trace_event *my_trace = get_trace_slot(my_trace_ring_ptr);
  18 *      if (my_trace)   // only need to check if we aren't overwriting
  19 *              my_trace = whatever;
  20 *
  21 * Later, to process the traces, provide a function pointer to
  22 * trace_ring_foreach().  This performs the func on all traces in the ring,
  23 * including the unused:
  24 *
  25 *      void trace_handler(void *trace_event, void *data)
  26 *      {
  27 *              whatever();
  28 *      }
  29 *      trace_ring_foreach(my_trace_ring_ptr, trace_handler, optional_blob);
  30 *
  31 * Rings can be racy or not, and can overwrite entries or not.  If you are not
  32 * overwriting, the ring will stop giving you slots.  You need to reset the ring
  33 * to get fresh slots again.  If you are overwriting, you don't need to check
  34 * the return value of get_trace_slot_overwrite().
  35 *
  36 * Given there is overwrite, tr_next doesn't really tell us which ones were
  37 * used.  So your handler should check for a flag or something.  Timestamps
  38 * might help make sense of the data in these cases too. */
  39
  40#pragma once
  41
  42#include <ros/common.h>
  43#include <assert.h>
  44
  45struct trace_ring {
  46        unsigned char                   *tr_buf;
  47        size_t                          tr_buf_sz;
  48        unsigned int                    tr_event_sz_shift;
  49        unsigned int                    tr_max;
  50        unsigned long                   tr_next;
  51};
  52
  53typedef void (*trace_handler_t)(void *event, void *blob);
  54
  55static inline void *get_trace_slot(struct trace_ring *tr);
  56static inline void *get_trace_slot_overwrite(struct trace_ring *tr);
  57static inline void *get_trace_slot_racy(struct trace_ring *tr);
  58static inline void *get_trace_slot_overwrite_racy(struct trace_ring *tr);
  59
  60void trace_ring_init(struct trace_ring *tr, void *buf, size_t buf_size,
  61                     size_t event_size);
  62void trace_ring_reset(struct trace_ring *tr);
  63void trace_ring_reset_and_clear(struct trace_ring *tr);
  64void trace_ring_foreach(struct trace_ring *tr, trace_handler_t tr_func,
  65                        void *data);
  66
  67/* Inlined funcs, declared above */
  68
  69/* Helper */
  70/* Get next trace ring slot with no wrapping */
  71static inline void *__get_tr_slot(struct trace_ring *tr, unsigned long ind)
  72{
  73        dassert(0 <= ind && ind < tr->tr_max);
  74        /* event sizes are rounded up to the nearest power of 2 (sz_shift) */
  75        return (void *) (tr->tr_buf + (ind << tr->tr_event_sz_shift));
  76}
  77
  78/* Get next trace ring slot with wrapping */
  79static inline void *
  80__get_tr_slot_overwrite(struct trace_ring *tr, unsigned long slot)
  81{
  82        /* tr_max is a power of 2, we're ignoring the upper bits of slot */
  83        slot &= tr->tr_max - 1;
  84        return __get_tr_slot(tr, slot);
  85}
  86
  87static inline void *get_trace_slot(struct trace_ring *tr)
  88{
  89        /* Using syncs, instead of atomics, since we access tr_next as both
  90         * atomic and 'normal'. */
  91        unsigned long my_slot = __sync_fetch_and_add(&tr->tr_next, 1);
  92
  93        /* We can briefly go over, so long as we subtract back down to where we
  94         * were before.  This will work so long as we don't have ~2^64
  95         * threads... */
  96        if (my_slot >= tr->tr_max) {
  97                __sync_fetch_and_add(&tr->tr_next, -1);
  98                return 0;
  99        }
 100        return __get_tr_slot(tr, my_slot);
 101}
 102
 103static inline void *get_trace_slot_overwrite(struct trace_ring *tr)
 104{
 105        return __get_tr_slot_overwrite(tr, __sync_fetch_and_add(&tr->tr_next, 1));
 106}
 107
 108static inline void *get_trace_slot_racy(struct trace_ring *tr)
 109{
 110        unsigned long my_slot = tr->tr_next;
 111
 112        if (my_slot >= tr->tr_max)
 113                return 0;
 114        tr->tr_next++;
 115        return __get_tr_slot(tr, my_slot);
 116}
 117
 118static inline void *get_trace_slot_overwrite_racy(struct trace_ring *tr)
 119{
 120        return __get_tr_slot_overwrite(tr, tr->tr_next++);
 121}
 122