踩内存问题分析工具

本文介绍了四种分析内存踩踏问题的工具:gdb的watch、mprotect、asan和perf。gdb的watch点可以在变量变化时暂停程序,asan提供内存检测,mprotect通过修改内存权限触发异常,perf利用硬件断点监控内存访问。每种方法都有其优缺点,适用于不同的调试场景。
摘要由CSDN通过智能技术生成

踩内存问题,大家都知道,是一个比较难分析的问题。

踩内存问题被发现,通常是程序崩溃的时候,能够生成coredump分析,知道是哪个内存被踩了,但通常是很难分析出是哪段代码出现了踩内存的问题。

本文会介绍几种分析踩内存问题的工具,有些工具是最近发现的,我还没有大量使用过,所以只是个简单的介绍,各位看官自行判断是否实用。

文章写的比较匆忙,内容可能会不太完整,请各位见谅。

gdb watch

发现踩内存问题后,我们可以通过gdb来起出现问题的应用程序,然后对出现问题的变量进行watch,每次这个变量被读取/修改时,程序都会暂停,我们就可以看一下是不是正常的修改。

这个网上资料比较多,就不详细介绍了。

这种方法的缺点: 每次修改都会停下来,如果这个变量经常被修改,那么基本是无法调试的。不知道能否通过gdb脚本来解决这个问题。
优点:很灵活,可以设置对很多变量的watch,当然设多了会占用比较多的cpu;暂停下来以后,可以看的信息很多

mprotect

mprotect可以修改内存的权限,可以将需要保护的内存设置为只读,然后每次访问这块内存,进程就会接收到一个异常信号(应该是SIGSEGV),然后可以对这个信号注册回调函数,进行你需要的处理,例如打印调用栈。

当然正常的访问是不应该进行处理的,所以在正常的访问前,先执行mprotect将内存改为可读写,访问结束后,再改为只读。

mprotect还有一个问题,它只能对一整个page进行设置,并不能指定到一个字节,所以回调里面还得判断访问的是否你需要监视的内存,如果不是,还得进行比较复杂的处理:1. 将这个page设置为可读写,否则这个修改就无法完成了,会一直触发SIGSEGV信号。2. 修改完成之后,还得重新设置为只读,这时候的设置比较麻烦了,应该已经回归到正常运行状态了。有一种处理方法,可以先在触发异常的代码的下一行将代码修改为INT3指令(类似gdb断点的原理),那么就会触发中断,在中断中再进行设置,设置完再将INT3修改回原来的代码。

还是比较麻烦的,不是很推荐使用。

asan

asan本身自带踩内存的检测,只要编译时带上asan,就能够进行一些踩内存的检测了。

使用asan进行检测会有一些踩内存问题无法检测到,主要是踩的内存不在asan加的保护内存中,而是踩的正常内存,此时就检测不出来。

如果出现这种问题,还可以采用asan提供的ASAN_POISON_MEMORY_REGION宏,对指定的内存进行保护
参考:https://github.com/google/sanitizers/wiki/AddressSanitizerManualPoisoning

同样的,使用poison时,在正常的访问前,需要先解除保护,也就是unpoison,访问完成后,再进行poison。

这样就要求对所有会访问监视内存的代码前后进行修改,否则正常访问也会报错了。

perf(hardware breakpoint)

gdb的watch,在数量比较少的时候,应该也是使用的hardware breakpoint。perf提供了可以手动设置hardware breakpoint的方法,针对指定的内存设置hardware breakpoint之后,对这个内存的读写就会触发中断,然后由指定的处理函数进行处理,如果不指定,则是默认输出到trace。

这个正好试用了一下,记录一下使用示例:

使用perf,在内存0x5594191c6018上设置了hardware breakpoint,采集每次对该内存的访问情况:

root/$ sudo perf record -e mem:0x5594191c6018 -p $(pgrep bcc_test)
^C[ perf record: Woken up 55 times to write data ]
[ perf record: Captured and wrote 14.168 MB perf.data (450225 samples) ]

perf script查看结果:

root/$ sudo perf script
        bcc_test 12823 5633590.352853:          1 mem:0x5594191c6018:      5594191c31c1 _plus0+0x18 (/home/xuhaitao/test/bcc/build/bcc_test)
        bcc_test 12823 5633590.352856:          1 mem:0x5594191c6018:      5594191c31cb _plus0+0x22 (/home/xuhaitao/test/bcc/build/bcc_test)
        bcc_test 12823 5633590.352858:          1 mem:0x5594191c6018:      5594191c31c1 _plus0+0x18 (/home/xuhaitao/test/bcc/build/bcc_test)
        bcc_test 12823 5633590.352859:          1 mem:0x5594191c6018:      5594191c31cb _plus0+0x22 (/home/xuhaitao/test/bcc/build/bcc_test)
        bcc_test 12823 5633590.352860:          1 mem:0x5594191c6018:      5594191c31c1 _plus0+0x18 (/home/xuhaitao/test/bcc/build/bcc_test)
        bcc_test 12823 5633590.352861:          1 mem:0x5594191c6018:      5594191c31cb _plus0+0x22 (/home/xuhaitao/test/bcc/build/bcc_test)
        ......

这样就能看到对内存0x5594191c6018进行访问和修改的位置了(_plus0+0x18和_plus0+0x22,也就是_plus0函数中的某个位置)。
这个使用是很灵活的,可以打印调用栈,还可以搭配ebpf进行使用,进行一些统计分析、数据过滤。
bcc也有人做过针对hardware breakpoint的开发,但没有入库,见:https://github.com/iovisor/bcc/pull/2456 希望以后能有成熟的bcc工具吧。

这种方法也有一些缺点:1. 你需要知道内存地址,在进程中是容易获取到的,但通过终端命令的方法设置的话,获取内存地址可能会有一些困难。 2. 可以设置的hardware point数量有限,一般x86是支持4个

除了终端命令的方式,也可以在代码中设置hardware point,参考系统提供的api:register_wide_hw_breakpoint

  • 1
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值