1 /* Copyright (c) 2006-2018 Dovecot authors, see the included COPYING file */
2 
3 #include "lib.h"
4 #include "str.h"
5 #include "backtrace-string.h"
6 
7 #define MAX_STACK_SIZE 30
8 #define BACKTRACE_SKIP_PREFIX "backtrace_"
9 
10 #if defined(HAVE_LIBUNWIND)
11 
12 #include <libunwind.h>
13 
backtrace_append_unwind(string_t * str)14 static int backtrace_append_unwind(string_t *str)
15 {
16 	size_t str_orig_size = str_len(str);
17 	char proc_name[256];
18 	int ret;
19 	unsigned int fp = 0;
20 	unw_cursor_t c;
21 	unw_context_t ctx;
22 	unw_proc_info_t pip;
23 	bool success = FALSE;
24 
25 	if ((ret = unw_getcontext(&ctx)) != 0) {
26 		str_printfa(str, "unw_getcontext() failed: %d", ret);
27 		return -1;
28 	}
29 	if ((ret = unw_init_local(&c, &ctx)) != 0) {
30 		str_printfa(str, "unw_init_local() failed: %d", ret);
31 		return -1;
32 	}
33 
34 	do {
35 		str_printfa(str, "#%d ", fp);
36 		if ((ret = unw_get_proc_info(&c, &pip)) != 0) {
37 			str_printfa(str, "[unw_get_proc_info_failed(): %d]", ret);
38 		} else if (pip.start_ip == 0 || pip.end_ip == 0) {
39 			str_append(str, "[no start/end information]");
40 		} else if ((ret = unw_get_proc_name(&c, proc_name, sizeof(proc_name), 0)) != 0 &&
41 			   ret != UNW_ENOMEM) {
42 			str_printfa(str, "[unw_get_proc_name() failed: %d]", ret);
43 		} else if (!success && str_begins(proc_name, BACKTRACE_SKIP_PREFIX)) {
44 			str_truncate(str, str_orig_size);
45 			continue;
46 		} else {
47 			str_append_max(str, proc_name, sizeof(proc_name));
48 			str_printfa(str, "[0x%08zx]", pip.start_ip);
49 			success = TRUE;
50 		}
51 		str_append(str, " -> ");
52 		fp++;
53 	} while ((ret = unw_step(&c)) > 0);
54 
55 	/* remove ' -> ' */
56 	if (str->used > 4)
57 		str_truncate(str, str->used - 4);
58 	return ret == 0 && success ? 0 : -1;
59 }
60 #endif
61 
62 #if defined(HAVE_BACKTRACE_SYMBOLS) && defined(HAVE_EXECINFO_H)
63 /* Linux */
64 #include <execinfo.h>
65 
backtrace_append_libc(string_t * str)66 static int backtrace_append_libc(string_t *str)
67 {
68 	size_t str_orig_size = str_len(str);
69 	void *stack[MAX_STACK_SIZE];
70 	char **strings;
71 	int ret, i;
72 
73 	ret = backtrace(stack, N_ELEMENTS(stack));
74 	if (ret <= 0)
75 		return -1;
76 
77 	strings = backtrace_symbols(stack, ret);
78 	for (i = 0; i < ret; i++) {
79 		if (str_len(str) > str_orig_size)
80 			str_append(str, " -> ");
81 
82 		if (strings == NULL) {
83 			/* out of memory case */
84 			str_printfa(str, "0x%p", stack[i]);
85 		} else if (str_len(str) != str_orig_size ||
86 			   !str_begins(strings[i], BACKTRACE_SKIP_PREFIX))
87 			str_append(str, strings[i]);
88 	}
89 	free(strings);
90 	return 0;
91 }
92 #elif defined(HAVE_WALKCONTEXT) && defined(HAVE_UCONTEXT_H)
93 /* Solaris */
94 #include <ucontext.h>
95 
96 struct walk_context {
97 	string_t *str;
98 	unsigned int pos;
99 };
100 
walk_callback(uintptr_t ptr,int signo ATTR_UNUSED,void * context)101 static int walk_callback(uintptr_t ptr, int signo ATTR_UNUSED,
102 			 void *context)
103 {
104 	struct walk_context *ctx = context;
105 
106 	if (ctx->pos > 0)
107 		str_append(ctx->str, " -> ");
108 	str_printfa(ctx->str, "0x%p", (void *)ptr);
109 	ctx->pos++;
110 	return 0;
111 }
112 
backtrace_append_libc(string_t * str)113 static int backtrace_append_libc(string_t *str)
114 {
115 	ucontext_t uc;
116 	struct walk_context ctx;
117 
118 	if (getcontext(&uc) < 0)
119 		return -1;
120 
121 	ctx.str = str;
122 	ctx.pos = 0;
123 	walkcontext(&uc, walk_callback, &ctx);
124 	return 0;
125 }
126 #else
backtrace_append_libc(string_t * str ATTR_UNUSED)127 static int backtrace_append_libc(string_t *str ATTR_UNUSED)
128 {
129 	return -1;
130 }
131 #endif
132 
backtrace_append(string_t * str)133 int backtrace_append(string_t *str)
134 {
135 #if defined(HAVE_LIBUNWIND)
136 	size_t orig_len = str_len(str);
137 	if (backtrace_append_unwind(str) == 0)
138 		return 0;
139 	/* failed to get useful backtrace. libc's own method is likely
140 	   better. */
141 	str_truncate(str, orig_len);
142 #endif
143 	return backtrace_append_libc(str);
144 }
145 
backtrace_get(const char ** backtrace_r)146 int backtrace_get(const char **backtrace_r)
147 {
148 	string_t *str;
149 
150 	str = t_str_new(512);
151 	if (backtrace_append(str) < 0)
152 		return -1;
153 
154 	*backtrace_r = str_c(str);
155 	return 0;
156 }
157