[PATCH 5/9] unwind: split unwind codes into front-end and back-end

Masatake YAMATO yamato at redhat.com
Tue Mar 13 17:28:10 UTC 2018


So the back-end can replaceable.

* unwind.c: Move all libunwind related code to unwind-libunwind.c.
  Put codes to implement the abstract interface are here.

* unwind.h: New file. Declares types for defining a unwinder.

* unwind-libunwind.c: New file defining unwinder adapted to unwind.h.
  Mostly derived libunwind related code from unwind.c.

* Makefile.am (USE_LIBUNWIND): Reintroduced condition.
libunwind related compiler flags are set here.
(CAN_UNWIND): Move libunwind related flags to USE_LIBUNWIND.
(strace_SOURCES): Add unwind.h in CAN_UNWIND condition, a new
header file. Add unwind-libunwind.c in USE_LIBUNWIND.

Signed-off-by: Masatake YAMATO <yamato at redhat.com>
---
 Makefile.am        |  14 +++-
 unwind-libunwind.c | 174 +++++++++++++++++++++++++++++++++++++++++++
 unwind.c           | 214 +++++++++++++++--------------------------------------
 unwind.h           |  74 ++++++++++++++++++
 4 files changed, 319 insertions(+), 157 deletions(-)
 create mode 100644 unwind-libunwind.c
 create mode 100644 unwind.h

diff --git a/Makefile.am b/Makefile.am
index 00c7b4bc..a4d274a5 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -339,10 +339,9 @@ strace_SOURCES =	\
 	# end of strace_SOURCES
 
 if CAN_UNWIND
-strace_SOURCES += unwind.c
-strace_CPPFLAGS += $(libunwind_CPPFLAGS)
-strace_LDFLAGS += $(libunwind_LDFLAGS)
-strace_LDADD += $(libunwind_LIBS)
+strace_SOURCES +=	\
+	unwind.c	\
+	unwind.h
 if USE_DEMANGLE
 strace_CPPFLAGS += $(libiberty_CPPFLAGS)
 strace_LDFLAGS += $(libiberty_LDFLAGS)
@@ -350,6 +349,13 @@ strace_LDADD += $(libiberty_LIBS)
 endif
 endif
 
+if USE_LIBUNWIND
+strace_SOURCES += unwind-libunwind.c
+strace_CPPFLAGS += $(libunwind_CPPFLAGS)
+strace_LDFLAGS += $(libunwind_LDFLAGS)
+strace_LDADD += $(libunwind_LIBS)
+endif
+
 @CODE_COVERAGE_RULES@
 CODE_COVERAGE_BRANCH_COVERAGE = 1
 CODE_COVERAGE_GENHTML_OPTIONS = $(CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT) \
