内存泄漏调试 ---- jemalloc的heap profiling

使用jemalloc时,可以通过profiling机制来发现并定位内存泄漏(memory leak)。本文翻译自原文并增加了一些例子。

1、安装

这里我们编译安装jemalloc.5.10,注意在configure的时候添加了–enable-prof选项,这样才能打开profiling机制。下文中通过MALLOC_CONF设置的参数都依赖于次选项。

wget https://github.com/jemalloc/jemalloc/archive/5.1.0.tar.gz
tar zxvf 5.1.0.tar.gz
cd jemalloc-5.1.0/
./autogen.sh
./configure --prefix=/usr/local/jemalloc-5.1.0 --enable-prof
make
make install

2、程序退出时的内存分配状态

作为最简单的情形,我们可以在程序退出时,查看还有哪些分配但未释放的内存,它们通常是内存泄漏的重要线索。

#include <stdio.h>
#include <stdlib.h>

void do_something(size_t i)
{
  // Leak some memory.
  malloc(i * 1024);
}

void do_something_else(size_t i)
{
  // Leak some memory.
  malloc(i * 4096);
}

int main(int argc, char **argv)
{
  size_t i, sz;

  for (i = 0; i < 80; i++)
  {
    do_something(i);
  }

  for (i = 0; i < 40; i++)
  {
    do_something_else(i);
  }

  return (0);
}

然后编译。注意:我们的代码里没有include jemalloc的头文件,编译的时候也不需要链接jemalloc库。启动的时候通过LD_PRELOAD指定jemalloc库的路径就可以了。这是jemalloc方便使用的地方。当然也可以include jemalloc的头文件并链接jemalloc库来使用jemalloc的其他功能(见后文)。

gcc test.c -o a.out 
2.1、标题程序退出时的泄漏
MALLOC_CONF=prof_leak:true,lg_prof_sample:0,prof_final:true LD_PRELOAD=/usr/local/jemalloc-5.1.0/lib/libjemalloc.so.2  ./a.out
<jemalloc>: Leak approximation summary: ~6926352 bytes, ~120 objects, >= 2 contexts
<jemalloc>: Run jeprof on "jeprof.34447.0.f.heap" for leak detail

程序退出时报告了泄漏的大概情况,多少自己,多少对象,并产生了一个”jeprof.34447.0.f.heap”文件,其中包含了详细信息。

2.2、泄漏的详细信息

使用jemalloc提供的jeprof工具,可以方便的查看”jeprof.34447.0.f.heap”文件:

/usr/local/jemalloc-5.1.0/bin/jeprof a.out jeprof.34447.0.f.heap
Using local file a.out.
Using local file jeprof.34447.0.f.heap.
Welcome to jeprof!  For help, type 'help'.
(jeprof) top
Total: 6.6 MB
     3.3  50.6%  50.6%      3.3  50.6% do_something
     3.3  49.4% 100.0%      3.3  49.4% do_something_else
     0.0   0.0% 100.0%      6.6 100.0% __libc_start_main
     0.0   0.0% 100.0%      6.6 100.0% _start
     0.0   0.0% 100.0%      6.6 100.0% main
(jeprof)
2.3、泄露代码的调用路径

jeprof工具也可以生成泄漏代码的调用路径图。

/usr/local/jemalloc-5.1.0/bin/jeprof  --show_bytes --pdf a.out jeprof.34447.0.f.heap > a.pdf

3、Heap Profiling

有时候,我们不能终止程序来看程序退出时的状态,jemalloc提供了一些方法来获取程序运行时的内存分配情况。

3.1、每1MB dump一次
export MALLOC_CONF="prof:true,lg_prof_interval:20"
LD_PRELOAD=/usr/local/jemalloc-5.1.0/lib/libjemalloc.so.2  ./a.out
ll
total 40
-rwxr-xr-x. 1 root root 8520 Jan  2 18:33 a.out
-rw-r--r--. 1 root root 3878 Jan  2 18:38 jeprof.34584.0.i0.heap
-rw-r--r--. 1 root root 3882 Jan  2 18:38 jeprof.34584.1.i1.heap
-rw-r--r--. 1 root root 3882 Jan  2 18:38 jeprof.34584.2.i2.heap
-rw-r--r--. 1 root root 4004 Jan  2 18:38 jeprof.34584.3.i3.heap
-rw-r--r--. 1 root root 4004 Jan  2 18:38 jeprof.34584.4.i4.heap
-rw-r--r--. 1 root root 4006 Jan  2 18:38 jeprof.34584.5.i5.heap

其中lg_prof_interval:20中的20表示1MB(2^20),prof:true是打开profiling。运行程序时,每分配(大约)1MB就会dump产生一个文件。

/usr/local/jemalloc-5.1.0/bin/jeprof a.out jeprof.34584.3.i3.heap
Using local file a.out.
Using local file jeprof.34584.3.i3.heap.
Welcome to jeprof!  For help, type 'help'.
(jeprof) top
Total: 5.8 MB
     4.8  81.8%  81.8%      4.8  81.8% do_something
     1.1  18.2% 100.0%      1.1  18.2% do_something_else
     0.0   0.0% 100.0%      5.8 100.0% __libc_start_main
     0.0   0.0% 100.0%      5.8 100.0% _start
     0.0   0.0% 100.0%      5.8 100.0% main
