addr2line方法使用总结
addr2line:将地址信息转化成函数名或行数(常用于找报错函数的调用者)。使用方法
拿这次报错信息为例。(函数报错,但是没有做异常判断!!!这里规范问题实在要吐槽一下)
0、我们已经知道函数的报错信息,但是更想知道是哪个函数调用了它。
1、首先需要得到报错函数的地址,定义一个局部变量caller,然后获取该变量的地址信息
然后报错时将地址信息打印出来。
2、得到报错的地址,需要在nfs路径下使用命令 arm-hisiv500-linux-addr2line 0x12345678 -e hikdsp -f.
其中arm-hisiv500-linux- 为H3平台的编译链,addr2line为该命令,0x12345678为报错的地址信息 -e 后跟编译的成果物,-f 为显示函数名,具体可参看man addr2line
3、为本次实现的定位的代码
#define __read_lr(caller) __asm__ __volatile__ ("mov %0, lr\n": "=&r" (caller)) /*返回操作数caller的地址*/
这里涉及到内联汇编的语法,内联汇编的用法有一个通用模板:
asm [ volatile ] (
assembler template
[ : output operands ] /* optional /
[ : input operands ] / optional /
[ : list of clobbered registers ] / optional */
);
asm为gcc关键字,表示接下来要嵌入汇编代码。为避免keyword asm与程序中其它部分产生命名冲突,gcc还支持__asm__关键字,与asm的作用等价。volatile为可选关键字,表示不需要gcc对下面的汇编代码做任何优化。同样出于避免命名冲突的原因,__volatile__也是gcc支持的与volatile等效的关键字。
由于我们是在C语言中内联汇编代码,故需用双引号""将命令括起来,以便gcc以字符串形式将这些命令传给汇编器。
在内联汇编中,操作数通常用数字来引用,具体的编号规则为:若命令共涉及n个操作数,则第1个输出操作数(the first output operand)被编号为0,第2个output operand编号为1,依次类推,最后1个输入操作数(the last input operand)则被编号为n-1
“=&”指的是输出地址;“r"是寄存器操作数约束(register operand constraint, r)。有了"r"的约束,表明操作数将被存储在指定的寄存器中,如本次该行代码表明将caller值存入lr(R14)寄存器,然后输出操作数caller的地址。
报错时打印出错误信息,然后使用:addr2line方法,打印信息如下
可以看出是哪个函数报错。
addr2line 方法2定位崩溃问题
这里写一个简单的崩溃程序,崩溃后dmsg查看报错地址信息
在这里插入代码片
#include<stdio.h>
/*addr2line 方法使用用例*/
void fun2(int *p)
{
int err =0;
err = *p;
printf("err =%d\n",*p);
return ;
}
int fun1()
{
int *p =NULL;
fun2(p) ;
return 0;
}
int main()
{
unsigned int u32num1 = 1,u32num2 = 2;
unsigned int u32Result = 0;
u32Result = u32num1 - u32num2;
printf("result = %d\n",u32Result); //-1
fun1();
return 0;
}
dmesg结果如下
使用addr2line
注意编译时加上-g,否则大概率出现?? -g表示不对编译结果进行优化,保留调试信息
- [ ]