就是抛出异常的时候将调用栈堆记录下来以保证能快速找到出错的原因,像调试器就都用这样的功能
解决方案
10
在http://www.codeproject.com搜“stack trace”
10
出错时先转储 各个寄存器的值, 加载的其他 so 的基地址, 再按需要转储当前堆栈 数据, 一般转储个 256K 就够了, 按这样的格式输出
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F aaaaaaaaaaaa
32位程序:
0x03020100
0x07060504
0x0B0A0908
0x0F0E0D0C
64位程序:
0x0706050403020100
0x0F0E0D0C0B0A0908
拿着这数据, 根据 mapfile, 人肉做 backtrace 没问题的, 就是累点, 在 mapfile 里查找像 0x0B0A0908 地址对应什么函数, 偏移多少, 顺便还能恢复出出错时各个参数, 变量的值, 苦逼的嵌入式程序员对这应该相当熟悉了( 实际上大部分时候就看看 ip 位置, 有当前出错的函数位置, 连猜带蒙就够了).
假如要稍微简单点, 编译的时候就设置不要省略栈帧, M$VC 加编译选项 /Oy- , gcc不要加编译选项 -fomit-frame-pointer , 假如是 arm 程序, 再加上 -mapcs-frame 编译选项 … 然后假如是用 glibc , 你就可以直接调用 backtrace … 假如不是 glibc, 本人写个 backtrace 也很简单说, 例如 x86 下,
;
; — default stack size : 256K —
;
; int __fastcall _x_peer_backtrace(void* [], int)
%ifndef backtrace_stack_size
%define backtrace_stack_size 0x40000
%endif
x4c_asm_i386_proc _x_peer_backtrace , 8
push edi
push ebx
push ebp
xor eax , eax
test edx , edx
jle short backtrace_done ; if( n <= 0 ) return 0
mov ebx , [esp+12] ;
mov [ecx] , ebx ;
lea edi , [esp+backtrace_stack_size] ; end of stack
get_next_frame:
lea ecx , [ecx+4]
lea eax , [eax+1]
add edx , -1
jz short backtrace_done ;
test ebp , 3
jnz short backtrace_done ; if( !dword_aligned( stack_frame) ) break
cmp ebp , esp ; if( stack_frame < stack ) break
jb short backtrace_done
cmp ebp , edi ; if( stack_frame > stack + backtrace_stack_size ) break
ja short backtrace_done
mov ebx , [ebp+4]
or ebx , ebx
jz short backtrace_done
mov [ecx] , ebx
mov ebp , [ebp+0] ;
jmp short get_next_frame
backtrace_done:
pop ebp
pop ebx
pop edi
ret
arm 下
struct stackframe {
struct stackframe *fp;
void* sp;
void* lr;
};
int _x_peer_backtrace ( void* syms[] , int size )
{
int i;
struct stackframe *tail = NULL;
void *sptr ;
if( size <= 0 )
return 0;
__asm__ __volatile__ (
“mov %0 , fp\n\t”
“mov %1 , sp\n\t”
: “=&r”(tail) , “=&r”(sptr) );
if( NULL == tail– )
return 0;
for( i = 0; tail && i<size; )
{
if( (void*)tail < sptr ||
(void*)tail > (void*)( (unsigned char*)sptr + BACKTRACESTACKSIZE ) )
break;
syms[i++] = tail->lr;
tail = tail->fp;
if( NULL == tail )
break;
–tail ;
}
return i;
}
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F aaaaaaaaaaaa
32位程序:
0x03020100
0x07060504
0x0B0A0908
0x0F0E0D0C
64位程序:
0x0706050403020100
0x0F0E0D0C0B0A0908
拿着这数据, 根据 mapfile, 人肉做 backtrace 没问题的, 就是累点, 在 mapfile 里查找像 0x0B0A0908 地址对应什么函数, 偏移多少, 顺便还能恢复出出错时各个参数, 变量的值, 苦逼的嵌入式程序员对这应该相当熟悉了( 实际上大部分时候就看看 ip 位置, 有当前出错的函数位置, 连猜带蒙就够了).
假如要稍微简单点, 编译的时候就设置不要省略栈帧, M$VC 加编译选项 /Oy- , gcc不要加编译选项 -fomit-frame-pointer , 假如是 arm 程序, 再加上 -mapcs-frame 编译选项 … 然后假如是用 glibc , 你就可以直接调用 backtrace … 假如不是 glibc, 本人写个 backtrace 也很简单说, 例如 x86 下,
;
; — default stack size : 256K —
;
; int __fastcall _x_peer_backtrace(void* [], int)
%ifndef backtrace_stack_size
%define backtrace_stack_size 0x40000
%endif
x4c_asm_i386_proc _x_peer_backtrace , 8
push edi
push ebx
push ebp
xor eax , eax
test edx , edx
jle short backtrace_done ; if( n <= 0 ) return 0
mov ebx , [esp+12] ;
mov [ecx] , ebx ;
lea edi , [esp+backtrace_stack_size] ; end of stack
get_next_frame:
lea ecx , [ecx+4]
lea eax , [eax+1]
add edx , -1
jz short backtrace_done ;
test ebp , 3
jnz short backtrace_done ; if( !dword_aligned( stack_frame) ) break
cmp ebp , esp ; if( stack_frame < stack ) break
jb short backtrace_done
cmp ebp , edi ; if( stack_frame > stack + backtrace_stack_size ) break
ja short backtrace_done
mov ebx , [ebp+4]
or ebx , ebx
jz short backtrace_done
mov [ecx] , ebx
mov ebp , [ebp+0] ;
jmp short get_next_frame
backtrace_done:
pop ebp
pop ebx
pop edi
ret
arm 下
struct stackframe {
struct stackframe *fp;
void* sp;
void* lr;
};
int _x_peer_backtrace ( void* syms[] , int size )
{
int i;
struct stackframe *tail = NULL;
void *sptr ;
if( size <= 0 )
return 0;
__asm__ __volatile__ (
“mov %0 , fp\n\t”
“mov %1 , sp\n\t”
: “=&r”(tail) , “=&r”(sptr) );
if( NULL == tail– )
return 0;
for( i = 0; tail && i<size; )
{
if( (void*)tail < sptr ||
(void*)tail > (void*)( (unsigned char*)sptr + BACKTRACESTACKSIZE ) )
break;
syms[i++] = tail->lr;
tail = tail->fp;
if( NULL == tail )
break;
–tail ;
}
return i;
}
30
BOOL WINAPI StackWalk64(
__in DWORD MachineType,
__in HANDLE hProcess,
__in HANDLE hThread,
__inout LPSTACKFRAME64 StackFrame,
__inout PVOID ContextRecord,
__in_opt PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine,
__in_opt PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine,
__in_opt PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine,
__in_opt PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress
);
获取全部调用栈的信息
__in DWORD MachineType,
__in HANDLE hProcess,
__in HANDLE hThread,
__inout LPSTACKFRAME64 StackFrame,
__inout PVOID ContextRecord,
__in_opt PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine,
__in_opt PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine,
__in_opt PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine,
__in_opt PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress
);
获取全部调用栈的信息
10
而且得在unwind之前用API导出调用栈,然后得配合调试信息解析出函数名。不同的平台上方法也不一样。这样生成stack trace代价要比Java那种的高得多。