(jeprof) quit

jeprof工具不仅可以查看详细信息或者生成调用路径图(如上所示),还可以用来比较两个dump(显示增量部分):

/usr/local/jemalloc-5.1.0/bin/jeprof a.out --base=jeprof.34584.2.i2.heap jeprof.34584.3.i3.heap
Using local file a.out.
Using local file jeprof.34584.3.i3.heap.
Welcome to jeprof!  For help, type 'help'.
(jeprof) top
Total: 1.6 MB
     1.1  66.2%  66.2%      1.1  66.2% do_something_else
     0.5  33.8% 100.0%      0.5  33.8% do_something
     0.0   0.0% 100.0%      1.6 100.0% __libc_start_main
     0.0   0.0% 100.0%      1.6 100.0% _start
     0.0   0.0% 100.0%      1.6 100.0% main
(jeprof)

其中--base指定比较的基础。如上例,dump jeprof.34584.3.i3.heap的时候,分配了5.8 MB内存,do_something和do_something_else分别占81.8%和18.2%;但和dump jeprof.34584.2.i2.heap的时候相比,多分配了1.6MB内存,do_something和do_something_else分别占66.2%和33.8%。可以预见,自己和自己比,没有内存被分配:

/usr/local/jemalloc-5.1.0/bin/jeprof a.out --base=jeprof.34584.2.i2.heap jeprof.34584.2.i2.heap
Using local file a.out.
Using local file jeprof.34584.2.i2.heap.
Welcome to jeprof!  For help, type 'help'.
(jeprof) top
Total: 0.0 MB
(jeprof)
3.2、每次达到新高时dump
export MALLOC_CONF="prof:true,prof_gdump:true"
LD_PRELOAD=/usr/local/jemalloc-5.1.0/lib/libjemalloc.so.2  ./a.out
3.3、在代码里手动dump

注意:需要include jemalloc的头文件并链接jemalloc库。

#include <stdio.h>
#include <stdlib.h>
#include <jemalloc/jemalloc.h>

void do_something(size_t i)
{
  // Leak some memory.
  malloc(i * 1024);
}

void do_something_else(size_t i)
{
  // Leak some memory.
  malloc(i * 4096);
}

int main(int argc, char **argv)
{
  size_t i, sz;

  for (i = 0; i < 80; i++)
  {
    do_something(i);
  }

  mallctl("prof.dump", NULL, NULL, NULL, 0);

  for (i = 0; i < 40; i++)
  {
    do_something_else(i);
  }

  mallctl("prof.dump", NULL, NULL, NULL, 0);

  return (0);
}

编译(指定jemalloc头文件路径,并链接jemalloc库):

gcc -I/usr/local/jemalloc-5.1.0/include test.c -L/usr/local/jemalloc-5.1.0/lib -ljemalloc

然后设置MALLOC_CONF并执行程序:

export MALLOC_CONF="prof:true,prof_prefix:jeprof.out"
LD_PRELOAD=/usr/local/jemalloc-5.1.0/lib/libjemalloc.so.2  ./a.out
# ls
a.out  jeprof.out.35307.0.m0.heap  jeprof.out.35307.1.m1.heap
3.4、稳定状态的内存分配

注意:需要include jemalloc的头文件并链接jemalloc库。
程序启动的时候,势必要分配内存,我们查找内存泄漏的时候,往往更关注程序在稳定状态时的内存分配:只要程序启动完成之后内存不再增长,就没有严重的泄漏问题。所以,稳定状态的内存profiling往往更有意义。设置MALLOC_CONF=prof_active:false,使得程序在启动的时候profiling是disabled;程序启动完成后,再通过mallctl(“prof.active”)来enable profiling;或者定时enable。

3.4.1、启动完成后enable profiling
#include <stdio.h>
#include <stdlib.h>
#include <jemalloc/jemalloc.h>

void do_something(size_t i)
{
  // Leak some memory.
  malloc(i * 1024);
}

void do_something_else(size_t i)
{
  // Leak some memory.
  malloc(i * 4096);
}

int main(int argc, char **argv)
{
  size_t i, sz;

  //initialization ...

  for (i = 0; i < 80; i++)
  {
    do_something(i);
  }

  //enter into steady-state...

  bool active = true;
  mallctl("prof.active", NULL, NULL, &active, sizeof(bool));

  for (i = 0; i < 40; i++)
  {
    do_something_else(i);
  }

  mallctl("prof.dump", NULL, NULL, NULL, 0);

  return (0);
}

编译,设置环境变量,并执行:

gcc -I/usr/local/jemalloc-5.1.0/include test2.c -L/usr/local/jemalloc-5.1.0/lib -ljemalloc
export MALLOC_CONF="prof:true,prof_active:false,prof_prefix:jeprof.out"
LD_PRELOAD=/usr/local/jemalloc-5.1.0/lib/libjemalloc.so.2  ./a.out
# ls
a.out  jeprof.out.36842.0.m0.heap 