diff --git a/unwind-libunwind.c b/unwind-libunwind.c
new file mode 100644
index 00000000..49e2c66e
--- /dev/null
+++ b/unwind-libunwind.c
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 2013 Luca Clementi <luca.clementi at gmail.com>
+ * Copyright (c) 2013-2018 The strace developers.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "defs.h"
+#include "unwind.h"
+
+#include "mmap_cache.h"
+#include <libunwind-ptrace.h>
+
+static unw_addr_space_t libunwind_as;
+
+static void
+init(void)
+{
+	libunwind_as = unw_create_addr_space(&_UPT_accessors, 0);
+	if (!libunwind_as)
+		error_msg_and_die("failed to create address space for stack tracing");
+	unw_set_caching_policy(libunwind_as, UNW_CACHE_GLOBAL);
+}
+
+static void *
+tcb_init(struct tcb *tcp)
+{
+	void *r = _UPT_create(tcp->pid);
+
+	if (!r)
+		perror_msg_and_die("_UPT_create");
+	return r;
+}
+
+static void
+tcb_fin(struct tcb *tcp)
+{
+	_UPT_destroy((struct UPT_info *)tcp->unwind_ctx);
+}
+
+static void
+get_symbol_name(unw_cursor_t *cursor, char **name,
+		size_t *size, unw_word_t *offset)
+{
+	for (;;) {
+		int rc = unw_get_proc_name(cursor, *name, *size, offset);
+
+		if (rc == 0)
+			break;
+		if (rc != -UNW_ENOMEM) {
+			**name = '\0';
+			*offset = 0;
+			break;
+		}
+		*name = xgrowarray(*name, size, 1);
+	}
+}
+
+static int
+print_stack_frame(struct tcb *tcp,
+		  unwind_call_action_fn call_action,
+		  unwind_error_action_fn error_action,
+		  void *data,
+		  unw_cursor_t *cursor,
+		  char **symbol_name,
+		  size_t *symbol_name_size)
+{
+	unw_word_t ip;
+	struct mmap_cache_t *cur_mmap_cache;
+
+	if (unw_get_reg(cursor, UNW_REG_IP, &ip) < 0) {
+		perror_msg("Can't walk the stack of process %d", tcp->pid);
+		return -1;
+	}
+
+	cur_mmap_cache = mmap_cache_search(tcp, ip);
+	if (cur_mmap_cache
+	    /* ignore mappings that have no PROT_EXEC bit set */
+	    && (cur_mmap_cache->protections & MMAP_CACHE_PROT_EXECUTABLE)) {
+		unsigned long true_offset;
+		unw_word_t function_offset;
+
+		get_symbol_name(cursor, symbol_name, symbol_name_size,
+				&function_offset);
+		true_offset = ip - cur_mmap_cache->start_addr +
+			cur_mmap_cache->mmap_offset;
+		call_action(data,
+			    cur_mmap_cache->binary_filename,
+			    *symbol_name,
+			    function_offset,
+			    true_offset);
+
+		return 0;
+	}
+
+	/*
+	 * there is a bug in libunwind >= 1.0
+	 * after a set_tid_address syscall
+	 * unw_get_reg returns IP == 0
+	 */
+	if (ip)
+		error_action(data, "unexpected_backtracing_error", ip);
+	return -1;
+}
+
+static void
+tcb_walk(struct tcb *tcp,
+	 unwind_call_action_fn call_action,
+	 unwind_error_action_fn error_action,
+	 void *data)
+{
+	char *symbol_name;
+	size_t symbol_name_size = 40;
+	unw_cursor_t cursor;
+	int stack_depth;
+
+	if (!tcp->mmap_cache)
+		error_msg_and_die("bug: mmap_cache is NULL");
+	if (tcp->mmap_cache_size == 0)
+		error_msg_and_die("bug: mmap_cache is empty");
+
+	symbol_name = xmalloc(symbol_name_size);
+
+	if (unw_init_remote(&cursor, libunwind_as,
+			    (struct UPT_info *)tcp->unwind_ctx) < 0)
+		perror_msg_and_die("Can't initiate libunwind");
+
+	for (stack_depth = 0; stack_depth < 256; ++stack_depth) {
+		if (print_stack_frame(tcp, call_action, error_action, data,
+				&cursor, &symbol_name, &symbol_name_size) < 0)
+			break;
+		if (unw_step(&cursor) <= 0)
+			break;
+	}
+	if (stack_depth >= 256)
+		error_action(data, "too many stack frames", 0);
+
+	free(symbol_name);
+}
+
+static void
+tcb_flush_cache(struct tcb *tcp)
+{
+	unw_flush_cache(libunwind_as, 0, 0);
+}
+
+const struct unwind_unwinder_t unwinder_libunwind = {
+	.name = "libunwind",
+	.init = init,
+	.tcb_init = tcb_init,
+	.tcb_fin = tcb_fin,
+	.tcb_walk = tcb_walk,
+	.tcb_flush_cache = tcb_flush_cache,
+};
diff --git a/unwind.c b/unwind.c
index f5b25fa3..83c5cff4 100644
--- a/unwind.c
+++ b/unwind.c
@@ -27,7 +27,7 @@
 
 #include "defs.h"
 #include "mmap_cache.h"
-#include <libunwind-ptrace.h>
+#include "unwind.h"
 
 #ifdef USE_DEMANGLE
 # if defined HAVE_DEMANGLE_H
@@ -37,18 +37,6 @@
 # endif
 #endif
 
-/*
- * Type used in stacktrace walker
- */
-typedef void (*call_action_fn)(void *data,
-			       const char *binary_filename,
-			       const char *symbol_name,
-			       unw_word_t function_offset,
-			       unsigned long true_offset);
-typedef void (*error_action_fn)(void *data,
-				const char *error,
-				unsigned long true_offset);
-
 /*
  * Type used in stacktrace capturing
  */
