]> granicus.if.org Git - strace/blob - unwind.c
Simplify print_lld_from_low_high_val ifdefery
[strace] / unwind.c
1 /*
2  * Copyright (c) 2013 Luca Clementi <luca.clementi@gmail.com>
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  * 3. The name of the author may not be used to endorse or promote products
13  *    derived from this software without specific prior written permission.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #include "defs.h"
28 #include <limits.h>
29 #include <libunwind-ptrace.h>
30
31 #ifdef _LARGEFILE64_SOURCE
32 # ifdef HAVE_FOPEN64
33 #  define fopen_for_input fopen64
34 # else
35 #  define fopen_for_input fopen
36 # endif
37 #else
38 # define fopen_for_input fopen
39 #endif
40
41 #define DPRINTF(F, A, ...) if (debug_flag) error_msg("[unwind(" A ")] " F, __VA_ARGS__)
42
43 /*
44  * Кeep a sorted array of cache entries,
45  * so that we can binary search through it.
46  */
47 struct mmap_cache_t {
48         /**
49          * example entry:
50          * 7fabbb09b000-7fabbb09f000 r-xp 00179000 fc:00 1180246 /lib/libc-2.11.1.so
51          *
52          * start_addr  is 0x7fabbb09b000
53          * end_addr    is 0x7fabbb09f000
54          * mmap_offset is 0x179000
55          * binary_filename is "/lib/libc-2.11.1.so"
56          */
57         unsigned long start_addr;
58         unsigned long end_addr;
59         unsigned long mmap_offset;
60         char *binary_filename;
61 };
62
63 /*
64  * Type used in stacktrace walker
65  */
66 typedef void (*call_action_fn)(void *data,
67                                const char *binary_filename,
68                                const char *symbol_name,
69                                unw_word_t function_offset,
70                                unsigned long true_offset);
71 typedef void (*error_action_fn)(void *data,
72                                 const char *error,
73                                 unsigned long true_offset);
74
75 /*
76  * Type used in stacktrace capturing
77  */
78 struct call_t {
79        struct call_t* next;
80        char *output_line;
81 };
82
83 struct queue_t {
84        struct call_t *tail;
85        struct call_t *head;
86 };
87
88 static void queue_print(struct queue_t *queue);
89 static void delete_mmap_cache(struct tcb *tcp, const char *caller);
90
91 static unw_addr_space_t libunwind_as;
92 static unsigned int mmap_cache_generation;
93
94 void
95 unwind_init(void)
96 {
97         libunwind_as = unw_create_addr_space(&_UPT_accessors, 0);
98         if (!libunwind_as)
99                 error_msg_and_die("failed to create address space for stack tracing");
100         unw_set_caching_policy(libunwind_as, UNW_CACHE_GLOBAL);
101 }
102
103 void
104 unwind_tcb_init(struct tcb *tcp)
105 {
106         if (tcp->libunwind_ui)
107                 return;
108
109         tcp->libunwind_ui = _UPT_create(tcp->pid);
110         if (!tcp->libunwind_ui)
111                 die_out_of_memory();
112
113         tcp->queue = xmalloc(sizeof(*tcp->queue));
114         tcp->queue->head = NULL;
115         tcp->queue->tail = NULL;
116 }
117
118 void
119 unwind_tcb_fin(struct tcb *tcp)
120 {
121         queue_print(tcp->queue);
122         free(tcp->queue);
123         tcp->queue = NULL;
124
125         delete_mmap_cache(tcp, __FUNCTION__);
126
127         _UPT_destroy(tcp->libunwind_ui);
128         tcp->libunwind_ui = NULL;
129 }
130
131 /*
132  * caching of /proc/ID/maps for each process to speed up stack tracing
133  *
134  * The cache must be refreshed after syscalls that affect memory mappings,
135  * e.g. mmap, mprotect, munmap, execve.
136  */
137 static void
138 build_mmap_cache(struct tcb* tcp)
139 {
140         FILE *fp;
141         struct mmap_cache_t *cache_head;
142         /* start with a small dynamically-allocated array and then expand it */
143         size_t cur_array_size = 10;
144         char filename[sizeof("/proc/4294967296/maps")];
145         char buffer[PATH_MAX + 80];
146
147         unw_flush_cache(libunwind_as, 0, 0);
148
149         sprintf(filename, "/proc/%u/maps", tcp->pid);
150         fp = fopen_for_input(filename, "r");
151         if (!fp) {
152                 perror_msg("fopen: %s", filename);
153                 return;
154         }
155
156         cache_head = xcalloc(cur_array_size, sizeof(*cache_head));
157
158         while (fgets(buffer, sizeof(buffer), fp) != NULL) {
159                 struct mmap_cache_t *entry;
160                 unsigned long start_addr, end_addr, mmap_offset;
161                 char exec_bit;
162                 char binary_path[PATH_MAX];
163
164                 if (sscanf(buffer, "%lx-%lx %*c%*c%c%*c %lx %*x:%*x %*d %[^\n]",
165                            &start_addr, &end_addr, &exec_bit,
166                            &mmap_offset, binary_path) != 5)
167                         continue;
168
169                 /* ignore mappings that have no PROT_EXEC bit set */
170                 if (exec_bit != 'x')
171                         continue;
172
173                 if (end_addr < start_addr) {
174                         error_msg("%s: unrecognized file format", filename);
175                         break;
176                 }
177
178                 /*
179                  * sanity check to make sure that we're storing
180                  * non-overlapping regions in ascending order
181                  */
182                 if (tcp->mmap_cache_size > 0) {
183                         entry = &cache_head[tcp->mmap_cache_size - 1];
184                         if (entry->start_addr == start_addr &&
185                             entry->end_addr == end_addr) {
186                                 /* duplicate entry, e.g. [vsyscall] */
187                                 continue;
188                         }
189                         if (start_addr <= entry->start_addr ||
190                             start_addr < entry->end_addr) {
191                                 error_msg("%s: overlapping memory region",
192                                           filename);
193                                 continue;
194                         }
195                 }
196
197                 if (tcp->mmap_cache_size >= cur_array_size) {
198                         cur_array_size *= 2;
199                         cache_head = xreallocarray(cache_head, cur_array_size,
200                                                    sizeof(*cache_head));
201                 }
202
203                 entry = &cache_head[tcp->mmap_cache_size];
204                 entry->start_addr = start_addr;
205                 entry->end_addr = end_addr;
206                 entry->mmap_offset = mmap_offset;
207                 entry->binary_filename = xstrdup(binary_path);
208                 tcp->mmap_cache_size++;
209         }
210         fclose(fp);
211         tcp->mmap_cache = cache_head;
212         tcp->mmap_cache_generation = mmap_cache_generation;
213
214         DPRINTF("tgen=%u, ggen=%u, tcp=%p, cache=%p",
215                 "cache-build",
216                 tcp->mmap_cache_generation,
217                 mmap_cache_generation,
218                 tcp, tcp->mmap_cache);
219 }
220
221 /* deleting the cache */
222 static void
223 delete_mmap_cache(struct tcb *tcp, const char *caller)
224 {
225         unsigned int i;
226
227         DPRINTF("tgen=%u, ggen=%u, tcp=%p, cache=%p, caller=%s",
228                 "cache-delete",
229                 tcp->mmap_cache_generation,
230                 mmap_cache_generation,
231                 tcp, tcp->mmap_cache, caller);
232
233         for (i = 0; i < tcp->mmap_cache_size; i++) {
234                 free(tcp->mmap_cache[i].binary_filename);
235                 tcp->mmap_cache[i].binary_filename = NULL;
236         }
237         free(tcp->mmap_cache);
238         tcp->mmap_cache = NULL;
239         tcp->mmap_cache_size = 0;
240 }
241
242 static bool
243 rebuild_cache_if_invalid(struct tcb *tcp, const char *caller)
244 {
245         if ((tcp->mmap_cache_generation != mmap_cache_generation)
246             && tcp->mmap_cache)
247                 delete_mmap_cache(tcp, caller);
248
249         if (!tcp->mmap_cache)
250                 build_mmap_cache(tcp);
251
252         if (!tcp->mmap_cache || !tcp->mmap_cache_size)
253                 return false;
254         else
255                 return true;
256 }
257
258 void
259 unwind_cache_invalidate(struct tcb* tcp)
260 {
261 #if SUPPORTED_PERSONALITIES > 1
262         if (tcp->currpers != DEFAULT_PERSONALITY) {
263                 /* disable strack trace */
264                 return;
265         }
266 #endif
267         mmap_cache_generation++;
268         DPRINTF("tgen=%u, ggen=%u, tcp=%p, cache=%p", "increment",
269                 tcp->mmap_cache_generation,
270                 mmap_cache_generation,
271                 tcp,
272                 tcp->mmap_cache);
273 }
274
275 static void
276 get_symbol_name(unw_cursor_t *cursor, char **name,
277                 size_t *size, unw_word_t *offset)
278 {
279         for (;;) {
280                 int rc = unw_get_proc_name(cursor, *name, *size, offset);
281                 if (rc == 0)
282                         break;
283                 if (rc != -UNW_ENOMEM) {
284                         **name = '\0';
285                         *offset = 0;
286                         break;
287                 }
288                 *name = xreallocarray(*name, 2, *size);
289                 *size *= 2;
290         }
291 }
292
293 static int
294 print_stack_frame(struct tcb *tcp,
295                   call_action_fn call_action,
296                   error_action_fn error_action,
297                   void *data,
298                   unw_cursor_t *cursor,
299                   char **symbol_name,
300                   size_t *symbol_name_size)
301 {
302         unw_word_t ip;
303         int lower = 0;
304         int upper = (int) tcp->mmap_cache_size - 1;
305
306         if (unw_get_reg(cursor, UNW_REG_IP, &ip) < 0) {
307                 perror_msg("Can't walk the stack of process %d", tcp->pid);
308                 return -1;
309         }
310
311         while (lower <= upper) {
312                 struct mmap_cache_t *cur_mmap_cache;
313                 int mid = (upper + lower) / 2;
314
315                 cur_mmap_cache = &tcp->mmap_cache[mid];
316
317                 if (ip >= cur_mmap_cache->start_addr &&
318                     ip < cur_mmap_cache->end_addr) {
319                         unsigned long true_offset;
320                         unw_word_t function_offset;
321
322                         get_symbol_name(cursor, symbol_name, symbol_name_size,
323                                         &function_offset);
324                         true_offset = ip - cur_mmap_cache->start_addr +
325                                 cur_mmap_cache->mmap_offset;
326                         call_action(data,
327                                     cur_mmap_cache->binary_filename,
328                                     *symbol_name,
329                                     function_offset,
330                                     true_offset);
331                         return 0;
332                 }
333                 else if (ip < cur_mmap_cache->start_addr)
334                         upper = mid - 1;
335                 else
336                         lower = mid + 1;
337         }
338
339         /*
340          * there is a bug in libunwind >= 1.0
341          * after a set_tid_address syscall
342          * unw_get_reg returns IP == 0
343          */
344         if(ip)
345                 error_action(data, "unexpected_backtracing_error", ip);
346         return -1;
347 }
348
349 /*
350  * walking the stack
351  */
352 static void
353 stacktrace_walk(struct tcb *tcp,
354                 call_action_fn call_action,
355                 error_action_fn error_action,
356                 void *data)
357 {
358         char *symbol_name;
359         size_t symbol_name_size = 40;
360         unw_cursor_t cursor;
361         int stack_depth;
362
363         if (!tcp->mmap_cache)
364                 error_msg_and_die("bug: mmap_cache is NULL");
365         if (tcp->mmap_cache_size == 0)
366                 error_msg_and_die("bug: mmap_cache is empty");
367
368         symbol_name = xmalloc(symbol_name_size);
369
370         if (unw_init_remote(&cursor, libunwind_as, tcp->libunwind_ui) < 0)
371                 perror_msg_and_die("Can't initiate libunwind");
372
373         for (stack_depth = 0; stack_depth < 256; ++stack_depth) {
374                 if (print_stack_frame(tcp, call_action, error_action, data,
375                                 &cursor, &symbol_name, &symbol_name_size) < 0)
376                         break;
377                 if (unw_step(&cursor) <= 0)
378                         break;
379         }
380         if (stack_depth >= 256)
381                 error_action(data, "too many stack frames", 0);
382
383         free(symbol_name);
384 }
385
386 /*
387  * printing an entry in stack to stream or buffer
388  */
389 /*
390  * we want to keep the format used by backtrace_symbols from the glibc
391  *
392  * ./a.out() [0x40063d]
393  * ./a.out() [0x4006bb]
394  * ./a.out() [0x4006c6]
395  * /lib64/libc.so.6(__libc_start_main+0xed) [0x7fa2f8a5976d]
396  * ./a.out() [0x400569]
397  */
398 #define STACK_ENTRY_SYMBOL_FMT                  \
399         " > %s(%s+0x%lx) [0x%lx]\n",            \
400         binary_filename,                        \
401         symbol_name,                            \
402         (unsigned long) function_offset,        \
403         true_offset
404 #define STACK_ENTRY_NOSYMBOL_FMT                \
405         " > %s() [0x%lx]\n",                    \
406         binary_filename, true_offset
407 #define STACK_ENTRY_BUG_FMT                     \
408         " > BUG IN %s\n"
409 #define STACK_ENTRY_ERROR_WITH_OFFSET_FMT       \
410         " > %s [0x%lx]\n", error, true_offset
411 #define STACK_ENTRY_ERROR_FMT                   \
412         " > %s\n", error
413
414 static void
415 print_call_cb(void *dummy,
416               const char *binary_filename,
417               const char *symbol_name,
418               unw_word_t function_offset,
419               unsigned long true_offset)
420 {
421         if (symbol_name && (symbol_name[0] != '\0'))
422                 tprintf(STACK_ENTRY_SYMBOL_FMT);
423         else if (binary_filename)
424                 tprintf(STACK_ENTRY_NOSYMBOL_FMT);
425         else
426                 tprintf(STACK_ENTRY_BUG_FMT, __FUNCTION__);
427
428         line_ended();
429 }
430
431 static void
432 print_error_cb(void *dummy,
433                const char *error,
434                unsigned long true_offset)
435 {
436         if (true_offset)
437                 tprintf(STACK_ENTRY_ERROR_WITH_OFFSET_FMT);
438         else
439                 tprintf(STACK_ENTRY_ERROR_FMT);
440
441         line_ended();
442 }
443
444 static char *
445 sprint_call_or_error(const char *binary_filename,
446                      const char *symbol_name,
447                      unw_word_t function_offset,
448                      unsigned long true_offset,
449                      const char *error)
450 {
451        char *output_line = NULL;
452        int n;
453
454        if (symbol_name)
455                n = asprintf(&output_line, STACK_ENTRY_SYMBOL_FMT);
456        else if (binary_filename)
457                n = asprintf(&output_line, STACK_ENTRY_NOSYMBOL_FMT);
458        else if (error)
459                n = true_offset
460                        ? asprintf(&output_line, STACK_ENTRY_ERROR_WITH_OFFSET_FMT)
461                        : asprintf(&output_line, STACK_ENTRY_ERROR_FMT);
462        else
463                n = asprintf(&output_line, STACK_ENTRY_BUG_FMT, __FUNCTION__);
464
465        if (n < 0)
466                error_msg_and_die("error in asprintf");
467
468        return output_line;
469 }
470
471 /*
472  * queue manipulators
473  */
474 static void
475 queue_put(struct queue_t *queue,
476           const char *binary_filename,
477           const char *symbol_name,
478           unw_word_t function_offset,
479           unsigned long true_offset,
480           const char *error)
481 {
482         struct call_t *call;
483
484         call = xmalloc(sizeof(*call));
485         call->output_line = sprint_call_or_error(binary_filename,
486                                                  symbol_name,
487                                                  function_offset,
488                                                  true_offset,
489                                                  error);
490         call->next = NULL;
491
492         if (!queue->head) {
493                 queue->head = call;
494                 queue->tail = call;
495         } else {
496                 queue->tail->next = call;
497                 queue->tail = call;
498         }
499 }
500
501 static void
502 queue_put_call(void *queue,
503                const char *binary_filename,
504                const char *symbol_name,
505                unw_word_t function_offset,
506                unsigned long true_offset)
507 {
508         queue_put(queue,
509                   binary_filename,
510                   symbol_name,
511                   function_offset,
512                   true_offset,
513                   NULL);
514 }
515
516 static void
517 queue_put_error(void *queue,
518                 const char *error,
519                 unsigned long ip)
520 {
521         queue_put(queue, NULL, NULL, 0, ip, error);
522 }
523
524 static void
525 queue_print(struct queue_t *queue)
526 {
527         struct call_t *call, *tmp;
528
529         queue->tail = NULL;
530         call = queue->head;
531         queue->head = NULL;
532         while (call) {
533                 tmp = call;
534                 call = call->next;
535
536                 tprints(tmp->output_line);
537                 line_ended();
538
539                 free(tmp->output_line);
540                 tmp->output_line = NULL;
541                 tmp->next = NULL;
542                 free(tmp);
543         }
544 }
545
546 /*
547  * printing stack
548  */
549 void
550 unwind_print_stacktrace(struct tcb* tcp)
551 {
552 #if SUPPORTED_PERSONALITIES > 1
553         if (tcp->currpers != DEFAULT_PERSONALITY) {
554                 /* disable strack trace */
555                 return;
556         }
557 #endif
558        if (tcp->queue->head) {
559                DPRINTF("tcp=%p, queue=%p", "queueprint", tcp, tcp->queue->head);
560                queue_print(tcp->queue);
561        }
562        else if (rebuild_cache_if_invalid(tcp, __FUNCTION__)) {
563                DPRINTF("tcp=%p, queue=%p", "stackprint", tcp, tcp->queue->head);
564                stacktrace_walk(tcp, print_call_cb, print_error_cb, NULL);
565        }
566 }
567
568 /*
569  * capturing stack
570  */
571 void
572 unwind_capture_stacktrace(struct tcb *tcp)
573 {
574 #if SUPPORTED_PERSONALITIES > 1
575         if (tcp->currpers != DEFAULT_PERSONALITY) {
576                 /* disable strack trace */
577                 return;
578         }
579 #endif
580         if (tcp->queue->head)
581                 error_msg_and_die("bug: unprinted entries in queue");
582
583         if (rebuild_cache_if_invalid(tcp, __FUNCTION__)) {
584                 stacktrace_walk(tcp, queue_put_call, queue_put_error,
585                                 tcp->queue);
586                 DPRINTF("tcp=%p, queue=%p", "captured", tcp, tcp->queue->head);
587         }
588 }