[PATCH 01/25] ldv/unwind: improve stacktrace feature(v3)

Masatake YAMATO yamato at redhat.com
Sun Nov 10 06:41:57 UTC 2013


Based on the discussion on strace-devel, I improved following points:

* use unwind_ as prefix for functions exported from unwind.c(v1)

* capture stacktrace of (_exit, exit_group and execve) at entering stage(v1, v3)

    About the most of system calls, stacktrace is captured and printed at
    the same time, the system call exiting.  However, this procedure makes
    unwanted result for some system calls.

    About execve, which is marked with STACKTRACE_CAPTURE_IN_ENTERING(CE),
    stacktrace is captured at the system call entering, and is printed at
    the exiting. By capturing in the system call entering, user can know
    the process context of execve being called(v1).

    It is the same for _exit and exit_group. However, the timing of
    printing is different from execve: printing when associated tcb
    object(tcp) is dropped because there is no exiting stage for them(v3).

    struct queue_t and some manipulators are introduced for
    capturing-at-entering and printing-at-exiting.

* control the maps cache with STACKTRACE_MAKE_CACHE_INVALID(CI) marker(v1)

    Currently stacktrace feature of strace needs the information of the
    memory mapping. Some of system calls like mmap change the memory
    mapping of the target process. Therefore unwind.c must track the
    modification of memory mapping.

    In older version the modification is tracked in print functions of
    each system call. In this patch STACKTRACE_MAKE_CACHE_INVALID(CI)
    marker is introduced. In stead of handling in print functions, the
    modification is tracked in system call entering only if current
    system call is marked with CI.

    If new system call modifying the memory mapping is added, just
    mark it with CI.

* mark CI on brk, mremap, shmat, shmdt, and remap_file_pages(v1)

* separate arch dependent CI/CE marking code to different patches(v2)

* simplify call_t data structure(v3)

    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>.

* handle multiple threads(v3)

    Rebuild caches for mmaps of all target threads if a thread invokes
    CI makred syscalls. This is inefficient; whether an address space
    is shared or not between the threads is not considered.

TODO:

* move the memory mapping cache to libuwind and use API for it

    See http://lists.nongnu.org/archive/html/libunwind-devel/2013-10/msg00001.html

* optimize mmap cache management

  When a CI makred syscall is invoked, update mmap caches only
  affected by the invocation.

* handle static link target

  Target compiled with gcc -static, stacktrace is not
  shown well through gdb can show it. Maybe libunwind
  side issue.

* add test cases

Signed-off-by: Masatake YAMATO <yamato at redhat.com>
---
 defs.h    |  16 ++-
 mem.c     |  17 ---
 process.c |  10 --
 strace.c  |  15 ++-
 syscall.c |  27 +++-
 unwind.c  | 424 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
 6 files changed, 420 insertions(+), 89 deletions(-)

diff --git a/defs.h b/defs.h
index 7f76008..ed1e53f 100644
--- a/defs.h
+++ b/defs.h
@@ -445,6 +445,8 @@ struct tcb {
 	struct UPT_info* libunwind_ui;
 	struct mmap_cache_t* mmap_cache;
 	unsigned int mmap_cache_size;
+	unsigned int mmap_cache_generation;
+	struct queue_t* queue;
 #endif
 };
 
@@ -557,6 +559,8 @@ extern const struct xlat whence_codes[];
 #define TRACE_DESC	040	/* Trace file descriptor-related syscalls. */
 #define TRACE_MEMORY	0100	/* Trace memory mapping-related syscalls. */
 #define SYSCALL_NEVER_FAILS	0200	/* Syscall is always successful. */
