[Tarantool-patches] [PATCH v3 4/4] fiber: refactor C backtrace and add changelog

eelchinov at tarantool.org eelchinov at tarantool.org
Wed Jul 14 14:12:52 MSK 2021


From: Egor Elchinov <elchinov.es at gmail.com>

Backtrace cache support and cxx demangling added.
DWARF version and lua test fixed
to support specific platforms.
Changelog added.

Closes: #4002
---
 .../gh-4002-fiber-creation-backtrace.md       |   8 +
 src/lib/core/backtrace.cc                     | 169 +++++++++++++-----
 .../gh-4002-fiber-creation-backtrace.result   |   4 +-
 .../gh-4002-fiber-creation-backtrace.test.lua |   4 +-
 4 files changed, 135 insertions(+), 50 deletions(-)
 create mode 100644 changelogs/unreleased/gh-4002-fiber-creation-backtrace.md

diff --git a/changelogs/unreleased/gh-4002-fiber-creation-backtrace.md b/changelogs/unreleased/gh-4002-fiber-creation-backtrace.md
new file mode 100644
index 000000000..1e1ac177a
--- /dev/null
+++ b/changelogs/unreleased/gh-4002-fiber-creation-backtrace.md
@@ -0,0 +1,8 @@
+## feature/fiber
+
+ * Added new subtable `parent_backtrace` to the `fiber.info()`
+   containing C and Lua backtrace chunks of fiber creation.
+ * Added `fiber.parent_bt_enable()` and `fiber.parent_bt_disable()`
+   options in order to switch on/off the ability to collect
+   parent backtraces for newly created fibers and to
+   show/hide `parent_backtrace` subtables in the `fiber.info()`.
diff --git a/src/lib/core/backtrace.cc b/src/lib/core/backtrace.cc
index ef8a70f1b..355b25d7e 100644
--- a/src/lib/core/backtrace.cc
+++ b/src/lib/core/backtrace.cc
@@ -47,7 +47,6 @@
 #include <libunwind.h>
 
 #ifdef TARGET_OS_DARWIN
-#include <execinfo.h>
 #include <dlfcn.h>
 #endif
 
@@ -79,19 +78,29 @@ backtrace_proc_cache_clear(void)
 	proc_cache = NULL;
 }
 