用jeprof查看,发现只有steady-state之后的内存分配:

/usr/local/jemalloc-5.1.0/bin/jeprof a.out jeprof.out.36842.0.m0.heap
Using local file a.out.
Using local file jeprof.out.36842.0.m0.heap.
Welcome to jeprof!  For help, type 'help'.
(jeprof) top
Total: 2.8 MB
     2.8 100.0% 100.0%      2.8 100.0% do_something_else
     0.0   0.0% 100.0%      2.8 100.0% __libc_start_main
     0.0   0.0% 100.0%      2.8 100.0% _start
     0.0   0.0% 100.0%      2.8 100.0% main
(jeprof)
3.4.2、定时enable profiling

还可以通过这样的流程定时dump:

bool active;

mallctl("prof.dump", NULL, NULL, NULL, 0);    //生成prof.1

active = true;
mallctl("prof.active", NULL, NULL, &active, sizeof(bool));

//sleep 30 seconds

active = false;
mallctl("prof.active", NULL, NULL, &active, sizeof(bool));

//sleep 30 seconds

mallctl("prof.dump", NULL, NULL, NULL, 0);   //生成prof.2

然后通过jeprof a.out --base=prof.1 prof.2来比较这两个dump,这可以突显出稳定状态下程序的内存分配行为。

3.5、还可以生成pdf来看
# 导出为 pdf
/usr/local/jemalloc-5.1.0/bin/jeprof --pdf a.out  --base=jeprof.17988.0.i0.heap jeprof.17988.17.i17.heap   > a.pdf

参考:
利用Jemalloc进行内存泄漏的调试
jemalloc的heap profiling

  • 17
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: "heap-use-after-free" 是一种常见的内存错误,通常发生在程序试图在释放了一块内存后仍然引用该内存地址的情况下。 在使用堆分配的内存时,如果程序在释放内存后还引用该内存地址,就会导致 "heap-use-after-free" 错误。这可能会导致程序崩溃、数据损坏或安全漏洞。 为了避免 "heap-use-after-free" 错误,程序员应该确保在释放内存后不再使用该内存地址。可以通过将指向已释放内存的指针设置为 NULL 或使用内存分配器函数(如 malloc、calloc、realloc 等)来避免这种错误。另外,一些内存调试工具也可以检测这种错误并提供更多的信息来帮助程序员调试。 ### 回答2: heap-use-after-free是一种常见的堆相关漏洞,它在程序中利用了一个已经被释放的堆内存。当我们释放堆内存后,如果不小心继续使用已经释放的内存,就会导致heap-use-after-free漏洞。 当一个堆内存被释放后,它会返回给堆管理器,可以被重新分配给其他程序使用。然而,如果我们在释放内存后,仍然持有对该内存的指针,并且在后续的代码中使用了该指针,就会造成heap-use-after-free漏洞。 这种错误的使用已经释放的内存可能导致程序的不可预测行为,甚至可以被恶意攻击者利用来执行任意代码。攻击者可以通过控制已释放的内存中的数据来改变程序的执行流程或者读取敏感信息。 为了防止heap-use-after-free漏洞的发生,我们应该遵循一些最佳实践。首先,确保在不再使用堆内存之前将其正确释放。其次,及时将已经释放的指针设置为NULL,以避免误用。此外,使用堆管理器提供的专用函数来分配和释放内存,避免手动管理内存,可以减少这类错误的发生。 最后,漏洞修复是非常重要的。在发现了heap-use-after-free漏洞后,我们应该尽快修复它,以避免潜在的安全问题。修复这类漏洞的方法包括修改程序逻辑、改变内存分配和释放的顺序等。 综上所述,heap-use-after-free是一种常见的堆相关漏洞,它产生于程序中继续使用已经释放的堆内存。为了防止这类漏洞的发生,我们需要注意正确地分配和释放内存,并及时修复已发现的漏洞。 ### 回答3: heap-use-after-free是指在堆上释放了某个内存块,但之后仍然对该内存块进行了访问或操作。这种错误通常发生在程序员没有正确管理内存的情况下。 当释放完一个内存块之后,程序应该立即将指向该内存块的指针置为NULL,以防止错误地访问已经释放的内存块。然而,如果未将指针置为NULL,并继续使用该指针进行读取或写入操作,就会产生heap-use-after-free错误。 这种错误可能导致一些严重的后果。例如,可能会导致程序崩溃、数据损坏,甚至存在潜在的安全风险。因此,程序员在使用堆上分配的内存时,必须遵守正确的内存管理规则,包括正确释放内存并及时将指针置为NULL。 为了避免heap-use-after-free错误,程序员可以采取以下几种措施。首先,尽量使用动态内存分配的高级抽象机制,如智能指针或垃圾回收器,以自动管理内存。其次,当手动管理内存时,要确保正确释放内存并将指针置为NULL。另外,可以使用工具进行内存泄漏检测和动态分析,以及进行严格的代码审查和测试,以提前发现和修复heap-use-after-free错误。 总之,heap-use-after-free是一种内存管理错误,程序员必须小心处理以避免产生严重的后果。正确的内存管理和代码审查是预防这种错误的有效方法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值