+#define STACKTRACE_MAKE_CACHE_INVALID   0400	/* Trigger proc/maps cache updating */
+#define STACKTRACE_CAPTURE_IN_ENTERING  01000	/* Capture stacktrace in "entering" stage */
 
 typedef enum {
 	CFLAG_NONE = 0,
@@ -737,12 +741,12 @@ extern void tv_mul(struct timeval *, struct timeval *, int);
 extern void tv_div(struct timeval *, struct timeval *, int);
 
 #ifdef USE_LIBUNWIND
-extern void init_unwind_addr_space(void);
-extern void init_libunwind_ui(struct tcb *tcp);
-extern void free_libunwind_ui(struct tcb *tcp);
-extern void alloc_mmap_cache(struct tcb* tcp);
-extern void delete_mmap_cache(struct tcb* tcp);
-extern void print_stacktrace(struct tcb* tcp);
+extern void unwind_init(void);
+extern void unwind_tcb_init(struct tcb *);
+extern void unwind_tcb_fin(struct tcb *);
+extern void unwind_cache_invalidate(struct tcb * );
+extern void unwind_stacktrace_capture(struct tcb *);
+extern void unwind_stacktrace_print(struct tcb *);
 #endif
 
 /* Strace log generation machinery.
diff --git a/mem.c b/mem.c
index ca6caa1..ef273c7 100644
--- a/mem.c
+++ b/mem.c
@@ -175,11 +175,6 @@ static int
 print_mmap(struct tcb *tcp, long *u_arg, unsigned long long offset)
 {
 	if (entering(tcp)) {
-#ifdef USE_LIBUNWIND
-		if (stack_trace_enabled)
-			delete_mmap_cache(tcp);
-#endif
-
 		/* addr */
 		if (!u_arg[0])
 			tprints("NULL, ");
@@ -309,12 +304,6 @@ sys_munmap(struct tcb *tcp)
 		tprintf("%#lx, %lu",
 			tcp->u_arg[0], tcp->u_arg[1]);
 	}
-#ifdef USE_LIBUNWIND
-	else {
-		if (stack_trace_enabled)
-			delete_mmap_cache(tcp);
-	}
-#endif
 	return 0;
 }
 
@@ -326,12 +315,6 @@ sys_mprotect(struct tcb *tcp)
 			tcp->u_arg[0], tcp->u_arg[1]);
 		printflags(mmap_prot, tcp->u_arg[2], "PROT_???");
 	}
-#ifdef USE_LIBUNWIND
-	else {
-		if (stack_trace_enabled)
-			delete_mmap_cache(tcp);
-	}
-#endif
 	return 0;
 }
 
diff --git a/process.c b/process.c
index 22bb5b6..ab3511c 100644
--- a/process.c
+++ b/process.c
@@ -992,12 +992,6 @@ sys_execve(struct tcb *tcp)
 			tprints("]");
 		}
 	}
-#ifdef USE_LIBUNWIND
-	else {
-		if (stack_trace_enabled)
-			delete_mmap_cache(tcp);
-	}
-#endif
 
 	return 0;
 }
