From acaadca080ff956a15c5901b37baad7e8114a8c9 Mon Sep 17 00:00:00 2001
From: Yuriy Vostrikov <vostrikov@corp.mail.ru>
Date: Thu, 13 Jan 2011 12:08:13 +0300
Subject: [PATCH] [core] Cleanup backtrace priniting.

---
 core/coro.c             |   2 -
 core/fiber.c            |  39 +++++-----
 core/tarantool.c        |   9 +--
 core/util.c             | 161 +++++++++++++++++++++++++++++-----------
 include/fiber.h         |   5 +-
 include/tbuf.h          |   1 -
 include/util.h          |  26 +++----
 third_party/coro/coro.c |   4 -
 8 files changed, 153 insertions(+), 94 deletions(-)

diff --git a/core/coro.c b/core/coro.c
index 2d35cbe9b2..13d2b6f58b 100644
--- a/core/coro.c
+++ b/core/coro.c
@@ -38,8 +38,6 @@
 #define MAP_ANONYMOUS MAP_ANON
 #endif
 
-extern void *main_stack_frame;
-
 struct tarantool_coro *
 tarantool_coro_create(struct tarantool_coro *coro, void (*f) (void *), void *data)
 {
diff --git a/core/fiber.c b/core/fiber.c
index 8968c3e926..c0fb5e04b8 100644
--- a/core/fiber.c
+++ b/core/fiber.c
@@ -89,6 +89,16 @@ fiber_msg(const struct tbuf *buf)
 KHASH_MAP_INIT_INT(fid2fiber, void *, realloc);
 static khash_t(fid2fiber) *fibers_registry;
 
+static void
+update_frame_address(struct fiber *fiber)
+{
+#ifdef BACKTRACE
+	fiber->last_frame = frame_addess();
+#else
+	(void)fiber;
+#endif
+}
+
 void
 fiber_call(struct fiber *callee)
 {
@@ -100,7 +110,7 @@ fiber_call(struct fiber *callee)
 	fiber = callee;
 	*sp++ = caller;
 
-	save_rbp(caller->rbp);
+	update_frame_address(caller);
 
 	callee->csw++;
 	coro_transfer(&caller->coro.ctx, &callee->coro.ctx);
@@ -117,7 +127,7 @@ fiber_raise(struct fiber *callee, jmp_buf exc, int value)
 	fiber = callee;
 	*sp++ = caller;
 
-	save_rbp(caller->rbp);
+	update_frame_address(caller);
 
 	callee->csw++;
 	coro_save_and_longjmp(&caller->coro.ctx, exc, value);
@@ -130,7 +140,7 @@ yield(void)
 	struct fiber *caller = fiber;
 
 	fiber = callee;
-	save_rbp(caller->rbp);
+	update_frame_address(caller);
 
 	callee->csw++;
 	coro_transfer(&caller->coro.ctx, &callee->coro.ctx);
@@ -1051,24 +1061,11 @@ fiber_info(struct tbuf *out)
 		tbuf_printf(out, "    fd: %4i\n", fiber->fd);
 		tbuf_printf(out, "    peer: %s\n", fiber_peer_name(fiber));
 		tbuf_printf(out, "    stack: %p\n", stack_top);
-
-#if defined(__x86) || defined (__amd64) || defined(__i386)
-		tbuf_printf(out, "    exc: %p, frame: %p\n", ((void **)fiber->exc)[3], ((void **)fiber->exc)[3] + 2 * sizeof(void *));
-
-		void *stack_bottom = fiber->coro.stack;
-		struct frame *frame = fiber->rbp;
-		tbuf_printf(out, "    backtrace:\n");
-		while (stack_bottom < (void *)frame && (void *)frame < stack_top) {
-			tbuf_printf(out, "        - { rbp: %p, frame: %p, pc: %p",
-				    frame, (void *)frame + 2 * sizeof(void *), frame->ret);
-#ifdef RESOLVE_SYMBOLS
-			struct fsym *s = addr2sym(frame->ret);
-			if (s)
-				tbuf_printf(out, " <%s+%i>", s->name, frame->ret - s->addr);
-#endif
-			tbuf_printf(out, " }\n");
-			frame = frame->rbp;
-		}
+		tbuf_printf(out, "    exc: %p, frame: %p\n",
+			    ((void **)fiber->exc)[3], ((void **)fiber->exc)[3] + 2 * sizeof(void *));
+#ifdef BACKTRACE
+		tbuf_printf(out, "    backtrace:\n%s",
+			    backtrace(fiber->last_frame, fiber->coro.stack, fiber->coro.stack_size));
 #endif
 	}
 }
