虽然我可以写出合理的C代码,但我的专业知识主要是Java,所以如果这个问题没有意义,我很抱歉。
我正在写一些代码来帮助我做堆分析。我是通过LLVM的工具来完成的。我需要的是一种从进程内部访问堆元数据的方法。这样的事情是可能的吗?我知道关于堆的信息存储在许多的 malloc_state
结构(main_arena
比如说)。) 如果我可以访问 main_arena
我可以开始列举不同的竞技场、堆、垃圾箱等。据我所知,这些变量都是静态定义的,所以不能被访问。
但是有没有什么方法可以获得这些信息呢?例如,我可以使用 /proc/$pid/mem
以某种方式泄露信息?
一旦我有了这些信息,我想基本上得到所有不同自由列表的信息。所以,我想要,对于每一个bin类型的bin,bin中的chunks数量和它们的大小。对于快、小、tcache的bin,我知道我只需要索引就可以算出大小。我已经研究了这些结构是如何实现的,以及如何在其中进行迭代。所以我需要的就是获得对这些内部结构的访问权。
我已经研究了 malloc_info
这是我的退路,但我也想得到有关的信息。tcache
而我认为这并不包括在... ... malloc_info
.
我考虑的一个方案是建立一个自定义版本的glibc有 malloc_struct
非静态声明的变量。但从我所看到的情况来看,构建自己的自定义glibc并不是很直接,因为你必须构建整个工具链。我使用的是clang,所以我必须针对我的自定义glibc从源头构建LLVM(至少这是我从研究这种方法中了解到的)。
我最近有一个类似的需求,所以我确实认为,能够进入到 main_arena
对于一个给定的进程来说,确实有它的价值,其中一个例子是事后的内存使用分析。
使用 dl_iterate_phdr
和 elf.h
,它是比较直接地解决了 main_arena
基于本地符号。
#define _GNU_SOURCE
#include <fcntl.h>
#include <link.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
// Ignored:
// - Non-x86_64 architectures
// - Resource and error handling
// - Style
static int cb(struct dl_phdr_info *info, size_t size, void *data)
{
if (strcmp(info->dlpi_name, "/lib64/libc.so.6") == 0) {
int fd = open(info->dlpi_name, O_RDONLY);
struct stat stat;
fstat(fd, &stat);
char *base = mmap(NULL, stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
Elf64_Ehdr *header = (Elf64_Ehdr *)base;
Elf64_Shdr *secs = (Elf64_Shdr*)(base+header->e_shoff);
for (unsigned secinx = 0; secinx < header->e_shnum; secinx++) {
if (secs[secinx].sh_type == SHT_SYMTAB) {
Elf64_Sym *symtab = (Elf64_Sym *)(base+secs[secinx].sh_offset);
char *symnames = (char *)(base + secs[secs[secinx].sh_link].sh_offset);
unsigned symcount = secs[secinx].sh_size/secs[secinx].sh_entsize;
for (unsigned syminx = 0; syminx < symcount; syminx++) {
if (strcmp(symnames+symtab[syminx].st_name, "main_arena") == 0) {
void *mainarena = ((char *)info->dlpi_addr)+symtab[syminx].st_value;
printf("main_arena found: %p\n", mainarena);
raise(SIGTRAP);
return 0;
}
}
}
}
}
return 0;
}
int main()
{
dl_iterate_phdr(cb, NULL);
return 0;
}
dl_iterate_phdr
是用来获取映射的glibc的基地址。该映射不包含所需的符号表(.symtab
),所以必须再次对库进行映射。最后的地址是由基址加上符号值决定的。
(gdb) run
Starting program: a.out
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
[New Thread 0x7ffff77f0700 (LWP 24834)]
main_arena found: 0x7ffff7baec60
Thread 1 "a.out" received signal SIGTRAP, Trace/breakpoint trap.
raise (sig=5) at ../sysdeps/unix/sysv/linux/raise.c:50
50 return ret;
(gdb) select 1
(gdb) print mainarena
$1 = (void *) 0x7ffff7baec60 <main_arena>
(gdb) print &main_arena
$3 = (struct malloc_state *) 0x7ffff7baec60 <main_arena>
符号值与 main_arena
所以找到了正确的地址。
还有其他 方式 要到 main_arena
而不依赖于库本身。在现有的堆中行走,可以发现 main_arena
比如说,但这个策略就不那么简单了。
当然,一旦你有了 main_arena
,你需要所有的内部类型定义才能检查数据。
我正在写一些代码来帮助我做堆分析。
什么样的堆分析?
我想基本得到所有不同自由列表的信息。所以我想,对于每一个bin类型的bin,bin中的chunks数量和它们的大小。对于快、小、tcache的bin,我知道我只需要索引来计算大小。
这些信息 只是 如果你打算改变 malloc
落实。它确实 不 如果你的目标是分析或改进堆的使用情况,那么尝试收集它是有意义的。申请所以,听起来你有一个 XY问题.
此外,像bin和tcache这样的东西只有在特定的上下文中才有意义。malloc
(TCMalloc和jemalloc不会有任何仓位)。
为了分析应用程序堆的使用情况,你可能需要使用 TCmalloc,因为它提供了一个 大量 的工具 堆剖析 和反省。