@@ -1200,10 +1194,6 @@ sys_waitid(struct tcb *tcp)
 		tprintf(", %ld, ", tcp->u_arg[1]);
 	}
 	else {
-#ifdef USE_LIBUNWIND
-		if (stack_trace_enabled)
-			delete_mmap_cache(tcp);
-#endif
 		/* siginfo */
 		printsiginfo_at(tcp, tcp->u_arg[2]);
 		/* options */
diff --git a/strace.c b/strace.c
index 7775b42..da3af64 100644
--- a/strace.c
+++ b/strace.c
@@ -696,7 +696,7 @@ alloctcb(int pid)
 
 #ifdef USE_LIBUNWIND
 			if (stack_trace_enabled)
-				init_libunwind_ui(tcp);
+			        unwind_tcb_init(tcp);
 #endif
 
 			nprocs++;
@@ -718,6 +718,11 @@ droptcb(struct tcb *tcp)
 	if (debug_flag)
 		fprintf(stderr, "dropped tcb for pid %d, %d remain\n", tcp->pid, nprocs);
 
+#ifdef USE_LIBUNWIND
+	if (stack_trace_enabled)
+	        unwind_tcb_fin(tcp);
+#endif
+
 	if (tcp->outf) {
 		if (followfork >= 2) {
 			if (tcp->curcol != 0)
@@ -735,12 +740,6 @@ droptcb(struct tcb *tcp)
 	if (printing_tcp == tcp)
 		printing_tcp = NULL;
 
-#ifdef USE_LIBUNWIND
-	if (stack_trace_enabled) {
-		delete_mmap_cache(tcp);
-		free_libunwind_ui(tcp);
-	}
-#endif
 	memset(tcp, 0, sizeof(*tcp));
 }
 
@@ -1805,7 +1804,7 @@ init(int argc, char *argv[])
 
 #ifdef USE_LIBUNWIND
 	if (stack_trace_enabled)
-		init_unwind_addr_space();
+		unwind_init();
 #endif
 
 	if (!followfork)
diff --git a/syscall.c b/syscall.c
index b01c023..8c4c738 100644
--- a/syscall.c
+++ b/syscall.c
@@ -97,6 +97,8 @@
 #define TM TRACE_MEMORY
 #define NF SYSCALL_NEVER_FAILS
 #define MA MAX_ARGS
+#define CI STACKTRACE_MAKE_CACHE_INVALID
+#define CE STACKTRACE_CAPTURE_IN_ENTERING
 
 const struct_sysent sysent0[] = {
 #include "syscallent.h"
@@ -124,6 +126,8 @@ static const struct_sysent sysent2[] = {
 #undef TM
 #undef NF
 #undef MA
+#undef CI
+#undef CE
 
 /*
  * `ioctlent.h' may be generated from `ioctlent.raw' by the auxiliary
@@ -2025,12 +2029,31 @@ trace_syscall_entering(struct tcb *tcp)
 	 || (tracing_paths && !pathtrace_match(tcp))
 	) {
 		tcp->flags |= TCB_INSYSCALL | TCB_FILTERED;
+#ifdef USE_LIBUNWIND
+		if (stack_trace_enabled)
+			if (tcp->s_ent->sys_flags & STACKTRACE_MAKE_CACHE_INVALID)
+				unwind_cache_invalidate(tcp);
+#endif
 		return 0;
 	}
 
 	tcp->flags &= ~TCB_FILTERED;
 
-	if (cflag == CFLAG_ONLY_STATS || hide_log_until_execve) {
+	if (cflag == CFLAG_ONLY_STATS) {
+		res = 0;
+		goto ret;
+	}
+
+#ifdef USE_LIBUNWIND
+	if (stack_trace_enabled) {
+		if (tcp->s_ent->sys_flags & STACKTRACE_CAPTURE_IN_ENTERING)
+			unwind_stacktrace_capture(tcp);
+		if (tcp->s_ent->sys_flags & STACKTRACE_MAKE_CACHE_INVALID)
+			unwind_cache_invalidate(tcp);
+	}
+#endif
+
+	if (hide_log_until_execve) {
 		res = 0;
 		goto ret;
 	}
@@ -2708,7 +2731,7 @@ trace_syscall_exiting(struct tcb *tcp)
 
 #ifdef USE_LIBUNWIND
 	if (stack_trace_enabled)
-		print_stacktrace(tcp);
+		unwind_stacktrace_print(tcp);
 #endif
 
  ret:
diff --git a/unwind.c b/unwind.c
index 0b8f0b0..4cb6325 100644
--- a/unwind.c
+++ b/unwind.c
@@ -28,10 +28,13 @@
 #include <limits.h>
 #include <libunwind-ptrace.h>
 
+#define DPRINTF(F, A, ...) if (debug_flag) fprintf(stderr, "[unwind:" A " {" F "}]\n", __VA_ARGS__);
+
 /*
  * Кeep a sorted array of cache entries,
  * so that we can binary search through it.
  */
+static unsigned int mmap_cache_generation;
 struct mmap_cache_t {
 	/**
 	 * example entry:
@@ -45,12 +48,33 @@ struct mmap_cache_t {
 	unsigned long start_addr;
 	unsigned long end_addr;
 	unsigned long mmap_offset;
-	char* binary_filename;
+	char *binary_filename;
+};
+
+/*
+ * Keep a captured stracktrace.
+ */
+struct call_t {
+	struct call_t* next;
+	char *output_line;
 };
 
+struct queue_t {
+	struct call_t *tail;
+	struct call_t *head;
+};
+typedef void (*call_action_fn)(void *data,
+			       char *binary_filename,
+			       char *symbol_name,
+			       unw_word_t function_off_set,
+			       unsigned long true_offset);
+typedef void (*error_action_fn)(void *data,
+				const char *error,
+				unsigned long true_offset);
+
 static unw_addr_space_t libunwind_as;
 
-void
+static void
 init_unwind_addr_space(void)
 {
 	libunwind_as = unw_create_addr_space(&_UPT_accessors, 0);
@@ -58,7 +82,7 @@ init_unwind_addr_space(void)
 		error_msg_and_die("failed to create address space for stack tracing");
 }
 
-void
+static void
 init_libunwind_ui(struct tcb *tcp)
 {
 	tcp->libunwind_ui = _UPT_create(tcp->pid);
@@ -66,7 +90,7 @@ init_libunwind_ui(struct tcb *tcp)
 		die_out_of_memory();
 }
 
-void
+static void
 free_libunwind_ui(struct tcb *tcp)
 {
 	_UPT_destroy(tcp->libunwind_ui);
@@ -78,8 +102,28 @@ free_libunwind_ui(struct tcb *tcp)
  *
  * The cache must be refreshed after some syscall: mmap, mprotect, munmap, execve
  */
-void
-alloc_mmap_cache(struct tcb* tcp)
+/* deleting the cache */
+static void
+delete_mmap_cache(struct tcb *tcp, const char* caller)
+{
+	unsigned int i;
+
+	DPRINTF("gen=%u, GEN=%u, tcp=%p, cache=%p, at=%s", "delete",
+		tcp->mmap_cache_generation,
+		mmap_cache_generation,
+		tcp, tcp->mmap_cache, caller);
+
+	for (i = 0; i < tcp->mmap_cache_size; i++) {
+		free(tcp->mmap_cache[i].binary_filename);
+		tcp->mmap_cache[i].binary_filename = NULL;
+	}
+	free(tcp->mmap_cache);
+	tcp->mmap_cache = NULL;
+	tcp->mmap_cache_size = 0;
+}
+
+static void
+build_mmap_cache(struct tcb *tcp)
 {
 	unsigned long start_addr, end_addr, mmap_offset;
 	char filename[sizeof ("/proc/0123456789/maps")];
@@ -109,13 +153,11 @@ alloc_mmap_cache(struct tcb* tcp)
 		       &start_addr, &end_addr, &mmap_offset, binary_path);
 
 		/* ignore special 'fake files' like "[vdso]", "[heap]", "[stack]", */
-		if (binary_path[0] == '[') {
+		if (binary_path[0] == '[')
 			continue;
-		}
 
-		if (binary_path[0] == '\0') {
+		if (binary_path[0] == '\0')
 			continue;
-		}
 
 		if (end_addr < start_addr)
 			perror_msg_and_die("%s: unrecognized maps file format",
@@ -152,25 +194,216 @@ alloc_mmap_cache(struct tcb* tcp)
 	}
 	fclose(fp);
 	tcp->mmap_cache = cache_head;
+	tcp->mmap_cache_generation = mmap_cache_generation;
+
+	DPRINTF("gen=%u, GEN=%u, tcp=%p, cache=%p", "build",
+		tcp->mmap_cache_generation,
+		mmap_cache_generation,
+		tcp, tcp->mmap_cache);
 }
 
-/* deleting the cache */
-void
-delete_mmap_cache(struct tcb* tcp)
+static bool
+is_mmap_cache_available(struct tcb *tcp, const char *caller)
 {
-	unsigned int i;
-	for (i = 0; i < tcp->mmap_cache_size; i++) {
-		free(tcp->mmap_cache[i].binary_filename);
-		tcp->mmap_cache[i].binary_filename = NULL;
+	if ((tcp->mmap_cache_generation != mmap_cache_generation)
+	    && tcp->mmap_cache)
+		delete_mmap_cache(tcp, caller);
+
+	if (!tcp->mmap_cache)
+		build_mmap_cache(tcp);
+
+	if (!tcp->mmap_cache || !tcp->mmap_cache_size)
+		return false;
+	else
+		return true;
+}
+
+
+/*
+ * stack entry formatter
+ */
+#define STACK_ENTRY_SYMBOL_FMT			\
+	" > %s(%s+0x%lx) [0x%lx]\n",		\
+	binary_filename,			\
+	symbol_name,				\
+	function_off_set,			\
+	true_offset
+#define STACK_ENTRY_NOSYMBOL_FMT		\
+	" > %s() [0x%lx]\n",			\
+	binary_filename, true_offset
+#define STACK_ENTRY_BUG_FMT			\
+	" > BUG IN %s\n"
+#define STACK_ENTRY_ERROR_WITH_OFFSET_FMT	\
+	" > %s [0x%lx]\n", error, true_offset
+#define STACK_ENTRY_ERROR_FMT			\
+	" > %s [0x%lx]\n", error, true_offset
+
+#define OUTPUT_LINE_BUFLEN 128
+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)
+{
+	int n;
+	char *output_line;
+	unsigned int buflen;
+
+	n = (OUTPUT_LINE_BUFLEN - 1);
+	output_line = NULL;
+
+	do {
+		buflen = n + 1;
+		output_line = realloc(output_line, buflen);
+		if (!output_line)
+			die_out_of_memory();
+
+		if (symbol_name)
+			n = snprintf(output_line, buflen, STACK_ENTRY_SYMBOL_FMT);
+		else if (binary_filename)
+			n = snprintf(output_line, buflen, STACK_ENTRY_NOSYMBOL_FMT);
+		else if (error)
+			n = true_offset
+				? snprintf(output_line, buflen, STACK_ENTRY_ERROR_WITH_OFFSET_FMT)
+				: snprintf(output_line, buflen, STACK_ENTRY_ERROR_FMT);
+		else
+			n = snprintf(output_line, buflen, STACK_ENTRY_BUG_FMT, __FUNCTION__);
+
+		if (n < 0)
+			error_msg_and_die("error in snrpintf");
+	} while(n >= buflen);
+
+	return output_line;
+}
+
+static void
+print_call(char *binary_filename,
+	   char *symbol_name,
+	   unw_word_t function_off_set,
+	   unsigned long true_offset)
+{
+	if (symbol_name)
+		tprintf(STACK_ENTRY_SYMBOL_FMT);
+	else if (binary_filename)
+		tprintf(STACK_ENTRY_NOSYMBOL_FMT);
+	else
+		tprintf(STACK_ENTRY_BUG_FMT, __FUNCTION__);
+
+	line_ended();
+}
+
+static void
+print_error(const char *error,
+	    unsigned long true_offset)
+{
+	if (true_offset)
+		tprintf(STACK_ENTRY_ERROR_WITH_OFFSET_FMT);
+	else
+		tprintf(STACK_ENTRY_ERROR_FMT);
+
+	line_ended();
+}
+
+/*
+ * Queue releated functions
+ */
+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;
 	}
-	free(tcp->mmap_cache);
-	tcp->mmap_cache = NULL;
-	tcp->mmap_cache_size = 0;
 }
 
-/* use libunwind to unwind the stack and print a backtrace */
-void
-print_stacktrace(struct tcb* tcp)
+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_free(struct queue_t *queue,
+	  void (* callback)(const char *output_line))
+{
+	struct call_t *call, *tmp;
+
+	queue->tail = NULL;
+	call = queue->head;
+	queue->head = NULL;
+	while (call) {
+		tmp = call;
+		call = call->next;
+
+		if (callback)
+			callback(tmp->output_line);
+
+		free(tmp->output_line);
+		tmp->output_line = NULL;
+		tmp->next = NULL;
+		free(tmp);
+	}
+}
+
+static void
+queue_printline(const char *output_line)
+{
+	tprints(output_line);
+	line_ended();
+}
+
+static void
+queue_print_and_free(struct tcb *tcp)
+{
+
+	DPRINTF("tcp=%p, queue=%p", "queueprint", tcp, tcp->queue->head);
+	queue_free(tcp->queue, queue_printline);
+}
+
+static void
+stacktrace_walk(struct tcb *tcp,
+		call_action_fn call_action,
+		error_action_fn error_action,
+		void *data)
 {
 	unw_word_t ip;
 	unw_cursor_t cursor;
@@ -179,14 +412,10 @@ print_stacktrace(struct tcb* tcp)
 	/* these are used for the binary search through the mmap_chace */
 	unsigned int lower, upper, mid;
 	size_t symbol_name_size = 40;
-	char * symbol_name;
-	struct mmap_cache_t* cur_mmap_cache;
+	char *symbol_name;
+	struct mmap_cache_t *cur_mmap_cache;
 	unsigned long true_offset;
 
-	if (!tcp->mmap_cache)
-		alloc_mmap_cache(tcp);
-	if (!tcp->mmap_cache || !tcp->mmap_cache_size)
-		return;
 
 	symbol_name = malloc(symbol_name_size);
 	if (!symbol_name)
@@ -236,25 +465,24 @@ print_stacktrace(struct tcb* tcp)
 					 * /lib64/libc.so.6(__libc_start_main+0xed) [0x7fa2f8a5976d]
 					 * ./a.out() [0x400569]
 					 */
-					tprintf(" > %s(%s+0x%lx) [0x%lx]\n",
-						cur_mmap_cache->binary_filename,
-						symbol_name, function_off_set, true_offset);
+					call_action(data,
+						    cur_mmap_cache->binary_filename,
+						    symbol_name,
+						    function_off_set,
+						    true_offset);
 				} else {
-					tprintf(" > %s() [0x%lx]\n",
-						cur_mmap_cache->binary_filename, true_offset);
+					call_action(data,
+						    cur_mmap_cache->binary_filename,
+						    symbol_name,
+						    0,
+						    true_offset);
 				}
-				line_ended();
 				break; /* stack frame printed */
 			}
 			else if (mid == 0) {
-				/*
-				 * there is a bug in libunwind >= 1.0
-				 * after a set_tid_address syscall
-				 * unw_get_reg returns IP == 0
-				 */
 				if(ip)
-					tprintf(" > backtracing_error\n");
-				line_ended();
+					error_action(data,
+						     "backtracing_error", 0);
 				goto ret;
 			}
 			else if (ip < cur_mmap_cache->start_addr)
@@ -264,19 +492,123 @@ print_stacktrace(struct tcb* tcp)
 
 		}
 		if (lower > upper) {
-			tprintf(" > backtracing_error [0x%lx]\n", ip);
-			line_ended();
+			error_action(data,
+				     "backtracing_error", ip);
 			goto ret;
 		}
 
 		ret_val = unw_step(&cursor);
 
 		if (++stack_depth > 255) {
-			tprintf("> too many stack frames\n");
-			line_ended();
+			error_action(data,
+				     "too many stack frames", 0);
 			break;
 		}
 	} while (ret_val > 0);
 ret:
 	free(symbol_name);
 }
