[PATCH v4 08/37] unwind: introduce queue_t for capturing stacktrace

Masatake YAMATO yamato at redhat.com
Wed Apr 16 06:33:06 UTC 2014


This is the second step for splitting capturing from printing.

`queue' field is added to tcb. captured stacktrace is stored here. The
field is initialized/finalized at unwind_tcb_init/unwind_tcb_fin.

`unwind_capture_stacktrace' is added new API exported from unwind.c.
The function captures the currest stack using `stracktrace_walker',
records it to the field of tcb; and don't print it. Priting is delayed
the next call of `unwind_print_stacktrace'.

`unwind_print_stacktrace' is extended. Now it checks queue field of
given tcb at the start of function. If the function finds captured
stacktrace is recorded in the field, it prints the recorded stacktrace
using `stracktrace_walker'.

Currently `unwind_capture_stacktrace' invocations are just put to
handlers of mmap, munmap, mprotect, and execve. I will prepare
better mechanism to inject `unwind_capture_stacktrace'.

Here is the difference of output with/without patch:

(without patch)
  execve("./test-fork", ["./test-fork"], [/* 56 vars */]) = 0
   > /usr/lib64/ld-2.18.so(check_one_fd.part.0+0x82) [0x11f0]

(with patch)
  execve("./test-fork", ["./test-fork"], [/* 54 vars */]) = 0
   > /usr/lib64/libc-2.18.so(execve+0x7) [0xbcd27]
   > /home/yamato/var/strace/strace(exec_or_die+0x10c) [0x26ac]
   > /home/yamato/var/strace/strace(startup_child+0x346) [0x134f6]
   > /home/yamato/var/strace/strace(init+0x89f) [0x13dff]
   > /home/yamato/var/strace/strace(main+0xa) [0x26ca]
   > /usr/lib64/libc-2.18.so(__libc_start_main+0xf5) [0x21d65]
   > /home/yamato/var/strace/strace(_start+0x29) [0x2799]

In older version output lines of captured elements are built when
printing. In this version they are built when capturing the stack.
As the result dynamic memory allocations are suppressed.
Suggested by Luca Clementi <luca.clementi at gmail.com>.

In older version the combination of snprintf and realloc are
used. In this version they are replaced with asprintf.
Suggested by Dmitry V. Levin" <ldv at altlinux.org>.

As suggested by Dmitry V. Levin <ldv at altlinux.org>, in this patch I remove unnecessary
indirections of calls (wrapper functions).

Signed-off-by: Masatake YAMATO <yamato at redhat.com>
---
 defs.h    |   2 +
 mem.c     |  12 ++++-
 process.c |   8 +++-
 unwind.c  | 154 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 4 files changed, 172 insertions(+), 4 deletions(-)

diff --git a/defs.h b/defs.h
index 177bcd6..b5f30c5 100644
--- a/defs.h
+++ b/defs.h
@@ -430,6 +430,7 @@ struct tcb {
 	struct UPT_info* libunwind_ui;
 	struct mmap_cache_t* mmap_cache;
 	unsigned int mmap_cache_size;
+	struct queue_t* queue;
 #endif
 };
 
@@ -732,6 +733,7 @@ extern void unwind_tcb_init(struct tcb *tcp);
 extern void unwind_tcb_fin(struct tcb *tcp);
 extern void unwind_cache_invalidate(struct tcb* tcp);
 extern void unwind_print_stacktrace(struct tcb* tcp);
+extern void unwind_capture_stacktrace(struct tcb* tcp);
 #endif
 
 /* Strace log generation machinery.
diff --git a/mem.c b/mem.c
index b3d598e..1c36360 100644
--- a/mem.c
+++ b/mem.c
@@ -179,8 +179,10 @@ print_mmap(struct tcb *tcp, long *u_arg, unsigned long long offset)
 {
 	if (entering(tcp)) {
 #ifdef USE_LIBUNWIND
-		if (stack_trace_enabled)
+		if (stack_trace_enabled) {
+			unwind_capture_stacktrace(tcp);
 			unwind_cache_invalidate(tcp);
+		}
 #endif
 
 		/* addr */
@@ -311,6 +313,10 @@ sys_munmap(struct tcb *tcp)
 	if (entering(tcp)) {
 		tprintf("%#lx, %lu",
 			tcp->u_arg[0], tcp->u_arg[1]);
+#ifdef USE_LIBUNWIND
+		if (stack_trace_enabled)
+			unwind_capture_stacktrace(tcp);
+#endif
 	}
 #ifdef USE_LIBUNWIND
 	else {
@@ -328,6 +334,10 @@ sys_mprotect(struct tcb *tcp)
 		tprintf("%#lx, %lu, ",
 			tcp->u_arg[0], tcp->u_arg[1]);
 		printflags(mmap_prot, tcp->u_arg[2], "PROT_???");
+#ifdef USE_LIBUNWIND
+		if (stack_trace_enabled)
+			unwind_capture_stacktrace(tcp);
+#endif
 	}
 #ifdef USE_LIBUNWIND
 	else {
diff --git a/process.c b/process.c
index b5c804c..40cc263 100644
--- a/process.c
+++ b/process.c
@@ -989,11 +989,17 @@ sys_execve(struct tcb *tcp)
 			printargv(tcp, tcp->u_arg[2]);
 			tprints("]");
 		}
+#ifdef USE_LIBUNWIND
+		if (stack_trace_enabled) {
+			unwind_capture_stacktrace(tcp);
+		}
+#endif
 	}
 #ifdef USE_LIBUNWIND
 	else {
-		if (stack_trace_enabled)
+		if (stack_trace_enabled) {
 			unwind_cache_invalidate(tcp);
+		}
 	}
 #endif
 