diff --git a/core/tarantool.c b/core/tarantool.c
index 908562ee53..b1ad3e7153 100644
--- a/core/tarantool.c
+++ b/core/tarantool.c
@@ -232,12 +232,14 @@ main(int argc, char **argv)
 	int n_accepted, n_skipped;
 	FILE *f;
 
-	save_rbp(main_stack_frame);
-
 	master_pid = getpid();
 	stat_init();
 	palloc_init();
 
+#ifdef RESOLVE_SYMBOLS
+	load_symbols(argv[0]);
+#endif
+
 	const char *short_opt = "c:pvVD";
 	const struct option long_opt[] = {
 		{.name = "config",
@@ -443,9 +445,6 @@ main(int argc, char **argv)
 		atexit(remove_pid);
 	}
 
-#ifdef RESOLVE_SYMBOLS
-	load_syms(argv[0]);
-#endif
 	argv = init_set_proc_title(argc, argv);
 
 #if defined(UTILITY)
diff --git a/core/util.c b/core/util.c
index 9d446eaaa2..5b9017f708 100644
--- a/core/util.c
+++ b/core/util.c
@@ -99,49 +99,106 @@ xrealloc(void *ptr, size_t size)
 	return ret;
 }
 
-void *main_stack_frame;
-#if defined(__x86) || defined (__amd64) || defined(__i386)
-static void
-print_trace(FILE *f)
+#ifdef BACKTRACE
+
+/*
+ * we use global static buffer because it is too late to do
+ * any allocation when we are printing bactrace and fiber's stacks are small
+ */
+
+static char backtrace_buf[4096 * 4];
+
+/*
+ * note, stack unwinding code assumes that binary is compiled with frame pointers
+ */
+
+struct frame {
+	struct frame *rbp;
+	void *ret;
+};
+
+char *
+backtrace(void *frame_, void *stack, size_t stack_size)
 {
-	void *dummy = NULL;
-	struct frame *frame;
-	save_rbp(dummy);
-	frame = dummy;
-
-	void *stack_top = fiber->coro.stack + fiber->coro.stack_size;
-	void *stack_bottom = fiber->coro.stack;
-	if (strcmp(fiber->name, "sched") == 0) {
-		stack_top = main_stack_frame + 128;
-		stack_bottom = frame;
-	}
-	fprintf(f, "backtrace:\n");
+	struct frame *frame = frame_;
+	void *stack_top = stack + stack_size;
+	void *stack_bottom = stack;
+
+	char *p = backtrace_buf;
+	size_t r, len = sizeof(backtrace_buf);
 	while (stack_bottom <= (void *)frame && (void *)frame < stack_top) {
-		fprintf(f, "  - { frame: %p, pc: %p }\n", frame + 2 * sizeof(void *), frame->ret);
+		r = snprintf(p, len, "        - { frame: %p, caller: %p",
+			     (void *)frame + 2 * sizeof(void *), frame->ret);
+
+		if (r >= len)
+			goto out;
+		p += r;
+		len -= r;
+
+#ifdef RESOLVE_SYMBOLS
+		struct symbol *s = addr2symbol(frame->ret);
+		if (s != NULL) {
+			r = snprintf(p, len, " <%s+%i> ", s->name, frame->ret - s->addr);
+			if (r >= len)
+				goto out;
+			p += r;
+			len -= r;
+
+		}
+#endif
+		r = snprintf(p, len, " }\r\n");
+		if (r >= len)
+			goto out;
+		p += r;
+		len -= r;
+
+#ifdef RESOLVE_SYMBOLS
+		if (s != NULL && strcmp(s->name, "main") == 0)
+			break;
+
+#endif
 		frame = frame->rbp;
 	}
+	r = 0;
+out:
+	p += MIN(len - 1, r);
+	*p = 0;
+        return backtrace_buf;
 }
 #endif
 
 void __attribute__ ((noreturn))
