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