blob: b854f0410857aa4356b73ad94bc65847f05da248 [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 smp safe, per cpu or racy, 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(). Per cpu
* rings are interrupt safe.
*
* 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>
#include <arch/arch.h>
#include <assert.h>
struct trace_ring {
uint8_t *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);
}
/* Interrupt safe modification of tr_next */
static inline unsigned int
__get_next_percpu_and_add(struct trace_ring *tr, int8_t n)
{
int8_t irq_state = 0;
disable_irqsave(&irq_state);
unsigned int ret = tr->tr_next;
tr->tr_next += n;
enable_irqsave(&irq_state);
return ret;
}
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_percpu(struct trace_ring *tr)
{
unsigned long my_slot = __get_next_percpu_and_add(tr, 1);
if (my_slot >= tr->tr_max) {
/* See comment in get_trace_slot. */
__get_next_percpu_and_add(tr, -1);
return 0;
}
return __get_tr_slot(tr, my_slot);
}
static inline void *get_trace_slot_overwrite_percpu(struct trace_ring *tr)
{
return __get_tr_slot_overwrite(tr, __get_next_percpu_and_add(tr, 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++);
}
#endif /* ROS_INC_TRACE_H */