使用jemalloc
实现信号驱动的程序堆栈信息打印
本文介绍应用如何集成jemalloc,在接收到SIGUSR1
信号10时打印程序的堆栈信息。
1. 编译jemalloc
首先,确保你已经编译并安装了启用prof
功能的jemalloc
。以下是ubuntu18.04上的编译步骤:
git clone https://github.com/jemalloc/jemalloc.git # 本文测试的版本是jemalloc-5.3.0
cd jemalloc
./configure --prefix=/usr/local --enable-prof
make -j10
make install
2. 测试
假如你的需要集成的程序为example,通过LD_PRELOAD
同时加载jemalloc
和钩子库,按照以下步骤运行和测试:
-
设置环境变量:
export MALLOC_CONF="prof:true,prof_active:true,lg_prof_sample:0,tcache:false,prof_prefix:jeprof.out"
prof:true
启用配置文件prof_active:true
启用性能分析lg_prof_sample:0
设置采样率为最频繁tcache:false
:禁用线程缓存(Thread Cache)。- 禁用线程缓存可能会影响性能,但在进行性能分析时,可以提供更准确的内存分配数据。
prof_prefix:jeprof.out
指定性能分析输出文件前缀
-
使用
killall -10 example
发送SIGUSR1
信号以触发堆栈信息打印:killall -10 example # 或者 killall -SIGUSR1 example
-
通过以上步骤,你可以在接收到
SIGUSR1
信号时打印jemalloc
的堆栈信息,并将其输出到本地目录。本文得到的堆栈信息文件名为
jeprof.out.60571.0.m0.heap
,通过如下命令分析该堆栈信息文件:jeprof --show_bytes --text --lines ./example ./jeprof.out.60571.0.m0.heap
-
解析的结果为:
$ jeprof --show_bytes --text --lines ./example ./jeprof.out.60571.0.m0.heap Using local file ./example. Using local file ./jeprof.out.60571.0.m0.heap. Total: 83512 B 83512 100.0% 100.0% 83512 100.0% prof_backtrace_impl /home/test/jemalloc-5.3.0/src/prof_sys.c:103 0 0.0% 100.0% 1024 1.2% _IO_new_file_overflow /build/glibc-2ORdQG/glibc-2.27/libio/fileops.c:759 0 0.0% 100.0% 1024 1.2% _IO_new_file_xsputn /build/glibc-2ORdQG/glibc-2.27/libio/fileops.c:1266 0 0.0% 100.0% 1024 1.2% __GI__IO_doallocbuf /build/glibc-2ORdQG/glibc-2.27/libio/genops.c:365 0 0.0% 100.0% 1024 1.2% __GI__IO_file_doallocate /build/glibc-2ORdQG/glibc-2.27/libio/filedoalloc.c:101 0 0.0% 100.0% 1024 1.2% __GI__IO_fwrite /build/glibc-2ORdQG/glibc-2.27/libio/iofwrite.c:39 0 0.0% 100.0% 1592 1.9% __libc_start_main /build/glibc-2ORdQG/glibc-2.27/csu/../csu/libc-start.c:310 0 0.0% 100.0% 81920 98.1% _dl_start_user :? 0 0.0% 100.0% 1592 1.9% _start ??:? 0 0.0% 100.0% 8 0.0% allocateDouble /home/test/jemalloc_test/example.cpp:19 0 0.0% 100.0% 80 0.1% allocateDynamicArray /home/test/jemalloc_test/example.cpp:32 (discriminator 1) 0 0.0% 100.0% 448 0.5% allocateIntArray /home/test/jemalloc_test/example.cpp:13 0 0.0% 100.0% 1024 1.2% allocateIntArray /home/test/jemalloc_test/example.cpp:14 0 0.0% 100.0% 1472 1.8% allocateMemory /home/test/jemalloc_test/example.cpp:51 0 0.0% 100.0% 8 0.0% allocateMemory /home/test/jemalloc_test/example.cpp:52 0 0.0% 100.0% 32 0.0% allocateMemory /home/test/jemalloc_test/example.cpp:53 0 0.0% 100.0% 80 0.1% allocateMemory /home/test/jemalloc_test/example.cpp:54 0 0.0% 100.0% 32 0.0% allocateString /home/test/jemalloc_test/example.cpp:25 0 0.0% 100.0% 81920 98.1% call_init /build/glibc-2ORdQG/glibc-2.27/elf/dl-init.c:72 0 0.0% 100.0% 83512 100.0% imalloc (inline) /home/test/jemalloc-5.3.0/src/jemalloc.c:2694 0 0.0% 100.0% 83512 100.0% imalloc_body (inline) /home/test/jemalloc-5.3.0/src/jemalloc.c:2550 0 0.0% 100.0% 83512 100.0% je_malloc_default /home/test/jemalloc-5.3.0/src/jemalloc.c:2722 0 0.0% 100.0% 83512 100.0% je_prof_backtrace /home/test/jemalloc-5.3.0/src/prof_sys.c:284 0 0.0% 100.0% 83512 100.0% je_prof_tctx_create /home/test/jemalloc-5.3.0/src/prof.c:195 0 0.0% 100.0% 1592 1.9% main /home/test/jemalloc_test/example.cpp:73 0 0.0% 100.0% 83512 100.0% prof_alloc_prep (inline) /home/test/jemalloc-5.3.0/include/jemalloc/internal/prof_inlines.h:141 0 0.0% 100.0% 81920 98.1% std::__once_callable ??:0 0 0.0% 100.0% 1024 1.2% std::__ostream_insert ??:? 0 0.0% 100.0% 1024 1.2% std::operator<< ??:? 0 0.0% 100.0% 568 0.7% void* fallback_impl /home/test/jemalloc-5.3.0/src/jemalloc_cpp.cpp:98
3. 本人测试的example.cpp代码
以下是完整的example.cpp
代码,编译方法: g++ -g -o example example.cpp
// g++ -g -o example example.cpp
#include <jemalloc/jemalloc.h> // jemalloc
#include <sys/mman.h> // mmap, munmap
#include <unistd.h> // usleep
#include <csignal> // signal, sigaction
#include <cstdlib> // rand()和srand()
#include <ctime> // time()
#include <iostream>
#include <string>
#include <vector>
// 分配int数组
void allocateIntArray() {
const int* intPtr = new int[100];
std::cout << "Allocated int array at: " << intPtr << std::endl;
}
// 分配double
void allocateDouble() {
const double* doublePtr = new double(3.14);
std::cout << "Allocated double at: " << doublePtr << ", value: " << *doublePtr << std::endl;
}
// 分配字符串
void allocateString() {
const std::string* strPtr = new std::string("Hello, World!");
std::cout << "Allocated string at: " << strPtr << ", value: " << *strPtr << std::endl;
}
// 分配动态数组
void allocateDynamicArray() {
size_t arraySize = 10;
size_t* const arrayPtr = new size_t[arraySize];
std::cout << "Allocated array of " << arraySize << " ints at: " << arrayPtr << std::endl;
for (size_t i = 0; i < arraySize; ++i) {
arrayPtr[i] = i;
}
}
// 使用mmap分配内存
void allocateMmap() {
size_t mmapSize = 4096; // 4KB
const void* mmapPtr = mmap(nullptr, mmapSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (mmapPtr == MAP_FAILED) {
perror("mmap failed");
} else {
std::cout << "Allocated mmap at: " << mmapPtr << ", size: " << mmapSize << " bytes" << std::endl;
}
}
void allocateMemory() {
allocateIntArray();
allocateDouble();
allocateString();
allocateDynamicArray();
allocateMmap();
}
// 信号处理函数
void signalHandler(int signum) {
std::cout << "Signal (" << signum << ") received, printing heap profile:" << std::endl;
mallctl("prof.dump", nullptr, nullptr, nullptr, 0);
}
int main() {
// 设置信号处理函数
struct sigaction action;
action.sa_handler = signalHandler;
sigemptyset(&action.sa_mask);
action.sa_flags = 0;
sigaction(SIGUSR1, &action, nullptr);
usleep(100000); // 100ms
allocateMemory();
while (true) {
usleep(100000); // 100ms
}
return 0;
}