背景
在实现Android native crash捕获功能的时候,需要读取crash进程的内存数据,比如在计算elf的 load bias、Build-Id时,需要读取一块内存区域的数据,比如在获取以某个寄存器值为地址的附近内存数据时,也需要读取一块内存数据。之前我实现的时候是使用ptrace
系统调用来读取crash进程数据的,当时查看了下,似乎ptrace不支持一次读取一块内存数据,所以当时我是多次PTRACE_PEEKDATA
来实现的,也就是多次调用下面的方法来获取crash进程的一块内存数据的:
1 | c++复制代码std::optional<long> readData(pid_t pid, void* addr) { |
上周在写相关文章:Android native crash sdk实现之crash捕获&tombstone信息的生成的时候,又想起来这个事情,感觉应该是有方法能够一次获取一个 memory block的。
/proc/pid/mem
通过搜索发现 Linux 上有个文件:/proc/pid/mem
,我们可以借助open
,lseek
,read
来访问目标进程的内存数据:
- 通过
open
打开/proc/pid/mem
,便可借助打开的这个fd访问目标进程的虚拟内存 - 通过
lseek
可以定位到指定的虚拟地址处 - 通过
read
可以读取一个内存块
当然打开/proc/pid/mem
肯定是需要权限的,否则就可以获取任意进程的内存数据了。不过我们的dumper进程是crash进程的子进程,可以PTRACE_ATTACH
到crash进程,也有权限open/proc/pid/mem
。
一次读取一个memory block
可以按如下方式打开crash进程的 mem fd:
1 | c++复制代码int openMemFd(pid_t targetPid) { |
可以按如下方式一次读取一个 memory block:
1 | c++复制代码ssize_t readMemoryBlock(int memfd, uint64_t addr, void* buf, size_t len) { |
以计算elf build-id 为例
上面提供了一个readMemoryBlock
方法可以一次读取一个memory block,我们以计算elf build-id 为例看下使用:
- 读取 elf header:
1 | c++复制代码ElfW(Ehdr) ehdr; |
- 读取 program header table:
1 | c++复制代码auto phdrAddr = mapAddr + ehdr.e_phoff; |
- 读取 PT_NOTE 并计算 build-id:
1 | c++复制代码for (int i = 0; i < ehdr.e_phnum; ++i) { |
优势
相比多次调用ptrace
系统调用,性能上应该会有优化,另外多次的系统调用在错误处理方面也会比较麻烦
本文转载自: 掘金