@@ -64,33 +52,39 @@ struct unwind_queue_t {
 
 static void queue_print(struct unwind_queue_t *queue);
 
-static unw_addr_space_t libunwind_as;
-
 static const char asprintf_error_str[] = "???";
 
+#ifdef USE_LIBUNWIND
+extern const struct unwind_unwinder_t unwinder_libunwind;
+#endif
+static const struct unwind_unwinder_t *unwinder;
+
 void
 unwind_init(void)
 {
-	libunwind_as = unw_create_addr_space(&_UPT_accessors, 0);
-	if (!libunwind_as)
-		error_msg_and_die("failed to create address space for stack tracing");
-	unw_set_caching_policy(libunwind_as, UNW_CACHE_GLOBAL);
+#ifdef USE_LIBUNWIND
+	unwinder = &unwinder_libunwind;
+#endif
+
+	if (!unwinder)
+		error_msg_and_die("bug: no unwinder");
+
+	if (unwinder->init)
+		unwinder->init();
 	mmap_cache_enable();
 }
 
 void
 unwind_tcb_init(struct tcb *tcp)
 {
-	if (tcp->unwind_ctx)
+	if (tcp->unwind_queue)
 		return;
 
-	tcp->unwind_ctx = _UPT_create(tcp->pid);
-	if (!tcp->unwind_ctx)
-		perror_msg_and_die("_UPT_create");
-
 	tcp->unwind_queue = xmalloc(sizeof(*tcp->unwind_queue));
 	tcp->unwind_queue->head = NULL;
 	tcp->unwind_queue->tail = NULL;
+
+	tcp->unwind_ctx = unwinder->tcb_init(tcp);
 }
 
 void
@@ -100,123 +94,10 @@ unwind_tcb_fin(struct tcb *tcp)
 	free(tcp->unwind_queue);
 	tcp->unwind_queue = NULL;
 
-	_UPT_destroy((struct UPT_info *)tcp->unwind_ctx);
+	unwinder->tcb_fin(tcp);
 	tcp->unwind_ctx = NULL;
 }
 
-static void
-get_symbol_name(unw_cursor_t *cursor, char **name,
-		size_t *size, unw_word_t *offset)
-{
-	for (;;) {
-		int rc = unw_get_proc_name(cursor, *name, *size, offset);
-		if (rc == 0)
-			break;
-		if (rc != -UNW_ENOMEM) {
-			**name = '\0';
-			*offset = 0;
-			break;
-		}
-		*name = xgrowarray(*name, size, 1);
-	}
-}
-
-static int
-print_stack_frame(struct tcb *tcp,
-		  call_action_fn call_action,
-		  error_action_fn error_action,
-		  void *data,
-		  unw_cursor_t *cursor,
-		  char **symbol_name,
-		  size_t *symbol_name_size)
-{
-	unw_word_t ip;
-	struct mmap_cache_t *cur_mmap_cache;
-
-	if (unw_get_reg(cursor, UNW_REG_IP, &ip) < 0) {
-		perror_msg("Can't walk the stack of process %d", tcp->pid);
-		return -1;
-	}
-
-	cur_mmap_cache = mmap_cache_search(tcp, ip);
-	if (cur_mmap_cache
-	    /* ignore mappings that have no PROT_EXEC bit set */
-	    && (cur_mmap_cache->protections & MMAP_CACHE_PROT_EXECUTABLE)) {
-		unsigned long true_offset;
-		unw_word_t function_offset;
-
-		get_symbol_name(cursor, symbol_name, symbol_name_size,
-				&function_offset);
-		true_offset = ip - cur_mmap_cache->start_addr +
-			cur_mmap_cache->mmap_offset;
-#ifdef USE_DEMANGLE
-		char *demangled_name =
-			cplus_demangle(*symbol_name,
-				       DMGL_AUTO | DMGL_PARAMS);
-#endif
-		call_action(data,
-			    cur_mmap_cache->binary_filename,
-#ifdef USE_DEMANGLE
-			    demangled_name ? demangled_name :
-#endif
-			    *symbol_name,
-			    function_offset,
-			    true_offset);
-#ifdef USE_DEMANGLE
-		free(demangled_name);
-#endif
-
-		return 0;
-	}
-
-	/*
-	 * there is a bug in libunwind >= 1.0
-	 * after a set_tid_address syscall
-	 * unw_get_reg returns IP == 0
-	 */
-	if (ip)
-		error_action(data, "unexpected_backtracing_error", ip);
-	return -1;
-}
-
-/*
- * walking the stack
- */
-static void
-stacktrace_walk(struct tcb *tcp,
-		call_action_fn call_action,
-		error_action_fn error_action,
-		void *data)
-{
-	char *symbol_name;
-	size_t symbol_name_size = 40;
-	unw_cursor_t cursor;
-	int stack_depth;
-
-	if (!tcp->mmap_cache)
-		error_msg_and_die("bug: mmap_cache is NULL");
-	if (tcp->mmap_cache_size == 0)
-		error_msg_and_die("bug: mmap_cache is empty");
-
-	symbol_name = xmalloc(symbol_name_size);
-
-	if (unw_init_remote(&cursor, libunwind_as,
-			    (struct UPT_info *)tcp->unwind_ctx) < 0)
-		perror_msg_and_die("Can't initiate libunwind");
-
-	for (stack_depth = 0; stack_depth < 256; ++stack_depth) {
-		if (print_stack_frame(tcp, call_action, error_action, data,
-				&cursor, &symbol_name, &symbol_name_size) < 0)
-			break;
-		if (unw_step(&cursor) <= 0)
-			break;
-	}
-	if (stack_depth >= 256)
-		error_action(data, "too many stack frames", 0);
-
-	free(symbol_name);
-}
-
 /*
  * printing an entry in stack to stream or buffer
  */
