blob: 1431da7f68cf1112f78ae52dd8f4965aba192849 [file] [log] [blame]
/* 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++);
}