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