-const char *
-get_proc_name(unw_cursor_t *unw_cur, unw_word_t *offset, bool skip_cache)
+int
+backtrace_proc_cache_find(unw_word_t ip, const char **name, unw_word_t *offset)
 {
-	static __thread char proc_name[BACKTRACE_NAME_MAX];
-	unw_word_t ip;
-	unw_get_reg(unw_cur, UNW_REG_IP, &ip);
+	struct proc_cache_entry *entry;
+	mh_int_t k;
 
-	if (skip_cache) {
-		unw_get_proc_name(unw_cur, proc_name, sizeof(proc_name),
-				  offset);
-		return proc_name;
+	if (proc_cache != NULL) {
+		k  = mh_i64ptr_find(proc_cache, ip, NULL);
+		if (k != mh_end(proc_cache)) {
+			entry = (struct proc_cache_entry *)
+				mh_i64ptr_node(proc_cache, k)->val;
+			*offset = entry->offset;
+			*name = entry->name;
+			return 0;
+		}
 	}
 
+	return -1;
+}
+
+int
+backtrace_proc_cache_put(unw_word_t ip, const char *name, unw_word_t offset)
+{
 	struct proc_cache_entry *entry;
 	struct mh_i64ptr_node_t node;
 	mh_int_t k;
@@ -99,39 +108,52 @@ get_proc_name(unw_cursor_t *unw_cur, unw_word_t *offset, bool skip_cache)
 	if (proc_cache == NULL) {
 		region_create(&cache_region, &cord()->slabc);
 		proc_cache = mh_i64ptr_new();
-		if (proc_cache == NULL) {
-			unw_get_proc_name(unw_cur, proc_name, sizeof(proc_name),
-					  offset);
-			goto error;
-		}
+		if (proc_cache == NULL)
+			return -1;
+	}
+
+	size_t size;
+	entry = region_alloc_object(&cache_region, typeof(*entry),
+				    &size);
+	if (entry == NULL)
+		return -1;
+
+	node.key = ip;
+	node.val = entry;
+	entry->offset = offset;
+	snprintf(entry->name, BACKTRACE_NAME_MAX - 1, "%s", name);
+	entry->name[BACKTRACE_NAME_MAX - 1] = 0;
+
+	k = mh_i64ptr_put(proc_cache, &node, NULL, NULL);
+	if (k == mh_end(proc_cache)) {
+		size_t used = region_used(&cache_region);
+		region_truncate(&cache_region, used - size);
+		return -1;
 	}
 
-	k  = mh_i64ptr_find(proc_cache, ip, NULL);
-	if (k != mh_end(proc_cache)) {
-		entry = (struct proc_cache_entry *)
-			mh_i64ptr_node(proc_cache, k)->val;
-		snprintf(proc_name, BACKTRACE_NAME_MAX, "%s", entry->name);
-		*offset = entry->offset;
-	}  else {
+	return 0;
+}
+
+const char *
+get_proc_name(unw_cursor_t *unw_cur, unw_word_t *offset, bool skip_cache)
+{
+	static __thread char proc_name[BACKTRACE_NAME_MAX];
+	const char *cache_name;
+	unw_word_t ip;
+
+	if (skip_cache) {
 		unw_get_proc_name(unw_cur, proc_name, sizeof(proc_name),
 				  offset);
-		size_t size;
-		entry = region_alloc_object(&cache_region, typeof(*entry),
-					    &size);
-		if (entry == NULL)
-			goto error;
-		node.key = ip;
-		node.val = entry;
-		snprintf(entry->name, BACKTRACE_NAME_MAX, "%s", proc_name);
-		entry->offset = *offset;
-
-		k = mh_i64ptr_put(proc_cache, &node, NULL, NULL);
-		if (k == mh_end(proc_cache)) {
-			free(entry);
-			goto error;
-		}
+		return proc_name;
+	}
+
+	unw_get_reg(unw_cur, UNW_REG_IP, &ip);
+	if (backtrace_proc_cache_find(ip, &cache_name, offset) == 0) {
+		return cache_name;
 	}
-error:
+
+	unw_get_proc_name(unw_cur, proc_name, sizeof(proc_name), offset);
+	backtrace_proc_cache_put(ip, proc_name, *offset);
 	return proc_name;
 }
 
@@ -450,7 +472,25 @@ backtrace_collect_ip(void **ip_buf, int limit)
 #ifndef TARGET_OS_DARWIN
 	unw_backtrace(ip_buf, limit);
 #else
-	backtrace(ip_buf, limit);
+	/*
+	 * This dumb implementation was chosen because the DARWIN
+	 * lacks unw_backtrace() routine from libunwind and
+	 * usual backtrace() from <execinfo.h> has less capabilities
+	 * than the libunwind version which uses DWARF.
+	 */
+	unw_cursor_t unw_cur;
+	unw_context_t unw_ctx;
+	int frame_no = 0;
+	unw_word_t ip;
+
+	unw_getcontext(&unw_ctx);
+	unw_init_local(&unw_cur, &unw_ctx);
+
+	while (frame_no < limit && unw_step(&unw_cur) > 0) {
+		unw_get_reg(&unw_cur, UNW_REG_IP, &ip);
+		ip_buf[frame_no] = (void *)ip;
+		++frame_no;
+	}
 #endif
 }
 
@@ -469,12 +509,15 @@ void
 backtrace_foreach_ip(backtrace_cb cb, void **ip_buf, int limit,
 		     void *cb_ctx)
 {
+	int demangle_status;
+	char *demangle_buf = NULL;
+	size_t demangle_buf_len = 0;
 #ifndef TARGET_OS_DARWIN
 	char proc_name[BACKTRACE_NAME_MAX];
 	unw_word_t ip = 0, offset = 0;
 	unw_proc_info_t pi;
 	int frame_no, ret = 0;
-	char *proc = NULL;
+	const char *proc = NULL;
 
 	unw_accessors_t *acc = unw_get_accessors(unw_local_addr_space);
 
@@ -487,21 +530,37 @@ backtrace_foreach_ip(backtrace_cb cb, void **ip_buf, int limit,
 	     frame_no++) {
 		ip = (unw_word_t)ip_buf[frame_no];
 
-		if (acc->get_proc_name == NULL) {
+		if (backtrace_proc_cache_find(ip, &proc, &offset) == 0) {
+			ret = 0;
+		} else if (acc->get_proc_name == NULL) {
 			ret = unw_get_proc_info_by_ip(unw_local_addr_space,
 						      ip, &pi, NULL);
 			offset = ip - pi.start_ip;
+			proc = NULL;
+			backtrace_proc_cache_put(ip, proc, offset);
 		} else {
 			ret = acc->get_proc_name(unw_local_addr_space, ip,
 						 proc_name, sizeof(proc_name),
 						 &offset, NULL);
 			proc = proc_name;
+			backtrace_proc_cache_put(ip, proc, offset);
+		}
+
+		if (proc != NULL) {
+			char *cxxname = abi::__cxa_demangle(proc, demangle_buf,
+							    &demangle_buf_len,
+							    &demangle_status);
+			if (cxxname != NULL) {
+				demangle_buf = cxxname;
+				proc = cxxname;
+			}
 		}
 		if (ret != 0 || cb(frame_no - 1, (void *)ip, proc,
 				   (size_t)offset, cb_ctx) != 0)
 			break;
 	}
 
+	free(demangle_buf);
 	if (ret != 0)
 		say_debug("unwinding error: %s", unw_strerror(ret));
 #else
@@ -509,19 +568,37 @@ backtrace_foreach_ip(backtrace_cb cb, void **ip_buf, int limit,
 	void *ip = NULL;
 	size_t offset = 0;
 	Dl_info dli;
+	const char *proc = NULL;
 
 	for (frame_no = 1; frame_no < limit && ip_buf[frame_no] != NULL;
 	     ++frame_no) {
 		ip = ip_buf[frame_no];
-		ret = dladdr(ip, &dli);
-		if (ret == 0)
-			break;
-		offset = (char *)ip - (char *)dli.dli_saddr;
+		if (backtrace_proc_cache_find((unw_word_t)ip, &proc, 
+					      &offset) == 0) {
+			ret = 1;
+		} else {
+			ret = dladdr(ip, &dli);
+			if (ret == 0)
+				break;
+			offset = (char *)ip - (char *)dli.dli_saddr;
+			proc = dli.dli_sname;
+			backtrace_proc_cache_put((unw_word_t)ip, proc, offset);
+		}
 
-		if (cb(frame_no - 1, ip, dli.dli_sname, offset, cb_ctx) != 0)
+		if (proc != NULL) {
+			char *cxxname = abi::__cxa_demangle(proc, demangle_buf,
+							    &demangle_buf_len,
+							    &demangle_status);
+			if (cxxname != NULL) {
+				demangle_buf = cxxname;
+				proc = cxxname;
+			}
+		}
+		if (cb(frame_no - 1, ip, proc, offset, cb_ctx) != 0)
 			break;
 	}
 
+	free(demangle_buf);
 	if (ret == 0)
 		say_debug("unwinding error: %i", ret);
 #endif
diff --git a/test/app/gh-4002-fiber-creation-backtrace.result b/test/app/gh-4002-fiber-creation-backtrace.result
index 6a075911d..91b0cb9ce 100644
--- a/test/app/gh-4002-fiber-creation-backtrace.result
+++ b/test/app/gh-4002-fiber-creation-backtrace.result
@@ -9,10 +9,10 @@ test_run = require('test_run').new()
  | ---
  | ...
 
-local stack_len = 0
+stack_len = 0
  | ---
  | ...
-local parent_stack_len = 0
+parent_stack_len = 0
  | ---
  | ...
 
diff --git a/test/app/gh-4002-fiber-creation-backtrace.test.lua b/test/app/gh-4002-fiber-creation-backtrace.test.lua
index 79a516860..3c39bb48e 100644
--- a/test/app/gh-4002-fiber-creation-backtrace.test.lua
+++ b/test/app/gh-4002-fiber-creation-backtrace.test.lua
@@ -2,8 +2,8 @@ yaml = require('yaml')
 fiber = require('fiber')
 test_run = require('test_run').new()
 
-local stack_len = 0
-local parent_stack_len = 0
+stack_len = 0
+parent_stack_len = 0
 
 test_run:cmd('setopt delimiter ";"')
 foo = function()
-- 
2.31.1



More information about the Tarantool-patches mailing list