From: Liu Yuan <tailai.ly at taobao.com> Signed-off-by: Liu Yuan <tailai.ly at taobao.com> --- sheep/trace/graph.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++ sheep/trace/mcount.S | 16 +++++++++- sheep/trace/trace.h | 17 ++++++++++ 3 files changed, 117 insertions(+), 1 deletions(-) create mode 100644 sheep/trace/graph.c diff --git a/sheep/trace/graph.c b/sheep/trace/graph.c new file mode 100644 index 0000000..01aa4ba --- /dev/null +++ b/sheep/trace/graph.c @@ -0,0 +1,85 @@ +#include <time.h> + +#include "trace.h" +#include "logger.h" +#include "util.h" + +static __thread unsigned 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_ret *trace, unsigned long *ret_func) +{ + ret_stack_index--; + trace->func = trace_ret_stack[ret_stack_index].func; + 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 notrace uint64_t clock_get_time(void) +{ + struct timespec ts; + + clock_gettime(CLOCK_REALTIME, &ts); + return (uint64_t)ts.tv_sec * 1000000000LL + (uint64_t)ts.tv_nsec; +} + +static notrace void default_trace_graph_return(struct trace_graph_ret *trace) +{ + struct ipinfo info; + if (get_ipinfo(trace->func, &info) < 0) + dprintf("0x%lx not found\n", trace->func); + + dprintf("%.*s: %llu (ns)\n", info.fn_namelen, info.fn_name, trace->return_time - trace->entry_time); +} + +static trace_func_graph_ent_t 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_ret trace; + unsigned long ret; + + pop_return_trace(&trace, &ret); + trace.return_time = clock_get_time(); + if (trace_graph_return) + trace_graph_return(&trace); + + return ret; +} + +/* 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) +{ + unsigned long old_addr = *ret_addr; + uint64_t entry_time; + struct trace_graph_ent trace; + + *ret_addr = (unsigned long)trace_return_caller; + entry_time = clock_get_time(); + push_return_trace(old_addr, entry_time, ip, &trace.depth); + trace.func = ip; + if (trace_graph_entry) + trace_graph_entry(&trace); +} + +register_tracer(graph_tracer); diff --git a/sheep/trace/mcount.S b/sheep/trace/mcount.S index 5f1e6b5..69c99ca 100644 --- a/sheep/trace/mcount.S +++ b/sheep/trace/mcount.S @@ -45,7 +45,7 @@ ENTRY(trace_caller) movq %r9, 48(%rsp) movq 0x38(%rsp), %rdi - movq 8(%rbp), %rsi + leaq 8(%rbp), %rsi subq $INSN_SIZE, %rdi .globl trace_call @@ -65,6 +65,20 @@ trace_call: trace_stub: retq +ENTRY(trace_return_caller) + subq $24, %rsp + + movq %rax, (%rsp) + movq %rdx, 8(%rsp) + + call trace_return_call + + movq %rax, %rdi + movq 8(%rsp), %rdx + movq (%rsp), %rax + addq $24, %rsp + jmp *%rdi + .globl NOP5 NOP5: .byte 0x0f,0x1f,0x44,0x00,0x00 # Intel recommended one for 5 bytes nops diff --git a/sheep/trace/trace.h b/sheep/trace/trace.h index 5dcbca5..d2b2c63 100644 --- a/sheep/trace/trace.h +++ b/sheep/trace/trace.h @@ -26,7 +26,22 @@ struct caller { const char *name; }; +struct trace_graph_ent { + unsigned long func; /* Current function */ + int depth; +}; + +struct trace_graph_ret { + unsigned long func; /* Current function */ + unsigned long long entry_time; + unsigned long long return_time; + int depth; +}; + 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_ret *); +typedef void (*trace_func_graph_ent_t)(struct trace_graph_ent *); /* stabs.c */ extern int get_ipinfo(unsigned long ip, struct ipinfo *info); @@ -37,6 +52,8 @@ extern void mcount_call(void); extern void trace_caller(void); extern void trace_call(unsigned long, unsigned long *); extern const unsigned char NOP5[]; +extern void trace_return_caller(void); +extern unsigned long trace_return_call(void); /* trace.c */ extern pthread_cond_t trace_cond; -- 1.7.8.2 |