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/ops.c | 4 +- sheep/trace/graph.c | 92 +++++++++------------------------ sheep/trace/mcount.S | 9 +--- sheep/trace/trace.c | 137 +++++++++++++++++++++++++++++++++++++++----------- sheep/trace/trace.h | 39 +++++++------- 5 files changed, 158 insertions(+), 123 deletions(-) diff --git a/sheep/ops.c b/sheep/ops.c index f0d89df..61ce782 100644 --- a/sheep/ops.c +++ b/sheep/ops.c @@ -779,9 +779,9 @@ static int local_trace_ops(const struct sd_req *req, struct sd_rsp *rsp, void *d int enable = req->data_length, ret; if (enable) - ret = trace_enable(); + ret = trace_enable("graph"); else - ret = trace_disable(); + ret = trace_disable("graph"); return ret; } diff --git a/sheep/trace/graph.c b/sheep/trace/graph.c index deaf6a4..2dafd22 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,40 @@ 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) +static notrace void graph_tracer_exit(const struct caller *this_fn, int depth) { - trace_buffer_push(sched_getcpu(), item); -} - -static notrace void default_trace_graph_return(struct trace_graph_item *item) -{ - 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) +static notrace 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; - - memset(&trace, 0, sizeof(trace)); + struct trace_graph_item trace = { + .type = TRACE_GRAPH_ENTRY, + .depth = depth, + }; - 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); +static struct tracer graph_tracer = { + .name = "graph", + + .enter = graph_tracer_enter, + .exit = graph_tracer_exit, +}; + +tracer_register(graph_tracer); diff --git a/sheep/trace/mcount.S b/sheep/trace/mcount.S index a621332..7499ac2 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_function_enter 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) @@ -56,7 +51,7 @@ ENTRY(trace_return_caller) movq %rax, (%rsp) movq %rdx, 8(%rsp) - call trace_return_call + call trace_function_exit movq %rax, %rdi movq 8(%rsp), %rdx diff --git a/sheep/trace/trace.c b/sheep/trace/trace.c index 17a3aa3..32e5a81 100644 --- a/sheep/trace/trace.c +++ b/sheep/trace/trace.c @@ -15,11 +15,16 @@ #include "trace.h" +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]; + 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 +60,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 +67,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,14 +76,9 @@ 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) +notrace void regist_tracer(struct tracer *tracer) { - if (make_text_writable((unsigned long)trace_call) < 0) - return -1; - - replace_trace_call((unsigned long)func); - trace_func = func; - return 0; + list_add_tail(&tracer->list, &tracers); } static notrace void patch_all_sites(unsigned long addr) @@ -100,27 +93,111 @@ static notrace void nop_all_sites(void) memcpy((void *)callers[i].mcount, NOP5, INSN_SIZE); } -notrace int trace_enable(void) +/* the entry point of the function */ +notrace void trace_function_enter(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 != NULL && uatomic_is_true(&tracer->enabled)) + 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_function_exit(void) +{ + struct tracer *tracer; + + ret_stack_index--; + + list_for_each_entry(tracer, &tracers, list) { + if (tracer->exit != NULL && uatomic_is_true(&tracer->enabled)) + tracer->exit(trace_ret_stack[ret_stack_index].caller, + ret_stack_index); + } + + return trace_ret_stack[ret_stack_index].ret; +} + +static notrace size_t count_enabled_tracers(void) { - if (trace_func == trace_call) { - sd_dprintf("no tracer available"); - return SD_RES_NO_TAG; + size_t nr = 0; + struct tracer *t; + + list_for_each_entry(t, &tracers, list) { + if (uatomic_is_true(&t->enabled)) + nr++; } - suspend_worker_threads(); - patch_all_sites((unsigned long)trace_caller); - resume_worker_threads(); - sd_dprintf("tracer enabled"); + return nr; +} + +static notrace struct tracer *find_tracer(const char *name) +{ + struct tracer *t; + + list_for_each_entry(t, &tracers, list) { + if (strcmp(t->name, name) == 0) + return t; + } + + return NULL; +} + +notrace int trace_enable(const char *name) +{ + struct tracer *tracer = find_tracer(name); + + if (tracer == NULL) { + sd_dprintf("no such tracer, %s", name); + return SD_RES_NO_SUPPORT; + } else if (uatomic_is_true(&tracer->enabled)) { + sd_dprintf("tracer %s is already enabled", name); + return SD_RES_INVALID_PARMS; + } + + uatomic_set_true(&tracer->enabled); + + if (count_enabled_tracers() == 1) { + suspend_worker_threads(); + patch_all_sites((unsigned long)trace_caller); + resume_worker_threads(); + } + sd_dprintf("tracer %s enabled", tracer->name); return SD_RES_SUCCESS; } -notrace int trace_disable(void) +notrace int trace_disable(const char *name) { - suspend_worker_threads(); - nop_all_sites(); - resume_worker_threads(); - sd_dprintf("tracer disabled"); + struct tracer *tracer = find_tracer(name); + + if (tracer == NULL) { + sd_dprintf("no such tracer, %s", name); + return SD_RES_NO_SUPPORT; + } else if (!uatomic_is_true(&tracer->enabled)) { + sd_dprintf("tracer %s is not enabled", name); + return SD_RES_INVALID_PARMS; + } + + uatomic_set_false(&tracer->enabled); + if (count_enabled_tracers() == 0) { + suspend_worker_threads(); + nop_all_sites(); + resume_worker_threads(); + } + sd_dprintf("tracer %s disabled", tracer->name); return SD_RES_SUCCESS; } diff --git a/sheep/trace/trace.h b/sheep/trace/trace.h index f65aba8..d1bd8b4 100644 --- a/sheep/trace/trace.h +++ b/sheep/trace/trace.h @@ -13,46 +13,51 @@ struct caller { const char *name; }; -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 *); +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 */ + uatomic_bool enabled; + struct list_head list; +}; -/* graph.c */ +#define SD_MAX_STACK_DEPTH 1024 /* mcount.S */ void mcount(void); void trace_caller(void); -void trace_call(unsigned long, unsigned long *); extern const unsigned char NOP5[]; void trace_return_caller(void); -unsigned long trace_return_call(void); +void trace_function_enter(unsigned long, unsigned long *); +unsigned long trace_function_exit(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); + void regist_tracer(struct tracer *tracer); + int trace_enable(const char *name); + int trace_disable(const char *name); int trace_buffer_pop(void *buf, uint32_t len); void trace_buffer_push(int cpuid, struct trace_graph_item *item); #else static inline int trace_init(void) { return 0; } - static inline int trace_enable(void) { return 0; } - static inline int trace_disable(void) { return 0; } + static inline int trace_enable(const char *name) { return 0; } + static inline int trace_disable(const char *name) { return 0; } static inline int trace_buffer_pop(void *buf, uint32_t len) { return 0; } static inline void trace_buffer_push( int cpuid, struct trace_graph_item *item) { return; } #endif /* HAVE_TRACE */ -#define register_tracer(new) \ +#define tracer_register(tracer) \ static void __attribute__((constructor)) \ -register_ ## _tracer(void) \ -{ \ - register_trace_function(new); \ +regist_ ##tracer(void) \ +{ \ + regist_tracer(&tracer); \ } #endif /* __ASSEMBLY__ */ -- 1.7.9.5 |