当前位置: 首页 > news >正文

扩展addr2line程序的功能,group_add2line() 脚本的实现

------------------------------------------------------------
author: hjjdebug
date: 2024年 08月 05日 星期一 16:19:07 CST
descrition:  扩展addr2line程序的功能,group_add2line() 脚本的实现
------------------------------------------------------------
扩展addr2line程序的功能,group_add2line() 脚本的实现
addr2line 程序简单介绍一下:
addr2line 是binutils 工具包中的一个工具,用法如下.
addr2line -e <file> -f <addr>
要求输入带调试信息的文件名称, 偏移地址
将会输出该地址对应到文件的函数名称及行号

group_addr2line要求:
在debug 时,我可以得到一组调用栈地址, 但这组地址对应着很多so文件
而且,记录的地址是内存中的绝对地址,而不是相对于so起始点的偏移地址.
这不能直接满足addr2line 的要求
给个例子吧:
index      time       addr            size     type     stc stacklist
9019992    10:09:48   0x7f5b41ee49b0  2580552  malloc   6   0:0x7f5bb01ca550 1:0x7f5bacb3be14 2:0x7f5baccb9a58 3:0x7f5baccb987a 4:0x4bc6ec 5:0x7f5ba8aac185 

我们只关注stacklist部分,即第7列,它可能由10几层20几层调用
过程:
第1. 由执行文件,找到pid值. 你可以用ps -ef <执行文件> |grep 一类命令来找到
第2. 从调用栈中提取出地址列表信息,即0x开头的地址, 这需要单词匹配,单词分割.
第3. 转换为偏移地址
步骤2得到的地址是绝对内存地址,要找到偏移地址,需要找到这个地址对应着哪个模块,模块的基地址是多少,才能知道偏移地址
这可以通过 /proc/pid/maps 文件来找到.

这种大规模的劳动人工无法胜任,只能交给电脑去完成!!

这里就设计到几个编程的要点. 都在代码里了,总之,用多了就熟了.
1. 字符串匹配 ,字符串分割, awk 强项
2. bash 和 awk 如何交互信息, awk print, bash的命令替换, here文档, read readarray
3. 用数组保留数据,用循环处理数据, 编程之强项
4. bash中16进制数据与字符串变换 $(())

5. 再体会一下here文档<<< 与重定向到进程输出的差别< <(cmd). 
   在ubuntu下跑的好好的程序, 在centos上必需要把here 文档改成重定向到进程输出.
   否则,readarray 读到的就不是一个array 而是一个元素, 可见代码还要结合具体的环境.
   ubuntu20 的bash 是5.0, centos7的bash 是4.2, 写代码还应该尽量适应早期版本
 

以下设计我们的group_addr2line 脚本
shell 脚本请看附件完整的代码
半自动,半手工化的操作过程.
------------------------------------------------------------
1. 提取执行命令对应的pid 信息:
------------------------------------------------------------
命令行脚本
$ ps -ef |grep multiview_compare|grep -v "grep"|awk '{print $2}'
结果
16517

------------------------------------------------------------
2. 提取调用栈地址信息到数组
------------------------------------------------------------
命令行脚本
$ cat 1.txt |awk '{for(i=7;i<NF;i++){split($i,a,":");print a[2]}}'
得到结果:
0:0x7f5bb01ca550
1:0x7f5bacb3be14
2:0x7f5baccb9a58
3:0x7f5baccb987a
4:0x4bc6ec
5:0x7f5ba8aac185 

------------------------------------------------------------
3.  获取 /proc/pid/maps 中有用的信息
------------------------------------------------------------
$ cat /proc/16517/maps |awk '{if( NF>=6 && !a[$6]){split($1,a2,"-");a[$6]=a2[1];print a[$6],$6}}'
会列出所有模块的起始地址
举例:
00400000 /home/hjj/multiview_compare/multiview_compare
01f48000 [heap]
7f5b6649c000 /usr/share/fonts/opentype/noto/NotoSansCJK-Bold.ttc
7f5b67893000 /SYSV00000000
7f5b6cdfe000 /usr/share/fonts/truetype/ubuntu/Ubuntu-B.ttf
....

------------------------------------------------------------
4. 对每一个地址,执行查询起始地址和模块名称的操作
------------------------------------------------------------
前期准备工作完成,根据输入地址,找到模块偏移地址和模块名称,用穷举法即可.
这是电脑的长处,写个函数吧,
 look_baseaddr_name()

------------------------------------------------------------
5. 对每一个地址,执行addr2line xxx 命令调用,输出结果.
------------------------------------------------------------
对于少量的地址, 用手工的方法也是可以得到结果的, 但大量的,完整的就有点麻烦了.

花了我不少时间编写调试,收获也不小,让电脑听自己的话,还是很惬意的,留存吧!
附件: bash 代码

