| /* 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. */ |
| |
| #pragma once |
| |
| #include <ros/common.h> |
| #include <assert.h> |
| |
| struct trace_ring { |
| unsigned char *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 */ |
| /* Get next trace ring slot with no wrapping */ |
| static inline void *__get_tr_slot(struct trace_ring *tr, unsigned long ind) |
| { |
| dassert(0 <= ind && ind < tr->tr_max); |
| /* event sizes are rounded up to the nearest power of 2 (sz_shift) */ |
| return (void *) (tr->tr_buf + (ind << tr->tr_event_sz_shift)); |
| } |
| |
| /* Get next trace ring slot with wrapping */ |
| static inline void * |
| __get_tr_slot_overwrite(struct trace_ring *tr, unsigned long slot) |
| { |
| /* tr_max is a power of 2, we're ignoring the upper bits of slot */ |
| slot &= tr->tr_max - 1; |
| return __get_tr_slot(tr, slot); |
| } |
| |
| 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_overwrite(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_overwrite(tr, tr->tr_next++); |
| } |