[sheepdog] [PATCH 04/10] trace: add support for stackable tracers
MORITA Kazutaka
morita.kazutaka at gmail.com
Wed Aug 7 17:49:02 CEST 2013
From: MORITA Kazutaka <morita.kazutaka at lab.ntt.co.jp>
This adds support for calling multiple tracers at entry/exit points of
functions. The benefits of this change is as follows:
- This moves complicated codes of tracers into trace/tracer.c and
simplifies trace/graph.c. It makes it very easy to add another
tracer.
- I'm going to add a thread checker on top of the tracer
infrastracture. Supporting stackable tracers allows us to use both
graph tracer and thread checker at the same time.
Signed-off-by: MORITA Kazutaka <morita.kazutaka at lab.ntt.co.jp>
---
sheep/trace/graph.c | 90 ++++++++----------------------
sheep/trace/mcount.S | 7 +--
sheep/trace/trace.c | 150 ++++++++++++++++++++++++++++++++++++++++----------
sheep/trace/trace.h | 24 +++++---
4 files changed, 160 insertions(+), 111 deletions(-)
diff --git a/sheep/trace/graph.c b/sheep/trace/graph.c
index deaf6a4..e069286 100644
--- a/sheep/trace/graph.c
+++ b/sheep/trace/graph.c
@@ -13,30 +13,7 @@
#include "trace.h"
-static __thread int ret_stack_index;
-static __thread struct trace_ret_stack {
- unsigned long ret;
- unsigned long func;
- unsigned long long entry_time;
-} trace_ret_stack[100]; /* FIXME: consider stack overrun */
-
-static void push_return_trace(unsigned long ret, unsigned long long etime,
- unsigned long func, int *depth)
-{
- trace_ret_stack[ret_stack_index].ret = ret;
- trace_ret_stack[ret_stack_index].func = func;
- trace_ret_stack[ret_stack_index].entry_time = etime;
- *depth = ret_stack_index;
- ret_stack_index++;
-}
-
-static void pop_return_trace(struct trace_graph_item *trace, unsigned long *ret_func)
-{
- ret_stack_index--;
- trace->entry_time = trace_ret_stack[ret_stack_index].entry_time;
- *ret_func = trace_ret_stack[ret_stack_index].ret;
- trace->depth = ret_stack_index;
-}
+static __thread unsigned long long entry_time[SD_MAX_STACK_DEPTH];
static notrace uint64_t clock_get_time(void)
{
@@ -46,59 +23,38 @@ static notrace uint64_t clock_get_time(void)
return (uint64_t)ts.tv_sec * 1000000000LL + (uint64_t)ts.tv_nsec;
}
-static notrace void default_trace_graph_entry(struct trace_graph_item *item)
-{
- trace_buffer_push(sched_getcpu(), item);
-}
-
-static notrace void default_trace_graph_return(struct trace_graph_item *item)
+notrace static void graph_tracer_exit(const struct caller *this_fn, int depth)
{
- trace_buffer_push(sched_getcpu(), item);
-}
-
-static trace_func_graph_ent_t trace_graph_entry = default_trace_graph_entry;
-static trace_func_graph_ret_t trace_graph_return = default_trace_graph_return;
-
-notrace unsigned long trace_return_call(void)
-{
- struct trace_graph_item trace;
- unsigned long ret;
-
- memset(&trace, 0, sizeof(trace));
+ struct trace_graph_item trace = {
+ .depth = depth,
+ .type = TRACE_GRAPH_RETURN,
+ .entry_time = entry_time[depth],
+ .return_time = clock_get_time(),
+ };
get_thread_name(trace.tname);
- trace.return_time = clock_get_time();
- pop_return_trace(&trace, &ret);
- trace.type = TRACE_GRAPH_RETURN;
- trace_graph_return(&trace);
- return ret;
+ trace_buffer_push(sched_getcpu(), &trace);
}
-/* Hook the return address and push it in the trace_ret_stack.
- *
- * ip: the address of the call instruction in the code.
- * ret_addr: the address of return address in the stack frame.
- */
-static notrace void graph_tracer(unsigned long ip, unsigned long *ret_addr)
+notrace static void graph_tracer_enter(const struct caller *this_fn, int depth)
{
- unsigned long old_addr = *ret_addr;
- uint64_t entry_time;
- struct trace_graph_item trace;
- struct caller *cr;
+ struct trace_graph_item trace = {
+ .type = TRACE_GRAPH_ENTRY,
+ .depth = depth,
+ };
- memset(&trace, 0, sizeof(trace));
-
- cr = trace_lookup_ip(ip);
- pstrcpy(trace.fname, sizeof(trace.fname), cr->name);
+ pstrcpy(trace.fname, sizeof(trace.fname), this_fn->name);
get_thread_name(trace.tname);
- *ret_addr = (unsigned long)trace_return_caller;
- entry_time = clock_get_time();
- push_return_trace(old_addr, entry_time, ip, &trace.depth);
- trace.type = TRACE_GRAPH_ENTRY;
+ entry_time[depth] = clock_get_time();
- trace_graph_entry(&trace);
+ trace_buffer_push(sched_getcpu(), &trace);
}
-register_tracer(graph_tracer);
+struct tracer graph_tracer = {
+ .name = "graph",
+
+ .enter = graph_tracer_enter,
+ .exit = graph_tracer_exit,
+};
diff --git a/sheep/trace/mcount.S b/sheep/trace/mcount.S
index a621332..54b1c97 100644
--- a/sheep/trace/mcount.S
+++ b/sheep/trace/mcount.S
@@ -33,9 +33,7 @@ ENTRY(trace_caller)
leaq 8(%rbp), %rsi
subq $INSN_SIZE, %rdi
-.globl trace_call
-trace_call:
- call trace_stub
+ call trace_call
movq 48(%rsp), %r9
movq 40(%rsp), %r8
@@ -45,9 +43,6 @@ trace_call:
movq 8(%rsp), %rcx
movq (%rsp), %rax
addq $0x38, %rsp
-
-.globl trace_stub
-trace_stub:
retq
ENTRY(trace_return_caller)
diff --git a/sheep/trace/trace.c b/sheep/trace/trace.c
index b32ce1d..014741c 100644
--- a/sheep/trace/trace.c
+++ b/sheep/trace/trace.c
@@ -18,8 +18,6 @@
static struct caller *callers;
static size_t nr_callers;
-static trace_func_t trace_func = trace_call;
-
static struct strbuf *buffer;
static pthread_mutex_t *buffer_lock;
static int nr_cpu;
@@ -55,13 +53,6 @@ static notrace void replace_call(unsigned long ip, unsigned long func)
memcpy((void *)ip, new, INSN_SIZE);
}
-static inline void replace_trace_call(unsigned long func)
-{
- unsigned long ip = (unsigned long)trace_call;
-
- replace_call(ip, func);
-}
-
static notrace int make_text_writable(unsigned long ip)
{
unsigned long start = ip & ~(getpagesize() - 1);
@@ -69,7 +60,7 @@ static notrace int make_text_writable(unsigned long ip)
return mprotect((void *)start, getpagesize() + INSN_SIZE, PROT_READ | PROT_EXEC | PROT_WRITE);
}
-notrace struct caller *trace_lookup_ip(unsigned long ip)
+static notrace struct caller *trace_lookup_ip(unsigned long ip)
{
const struct caller key = {
.mcount = ip,
@@ -78,16 +69,6 @@ notrace struct caller *trace_lookup_ip(unsigned long ip)
return xbsearch(&key, callers, nr_callers, caller_cmp);
}
-notrace int register_trace_function(trace_func_t func)
-{
- if (make_text_writable((unsigned long)trace_call) < 0)
- return -1;
-
- replace_trace_call((unsigned long)func);
- trace_func = func;
- return 0;
-}
-
static notrace void patch_all_sites(unsigned long addr)
{
for (int i = 0; i < nr_callers; i++)
@@ -100,29 +81,138 @@ static notrace void nop_all_sites(void)
memcpy((void *)callers[i].mcount, NOP5, INSN_SIZE);
}
-notrace int trace_enable(void)
+static LIST_HEAD(tracers);
+static __thread int ret_stack_index;
+static __thread struct {
+ const struct caller *caller;
+ unsigned long ret;
+} trace_ret_stack[SD_MAX_STACK_DEPTH];
+
+/* the entry point of the function */
+notrace void trace_call(unsigned long ip, unsigned long *ret_addr)
+{
+ struct tracer *tracer;
+ const struct caller *caller;
+
+ assert(ret_stack_index < ARRAY_SIZE(trace_ret_stack));
+
+ caller = trace_lookup_ip(ip);
+
+ list_for_each_entry(tracer, &tracers, list) {
+ if (tracer->enter && !tracer->removed)
+ tracer->enter(caller, ret_stack_index);
+ }
+
+ trace_ret_stack[ret_stack_index].caller = caller;
+ trace_ret_stack[ret_stack_index].ret = *ret_addr;
+ ret_stack_index++;
+ *ret_addr = (unsigned long)trace_return_caller;
+}
+
+/* the exit point of the function */
+notrace unsigned long trace_return_call(void)
+{
+ struct tracer *tracer;
+
+ ret_stack_index--;
+
+ list_for_each_entry(tracer, &tracers, list) {
+ if (tracer->exit && !tracer->removed)
+ tracer->exit(trace_ret_stack[ret_stack_index].caller,
+ ret_stack_index);
+ }
+
+ return trace_ret_stack[ret_stack_index].ret;
+}
+
+notrace static size_t count_tracers(void)
+{
+ size_t nr = 0;
+ struct tracer *t;
+
+ list_for_each_entry(t, &tracers, list) {
+ if (!t->removed)
+ nr++;
+ }
+
+ return nr;
+}
+
+notrace static bool find_registered_tracer(struct tracer *tracer)
+{
+ struct tracer *t;
+
+ list_for_each_entry(t, &tracers, list) {
+ if (t == tracer)
+ return !t->removed;
+ }
+
+ return false;
+}
+
+notrace int trace_register(struct tracer *tracer)
{
- if (trace_func == trace_call) {
- sd_dprintf("no tracer available");
- return SD_RES_NO_TAG;
+ if (find_registered_tracer(tracer)) {
+ sd_eprintf("tracer %s is already registered", tracer->name);
+ return SD_RES_INVALID_PARMS;
}
+ if (tracer->removed)
+ tracer->removed = false;
+ else
+ list_add_tail(&tracer->list, &tracers);
+
+ if (count_tracers() == 1)
+ patch_all_sites((unsigned long)trace_caller);
+
+ return SD_RES_SUCCESS;
+}
+
+notrace int trace_unregister(struct tracer *tracer)
+{
+ if (!find_registered_tracer(tracer)) {
+ sd_eprintf("tracer %s is not registered", tracer->name);
+ return SD_RES_INVALID_PARMS;
+ }
+
+ /*
+ * Calling list_del() is not safe because another thread may be
+ * iterating over the list.
+ */
+ tracer->removed = true;
+
+ if (count_tracers() == 0)
+ nop_all_sites();
+
+ return SD_RES_SUCCESS;
+}
+
+notrace int trace_enable(void)
+{
+ int ret;
+
suspend_worker_threads();
- patch_all_sites((unsigned long)trace_caller);
+ ret = trace_register(&graph_tracer);
resume_worker_threads();
- sd_dprintf("tracer enabled");
- return SD_RES_SUCCESS;
+ if (ret == SD_RES_SUCCESS)
+ sd_dprintf("tracer enabled");
+
+ return ret;
}
notrace int trace_disable(void)
{
+ int ret;
+
suspend_worker_threads();
- nop_all_sites();
+ ret = trace_unregister(&graph_tracer);
resume_worker_threads();
- sd_dprintf("tracer disabled");
- return SD_RES_SUCCESS;
+ if (ret == SD_RES_SUCCESS)
+ sd_dprintf("tracer disabled");
+
+ return ret;
}
notrace int trace_buffer_pop(void *buf, uint32_t len)
diff --git a/sheep/trace/trace.h b/sheep/trace/trace.h
index f65aba8..c4229e6 100644
--- a/sheep/trace/trace.h
+++ b/sheep/trace/trace.h
@@ -13,12 +13,26 @@ struct caller {
const char *name;
};
+struct tracer {
+ const char *name;
+
+ void (*enter)(const struct caller *this_fn, int depth);
+ void (*exit)(const struct caller *this_fn, int depth);
+
+ /* internal use only */
+ bool removed;
+ struct list_head list;
+};
+
+#define SD_MAX_STACK_DEPTH 1024
+
typedef void (*trace_func_t)(unsigned long ip, unsigned long *parent_ip);
/* Type of the callback handlers for function entry and return */
typedef void (*trace_func_graph_ret_t)(struct trace_graph_item *);
typedef void (*trace_func_graph_ent_t)(struct trace_graph_item *);
/* graph.c */
+extern struct tracer graph_tracer;
/* mcount.S */
void mcount(void);
@@ -31,10 +45,8 @@ unsigned long trace_return_call(void);
/* trace.c */
#ifdef HAVE_TRACE
int trace_init(void);
- int register_trace_function(trace_func_t func);
int trace_enable(void);
int trace_disable(void);
- struct caller *trace_lookup_ip(unsigned long ip);
int trace_buffer_pop(void *buf, uint32_t len);
void trace_buffer_push(int cpuid, struct trace_graph_item *item);
@@ -48,12 +60,8 @@ unsigned long trace_return_call(void);
#endif /* HAVE_TRACE */
-#define register_tracer(new) \
-static void __attribute__((constructor)) \
-register_ ## _tracer(void) \
-{ \
- register_trace_function(new); \
-}
+int trace_register(struct tracer *tracer);
+int trace_unregister(struct tracer *tracer);
#endif /* __ASSEMBLY__ */
#endif
--
1.7.9.5
More information about the sheepdog
mailing list