[sheepdog] [PATCH v3 04/10] trace: add support for stackable tracers

MORITA Kazutaka morita.kazutaka at gmail.com
Fri Aug 9 11:09:01 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/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




More information about the sheepdog mailing list