-    assert_fail(const char *assertion, const char *file, unsigned int line, const char *function)
+assert_fail(const char *assertion, const char *file, unsigned int line, const char *function)
 {
 	fprintf(stderr, "%s:%i: %s: assertion %s failed.\n", file, line, function, assertion);
-#if defined(__x86) || defined (__amd64) || defined(__i386)
-	print_trace(stderr);
+
+#ifdef BACKTRACE
+	void *frame = frame_addess();
+	void *stack_top;
+	size_t stack_size;
+
+	if (fiber == NULL || fiber->name == NULL || strcmp(fiber->name, "sched") == 0) {
+		stack_top = frame; /* we don't know where the system stack top is */
+		stack_size = __libc_stack_end - frame;
+	} else {
+		stack_top = fiber->coro.stack;
+		stack_size = fiber->coro.stack_size;
+	}
+
+	fprintf(stderr, "%s", backtrace(frame, stack_top, stack_size));
 #endif
 	close_all_xcpt(0);
 	abort();
 }
 
 #ifdef RESOLVE_SYMBOLS
-struct fsym *fsyms;
-size_t fsyms_count;
+static struct symbol *symbols;
+static size_t symbol_count;
 
 int
-compare_fsym(const void *_a, const void *_b)
+compare_symbol(const void *_a, const void *_b)
 {
-	const struct fsym *a = _a, *b = _b;
+	const struct symbol *a = _a, *b = _b;
 	if (a->addr > b->addr)
 		return 1;
 	if (a->addr == b->addr)
@@ -149,11 +206,11 @@ compare_fsym(const void *_a, const void *_b)
 	return -1;
 }
 
-void __attribute__((constructor))
-load_syms(const char *name)
+void
+load_symbols(const char *name)
 {
 	long storage_needed;
-	asymbol **symbol_table;
+	asymbol **symbol_table = NULL;
 	long number_of_symbols;
 	bfd *h;
 	char **matching;
@@ -180,12 +237,24 @@ load_syms(const char *name)
 	if (number_of_symbols < 0)
 		goto out;
 
-	for (int i = 0; i < number_of_symbols; i++)
+	for (int i = 0; i < number_of_symbols; i++) {
+		struct bfd_section *section;
+		unsigned long int vma, size;
+		section = bfd_get_section(symbol_table[i]);
+		vma = bfd_get_section_vma(h, section);
+		size = bfd_get_section_size(section);
+
 		if (symbol_table[i]->flags & BSF_FUNCTION &&
-		    symbol_table[i]->value > 0)
-			fsyms_count++;
+		    vma + symbol_table[i]->value > 0 &&
+		    symbol_table[i]->value < size)
+			symbol_count++;
+	}
+
+	if (symbol_count == 0)
+		goto out;
+
 	j = 0;
-	fsyms = malloc(fsyms_count * sizeof(struct fsym));
+	symbols = malloc(symbol_count * sizeof(struct symbol));
 
 	for (int i = 0; i < number_of_symbols; i++) {
 		struct bfd_section *section;
@@ -198,42 +267,48 @@ load_syms(const char *name)
 		    vma + symbol_table[i]->value > 0 &&
 		    symbol_table[i]->value < size)
 		{
-			fsyms[j].name = strdup(symbol_table[i]->name);
-			fsyms[j].addr = (void *)(uintptr_t)(vma + symbol_table[i]->value);
-			fsyms[j].end = (void *)(uintptr_t)(vma + size);
+			symbols[j].name = strdup(symbol_table[i]->name);
+			symbols[j].addr = (void *)(uintptr_t)(vma + symbol_table[i]->value);
+			symbols[j].end = (void *)(uintptr_t)(vma + size);
 			j++;
 		}
 	}
 
-	qsort(fsyms, fsyms_count, sizeof(struct fsym), compare_fsym);
+	qsort(symbols, symbol_count, sizeof(struct symbol), compare_symbol);
+
+	for (int j = 0; j < symbol_count - 1; j++)
+		symbols[j].end = MIN(symbols[j].end, symbols[j + 1].addr - 1);
 
 out:
+	if (symbol_count == 0)
+		say_warn("no symbols were loaded");
+
 	if (symbol_table)
 		free(symbol_table);
 }
 