@@ -229,10 +110,10 @@ stacktrace_walk(struct tcb *tcp,
  * /lib64/libc.so.6(__libc_start_main+0xed) [0x7fa2f8a5976d]
  * ./a.out() [0x400569]
  */
-#define STACK_ENTRY_SYMBOL_FMT			\
+#define STACK_ENTRY_SYMBOL_FMT(SYM)		\
 	" > %s(%s+0x%lx) [0x%lx]\n",		\
 	binary_filename,			\
-	symbol_name,				\
+	(SYM),					\
 	(unsigned long) function_offset,	\
 	true_offset
 #define STACK_ENTRY_NOSYMBOL_FMT		\
@@ -249,11 +130,24 @@ static void
 print_call_cb(void *dummy,
 	      const char *binary_filename,
 	      const char *symbol_name,
-	      unw_word_t function_offset,
+	      unwind_function_offset_t function_offset,
 	      unsigned long true_offset)
 {
-	if (symbol_name && (symbol_name[0] != '\0'))
-		tprintf(STACK_ENTRY_SYMBOL_FMT);
+	if (symbol_name && (symbol_name[0] != '\0')) {
+#ifdef USE_DEMANGLE
+		char *demangled_name =
+			cplus_demangle(symbol_name,
+				       DMGL_AUTO | DMGL_PARAMS);
+#endif
+		tprintf(STACK_ENTRY_SYMBOL_FMT(
+#ifdef USE_DEMANGLE
+					       demangled_name ? demangled_name :
+#endif
+					       symbol_name));
+#ifdef USE_DEMANGLE
+		free(demangled_name);
+#endif
+	}
 	else if (binary_filename)
 		tprintf(STACK_ENTRY_NOSYMBOL_FMT);
 	else
@@ -278,15 +172,29 @@ print_error_cb(void *dummy,
 static char *
 sprint_call_or_error(const char *binary_filename,
 		     const char *symbol_name,
-		     unw_word_t function_offset,
+		     unwind_function_offset_t function_offset,
 		     unsigned long true_offset,
 		     const char *error)
 {
 	char *output_line = NULL;
 	int n;
 
-	if (symbol_name)
-		n = asprintf(&output_line, STACK_ENTRY_SYMBOL_FMT);
+	if (symbol_name) {
+#ifdef USE_DEMANGLE
+		char *demangled_name =
+			cplus_demangle(symbol_name,
+				       DMGL_AUTO | DMGL_PARAMS);
+#endif
+		n = asprintf(&output_line, STACK_ENTRY_SYMBOL_FMT(
+#ifdef USE_DEMANGLE
+								  demangled_name ? demangled_name :
+#endif
+								  symbol_name
+								  ));
+#ifdef USE_DEMANGLE
+		free(demangled_name);
+#endif
+	}
 	else if (binary_filename)
 		n = asprintf(&output_line, STACK_ENTRY_NOSYMBOL_FMT);
 	else if (error)
@@ -311,7 +219,7 @@ static void
 queue_put(struct unwind_queue_t *queue,
 	  const char *binary_filename,
 	  const char *symbol_name,
-	  unw_word_t function_offset,
+	  unwind_function_offset_t function_offset,
 	  unsigned long true_offset,
 	  const char *error)
 {
@@ -338,7 +246,7 @@ static void
 queue_put_call(void *queue,
 	       const char *binary_filename,
 	       const char *symbol_name,
-	       unw_word_t function_offset,
+	       unwind_function_offset_t function_offset,
 	       unsigned long true_offset)
 {
 	queue_put(queue,
@@ -401,11 +309,11 @@ unwind_tcb_print(struct tcb *tcp)
 		queue_print(queue);
 	} else switch (mmap_cache_rebuild_if_invalid(tcp, __func__)) {
 		case MMAP_CACHE_REBUILD_RENEWED:
-			unw_flush_cache(libunwind_as, 0, 0);
+			unwinder->tcb_flush_cache(tcp);
 			ATTRIBUTE_FALLTHROUGH;
 		case MMAP_CACHE_REBUILD_READY:
 			debug_func_msg("walk: tcp=%p, queue=%p", tcp, queue->head);
-			stacktrace_walk(tcp, print_call_cb, print_error_cb, NULL);
+			unwinder->tcb_walk(tcp, print_call_cb, print_error_cb, NULL);
 			break;
 		default:
 			/* Do nothing */
@@ -433,11 +341,11 @@ unwind_tcb_capture(struct tcb *tcp)
 
 	switch (mmap_cache_rebuild_if_invalid(tcp, __func__)) {
 	case MMAP_CACHE_REBUILD_RENEWED:
-		unw_flush_cache(libunwind_as, 0, 0);
+		unwinder->tcb_flush_cache(tcp);
 		ATTRIBUTE_FALLTHROUGH;
 	case MMAP_CACHE_REBUILD_READY:
-		stacktrace_walk(tcp, queue_put_call, queue_put_error,
-				queue);
+		unwinder->tcb_walk(tcp, queue_put_call, queue_put_error,
+			      queue);
 		debug_func_msg("tcp=%p, queue=%p", tcp, queue->head);
 		break;
 	default:
diff --git a/unwind.h b/unwind.h
new file mode 100644
index 00000000..59603321
--- /dev/null
+++ b/unwind.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2013 Luca Clementi <luca.clementi at gmail.com>
+ * Copyright (c) 2013-2018 The strace developers.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef STRACE_UNWIND_H
+#define STRACE_UNWIND_H
+
+/* Interface for backends (unwinders) See defs.h for the frontend. */
+
+#include "defs.h"
+
+/*
+ * Type used in stacktrace walker
+ */
+
+/* This storage be enough large to store unw_word_t. */
+typedef unsigned long unwind_function_offset_t;
+
+typedef void (*unwind_call_action_fn)(void *data,
+				      const char *binary_filename,
+				      const char *symbol_name,
+				      unwind_function_offset_t function_offset,
+				      unsigned long true_offset);
+typedef void (*unwind_error_action_fn)(void *data,
+				       const char *error,
+				       unsigned long true_offset);
+
+struct unwind_unwinder_t {
+	const char *name;
+
+	/* Initialize the unwinder. */
+	void   (*init)(void);
+
+	/* Make/destroy a context data attached to tcb. */
+	void * (*tcb_init)(struct tcb *);
+	void   (*tcb_fin)(struct tcb *);
+
+	/* Walk stack */
+	void   (*tcb_walk)(struct tcb *,
+			   unwind_call_action_fn,
+			   unwind_error_action_fn,
+			   void *);
+
+	/* Rebuild unwinder internal cache. Called when mmap cache
+	 * subsystem detects the change of memory mapping of target
+	 * process.
+	 */
+	void   (*tcb_flush_cache)(struct tcb *);
+};
+
+#endif /* !STRACE_UNWIND_H */
-- 
2.14.3



More information about the Strace-devel mailing list