diff options
author | Elliott Hughes <enh@google.com> | 2014-07-01 17:40:01 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2014-07-01 14:58:20 +0000 |
commit | a2adbed6e2d3ce85ebb167e16ae370681a8b5188 (patch) | |
tree | 1759d16843da627450864d6520f064387fda14e6 | |
parent | d317951a6b15f0ca6e2a697d4796895b876985d1 (diff) | |
parent | cedbb451692d6631421e9df576b72129151d303c (diff) | |
download | strace-android-5.1.1_r1.tar.gz |
Merge changes Icedbb451,I57fac759,I2734a701,Ib0764206,Ic588b205, ...android-cts-5.1_r9android-cts-5.1_r8android-cts-5.1_r7android-cts-5.1_r6android-cts-5.1_r5android-cts-5.1_r4android-cts-5.1_r3android-cts-5.1_r28android-cts-5.1_r27android-cts-5.1_r26android-cts-5.1_r25android-cts-5.1_r24android-cts-5.1_r23android-cts-5.1_r22android-cts-5.1_r21android-cts-5.1_r20android-cts-5.1_r2android-cts-5.1_r19android-cts-5.1_r18android-cts-5.1_r17android-cts-5.1_r16android-cts-5.1_r15android-cts-5.1_r14android-cts-5.1_r13android-cts-5.1_r10android-cts-5.1_r1android-cts-5.0_r9android-cts-5.0_r8android-cts-5.0_r7android-cts-5.0_r6android-cts-5.0_r5android-cts-5.0_r4android-cts-5.0_r3android-5.1.1_r9android-5.1.1_r8android-5.1.1_r7android-5.1.1_r6android-5.1.1_r5android-5.1.1_r4android-5.1.1_r38android-5.1.1_r37android-5.1.1_r36android-5.1.1_r35android-5.1.1_r34android-5.1.1_r33android-5.1.1_r30android-5.1.1_r3android-5.1.1_r29android-5.1.1_r28android-5.1.1_r26android-5.1.1_r25android-5.1.1_r24android-5.1.1_r23android-5.1.1_r22android-5.1.1_r20android-5.1.1_r2android-5.1.1_r19android-5.1.1_r18android-5.1.1_r17android-5.1.1_r16android-5.1.1_r15android-5.1.1_r14android-5.1.1_r13android-5.1.1_r12android-5.1.1_r10android-5.1.1_r1android-5.1.0_r5android-5.1.0_r4android-5.1.0_r3android-5.1.0_r1android-5.0.2_r3android-5.0.2_r1android-5.0.1_r1android-5.0.0_r7android-5.0.0_r6android-5.0.0_r5.1android-5.0.0_r5android-5.0.0_r4android-5.0.0_r3android-5.0.0_r2android-5.0.0_r1lollipop-releaselollipop-mr1-wfc-releaselollipop-mr1-releaselollipop-mr1-fi-releaselollipop-mr1-devlollipop-mr1-cts-releaselollipop-devlollipop-cts-release
* changes:
Merge remote-tracking branch 'strace/master' into HEAD
xtensa: sort values in struct_user_offsets
Document -k option as experimental
tests: robustify -k test
tests: enhance -k test
unwind: ignore memory mappings that have no PROT_EXEC bit set
unwind: cleanup build_mmap_cache
unwind: remove unused field from mmap_cache_t
unwind: refactor stacktrace_walk
unwind: constify binary_filename and symbol_name functions arguments
unwind: disable stack trace with multiple personalities
-rw-r--r-- | NEWS | 2 | ||||
-rw-r--r-- | process.c | 34 | ||||
-rw-r--r-- | strace.1 | 2 | ||||
-rw-r--r-- | strace.c | 2 | ||||
-rw-r--r-- | tests/Makefile.am | 2 | ||||
-rw-r--r-- | tests/stack-fcall-0.c | 6 | ||||
-rw-r--r-- | tests/stack-fcall-1.c | 6 | ||||
-rw-r--r-- | tests/stack-fcall-2.c | 6 | ||||
-rw-r--r-- | tests/stack-fcall-3.c | 6 | ||||
-rw-r--r-- | tests/stack-fcall.c | 20 | ||||
-rwxr-xr-x | tests/strace-k.test | 2 | ||||
-rw-r--r-- | unwind.c | 317 |
12 files changed, 211 insertions, 194 deletions
@@ -6,7 +6,7 @@ Noteworthy changes in release 4.9 (????-??-??) added --enable-arm-oabi option to enable it at configure time. * Improvements - * Added -k option to print stack trace after each traced syscall. + * Added experimental -k option to print stack trace after each traced syscall. * Added -w option to produce stats on syscall latency. (addresses Debian bug #457497). * Added ARC architecture support. @@ -1865,6 +1865,23 @@ const struct xlat struct_user_offsets[] = { { 4*33, "sr" }, #endif #ifdef XTENSA + { REG_A_BASE, "a0" }, + { REG_A_BASE+1, "a1" }, + { REG_A_BASE+2, "a2" }, + { REG_A_BASE+3, "a3" }, + { REG_A_BASE+4, "a4" }, + { REG_A_BASE+5, "a5" }, + { REG_A_BASE+6, "a6" }, + { REG_A_BASE+7, "a7" }, + { REG_A_BASE+8, "a8" }, + { REG_A_BASE+9, "a9" }, + { REG_A_BASE+10, "a10" }, + { REG_A_BASE+11, "a11" }, + { REG_A_BASE+12, "a12" }, + { REG_A_BASE+13, "a13" }, + { REG_A_BASE+14, "a14" }, + { REG_A_BASE+15, "a15" }, + { REG_PC, "pc" }, { SYSCALL_NR, "syscall_nr" }, { REG_AR_BASE, "ar0" }, { REG_AR_BASE+1, "ar1" }, @@ -1937,23 +1954,6 @@ const struct xlat struct_user_offsets[] = { { REG_WB, "wb" }, { REG_WS, "ws" }, { REG_PS, "ps" }, - { REG_PC, "pc" }, - { REG_A_BASE, "a0" }, - { REG_A_BASE+1, "a1" }, - { REG_A_BASE+2, "a2" }, - { REG_A_BASE+3, "a3" }, - { REG_A_BASE+4, "a4" }, - { REG_A_BASE+5, "a5" }, - { REG_A_BASE+6, "a6" }, - { REG_A_BASE+7, "a7" }, - { REG_A_BASE+8, "a8" }, - { REG_A_BASE+9, "a9" }, - { REG_A_BASE+10, "a10" }, - { REG_A_BASE+11, "a11" }, - { REG_A_BASE+12, "a12" }, - { REG_A_BASE+13, "a13" }, - { REG_A_BASE+14, "a14" }, - { REG_A_BASE+15, "a15" }, #endif /* Other fields in "struct user" */ @@ -268,7 +268,7 @@ Print the help summary. Print the instruction pointer at the time of the system call. .TP .B \-k -Print the execution stack trace of the traced processes after each system call. +Print the execution stack trace of the traced processes after each system call (experimental). .TP .B \-q Suppress messages about attaching, detaching etc. This happens @@ -238,7 +238,7 @@ usage: strace [-CdffhiqrtttTvVxxy] [-I n] [-e expr]...\n\ -P path -- trace accesses to path\n\ " #ifdef USE_LIBUNWIND -"-k obtain stack trace between each syscall\n\ +"-k obtain stack trace between each syscall (experimental)\n\ " #endif /* ancient, no one should use it diff --git a/tests/Makefile.am b/tests/Makefile.am index efe646db..922452ab 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -11,6 +11,8 @@ check_PROGRAMS = \ uio uio_CFLAGS = $(AM_CFLAGS) -D_FILE_OFFSET_BITS=64 +stack_fcall_SOURCES = stack-fcall.c \ + stack-fcall-0.c stack-fcall-1.c stack-fcall-2.c stack-fcall-3.c TESTS = \ ptrace_setoptions.test \ diff --git a/tests/stack-fcall-0.c b/tests/stack-fcall-0.c new file mode 100644 index 00000000..12a260de --- /dev/null +++ b/tests/stack-fcall-0.c @@ -0,0 +1,6 @@ +int f1(int i); + +int f0(int i) +{ + return f1(i) - i; +} diff --git a/tests/stack-fcall-1.c b/tests/stack-fcall-1.c new file mode 100644 index 00000000..8716702d --- /dev/null +++ b/tests/stack-fcall-1.c @@ -0,0 +1,6 @@ +int f2(int i); + +int f1(int i) +{ + return f2(i) + i; +} diff --git a/tests/stack-fcall-2.c b/tests/stack-fcall-2.c new file mode 100644 index 00000000..19f8cf83 --- /dev/null +++ b/tests/stack-fcall-2.c @@ -0,0 +1,6 @@ +int f3(int i); + +int f2(int i) +{ + return f3(i) - i; +} diff --git a/tests/stack-fcall-3.c b/tests/stack-fcall-3.c new file mode 100644 index 00000000..3af1667f --- /dev/null +++ b/tests/stack-fcall-3.c @@ -0,0 +1,6 @@ +#include <unistd.h> + +int f3(int i) +{ + return getpid() + i; +} diff --git a/tests/stack-fcall.c b/tests/stack-fcall.c index 1c66fefa..134d54f6 100644 --- a/tests/stack-fcall.c +++ b/tests/stack-fcall.c @@ -1,23 +1,7 @@ -#include <unistd.h> -#include <sys/types.h> - -/* Use "volatile" to avoid compiler optimization. */ - -int f1(int i) -{ - static pid_t (* volatile g)(void) = getpid; - return g() + i; -} - -int f0(volatile int i) -{ - static int (* volatile g)(int) = f1; - return g(i) - i; -} +int f0(int i); int main(int argc, char** argv) { - static int (* volatile g)(int) = f0; - g(argc); + f0(argc); return 0; } diff --git a/tests/strace-k.test b/tests/strace-k.test index 3845c8ce..f757fb91 100755 --- a/tests/strace-k.test +++ b/tests/strace-k.test @@ -23,7 +23,7 @@ $STRACE $args > $LOG 2>&1 || { fail_ "$STRACE $args failed" } -expected='getpid f1 f0 main ' +expected='getpid f3 f2 f1 f0 main ' result=$(sed -n '1,/(main+0x[a-f0-9]\+) .*/ s/^.*(\([^+]\+\)+0x[a-f0-9]\+) .*/\1/p' $LOG | tr '\n' ' ') @@ -47,7 +47,7 @@ struct mmap_cache_t { /** * example entry: - * 7fabbb09b000-7fabbb09f000 r--p 00179000 fc:00 1180246 /lib/libc-2.11.1.so + * 7fabbb09b000-7fabbb09f000 r-xp 00179000 fc:00 1180246 /lib/libc-2.11.1.so * * start_addr is 0x7fabbb09b000 * end_addr is 0x7fabbb09f000 @@ -57,16 +57,15 @@ struct mmap_cache_t { unsigned long start_addr; unsigned long end_addr; unsigned long mmap_offset; - char* binary_filename; - bool deleted; + char *binary_filename; }; /* * Type used in stacktrace walker */ typedef void (*call_action_fn)(void *data, - char *binary_filename, - char *symbol_name, + const char *binary_filename, + const char *symbol_name, unw_word_t function_offset, unsigned long true_offset); typedef void (*error_action_fn)(void *data, @@ -131,28 +130,22 @@ unwind_tcb_fin(struct tcb *tcp) /* * caching of /proc/ID/maps for each process to speed up stack tracing * - * The cache must be refreshed after some syscall: mmap, mprotect, munmap, execve + * The cache must be refreshed after syscalls that affect memory mappings, + * e.g. mmap, mprotect, munmap, execve. */ static void build_mmap_cache(struct tcb* tcp) { - unsigned long start_addr, end_addr, mmap_offset; - char filename[sizeof ("/proc/0123456789/maps")]; - char buffer[PATH_MAX + 80]; - char binary_path[PATH_MAX]; - struct mmap_cache_t *cur_entry, *prev_entry; + FILE *fp; + struct mmap_cache_t *cache_head; /* start with a small dynamically-allocated array and then expand it */ size_t cur_array_size = 10; - struct mmap_cache_t *cache_head; - FILE *fp; - - const char *deleted = " (deleted)"; - size_t blen; - size_t dlen; + char filename[sizeof("/proc/4294967296/maps")]; + char buffer[PATH_MAX + 80]; - unw_flush_cache (libunwind_as, 0, 0); + unw_flush_cache(libunwind_as, 0, 0); - sprintf(filename, "/proc/%d/maps", tcp->pid); + sprintf(filename, "/proc/%u/maps", tcp->pid); fp = fopen_for_input(filename, "r"); if (!fp) { perror_msg("fopen: %s", filename); @@ -164,59 +157,60 @@ build_mmap_cache(struct tcb* tcp) die_out_of_memory(); while (fgets(buffer, sizeof(buffer), fp) != NULL) { - binary_path[0] = '\0'; // 'reset' it just to be paranoid - - sscanf(buffer, "%lx-%lx %*c%*c%*c%*c %lx %*x:%*x %*d %[^\n]", - &start_addr, &end_addr, &mmap_offset, binary_path); - - /* ignore special 'fake files' like "[vdso]", "[heap]", "[stack]", */ - if (binary_path[0] == '[') { + struct mmap_cache_t *entry; + unsigned long start_addr, end_addr, mmap_offset; + char exec_bit; + char binary_path[PATH_MAX]; + + if (sscanf(buffer, "%lx-%lx %*c%*c%c%*c %lx %*x:%*x %*d %[^\n]", + &start_addr, &end_addr, &exec_bit, + &mmap_offset, binary_path) != 5) continue; - } - if (binary_path[0] == '\0') { + /* ignore mappings that have no PROT_EXEC bit set */ + if (exec_bit != 'x') continue; - } - - if (end_addr < start_addr) - perror_msg_and_die("%s: unrecognized maps file format", - filename); - - cur_entry = &cache_head[tcp->mmap_cache_size]; - cur_entry->start_addr = start_addr; - cur_entry->end_addr = end_addr; - cur_entry->mmap_offset = mmap_offset; - cur_entry->binary_filename = strdup(binary_path); - dlen = strlen(deleted); - blen = strlen(binary_path); - if (blen >= dlen && strcmp(binary_path + blen - dlen, deleted) == 0) - cur_entry->deleted = true; - else - cur_entry->deleted = false; + if (end_addr < start_addr) { + error_msg("%s: unrecognized file format", filename); + break; + } /* * sanity check to make sure that we're storing * non-overlapping regions in ascending order */ if (tcp->mmap_cache_size > 0) { - prev_entry = &cache_head[tcp->mmap_cache_size - 1]; - if (prev_entry->start_addr >= cur_entry->start_addr) - perror_msg_and_die("Overlaying memory region in %s", - filename); - if (prev_entry->end_addr > cur_entry->start_addr) - perror_msg_and_die("Overlaying memory region in %s", - filename); + entry = &cache_head[tcp->mmap_cache_size - 1]; + if (entry->start_addr == start_addr && + entry->end_addr == end_addr) { + /* duplicate entry, e.g. [vsyscall] */ + continue; + } + if (start_addr <= entry->start_addr || + start_addr < entry->end_addr) { + error_msg("%s: overlapping memory region", + filename); + continue; + } } - tcp->mmap_cache_size++; - /* resize doubling its size */ if (tcp->mmap_cache_size >= cur_array_size) { cur_array_size *= 2; - cache_head = realloc(cache_head, cur_array_size * sizeof(*cache_head)); + cache_head = realloc(cache_head, + cur_array_size * sizeof(*cache_head)); if (!cache_head) die_out_of_memory(); } + + entry = &cache_head[tcp->mmap_cache_size]; + entry->start_addr = start_addr; + entry->end_addr = end_addr; + entry->mmap_offset = mmap_offset; + entry->binary_filename = strdup(binary_path); + if (!entry->binary_filename) + die_out_of_memory(); + tcp->mmap_cache_size++; } fclose(fp); tcp->mmap_cache = cache_head; @@ -269,6 +263,12 @@ rebuild_cache_if_invalid(struct tcb *tcp, const char *caller) void unwind_cache_invalidate(struct tcb* tcp) { +#if SUPPORTED_PERSONALITIES > 1 + if (tcp->currpers != DEFAULT_PERSONALITY) { + /* disable strack trace */ + return; + } +#endif mmap_cache_generation++; DPRINTF("tgen=%u, ggen=%u, tcp=%p, cache=%p", "increment", tcp->mmap_cache_generation, @@ -277,6 +277,82 @@ unwind_cache_invalidate(struct tcb* tcp) tcp->mmap_cache); } +static void +get_symbol_name(unw_cursor_t *cursor, char **name, + size_t *size, unw_word_t *offset) +{ + for (;;) { + int rc = unw_get_proc_name(cursor, *name, *size, offset); + if (rc == 0) + break; + if (rc != -UNW_ENOMEM) { + **name = '\0'; + *offset = 0; + break; + } + *size *= 2; + *name = realloc(*name, *size); + if (!*name) + die_out_of_memory(); + } +} + +static int +print_stack_frame(struct tcb *tcp, + call_action_fn call_action, + error_action_fn error_action, + void *data, + unw_cursor_t *cursor, + char **symbol_name, + size_t *symbol_name_size) +{ + unw_word_t ip; + int lower = 0; + int upper = (int) tcp->mmap_cache_size - 1; + + if (unw_get_reg(cursor, UNW_REG_IP, &ip) < 0) { + perror_msg("Can't walk the stack of process %d", tcp->pid); + return -1; + } + + while (lower <= upper) { + struct mmap_cache_t *cur_mmap_cache; + int mid = (upper + lower) / 2; + + cur_mmap_cache = &tcp->mmap_cache[mid]; + + if (ip >= cur_mmap_cache->start_addr && + ip < cur_mmap_cache->end_addr) { + unsigned long true_offset; + unw_word_t function_offset; + + get_symbol_name(cursor, symbol_name, symbol_name_size, + &function_offset); + true_offset = ip - cur_mmap_cache->start_addr + + cur_mmap_cache->mmap_offset; + call_action(data, + cur_mmap_cache->binary_filename, + *symbol_name, + function_offset, + true_offset); + return 0; + } + else if (ip < cur_mmap_cache->start_addr) + upper = mid - 1; + else + lower = mid + 1; + } + + /* + * there is a bug in libunwind >= 1.0 + * after a set_tid_address syscall + * unw_get_reg returns IP == 0 + */ + if(ip) + error_action(data, "unexpected_backtracing_error", ip); + return -1; +} + /* * walking the stack */ @@ -286,17 +362,10 @@ stacktrace_walk(struct tcb *tcp, error_action_fn error_action, void *data) { - unw_word_t ip; - unw_cursor_t cursor; - unw_word_t function_offset; - int stack_depth = 0, ret_val; - /* these are used for the binary search through the mmap_chace */ - int lower, upper, mid; + char *symbol_name; size_t symbol_name_size = 40; - char * symbol_name; - struct mmap_cache_t* cur_mmap_cache; - unsigned long true_offset; - bool berror_expected = false; + unw_cursor_t cursor; + int stack_depth; if (!tcp->mmap_cache) error_msg_and_die("bug: mmap_cache is NULL"); @@ -310,90 +379,16 @@ stacktrace_walk(struct tcb *tcp, if (unw_init_remote(&cursor, libunwind_as, tcp->libunwind_ui) < 0) perror_msg_and_die("Can't initiate libunwind"); - do { - /* looping on the stack frame */ - if (unw_get_reg(&cursor, UNW_REG_IP, &ip) < 0) { - perror_msg("Can't walk the stack of process %d", tcp->pid); + for (stack_depth = 0; stack_depth < 256; ++stack_depth) { + if (print_stack_frame(tcp, call_action, error_action, data, + &cursor, &symbol_name, &symbol_name_size) < 0) break; - } - - lower = 0; - upper = (int) tcp->mmap_cache_size - 1; - - while (lower <= upper) { - /* find the mmap_cache and print the stack frame */ - mid = (upper + lower) / 2; - cur_mmap_cache = &tcp->mmap_cache[mid]; - - if (ip >= cur_mmap_cache->start_addr && - ip < cur_mmap_cache->end_addr) { - for (;;) { - symbol_name[0] = '\0'; - ret_val = unw_get_proc_name(&cursor, symbol_name, - symbol_name_size, &function_offset); - if (ret_val != -UNW_ENOMEM) - break; - symbol_name_size *= 2; - symbol_name = realloc(symbol_name, symbol_name_size); - if (!symbol_name) - die_out_of_memory(); - } - - if (cur_mmap_cache->deleted) - berror_expected = true; - - true_offset = ip - cur_mmap_cache->start_addr + - cur_mmap_cache->mmap_offset; - if (symbol_name[0]) { - call_action(data, - cur_mmap_cache->binary_filename, - symbol_name, - function_offset, - true_offset); - } else { - call_action(data, - cur_mmap_cache->binary_filename, - symbol_name, - 0, - true_offset); - } - break; /* stack frame printed */ - } - else if (mid == 0) { - /* - * there is a bug in libunwind >= 1.0 - * after a set_tid_address syscall - * unw_get_reg returns IP == 0 - */ - if(ip) - error_action(data, - "backtracing_error", 0); - goto ret; - } - else if (ip < cur_mmap_cache->start_addr) - upper = mid - 1; - else - lower = mid + 1; - - } - if (lower > upper) { - error_action(data, - berror_expected - ?"expected_backtracing_error" - :"unexpected_backtracing_error", - ip); - goto ret; - } - - ret_val = unw_step(&cursor); - - if (++stack_depth > 255) { - error_action(data, - "too many stack frames", 0); + if (unw_step(&cursor) <= 0) break; - } - } while (ret_val > 0); -ret: + } + if (stack_depth >= 256) + error_action(data, "too many stack frames", 0); + free(symbol_name); } @@ -427,8 +422,8 @@ ret: static void print_call_cb(void *dummy, - char *binary_filename, - char *symbol_name, + const char *binary_filename, + const char *symbol_name, unw_word_t function_offset, unsigned long true_offset) { @@ -456,8 +451,8 @@ print_error_cb(void *dummy, } static char * -sprint_call_or_error(char *binary_filename, - char *symbol_name, +sprint_call_or_error(const char *binary_filename, + const char *symbol_name, unw_word_t function_offset, unsigned long true_offset, const char *error) @@ -487,8 +482,8 @@ sprint_call_or_error(char *binary_filename, */ static void queue_put(struct queue_t *queue, - char *binary_filename, - char *symbol_name, + const char *binary_filename, + const char *symbol_name, unw_word_t function_offset, unsigned long true_offset, const char *error) @@ -517,8 +512,8 @@ queue_put(struct queue_t *queue, static void queue_put_call(void *queue, - char *binary_filename, - char *symbol_name, + const char *binary_filename, + const char *symbol_name, unw_word_t function_offset, unsigned long true_offset) { @@ -566,6 +561,12 @@ queue_print(struct queue_t *queue) void unwind_print_stacktrace(struct tcb* tcp) { +#if SUPPORTED_PERSONALITIES > 1 + if (tcp->currpers != DEFAULT_PERSONALITY) { + /* disable strack trace */ + return; + } +#endif if (tcp->queue->head) { DPRINTF("tcp=%p, queue=%p", "queueprint", tcp, tcp->queue->head); queue_print(tcp->queue); @@ -582,6 +583,12 @@ unwind_print_stacktrace(struct tcb* tcp) void unwind_capture_stacktrace(struct tcb *tcp) { +#if SUPPORTED_PERSONALITIES > 1 + if (tcp->currpers != DEFAULT_PERSONALITY) { + /* disable strack trace */ + return; + } +#endif if (tcp->queue->head) error_msg_and_die("bug: unprinted entries in queue"); |