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:
parent
6141392856
commit
2d534daaa6
128
unwind.c
128
unwind.c
@ -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);
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user