1 /*
2 * Copyright (c) 2013 Luca Clementi <luca.clementi@gmail.com>
3 * Copyright (c) 2013-2023 The strace developers.
4 *
5 * SPDX-License-Identifier: LGPL-2.1-or-later
6 */
7
8 #include "defs.h"
9 #include "unwind.h"
10
11 #ifdef USE_DEMANGLE
12 /* Avoids including libiberty.h that has several undesirable definitions */
13 # define LIBIBERTY_H
14
15 # if defined HAVE_DEMANGLE_H
16 # include <demangle.h>
17 # elif defined HAVE_LIBIBERTY_DEMANGLE_H
18 # include <libiberty/demangle.h>
19 # endif /* HAVE_DEMANGLE_H */
20 #endif /* USE_DEMANGLE */
21
22 /*
23 * Type used in stacktrace capturing
24 */
25 struct call_t {
26 struct call_t *next;
27 char *output_line;
28 };
29
30 struct unwind_queue_t {
31 struct call_t *tail;
32 struct call_t *head;
33 };
34
35 static void queue_print(struct unwind_queue_t *queue);
36
37 static const char asprintf_error_str[] = "???";
38
39 void
40 unwind_init(void)
41 {
42 if (unwinder.init)
43 unwinder.init();
44 }
45
46 void
47 unwind_tcb_init(struct tcb *tcp)
48 {
49 if (tcp->unwind_queue)
50 return;
51
52 tcp->unwind_queue = xmalloc(sizeof(*tcp->unwind_queue));
53 tcp->unwind_queue->head = NULL;
54 tcp->unwind_queue->tail = NULL;
55
56 tcp->unwind_ctx = unwinder.tcb_init(tcp);
57 }
58
59 void
60 unwind_tcb_fin(struct tcb *tcp)
61 {
62 if (!tcp->unwind_queue)
63 return;
64
65 queue_print(tcp->unwind_queue);
66 free(tcp->unwind_queue);
67 tcp->unwind_queue = NULL;
68
69 unwinder.tcb_fin(tcp);
70 tcp->unwind_ctx = NULL;
71 }
72
73 /*
74 * printing an entry in stack to stream or buffer
75 */
76 /*
77 * we want to keep the format used by backtrace_symbols from the glibc
78 *
79 * ./a.out() [0x40063d]
80 * ./a.out() [0x4006bb]
81 * ./a.out() [0x4006c6]
82 * /lib64/libc.so.6(__libc_start_main+0xed) [0x7fa2f8a5976d]
83 * ./a.out() [0x400569]
84 */
85 #define STACK_ENTRY_SYMBOL_FMT(SYM) \
86 " > %s(%s+0x%lx) [0x%lx]\n", \
87 binary_filename, \
88 (SYM), \
89 (unsigned long) function_offset, \
90 true_offset
91 #define STACK_ENTRY_NOSYMBOL_FMT \
92 " > %s() [0x%lx]\n", \
93 binary_filename, true_offset
94 #define STACK_ENTRY_BUG_FMT \
95 " > BUG IN %s\n"
96 #define STACK_ENTRY_ERROR_WITH_OFFSET_FMT \
97 " > %s [0x%lx]\n", error, true_offset
98 #define STACK_ENTRY_ERROR_FMT \
99 " > %s\n", error
100
101 static void
102 print_call_cb(void *dummy,
103 const char *binary_filename,
104 const char *symbol_name,
105 unwind_function_offset_t function_offset,
106 unsigned long true_offset)
107 {
108 if (symbol_name && (symbol_name[0] != '\0')) {
109 #ifdef USE_DEMANGLE
110 char *demangled_name =
111 cplus_demangle(symbol_name,
112 DMGL_AUTO | DMGL_PARAMS);
113 #endif
114 tprintf_string(STACK_ENTRY_SYMBOL_FMT(
115 #ifdef USE_DEMANGLE
116 demangled_name ? demangled_name :
117 #endif
118 symbol_name));
119 #ifdef USE_DEMANGLE
120 free(demangled_name);
121 #endif
122 }
123 else if (binary_filename)
124 tprintf_string(STACK_ENTRY_NOSYMBOL_FMT);
125 else
126 tprintf_string(STACK_ENTRY_BUG_FMT, __func__);
127
128 line_ended();
129 }
130
131 static void
132 print_error_cb(void *dummy,
133 const char *error,
134 unsigned long true_offset)
135 {
136 if (true_offset)
137 tprintf_string(STACK_ENTRY_ERROR_WITH_OFFSET_FMT);
138 else
139 tprintf_string(STACK_ENTRY_ERROR_FMT);
140
141 line_ended();
142 }
143
144 static char *
145 sprint_call_or_error(const char *binary_filename,
146 const char *symbol_name,
147 unwind_function_offset_t function_offset,
148 unsigned long true_offset,
149 const char *error)
150 {
151 char *output_line = NULL;
152 int n;
153
154 if (symbol_name) {
155 #ifdef USE_DEMANGLE
156 char *demangled_name =
157 cplus_demangle(symbol_name,
158 DMGL_AUTO | DMGL_PARAMS);
159 #endif
160 n = asprintf(&output_line,
161 STACK_ENTRY_SYMBOL_FMT(
162 #ifdef USE_DEMANGLE
163 demangled_name ? demangled_name :
164 #endif
165 symbol_name));
166 #ifdef USE_DEMANGLE
167 free(demangled_name);
168 #endif
169 }
170 else if (binary_filename)
171 n = asprintf(&output_line, STACK_ENTRY_NOSYMBOL_FMT);
172 else if (error)
173 n = true_offset
174 ? asprintf(&output_line, STACK_ENTRY_ERROR_WITH_OFFSET_FMT)
175 : asprintf(&output_line, STACK_ENTRY_ERROR_FMT);
176 else
177 n = asprintf(&output_line, STACK_ENTRY_BUG_FMT, __func__);
178
179 if (n < 0) {
180 perror_func_msg("asprintf");
181 output_line = (char *) asprintf_error_str;
182 }
183
184 return output_line;
185 }
186
187 /*
188 * queue manipulators
189 */
190 static void
191 queue_put(struct unwind_queue_t *queue,
192 const char *binary_filename,
193 const char *symbol_name,
194 unwind_function_offset_t function_offset,
195 unsigned long true_offset,
196 const char *error)
197 {
198 struct call_t *call;
199
200 call = xmalloc(sizeof(*call));
201 call->output_line = sprint_call_or_error(binary_filename,
202 symbol_name,
203 function_offset,
204 true_offset,
205 error);
206 call->next = NULL;
207
208 if (!queue->head) {
209 queue->head = call;
210 queue->tail = call;
211 } else {
212 queue->tail->next = call;
213 queue->tail = call;
214 }
215 }
216
217 static void
218 queue_put_call(void *queue,
219 const char *binary_filename,
220 const char *symbol_name,
221 unwind_function_offset_t function_offset,
222 unsigned long true_offset)
223 {
224 queue_put(queue,
225 binary_filename,
226 symbol_name,
227 function_offset,
228 true_offset,
229 NULL);
230 }
231
232 static void
233 queue_put_error(void *queue,
234 const char *error,
235 unsigned long ip)
236 {
237 queue_put(queue, NULL, NULL, 0, ip, error);
238 }
239
240 static void
241 queue_print(struct unwind_queue_t *queue)
242 {
243 struct call_t *call, *tmp;
244
245 queue->tail = NULL;
246 call = queue->head;
247 queue->head = NULL;
248 while (call) {
249 tmp = call;
250 call = call->next;
251
252 tprints_string(tmp->output_line);
253 line_ended();
254
255 if (tmp->output_line != asprintf_error_str)
256 free(tmp->output_line);
257
258 tmp->output_line = NULL;
259 tmp->next = NULL;
260 free(tmp);
261 }
262 }
263
264 /*
265 * printing stack
266 */
267 void
268 unwind_tcb_print(struct tcb *tcp)
269 {
270 #if defined(USE_LIBUNWIND) && (SUPPORTED_PERSONALITIES > 1)
271 if (tcp->currpers != DEFAULT_PERSONALITY) {
272 /* disable stack trace */
273 return;
274 }
275 #endif
276 if (tcp->unwind_queue->head) {
277 debug_func_msg("head: tcp=%p, queue=%p",
278 tcp, tcp->unwind_queue->head);
279 queue_print(tcp->unwind_queue);
280 } else
281 unwinder.tcb_walk(tcp, print_call_cb, print_error_cb, NULL);
282 }
283
284 /*
285 * capturing stack
286 */
287 void
288 unwind_tcb_capture(struct tcb *tcp)
289 {
290 #if defined(USE_LIBUNWIND) && (SUPPORTED_PERSONALITIES > 1)
291 if (tcp->currpers != DEFAULT_PERSONALITY) {
292 /* disable stack trace */
293 return;
294 }
295 #endif
296 if (tcp->unwind_queue->head)
297 error_msg_and_die("bug: unprinted entries in queue");
298 else {
299 debug_func_msg("walk: tcp=%p, queue=%p",
300 tcp, tcp->unwind_queue->head);
301 unwinder.tcb_walk(tcp, queue_put_call, queue_put_error,
302 tcp->unwind_queue);
303 }
304 }