-struct fsym *
-addr2sym(void *addr)
+struct symbol *
+addr2symbol(void *addr)
 {
-	uint low = 0, high = fsyms_count, middle = -1;
-	struct fsym *ret, key = {.addr = addr};
+	int low = 0, high = symbol_count, middle = -1;
+	struct symbol *ret, key = {.addr = addr};
 
 	while(low < high) {
 		middle = low + ((high - low) >> 1);
-		int diff = compare_fsym(fsyms + middle, &key);
+		int diff = compare_symbol(symbols + middle, &key);
 
 		if (diff < 0) {
 			low = middle + 1;
 		} else if (diff > 0) {
 			high = middle;
 		} else {
-			ret = fsyms + middle;
+			ret = symbols + middle;
 			goto out;
 		}
 	}
-	ret = fsyms + middle - 1;
+	ret = symbols + high - 1;
 out:
-	if (middle != -1 && ret->addr <= key.addr && key.addr < ret->end)
+	if (middle != -1 && ret->addr <= key.addr && key.addr <= ret->end)
 		return ret;
 	return NULL;
 }
diff --git a/include/fiber.h b/include/fiber.h
index eb68fc13db..b0af53bfa9 100644
--- a/include/fiber.h
+++ b/include/fiber.h
@@ -39,6 +39,7 @@
 #include <tbuf.h>
 #include <say.h>
 #include <coro.h>
+#include <util.h>
 
 #define FIBER_EXIT -1
 
@@ -54,8 +55,8 @@ struct ring {
 
 struct fiber {
 	ev_io io;
-#if CORO_ASM
-	void *rbp;
+#ifdef BACKTRACE
+	void *last_frame;
 #endif
 	int csw;
 	struct tarantool_coro coro;
diff --git a/include/tbuf.h b/include/tbuf.h
index 729a8494dc..338ecada8b 100644
--- a/include/tbuf.h
+++ b/include/tbuf.h
@@ -63,7 +63,6 @@ size_t tbuf_reserve(struct tbuf *b, size_t count);
 void tbuf_reset(struct tbuf *b);
 void *tbuf_peek(struct tbuf *b, size_t count);
 
-void tbuf_append(struct tbuf *b, const void *data, size_t len);
 void tbuf_append_field(struct tbuf *b, void *f);
 void tbuf_printf(struct tbuf *b, const char *format, ...)
     __attribute__ ((format(FORMAT_PRINTF, 2, 3)));
diff --git a/include/util.h b/include/util.h
index 8757f7a6a3..4f23a4df19 100644
--- a/include/util.h
+++ b/include/util.h
@@ -112,29 +112,23 @@ void *xrealloc(void *ptr, size_t size);
 
 void __gcov_flush();
 
-struct frame {
-	struct frame *rbp;
-	void *ret;
-};
 
-#if __amd64
-#  define save_rbp(rbp) asm("movq %%rbp, %0"::"m"(rbp))
-#elif __i386
-#  define save_rbp(rbp) asm("movl %%ebp, %0"::"m"(rbp))
-#else
-#  define save_rbp(rbp) (void)0
-#endif
+extern void *__libc_stack_end;
 
-extern void *main_stack_frame;
+#if __GNUC__ && (defined(__x86) || defined (__amd64) || defined(__i386))
+#define BACKTRACE
+#define frame_addess() __builtin_frame_address(0)
+char *backtrace(void *frame, void *stack, size_t stack_size);
+#endif
 
 #ifdef RESOLVE_SYMBOLS
-struct fsym {
-	void* addr;
+struct symbol {
+	void *addr;
 	const char *name;
 	void *end;
 };
-struct fsym *addr2sym(void *addr);
-void load_syms(const char *name);
+struct symbol *addr2symbol(void *addr);
+void load_symbols(const char *name);
 #endif
 
 #ifdef NDEBUG
diff --git a/third_party/coro/coro.c b/third_party/coro/coro.c
index 3546628c19..ff41ffd186 100644
--- a/third_party/coro/coro.c
+++ b/third_party/coro/coro.c
@@ -305,11 +305,7 @@ coro_create (coro_context *ctx, coro_func coro, void *arg, void *sptr, long ssiz
 
 # elif CORO_ASM
 
-  extern void * main_stack_frame;
-
   ctx->sp = (void **)(ssize + (char *)sptr);
-  *--ctx->sp = (void *)main_stack_frame;
-  *--ctx->sp = (void *)main_stack_frame;
   *--ctx->sp = (void *)0xdeadbeef; /* needed for alignment only */
   *--ctx->sp = (void *)coro_init;
   ctx->sp -= NUM_SAVED;
-- 
GitLab