作为调试目的,有时候可能需要获取当前函数的调用者信息,比如在 malloc 申请一块内存的时候,记录 caller 的地址,当发生内存泄漏或者内存破坏时,可以使用 caller 地址大致定位是哪块代码有问题。
我们可以通过 GCC 的内置接口void *
__builtin_return_address (unsigned int level)
获取当前函数的返回地址,也就是调用者。
参数 level 表示扫描的栈帧层级,0 表示返回当前函数的返回地址,1 便是当前函数的调用者的返回地址,以此类推。
测试代码
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
| void test_return(void)
{
shell_printf("test return\r\n");
void *p = __builtin_return_address (0);
shell_printf("1 %p\r\n", p);
p = __builtin_extract_return_addr (__builtin_return_address (0));
shell_printf("2 %p\r\n", p);
}
void test_return1(void)
{
test_return();
shell_printf("test return\r\n");
}
/**
* @brief test command
*/
void shell_test_cmd(int argc, char *argv)
{
unsigned int i;
shell_printf("test command:\r\n");
for (i = 0; i < argc; i++)
{
shell_printf("paras %d: %s\r\n", i, &(argv[(int)argv[i]]));
}
test_return1();
void *p = test_return1;
shell_printf("%p\r\n", p);
}
|
1
2
3
4
5
6
7
8
| nr@root:test
test command:
paras 0: test
test return
1 004EADA6
2 004EADA6
test return
004EAFB0
|
004EADA6 是 test_return 的返回地址。004EAFB0 是直接打印的函数地址,是 test_return 的入口地址。然后通过 addr2line.exe 验证这两个地址。
1
2
3
4
5
| D:\workspace\QT\qt_idf\examples\shell>D:\workspace\QT\qt_idf\tools\mingw32\bin\addr2line.exe -e D:/workspace/QT/qt_idf/examples/shell/build/build_out/target/shell.exe -f 004EADA6 004EAFB0
test_return1
D:/workspace/QT/qt_idf/examples/shell/cmds/cmds.c:56
test_return1
D:/workspace/QT/qt_idf/examples/shell/cmds/cmds.c:54
|
004EADA6 是 test_return1 中第 56 行代码位置,004EAFB0 是 test_return1 中第 54 行代码位置。
另外,使用 addr2line.exe 还碰到出现 dwarf error: could not find abbrev number 108. 问题,原因是编译的时候没有打开调试信息,在编译配置中增加 -g3 即可解决。