]> granicus.if.org Git - strace/blob - unwind.c
unwind: enable dwarf cache of libunwind
[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 #define DPRINTF(F, A, ...) if (debug_flag) fprintf(stderr, " [unwind(" A ")] " F "\n", __VA_ARGS__)
32
33 /*
34  * Кeep a sorted array of cache entries,
35  * so that we can binary search through it.
36  */
37 struct mmap_cache_t {
38         /**
39          * example entry:
40          * 7fabbb09b000-7fabbb09f000 r--p 00179000 fc:00 1180246 /lib/libc-2.11.1.so
41          *
42          * start_addr  is 0x7fabbb09b000
43          * end_addr    is 0x7fabbb09f000
44          * mmap_offset is 0x179000
45          * binary_filename is "/lib/libc-2.11.1.so"
46          */
47         unsigned long start_addr;
48         unsigned long end_addr;
49         unsigned long mmap_offset;
50         char* binary_filename;
51         bool deleted;
52 };
53
54 /*
55  * Type used in stacktrace walker
56  */
57 typedef void (*call_action_fn)(void *data,
58                                char *binary_filename,
59                                char *symbol_name,
60                                unw_word_t function_off_set,
61                                unsigned long true_offset);
62 typedef void (*error_action_fn)(void *data,
63                                 const char *error,
64                                 unsigned long true_offset);
65
66 /*
67  * Type used in stacktrace capturing
68  */
69 struct call_t {
70        struct call_t* next;
71        char *output_line;
72 };
73
74 struct queue_t {
75        struct call_t *tail;
76        struct call_t *head;
77 };
78
79 static void queue_print(struct queue_t *queue);
80 static void delete_mmap_cache(struct tcb *tcp, const char *caller);
81
82 static unw_addr_space_t libunwind_as;
83 static unsigned int mmap_cache_generation;
84
85 void
86 unwind_init(void)
87 {
88         libunwind_as = unw_create_addr_space(&_UPT_accessors, 0);
89         if (!libunwind_as)
90                 error_msg_and_die("failed to create address space for stack tracing");
91         unw_set_caching_policy(libunwind_as, UNW_CACHE_GLOBAL);
92 }
93
94 void
95 unwind_tcb_init(struct tcb *tcp)
96 {
97         tcp->libunwind_ui = _UPT_create(tcp->pid);
98         if (!tcp->libunwind_ui)
99                 die_out_of_memory();
100
101         tcp->queue = malloc(sizeof(*tcp->queue));
102         if (!tcp->queue)
103                 die_out_of_memory();
104         tcp->queue->head = NULL;
105         tcp->queue->tail = NULL;
106 }
107
108 void
109 unwind_tcb_fin(struct tcb *tcp)
110 {
111         queue_print(tcp->queue);
112         free(tcp->queue);
113         tcp->queue = NULL;
114
115         delete_mmap_cache(tcp, __FUNCTION__);
116
117         _UPT_destroy(tcp->libunwind_ui);
118         tcp->libunwind_ui = NULL;
119 }
120
121 /*
122  * caching of /proc/ID/maps for each process to speed up stack tracing
123  *
124  * The cache must be refreshed after some syscall: mmap, mprotect, munmap, execve
125  */
126 static void
127 build_mmap_cache(struct tcb* tcp)
128 {
129         unsigned long start_addr, end_addr, mmap_offset;
130         char filename[sizeof ("/proc/0123456789/maps")];
131         char buffer[PATH_MAX + 80];
132         char binary_path[PATH_MAX];
133         struct mmap_cache_t *cur_entry, *prev_entry;
134         /* start with a small dynamically-allocated array and then expand it */
135         size_t cur_array_size = 10;
136         struct mmap_cache_t *cache_head;
137         FILE *fp;
138
139         const char *deleted = " (deleted)";
140         size_t blen;
141         size_t dlen;
142
143         unw_flush_cache (libunwind_as, 0, 0);
144
145         sprintf(filename, "/proc/%d/maps", tcp->pid);
146         fp = fopen(filename, "r");
147         if (!fp) {
148                 perror_msg("fopen: %s", filename);
149                 return;
150         }
151
152         cache_head = calloc(cur_array_size, sizeof(*cache_head));
153         if (!cache_head)
154                 die_out_of_memory();
155
156         while (fgets(buffer, sizeof(buffer), fp) != NULL) {
157                 binary_path[0] = '\0'; // 'reset' it just to be paranoid
158
159                 sscanf(buffer, "%lx-%lx %*c%*c%*c%*c %lx %*x:%*x %*d %[^\n]",
160                        &start_addr, &end_addr, &mmap_offset, binary_path);
161
162                 /* ignore special 'fake files' like "[vdso]", "[heap]", "[stack]", */
163                 if (binary_path[0] == '[') {
164                         continue;
165                 }
166
167                 if (binary_path[0] == '\0') {
168                         continue;
169                 }
170
171                 if (end_addr < start_addr)
172                         perror_msg_and_die("%s: unrecognized maps file format",
173                                            filename);
174
175                 cur_entry = &cache_head[tcp->mmap_cache_size];
176                 cur_entry->start_addr = start_addr;
177                 cur_entry->end_addr = end_addr;
178                 cur_entry->mmap_offset = mmap_offset;
179                 cur_entry->binary_filename = strdup(binary_path);
180
181                 dlen = strlen(deleted);
182                 blen = strlen(binary_path);
183                 if (blen >= dlen && strcmp(binary_path + blen - dlen, deleted) == 0)
184                         cur_entry->deleted = true;
185                 else
186                         cur_entry->deleted = false;
187
188                 /*
189                  * sanity check to make sure that we're storing
190                  * non-overlapping regions in ascending order
191                  */
192                 if (tcp->mmap_cache_size > 0) {
193                         prev_entry = &cache_head[tcp->mmap_cache_size - 1];
194                         if (prev_entry->start_addr >= cur_entry->start_addr)
195                                 perror_msg_and_die("Overlaying memory region in %s",
196                                                    filename);
197                         if (prev_entry->end_addr > cur_entry->start_addr)
198                                 perror_msg_and_die("Overlaying memory region in %s",
199                                                    filename);
200                 }
201                 tcp->mmap_cache_size++;
202
203                 /* resize doubling its size */
204                 if (tcp->mmap_cache_size >= cur_array_size) {
205                         cur_array_size *= 2;
206                         cache_head = realloc(cache_head, cur_array_size * sizeof(*cache_head));
207                         if (!cache_head)
208                                 die_out_of_memory();
209                 }
210         }
211         fclose(fp);
212         tcp->mmap_cache = cache_head;
213         tcp->mmap_cache_generation = mmap_cache_generation;
214
215         DPRINTF("tgen=%u, ggen=%u, tcp=%p, cache=%p",
216                 "cache-build",
217                 tcp->mmap_cache_generation,
218                 mmap_cache_generation,
219                 tcp, tcp->mmap_cache);
220 }
221
222 /* deleting the cache */
223 static void
224 delete_mmap_cache(struct tcb *tcp, const char *caller)
225 {
226         unsigned int i;
227
228         DPRINTF("tgen=%u, ggen=%u, tcp=%p, cache=%p, caller=%s",
229                 "cache-delete",
230                 tcp->mmap_cache_generation,
231                 mmap_cache_generation,
232                 tcp, tcp->mmap_cache, caller);
233
234         for (i = 0; i < tcp->mmap_cache_size; i++) {
235                 free(tcp->mmap_cache[i].binary_filename);
236                 tcp->mmap_cache[i].binary_filename = NULL;
237         }
238         free(tcp->mmap_cache);
239         tcp->mmap_cache = NULL;
240         tcp->mmap_cache_size = 0;
241 }
242
243 static bool
244 rebuild_cache_if_invalid(struct tcb *tcp, const char *caller)
245 {
246         if ((tcp->mmap_cache_generation != mmap_cache_generation)
247             && tcp->mmap_cache)
248                 delete_mmap_cache(tcp, caller);
249
250         if (!tcp->mmap_cache)
251                 build_mmap_cache(tcp);
252
253         if (!tcp->mmap_cache || !tcp->mmap_cache_size)
254                 return false;
255         else
256                 return true;
257 }
258
259 void
260 unwind_cache_invalidate(struct tcb* tcp)
261 {
262         mmap_cache_generation++;
263         DPRINTF("tgen=%u, ggen=%u, tcp=%p, cache=%p", "increment",
264                 tcp->mmap_cache_generation,
265                 mmap_cache_generation,
266                 tcp,
267                 tcp->mmap_cache);
268 }
269
270 /*
271  * walking the stack
272  */
273 static void
274 stacktrace_walk(struct tcb *tcp,
275                 call_action_fn call_action,
276                 error_action_fn error_action,
277                 void *data)
278 {
279         unw_word_t ip;
280         unw_cursor_t cursor;
281         unw_word_t function_off_set;
282         int stack_depth = 0, ret_val;
283         /* these are used for the binary search through the mmap_chace */
284         unsigned int lower, upper, mid;
285         size_t symbol_name_size = 40;
286         char * symbol_name;
287         struct mmap_cache_t* cur_mmap_cache;
288         unsigned long true_offset;
289         bool berror_expected = false;
290
291         if (!tcp->mmap_cache)
292                 error_msg_and_die("bug: mmap_cache is NULL");
293         if (tcp->mmap_cache_size == 0)
294                 error_msg_and_die("bug: mmap_cache is empty");
295
296         symbol_name = malloc(symbol_name_size);
297         if (!symbol_name)
298                 die_out_of_memory();
299
300         if (unw_init_remote(&cursor, libunwind_as, tcp->libunwind_ui) < 0)
301                 perror_msg_and_die("Can't initiate libunwind");
302
303         do {
304                 /* looping on the stack frame */
305                 if (unw_get_reg(&cursor, UNW_REG_IP, &ip) < 0) {
306                         perror_msg("Can't walk the stack of process %d", tcp->pid);
307                         break;
308                 }
309
310                 lower = 0;
311                 upper = tcp->mmap_cache_size - 1;
312
313                 while (lower <= upper) {
314                         /* find the mmap_cache and print the stack frame */
315                         mid = (upper + lower) / 2;
316                         cur_mmap_cache = &tcp->mmap_cache[mid];
317
318                         if (ip >= cur_mmap_cache->start_addr &&
319                             ip < cur_mmap_cache->end_addr) {
320                                 for (;;) {
321                                         symbol_name[0] = '\0';
322                                         ret_val = unw_get_proc_name(&cursor, symbol_name,
323                                                 symbol_name_size, &function_off_set);
324                                         if (ret_val != -UNW_ENOMEM)
325                                                 break;
326                                         symbol_name_size *= 2;
327                                         symbol_name = realloc(symbol_name, symbol_name_size);
328                                         if (!symbol_name)
329                                                 die_out_of_memory();
330                                 }
331
332                                 if (cur_mmap_cache->deleted)
333                                         berror_expected = true;
334
335                                 true_offset = ip - cur_mmap_cache->start_addr +
336                                         cur_mmap_cache->mmap_offset;
337                                 if (symbol_name[0]) {
338                                         call_action(data,
339                                                     cur_mmap_cache->binary_filename,
340                                                     symbol_name,
341                                                     function_off_set,
342                                                     true_offset);
343                                 } else {
344                                         call_action(data,
345                                                     cur_mmap_cache->binary_filename,
346                                                     symbol_name,
347                                                     0,
348                                                     true_offset);
349                                 }
350                                 break; /* stack frame printed */
351                         }
352                         else if (mid == 0) {
353                                 /*
354                                  * there is a bug in libunwind >= 1.0
355                                  * after a set_tid_address syscall
356                                  * unw_get_reg returns IP == 0
357                                  */
358                                 if(ip)
359                                         error_action(data,
360                                                      "backtracing_error", 0);
361                                 goto ret;
362                         }
363                         else if (ip < cur_mmap_cache->start_addr)
364                                 upper = mid;
365                         else
366                                 lower = mid + 1;
367
368                 }
369                 if (lower > upper) {
370                         error_action(data,
371                                      berror_expected
372                                      ?"expected_backtracing_error"
373                                      :"unexpected_backtracing_error",
374                                      ip);
375                         goto ret;
376                 }
377
378                 ret_val = unw_step(&cursor);
379
380                 if (++stack_depth > 255) {
381                         error_action(data,
382                                      "too many stack frames", 0);
383                         break;
384                 }
385         } while (ret_val > 0);
386 ret:
387         free(symbol_name);
388 }
389
390 /*
391  * printing an entry in stack to stream or buffer
392  */
393 /*
394  * we want to keep the format used by backtrace_symbols from the glibc
395  *
396  * ./a.out() [0x40063d]
397  * ./a.out() [0x4006bb]
398  * ./a.out() [0x4006c6]
399  * /lib64/libc.so.6(__libc_start_main+0xed) [0x7fa2f8a5976d]
400  * ./a.out() [0x400569]
401  */
402 #define STACK_ENTRY_SYMBOL_FMT                  \
403         " > %s(%s+0x%lx) [0x%lx]\n",            \
404         binary_filename,                        \
405         symbol_name,                            \
406         function_off_set,                       \
407         true_offset
408 #define STACK_ENTRY_NOSYMBOL_FMT                \
409         " > %s() [0x%lx]\n",                    \
410         binary_filename, true_offset
411 #define STACK_ENTRY_BUG_FMT                     \
412         " > BUG IN %s\n"
413 #define STACK_ENTRY_ERROR_WITH_OFFSET_FMT       \
414         " > %s [0x%lx]\n", error, true_offset
415 #define STACK_ENTRY_ERROR_FMT                   \
416         " > %s\n", error
417
418 static void
419 print_call_cb(void *dummy,
420               char *binary_filename,
421               char *symbol_name,
422               unw_word_t function_off_set,
423               unsigned long true_offset)
424 {
425         if (symbol_name)
426                 tprintf(STACK_ENTRY_SYMBOL_FMT);
427         else if (binary_filename)
428                 tprintf(STACK_ENTRY_NOSYMBOL_FMT);
429         else
430                 tprintf(STACK_ENTRY_BUG_FMT, __FUNCTION__);
431
432         line_ended();
433 }
434
435 static void
436 print_error_cb(void *dummy,
437                const char *error,
438                unsigned long true_offset)
439 {
440         if (true_offset)
441                 tprintf(STACK_ENTRY_ERROR_WITH_OFFSET_FMT);
442         else
443                 tprintf(STACK_ENTRY_ERROR_FMT);
444
445         line_ended();
446 }
447
448 static char *
449 sprint_call_or_error(char *binary_filename,
450                      char *symbol_name,
451                      unw_word_t function_off_set,
452                      unsigned long true_offset,
453                      const char *error)
454 {
455        char *output_line = NULL;
456        int n;
457
458        if (symbol_name)
459                n = asprintf(&output_line, STACK_ENTRY_SYMBOL_FMT);
460        else if (binary_filename)
461                n = asprintf(&output_line, STACK_ENTRY_NOSYMBOL_FMT);
462        else if (error)
463                n = true_offset
464                        ? asprintf(&output_line, STACK_ENTRY_ERROR_WITH_OFFSET_FMT)
465                        : asprintf(&output_line, STACK_ENTRY_ERROR_FMT);
466        else
467                n = asprintf(&output_line, STACK_ENTRY_BUG_FMT, __FUNCTION__);
468
469        if (n < 0)
470                error_msg_and_die("error in asprintf");
471
472        return output_line;
473 }
474
475 /*
476  * queue manipulators
477  */
478 static void
479 queue_put(struct queue_t *queue,
480           char *binary_filename,
481           char *symbol_name,
482           unw_word_t function_off_set,
483           unsigned long true_offset,
484           const char *error)
485 {
486         struct call_t *call;
487
488         call = malloc(sizeof(*call));
489         if (!call)
490                 die_out_of_memory();
491
492         call->output_line = sprint_call_or_error(binary_filename,
493                                                  symbol_name,
494                                                  function_off_set,
495                                                  true_offset,
496                                                  error);
497         call->next = NULL;
498
499         if (!queue->head) {
500                 queue->head = call;
501                 queue->tail = call;
502         } else {
503                 queue->tail->next = call;
504                 queue->tail = call;
505         }
506 }
507
508 static void
509 queue_put_call(void *queue,
510                char *binary_filename,
511                char *symbol_name,
512                unw_word_t function_off_set,
513                unsigned long true_offset)
514 {
515         queue_put(queue,
516                   binary_filename,
517                   symbol_name,
518                   function_off_set,
519                   true_offset,
520                   NULL);
521 }
522
523 static void
524 queue_put_error(void *queue,
525                 const char *error,
526                 unw_word_t ip)
527 {
528         queue_put(queue, NULL, NULL, 0, ip, error);
529 }
530
531 static void
532 queue_print(struct queue_t *queue)
533 {
534         struct call_t *call, *tmp;
535
536         queue->tail = NULL;
537         call = queue->head;
538         queue->head = NULL;
539         while (call) {
540                 tmp = call;
541                 call = call->next;
542
543                 tprints(tmp->output_line);
544                 line_ended();
545
546                 free(tmp->output_line);
547                 tmp->output_line = NULL;
548                 tmp->next = NULL;
549                 free(tmp);
550         }
551 }
552
553 /*
554  * printing stack
555  */
556 void
557 unwind_print_stacktrace(struct tcb* tcp)
558 {
559        if (tcp->queue->head) {
560                DPRINTF("tcp=%p, queue=%p", "queueprint", tcp, tcp->queue->head);
561                queue_print(tcp->queue);
562        }
563        else if (rebuild_cache_if_invalid(tcp, __FUNCTION__)) {
564                DPRINTF("tcp=%p, queue=%p", "stackprint", tcp, tcp->queue->head);
565                stacktrace_walk(tcp, print_call_cb, print_error_cb, NULL);
566        }
567 }
568
569 /*
570  * capturing stack
571  */
572 void
573 unwind_capture_stacktrace(struct tcb *tcp)
574 {
575         if (tcp->queue->head)
576                 error_msg_and_die("bug: unprinted entries in queue");
577
578         if (rebuild_cache_if_invalid(tcp, __FUNCTION__)) {
579                 stacktrace_walk(tcp, queue_put_call, queue_put_error,
580                                 tcp->queue);
581                 DPRINTF("tcp=%p, queue=%p", "captured", tcp, tcp->queue->head);
582         }
583 }