本文最后更新于:2026年5月14日 下午
初体验
关键字:trace
有时需要追踪函数究竟挂在了哪儿,gdb是一个不错的调试工具,但遇到栈被破坏的情况,并不能打印出调用栈,这时下面的方法就排上用处了。
增加gcc编译选项 -finstrument-functions
gcc会自动在函数的入口和出口增加一个函数调用,函数原型为:
1 2
| void __cyg_profile_func_exit(void* callee, void* callsite) __attribute__((no_instrument_function)); void __cyg_profile_func_enter(void* callee, void* callsite) __attribute__((no_instrument_function));
|
以下为自定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| #include <execinfo.h>
void __cyg_profile_func_exit(void* callee, void* callsite) __attribute__((no_instrument_function)); void __cyg_profile_func_enter(void* callee, void* callsite) __attribute__((no_instrument_function));
void __cyg_profile_func_enter(void* callee, void* callsite) { void *funptr = callee; char **p = backtrace_symbols(&funptr, 1); printf("\033[;31m Entering: %s \033[0m\n", *p); free(p); }
void __cyg_profile_func_exit(void* callee, void* callsite) { void *funptr = callee; char **p = backtrace_symbols(&funptr, 1); printf("\033[;31m Exiting: %s \033[0m\n", *p); free(p); }
|
运行时就会打印出来函数调用来了,如:
1 2 3 4 5
| Entering: ./blink() [0x1bb2c] Entering: ./blink() [0x2028c] Entering: ./blink() [0x1ffe4] Entering: ./blink() [0x1f400] Exiting: ./blink() [0x1f400]
|
再用addr2line(进程加了-g编译)来解析地址:
1
| /opt/toolchains/crosstools-arm-gcc-9.2-linux-4.19-glibc-2.30-binutils-2.32/usr/bin/arm-buildroot-linux-gnueabi-addr2line -e tmp/blink -f 0x1f400
|
输出如下:
1 2
| hgdpi_is_debug /home/leon/code/bcm-ax3000-cmcc/userspace/private/apps/blink-sdk-publish/adapt/public_adapt/blink_msg.cpp:57
|
工程中好用的一个实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
| #define _GNU_SOURCE #include <stdio.h> #include <stdint.h> #include <dlfcn.h> #include "hi_util_log.h"
static __thread int depth;
void __attribute__((no_instrument_function)) print_indent() { for (int i = 0; i < depth; i++) printf(" "); }
void __attribute__((no_instrument_function)) print_name(void *func) { Dl_info info;
if (!dladdr(func, &info)) { printf("%p", func); return; }
if (info.dli_sname) { printf("%s", info.dli_sname); return; }
if (info.dli_fname && info.dli_fbase) { printf("%s+0x%lx", info.dli_fname, (unsigned long)((char *)func - (char *)info.dli_fbase)); return; }
printf("%p", func); }
void __attribute__((no_instrument_function)) __cyg_profile_func_enter(void *this_fn, void *call_site) { if (hi_log_call_trace_get()) { print_indent(); printf("-> "); print_name(this_fn); printf("\n"); }
depth++; }
void __attribute__((no_instrument_function)) __cyg_profile_func_exit(void *this_fn, void *call_site) { if (depth > 0) depth--;
if (hi_log_call_trace_get()) { print_indent(); printf("<- "); print_name(this_fn); printf("\n"); } }
|
打印示例:
1 2 3 4 5 6 7 8 9 10 11
| <- /lib/libhi_ipc.so+0x6b1b -> /lib/libmib.so+0x9d29 <- /lib/libmib.so+0x9d29 -> /lib/libmib.so+0xbbe0 -> mib_conf_global_configs <- mib_conf_global_configs -> mib_table_config_get_json_config <- mib_table_config_get_json_config -> mib_json_dump_file -> mib_conf_global_configs
|
注意
(1)static内敛的函数不会打印函数名,能打印-> /lib/libmib.so+0xbb5a,通过addr2line解析为:
1 2 3
| $ addr2line -e build-x86/rootfs/lib/libmib.so -f 0xbb5a mib_table_handler_configfile_backup /home/leon/code2/starnet_plt/gateway/cml/mib_v2/source/mib_table_handler.c:506
|
(2)跟踪函数下面的所有调用都要添加__attribute__((no_instrument_function))属性,避免产生循环
(3)编译选项添加-finstrument-functions,链接选项添加-rdynamic,链接库添加dl