+
+static void
+stacktrace_capture(struct tcb *tcp)
+{
+	stacktrace_walk(tcp, queue_put_call, queue_put_error,
+			tcp->queue);
+}
+
+
+static void
+print_call_cb(void *dummy,
+	      char *binary_filename,
+	      char *symbol_name,
+	      unw_word_t function_off_set,
+	      unsigned long true_offset)
+{
+	print_call(binary_filename,
+		   symbol_name,
+		   function_off_set,
+		   true_offset);
+}
+
+static void
+print_error_cb(void *dummy,
+	       const char *error,
+	       unsigned long true_offset)
+{
+	print_error(error, true_offset);
+}
+
+static void
+stacktrace_print(struct tcb *tcp)
+{
+	DPRINTF("tcp=%p, queue=%p", "stackprint", tcp, tcp->queue->head);
+	stacktrace_walk(tcp, print_call_cb, print_error_cb, NULL);
+}
+
+/*
+ *  Exported functions
+ *  use libunwind to unwind the stack and print a backtrace
+ */
+void
+unwind_init(void)
+{
+	init_unwind_addr_space();
+}
+
+void
+unwind_tcb_init(struct tcb *tcp)
+{
+	init_libunwind_ui(tcp);
+	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)
+{
+	if (tcp->s_ent->sys_flags & STACKTRACE_CAPTURE_IN_ENTERING)
+		queue_print_and_free(tcp);
+	else
+		queue_free(tcp->queue, NULL);
+	free(tcp->queue);
+	tcp->queue = NULL;
+
+	delete_mmap_cache(tcp, __FUNCTION__);
+	free_libunwind_ui(tcp);
+
+}
+
+void
+unwind_cache_invalidate(struct tcb *tcp)
+{
+	mmap_cache_generation++;
+	DPRINTF("gen=%u, GEN=%u, tcp=%p, cache=%p", "increment",
+		tcp->mmap_cache_generation,
+		mmap_cache_generation,
+		tcp,
+		tcp->mmap_cache);
+}
+
+void
+unwind_stacktrace_capture(struct tcb *tcp)
+{
+	queue_free(tcp->queue, NULL);
+
+	if (is_mmap_cache_available(tcp, __FUNCTION__)) {
+		stacktrace_capture(tcp);
+		DPRINTF("tcp=%p, queue=%p", "captured", tcp, tcp->queue->head);
+	}
+}
+
+void
+unwind_stacktrace_print(struct tcb *tcp)
+{
+	if (tcp->s_ent->sys_flags & STACKTRACE_CAPTURE_IN_ENTERING)
+		queue_print_and_free(tcp);
+	else if (is_mmap_cache_available(tcp, __FUNCTION__))
+		stacktrace_print(tcp);
+}
+
-- 
1.8.3.1





More information about the Strace-devel mailing list