unwind: introduce stacktrace_walker

In current implementation, the stack trace is captured and printed at
the same time, in trace_syscall_exiting.  This approach cannot
provide user expected information when a system call changes the
memory mapping.  In such cases, the stack trace should be captured on
entering syscall and printed on exiting.

As the initial step for splitting capturing from printing, this change
introduces stacktrace_walker utility function.  It can be used both for
capturing in trace_syscall_entering and printing in
trace_syscall_exiting.

Signed-off-by: Masatake YAMATO <yamato@redhat.com>
This commit is contained in:
Masatake YAMATO 2014-04-16 15:33:04 +09:00 committed by Dmitry V. Levin
parent 6141392856
commit 2d534daaa6

128
unwind.c
View File

@ -48,6 +48,19 @@ struct mmap_cache_t {
char* binary_filename; char* binary_filename;
}; };
/*
* Type used in stacktrace walker
*/
typedef void (*call_action_fn)(void *data,
char *binary_filename,
char *symbol_name,
unw_word_t function_off_set,
unsigned long true_offset);
typedef void (*error_action_fn)(void *data,
const char *error,
unsigned long true_offset);
static unw_addr_space_t libunwind_as; static unw_addr_space_t libunwind_as;
void void
@ -169,9 +182,14 @@ unwind_cache_invalidate(struct tcb* tcp)
tcp->mmap_cache_size = 0; tcp->mmap_cache_size = 0;
} }
/* use libunwind to unwind the stack and print a backtrace */ /*
void * walking the stack
unwind_print_stacktrace(struct tcb* tcp) */
static void
stacktrace_walk(struct tcb *tcp,
call_action_fn call_action,
error_action_fn error_action,
void *data)
{ {
unw_word_t ip; unw_word_t ip;
unw_cursor_t cursor; unw_cursor_t cursor;
@ -228,23 +246,18 @@ unwind_print_stacktrace(struct tcb* tcp)
true_offset = ip - cur_mmap_cache->start_addr + true_offset = ip - cur_mmap_cache->start_addr +
cur_mmap_cache->mmap_offset; cur_mmap_cache->mmap_offset;
if (symbol_name[0]) { if (symbol_name[0]) {
/* call_action(data,
* we want to keep the format used by backtrace_symbols from the glibc cur_mmap_cache->binary_filename,
* symbol_name,
* ./a.out() [0x40063d] function_off_set,
* ./a.out() [0x4006bb] true_offset);
* ./a.out() [0x4006c6]
* /lib64/libc.so.6(__libc_start_main+0xed) [0x7fa2f8a5976d]
* ./a.out() [0x400569]
*/
tprintf(" > %s(%s+0x%lx) [0x%lx]\n",
cur_mmap_cache->binary_filename,
symbol_name, function_off_set, true_offset);
} else { } else {
tprintf(" > %s() [0x%lx]\n", call_action(data,
cur_mmap_cache->binary_filename, true_offset); cur_mmap_cache->binary_filename,
symbol_name,
0,
true_offset);
} }
line_ended();
break; /* stack frame printed */ break; /* stack frame printed */
} }
else if (mid == 0) { else if (mid == 0) {
@ -254,8 +267,8 @@ unwind_print_stacktrace(struct tcb* tcp)
* unw_get_reg returns IP == 0 * unw_get_reg returns IP == 0
*/ */
if(ip) if(ip)
tprintf(" > backtracing_error\n"); error_action(data,
line_ended(); "backtracing_error", 0);
goto ret; goto ret;
} }
else if (ip < cur_mmap_cache->start_addr) else if (ip < cur_mmap_cache->start_addr)
@ -265,19 +278,86 @@ unwind_print_stacktrace(struct tcb* tcp)
} }
if (lower > upper) { if (lower > upper) {
tprintf(" > backtracing_error [0x%lx]\n", ip); error_action(data,
line_ended(); "backtracing_error", ip);
goto ret; goto ret;
} }
ret_val = unw_step(&cursor); ret_val = unw_step(&cursor);
if (++stack_depth > 255) { if (++stack_depth > 255) {
tprintf("> too many stack frames\n"); error_action(data,
line_ended(); "too many stack frames", 0);
break; break;
} }
} while (ret_val > 0); } while (ret_val > 0);
ret: ret:
free(symbol_name); free(symbol_name);
} }
/*
* printing an entry in stack
*/
/*
* we want to keep the format used by backtrace_symbols from the glibc
*
* ./a.out() [0x40063d]
* ./a.out() [0x4006bb]
* ./a.out() [0x4006c6]
* /lib64/libc.so.6(__libc_start_main+0xed) [0x7fa2f8a5976d]
* ./a.out() [0x400569]
*/
#define STACK_ENTRY_SYMBOL_FMT \
" > %s(%s+0x%lx) [0x%lx]\n", \
binary_filename, \
symbol_name, \
function_off_set, \
true_offset
#define STACK_ENTRY_NOSYMBOL_FMT \
" > %s() [0x%lx]\n", \
binary_filename, true_offset
#define STACK_ENTRY_BUG_FMT \
" > BUG IN %s\n"
#define STACK_ENTRY_ERROR_WITH_OFFSET_FMT \
" > %s [0x%lx]\n", error, true_offset
#define STACK_ENTRY_ERROR_FMT \
" > %s\n", error
static void
print_call_cb(void *dummy,
char *binary_filename,
char *symbol_name,
unw_word_t function_off_set,
unsigned long true_offset)
{
if (symbol_name)
tprintf(STACK_ENTRY_SYMBOL_FMT);
else if (binary_filename)
tprintf(STACK_ENTRY_NOSYMBOL_FMT);
else
tprintf(STACK_ENTRY_BUG_FMT, __FUNCTION__);
line_ended();
}
static void
print_error_cb(void *dummy,
const char *error,
unsigned long true_offset)
{
if (true_offset)
tprintf(STACK_ENTRY_ERROR_WITH_OFFSET_FMT);
else
tprintf(STACK_ENTRY_ERROR_FMT);
line_ended();
}
/*
* printing stack
*/
void
unwind_print_stacktrace(struct tcb* tcp)
{
stacktrace_walk(tcp, print_call_cb, print_error_cb, NULL);
}