]> granicus.if.org Git - strace/blob - unwind.c
syscall_entering_trace: set TCB_FILTERED along with TCB_HIDE_LOG
[strace] / unwind.c
1 /*
2  * Copyright (c) 2013 Luca Clementi <luca.clementi@gmail.com>
3  * Copyright (c) 2013-2018 The strace developers.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. The name of the author may not be used to endorse or promote products
14  *    derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27
28 #include "defs.h"
29 #include "unwind.h"
30
31 #ifdef USE_DEMANGLE
32 # if defined HAVE_DEMANGLE_H
33 #  include <demangle.h>
34 # elif defined HAVE_LIBIBERTY_DEMANGLE_H
35 #  include <libiberty/demangle.h>
36 # endif
37 #endif
38
39 /*
40  * Type used in stacktrace capturing
41  */
42 struct call_t {
43         struct call_t *next;
44         char *output_line;
45 };
46
47 struct unwind_queue_t {
48         struct call_t *tail;
49         struct call_t *head;
50 };
51
52 static void queue_print(struct unwind_queue_t *queue);
53
54 static const char asprintf_error_str[] = "???";
55
56 void
57 unwind_init(void)
58 {
59         if (unwinder.init)
60                 unwinder.init();
61 }
62
63 void
64 unwind_tcb_init(struct tcb *tcp)
65 {
66         if (tcp->unwind_queue)
67                 return;
68
69         tcp->unwind_queue = xmalloc(sizeof(*tcp->unwind_queue));
70         tcp->unwind_queue->head = NULL;
71         tcp->unwind_queue->tail = NULL;
72
73         tcp->unwind_ctx = unwinder.tcb_init(tcp);
74 }
75
76 void
77 unwind_tcb_fin(struct tcb *tcp)
78 {
79         if (!tcp->unwind_queue)
80                 return;
81
82         queue_print(tcp->unwind_queue);
83         free(tcp->unwind_queue);
84         tcp->unwind_queue = NULL;
85
86         unwinder.tcb_fin(tcp);
87         tcp->unwind_ctx = NULL;
88 }
89
90 /*
91  * printing an entry in stack to stream or buffer
92  */
93 /*
94  * we want to keep the format used by backtrace_symbols from the glibc
95  *
96  * ./a.out() [0x40063d]
97  * ./a.out() [0x4006bb]
98  * ./a.out() [0x4006c6]
99  * /lib64/libc.so.6(__libc_start_main+0xed) [0x7fa2f8a5976d]
100  * ./a.out() [0x400569]
101  */
102 #define STACK_ENTRY_SYMBOL_FMT(SYM)             \
103         " > %s(%s+0x%lx) [0x%lx]\n",            \
104         binary_filename,                        \
105         (SYM),                                  \
106         (unsigned long) function_offset,        \
107         true_offset
108 #define STACK_ENTRY_NOSYMBOL_FMT                \
109         " > %s() [0x%lx]\n",                    \
110         binary_filename, true_offset
111 #define STACK_ENTRY_BUG_FMT                     \
112         " > BUG IN %s\n"
113 #define STACK_ENTRY_ERROR_WITH_OFFSET_FMT       \
114         " > %s [0x%lx]\n", error, true_offset
115 #define STACK_ENTRY_ERROR_FMT                   \
116         " > %s\n", error
117
118 static void
119 print_call_cb(void *dummy,
120               const char *binary_filename,
121               const char *symbol_name,
122               unwind_function_offset_t function_offset,
123               unsigned long true_offset)
124 {
125         if (symbol_name && (symbol_name[0] != '\0')) {
126 #ifdef USE_DEMANGLE
127                 char *demangled_name =
128                         cplus_demangle(symbol_name,
129                                        DMGL_AUTO | DMGL_PARAMS);
130 #endif
131                 tprintf(STACK_ENTRY_SYMBOL_FMT(
132 #ifdef USE_DEMANGLE
133                                                demangled_name ? demangled_name :
134 #endif
135                                                symbol_name));
136 #ifdef USE_DEMANGLE
137                 free(demangled_name);
138 #endif
139         }
140         else if (binary_filename)
141                 tprintf(STACK_ENTRY_NOSYMBOL_FMT);
142         else
143                 tprintf(STACK_ENTRY_BUG_FMT, __func__);
144
145         line_ended();
146 }
147
148 static void
149 print_error_cb(void *dummy,
150                const char *error,
151                unsigned long true_offset)
152 {
153         if (true_offset)
154                 tprintf(STACK_ENTRY_ERROR_WITH_OFFSET_FMT);
155         else
156                 tprintf(STACK_ENTRY_ERROR_FMT);
157
158         line_ended();
159 }
160
161 static char *
162 sprint_call_or_error(const char *binary_filename,
163                      const char *symbol_name,
164                      unwind_function_offset_t function_offset,
165                      unsigned long true_offset,
166                      const char *error)
167 {
168         char *output_line = NULL;
169         int n;
170
171         if (symbol_name) {
172 #ifdef USE_DEMANGLE
173                 char *demangled_name =
174                         cplus_demangle(symbol_name,
175                                        DMGL_AUTO | DMGL_PARAMS);
176 #endif
177                 n = asprintf(&output_line,
178                              STACK_ENTRY_SYMBOL_FMT(
179 #ifdef USE_DEMANGLE
180                                                     demangled_name ? demangled_name :
181 #endif
182                                                     symbol_name));
183 #ifdef USE_DEMANGLE
184                 free(demangled_name);
185 #endif
186         }
187         else if (binary_filename)
188                 n = asprintf(&output_line, STACK_ENTRY_NOSYMBOL_FMT);
189         else if (error)
190                 n = true_offset
191                         ? asprintf(&output_line, STACK_ENTRY_ERROR_WITH_OFFSET_FMT)
192                         : asprintf(&output_line, STACK_ENTRY_ERROR_FMT);
193         else
194                 n = asprintf(&output_line, STACK_ENTRY_BUG_FMT, __func__);
195
196         if (n < 0) {
197                 perror_func_msg("asprintf");
198                 output_line = (char *) asprintf_error_str;
199         }
200
201         return output_line;
202 }
203
204 /*
205  * queue manipulators
206  */
207 static void
208 queue_put(struct unwind_queue_t *queue,
209           const char *binary_filename,
210           const char *symbol_name,
211           unwind_function_offset_t function_offset,
212           unsigned long true_offset,
213           const char *error)
214 {
215         struct call_t *call;
216
217         call = xmalloc(sizeof(*call));
218         call->output_line = sprint_call_or_error(binary_filename,
219                                                  symbol_name,
220                                                  function_offset,
221                                                  true_offset,
222                                                  error);
223         call->next = NULL;
224
225         if (!queue->head) {
226                 queue->head = call;
227                 queue->tail = call;
228         } else {
229                 queue->tail->next = call;
230                 queue->tail = call;
231         }
232 }
233
234 static void
235 queue_put_call(void *queue,
236                const char *binary_filename,
237                const char *symbol_name,
238                unwind_function_offset_t function_offset,
239                unsigned long true_offset)
240 {
241         queue_put(queue,
242                   binary_filename,
243                   symbol_name,
244                   function_offset,
245                   true_offset,
246                   NULL);
247 }
248
249 static void
250 queue_put_error(void *queue,
251                 const char *error,
252                 unsigned long ip)
253 {
254         queue_put(queue, NULL, NULL, 0, ip, error);
255 }
256
257 static void
258 queue_print(struct unwind_queue_t *queue)
259 {
260         struct call_t *call, *tmp;
261
262         queue->tail = NULL;
263         call = queue->head;
264         queue->head = NULL;
265         while (call) {
266                 tmp = call;
267                 call = call->next;
268
269                 tprints(tmp->output_line);
270                 line_ended();
271
272                 if (tmp->output_line != asprintf_error_str)
273                         free(tmp->output_line);
274
275                 tmp->output_line = NULL;
276                 tmp->next = NULL;
277                 free(tmp);
278         }
279 }
280
281 /*
282  * printing stack
283  */
284 void
285 unwind_tcb_print(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                 debug_func_msg("head: tcp=%p, queue=%p",
295                                tcp, tcp->unwind_queue->head);
296                 queue_print(tcp->unwind_queue);
297         } else
298                 unwinder.tcb_walk(tcp, print_call_cb, print_error_cb, NULL);
299 }
300
301 /*
302  * capturing stack
303  */
304 void
305 unwind_tcb_capture(struct tcb *tcp)
306 {
307 #if SUPPORTED_PERSONALITIES > 1
308         if (tcp->currpers != DEFAULT_PERSONALITY) {
309                 /* disable stack trace */
310                 return;
311         }
312 #endif
313         if (tcp->unwind_queue->head)
314                 error_msg_and_die("bug: unprinted entries in queue");
315         else {
316                 debug_func_msg("walk: tcp=%p, queue=%p",
317                                tcp, tcp->unwind_queue->head);
318                 unwinder.tcb_walk(tcp, queue_put_call, queue_put_error,
319                                   tcp->unwind_queue);
320         }
321 }