#!/bin/bash
if [ $# -lt 1 ] || [ "$1" = "-h" ] 
#if [ $# -lt 1 ]
thenecho "Usage $0 <prog> <addr>"echo "Example $0 multiview_compare 1.txt"exit 1
fi#提取pid , read 从一行取
read pid < <(ps -ef|awk -v file=$1 '{if(match($8,file))print $2}')
declare -p pid
path="/proc/$pid/maps"
echo $path# 让程序支持管道操作
if [ $# -lt 2 ]
theninput="/dev/stdin"
elseinput=$2
fi#文件$1中 提取地址信息到数组 addrs[] , readarray 可以从多行中提取信息到数组
readarray -t addrs < <(awk '{for(i=7;i<NF;i++){split($i,a,":");print substr(a[2],3)}}' $input)
#declare -p addrs
#echo "addrs[0] is:" ${addrs[0]}
#echo "addrs[1] is:" ${addrs[1]}#提取maps 信息
readarray -t maps < <(awk '{if( NF>=6 && !a[$6]){split($1,a2,"-");a[$6]=a2[1];print a[$6],$6}}' $path)
#declare -p maps#将maps 信息提取为so_addr[] 和 so_name[], read 从单行取, 一次可以取多列
echo "number: ${#maps[@]}"
for(( i=0; i<${#maps[@]}; i++))
doread so_addr[$i] so_name[$i] <<< ${maps[$i]}
done
#declare -p so_addr
#declare -p so_name
#echo ${so_addr[0]}
#echo ${so_name[0]}
#echo ${so_addr[1]}
#echo ${so_name[1]}#根据地址查找模块名称及基地址,写了一个函数
look_baseaddr_name()
{local i addrlet addr=$((16#$1))for((i=0;i<${#so_addr[@]};i++))dolet next_val=$((16#${so_addr[$i+1]}))if [ $((16#${so_addr[$i]})) -lt $addr  -a $addr -lt $next_val ]thenbreak;fidoneif [ $i -ne ${#so_addr[@]} ]thenecho "${so_addr[$i]} ${so_name[$i]}" #输出结果fi}# 循环使用addr2line 输出信息
for((i=0;i<${#addrs[@]};i++))
doread base_addr fnd_name <<< $(look_baseaddr_name ${addrs[$i]})if [ $base_addr ]thenaddr=$((16#${addrs[$i]}))if [ $addr -gt 10000000 ]thenlet offset=$addr-$((16#$base_addr))elselet offset=$addr # 非so文件不用计算偏移ficmd="addr2line -e $fnd_name -f $(printf "%x" $offset)"echo "$i $cmd"$cmd | c++filtecho ""fi
done

执行结果演示:

$ group_addr2line.sh  multiview_compare 1.txt 
0 addr2line -e /home/hjj/gitSource/ld_preload/libpreload/libpreload_udpsend.so -f 1cc0
posix_memalign
/home/hjj/gitSource/ld_preload/libpreload/ld_preload_udpsend.c:294

1 addr2line -e /opt/ffmpeg_build/lib/libavutil.so.56.70.100 -f 3b660
av_malloc
/storage/source/FFmpeg-n4.4/libavutil/mem.c:86

2 addr2line -e /opt/ffmpeg_build/lib/libavutil.so.56.70.100 -f 19723
av_buffer_alloc
/storage/source/FFmpeg-n4.4/libavutil/buffer.c:72

...... 太长了,忽略18项.

20 addr2line -e /opt/ffmpeg_build/lib/libavcodec.so.58.134.100 -f 28ed94
avcodec_send_packet
/storage/source/FFmpeg-n4.4/libavcodec/decode.c:608

21 addr2line -e /home/hjj/multiview_compare/multiview_compare -f 4a891c
FFmpegThread::decode_packet(AVCodecContext*, AVPacket*)
/home/hjj/multiview_compare/myffmpeg/myffmpeg.cpp:1817

22 addr2line -e /home/hjj/multiview_compare/multiview_compare -f 49f9ac
FFmpegThread::run()
/home/hjj/multiview_compare/myffmpeg/myffmpeg.cpp:330

http://www.lryc.cn/news/416471.html

相关文章:

  • idea中修改项目名称
  • Flink开发语言使用Java还是Scala合适?
  • C++STL专题 vector底层实现
  • 【Linux】装机常用配置
  • oracle库PASSWORD_VERSIONS 对应的加密方式
  • 分享一个基于微信小程序的乡村医疗上门服务预约平台(源码、调试、LW、开题、PPT)
  • 切香肠(Sausage)
  • Session与Cookie以及Cache区别,及应用场景
  • Debian | 更换 Gnome 至 Xfce4
  • 在使用JSON过程中遇到的一个空间释放问题
  • 基于ThinkPHP开发的校园跑腿社区小程序系统源码,包含前后端代码
  • 不同专业方向如何在ChatGPT的帮助下完成选题
  • MathType7.4中文版本功能详解!你的数学公式编辑神器
  • 在 PhpStorm 中为 .java 文件启用语法高亮,需要正确配置文件类型和关联语言。
  • 2024年8月1日(前端服务器的配置以及tomcat环境的配置)
  • 基于tcp,html,数据库的在线信息查询系统项目总结
  • P1032 [NOIP2002 提高组] 字串变换
  • Android 12系统源码_多屏幕(一)多屏幕设备显示Activity
  • 如何判断IP地址属于住宅IP还是机房IP
  • C#TreeView控件应用
  • 计算机网络-数据链路层
  • 农场游戏中的时间管理实例
  • css 数字平铺布局
  • 【开源】嵌入式Linux(IMX6U)应用层综合项目(2)--智能家居APP
  • CUDA常见编译器配置问题一览
  • 【Android】系统级应用升级后的安装位置
  • uniapp 使用renderjs通信
  • PostgreSQL 15
  • 给本地设备搭建一个云端语音助手
  • yolov5车辆类型识别TXT数据集