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