void func_example(void)
{
FUNC_RECORD();
// ...
}
void __cyg_profile_func_enter (void *this_fn, void *call_site);
void __cyg_profile_func_exit (void *this_fn, void *call_site);
int add(int a, int b)
{
return a+b;
}
int max(int a, int b)
{
return a>b? a:b;
}
void __attribute__((no_instrument_function)) __cyg_profile_func_enter(void *this, void *call)
{
printf("<Enter> this: %p, call: %p\n", this, call);
}
void __attribute__((no_instrument_function)) __cyg_profile_func_exit(void *this, void *call)
{
printf("<Exit> this: %p, call: %p\n", this, call);
}
int main(void)
{
printf("max=%d\n", max(add(123,321),456));
return 0;
}
./main
<Enter> this: 0x100401184, call: 0x7ffc95ee80c1
<Enter> this: 0x100401080, call: 0x1004011b9
<Exit> this: 0x100401080, call: 0x1004011b9
<Enter> this: 0x1004010cf, call: 0x1004011c5
<Exit> this: 0x1004010cf, call: 0x1004011c5
max=456
<Exit> this: 0x100401184, call: 0x7ffc95ee80c1
addr2line -e main -a 0x100401080 -fps
0x0000000100401080: add at main.c:4
另外,以上命令行中的-e后接要查地址的可执行程序,案例就是编译后生成的main,-a后接要查询的地址了。而-fps呢?嘿嘿,我讲你也记不住,还不如自己逐个去掉它试试。
好了,以上的输出转换成函数后就是这样的了
<Enter> this: main, call: ??
<Enter> this: add, call: main
<Exit> this: add, call: add
<Enter> this: max, call: main
<Exit> this: max, call: main
max=456
<Exit> this: main, call: ??
对着上面的函数源码看,这下子清晰了吧。
不知你有没有发现,我上面的案例源码中有两个__attribute__((no_instrument_function)),这表示告诉编译器这两个函数不要记录那个entry和exit的调用。
好了,完了吗?
还没,因为每个地址都靠addr2line一个个查,着实很麻烦。当然,程序员很喜欢写脚本的,写个shell或者python脚本遍历一遍这些地址不就完了么。
是的,领导给你指导解决问题的时候也许是这么认为的。但是当你要搞一堆很复杂的函数调用的时候,你会发现更深层次的问题。例如发现这玩意在Windows上(Windows上的Cygwin)非常慢,并不能麻烦完成领导给你的任务。
我测试了十几万行的log地址转换的时候,等得快要崩溃了!我换到了Linux上搞,好了那么一点,但还是很慢啊!我要的是秒上转换!
我突然又想到了一个办法,就是通过MAP文件查找。后续打算在我分析AUTOSAR BSW组件的时候详细介绍这个过程,以及分享这个脚本给大家。
这么好玩,你不打算试试吗?
这文章你扔进收藏夹也是吃灰的,不如点击个转发、在看和赞!