diff --git a/unwind.c b/unwind.c
index bd30fc2..f9af146 100644
--- a/unwind.c
+++ b/unwind.c
@@ -62,6 +62,19 @@ typedef void (*error_action_fn)(void *data,
 				const char *error,
 				unsigned long true_offset);
 
+/*
+ * Type used in stacktrace capturing
+ */
+struct call_t {
+       struct call_t* next;
+       char *output_line;
+};
+
+struct queue_t {
+       struct call_t *tail;
+       struct call_t *head;
+};
+static void queue_print(struct queue_t *queue);
 
 static unw_addr_space_t libunwind_as;
 
@@ -79,12 +92,23 @@ unwind_tcb_init(struct tcb *tcp)
 	tcp->libunwind_ui = _UPT_create(tcp->pid);
 	if (!tcp->libunwind_ui)
 		die_out_of_memory();
+
+	tcp->queue = malloc(sizeof(*tcp->queue));
+	if (!tcp->queue)
+		die_out_of_memory();
+	tcp->queue->head = NULL;
+	tcp->queue->tail = NULL;
 }
 
 void
 unwind_tcb_fin(struct tcb *tcp)
 {
+	queue_print(tcp->queue);
+	free(tcp->queue);
+	tcp->queue = NULL;
+
 	unwind_cache_invalidate(tcp);
+
 	_UPT_destroy(tcp->libunwind_ui);
 	tcp->libunwind_ui = NULL;
 }
@@ -298,7 +322,7 @@ ret:
 }
 
 /*
- * printing an entry in stack
+ * printing an entry in stack to stream or buffer
  */
 /*
  * we want to keep the format used by backtrace_symbols from the glibc
@@ -355,11 +379,137 @@ print_error_cb(void *dummy,
 	line_ended();
 }
 
+static char *
+sprint_call_or_error(char *binary_filename,
+		     char *symbol_name,
+		     unw_word_t function_off_set,
+		     unsigned long true_offset,
+		     const char *error)
+{
+       char *output_line = NULL;
+       int n;
+
+       if (symbol_name)
+               n = asprintf(&output_line, STACK_ENTRY_SYMBOL_FMT);
+       else if (binary_filename)
+               n = asprintf(&output_line, STACK_ENTRY_NOSYMBOL_FMT);
+       else if (error)
+               n = true_offset
+                       ? asprintf(&output_line, STACK_ENTRY_ERROR_WITH_OFFSET_FMT)
+                       : asprintf(&output_line, STACK_ENTRY_ERROR_FMT);
+       else
+               n = asprintf(&output_line, STACK_ENTRY_BUG_FMT, __FUNCTION__);
+
+       if (n < 0)
+               error_msg_and_die("error in asprintf");
+
+       return output_line;
+}
+
+/*
+ * queue manipulators
+ */
+static void
+queue_put(struct queue_t *queue,
+	  char *binary_filename,
+	  char *symbol_name,
+	  unw_word_t function_off_set,
+	  unsigned long true_offset,
+	  const char *error)
+{
+	struct call_t *call;
+
+	call = malloc(sizeof(*call));
+	if (!call)
+		die_out_of_memory();
+
+	call->output_line = sprint_call_or_error(binary_filename,
+						 symbol_name,
+						 function_off_set,
+						 true_offset,
+						 error);
+	call->next = NULL;
+
+	if (!queue->head) {
+		queue->head = call;
+		queue->tail = call;
+	} else {
+		queue->tail->next = call;
+		queue->tail = call;
+	}
+}
+
+static void
+queue_put_call(void *queue,
+	       char *binary_filename,
+	       char *symbol_name,
+	       unw_word_t function_off_set,
+	       unsigned long true_offset)
+{
+	queue_put(queue,
+		  binary_filename,
+		  symbol_name,
+		  function_off_set,
+		  true_offset,
+		  NULL);
+}
+
+static void
+queue_put_error(void *queue,
+		const char *error,
+		unw_word_t ip)
+{
+	queue_put(queue, NULL, NULL, 0, ip, error);
+}
+
+static void
+queue_print(struct queue_t *queue)
+{
+	struct call_t *call, *tmp;
+
+	queue->tail = NULL;
+	call = queue->head;
+	queue->head = NULL;
+	while (call) {
+		tmp = call;
+		call = call->next;
+
+		tprints(tmp->output_line);
+		line_ended();
+
+		free(tmp->output_line);
+		tmp->output_line = NULL;
+		tmp->next = NULL;
+		free(tmp);
+	}
+}
+
 /*
  * printing stack
  */
 void
 unwind_print_stacktrace(struct tcb* tcp)
 {
-	stacktrace_walk(tcp, print_call_cb, print_error_cb, NULL);
+       if (tcp->queue->head) {
+	       DPRINTF("tcp=%p, queue=%p", "queueprint", tcp, tcp->queue->head);
+	       queue_print(tcp->queue);
+       }
+       else {
+               DPRINTF("tcp=%p, queue=%p", "stackprint", tcp, tcp->queue->head);
+               stacktrace_walk(tcp, print_call_cb, print_error_cb, NULL);
+       }
+}
+
+/*
+ * capturing stack
+ */
+void
+unwind_capture_stacktrace(struct tcb *tcp)
+{
+	if (tcp->queue->head)
+		error_msg_and_die("bug: unprinted entries in queue");
+
+	stacktrace_walk(tcp, queue_put_call, queue_put_error,
+			tcp->queue);
+	DPRINTF("tcp=%p, queue=%p", "captured", tcp, tcp->queue->head);
 }
-- 
1.9.0





More information about the Strace-devel mailing list