Home libumem Introduce
Post
Cancel

libumem Introduce

一款你值得拥有的内存问题定位神器

一 背景介绍


​ 使用C语言开发时,对于内存资源的申请和释放时非常常见的场景,但这也给广大程序带来了很多困扰,一旦出现如越界访问,重复释放,use after free,内存泄露等问题时,定位起来非常麻烦。

​ 因为这个原因,现有的大部分内存管理框架或多或少的都会提供一些内存调测能力,协助定位问题。但是目前用起来不是太难使用,就是约束限制太强,使用体验太差,因此到现在也没有出现网红款,今天我们就给大家带来一款内存调测神器:libumem。

​ libumem类似于linux上的ptmalloc/tcmalloc等用户态内存管理框架,提供高效的内存管理功能,同时提供了非常丰富及高效的调测信息记录功能,配合mdb工具,可以轻松观察程序内存的分配情况和定位内存泄漏的问题。

​ libumem最初在Solaris 9中提供,OmniTI lab将此软件移植到其他流行的操作系统,如linux,我们基于OmniTI的工作成果上,将此配套调测工具(mdb)适配移植到openEuler上,可以轻松使用该工具来定位和调试我们的内存问题。

二 libumem和Linux现有对比


libumem和Linux现有用户态内存管理框架调测能力对比

2.1 调测能力对比

  1. libumem相比于其他用户态内存框架,支持更多的内存调测能力,更容易解决内存问题。
  2. libumem不需要用户程序重编,提供兼容ptmalloc的接口,方便用户使用。
library memory overrun double free use after free wild free access uninited read invalid memory memory leak use after return stack overflow
libumem Yes Yes Yes - Yes - Yes - -
ptmalloc(Glibc) - Yes - Yes - - Yes - Yes(if use memcpy, strcpy, etc)
TCMalloc(Gperftools) - - - - - - Yes - -
Jemalloc - - - - - - Yes(build with –enable-prof) - -

2.2 调测性能对比

测试对象: libumem、ptmalloc(glibc)、 tcmalloc(gperftools)、jemalloc(facebook)。

各内存管理框架的调测功能使用方法:

library version enable memory Debugger(Common use)
ptmalloc(Glibc) glibc-2.28 export MALLOC_CHECK_=3;export MTRACE=/root/mtrace.log(配合进程代码修改)
tcMalloc(Gperftools) gperftools 2.7.90 export HEAPCHECK=normal
jemalloc Jemalloc 5.1.0 export MALLOC_CONF=prof_leak:true,lg_prof_sample:0,prof_final:true(jemalloc build with –enable-prof)
libumem libumem-1.0 export UMEM_DEBUG=default; epxort UMEM_LOGGING=transaction
2.2.1 测试环境及测试工具
  • 测试环境:硬件环境:X86_64 CPU 2.3GHz 16G内存虚拟机; OS版本:openEuler 20.03 LTS版本
  • 测试工具libMicro 工具,测试malloc的性能。

2.2.2 测试结果

内存管理框架调测功能全开情况,在单线程和多线程的条件下,申请内存size < 10k以内,libumem申请效率最优;

三 libumem及相关调测工具的使用


目前libumem只支持x86_64, 后续会增加arm64支持; libumem的调测需要配合mdb工具使用,mdb目前支持libumem调测的命令如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
findleaks                - search for potential memory leaks
ugrep                    - search user address space for a pointer
umalog                   - display umem transaction log and stack traces
umastat                  - umem allocator stats
umausers                 - display current medium and large users of the umem allocator
umem_cache               - print a umem cache
umem_debug               - toggle umem dcmd/walk debugging
umem_log                 - dump umem transaction log
umem_malloc_dist         - report distribution of outstanding malloc()s
umem_malloc_info         - report information about malloc()s by cache
umem_status              - Print umem status and message buffer
umem_verify              - check integrity of umem-managed memory
vmem                     - print a vmem_t
vmem_seg                 - print or filter a vmem_seg
bufctl                   - print or filter a bufctl
bufctl_audit             - print a bufctl_audit
allocdby                 - given a thread, print its allocated buffers

3.1 libumem使用

(1) libumem构建

代码仓: https://gitee.com/src-openeuler/libumem, 构建完成后直接rpm安装。

1
2
[root@localhost ~]#  git clone git@gitee.com:src-openeuler/libumem.git
[root@localhost ~]#  rpmbuild -ba -D '_sourcedir `pwd`' libumem.spec
(2) libumem使用

示例代码:这段代码中存在两处内存泄漏,一处内存破坏(use after free)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>

void test_leak_malloc(void)
{
        char *p = malloc(50); //not free
        p = malloc(100);
        free(p);
}

void test_UAF(void)
{
        char *p = malloc(50); 
        free(p);
        *p = 1; // use after freed
}

void main(void)
{
        char *p = malloc(50); //not free
        test_leak_malloc();
        test_UAF();
        pause();
        return;
}
1
[root@localhost ~]# gcc -g test.c -o test

libumem通过LD_PRELOAD环境变量使用方法

1
2
[root@localhost ~]# UMEM_DEBUG=default, UMEM_LOGGING=transaction LD_PRELOAD=/usr/local/lib/libumem.so.0 ./test&
//安装libumem-devel后,man umem_debug查看UMEM_DEBUG、UMEM_LOGGING的含义。

3.2 调测功能使用

(1) mdb工具构建

代码仓: https://github.com/openEuler-Computing/mdb

1
2
3
[root@localhost ~]# git clone https://github.com/openEuler-Computing/mdb.git
[root@localhost ~]# yum install byacc flex libbsd-devel elfutils-libelf-devel zlib-devel libumem-devel 
[root@localhost ~]# cd mdb/common; make all -j 4; make install
(2) 定位内存泄漏
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
[root@localhost ~]# mdb -p `pidof test` //使用mdb attach到test进程(mdb -p pid)
loading modules: [ libdl-2.28.so libpthread-2.28.so libc-2.28.so libumem.so.0.0.0 ld-2.28.so ]
::findleaks -d
    
CACHE             LEAKED           BUFCTL CALLER
00007f3db1bba048       1 00007f3db1b3e7e0 main+0x12
00007f3db1bba048       1 00007f3db1b3e8c0 test_leak_malloc+0x12
------------------------------------------------------------------------
           Total       2 buffers, 160 bytes
umem_alloc_80 leak: 1 buffer, 80 bytes
            ADDR          BUFADDR        TIMESTAMP           THREAD
                            CACHE          LASTLOG         CONTENTS
    7f3db1b3e7e0     7f3db1b6bf60 60116209000b4637      -1312650368
                     7f3db1bba048     7f3db1bf8300                0
                 libumem.so.0.0.0`umem_alloc+0x98
                 libumem.so.0.0.0`malloc+0x39
                 main+0x12 //memory leak here
                 libc-2.28.so`__libc_start_main+0xf3
                 _start+0x2e

umem_alloc_80 leak: 1 buffer, 80 bytes
            ADDR          BUFADDR        TIMESTAMP           THREAD
                            CACHE          LASTLOG         CONTENTS
    7f3db1b3e8c0     7f3db1b6bef0 60116209000b463c      -1312650368
                     7f3db1bba048     7f3db1bf83c0                0
                 libumem.so.0.0.0`umem_alloc+0x98
                 libumem.so.0.0.0`malloc+0x39
                 test_leak_malloc+0x12 //memory leak here
                 main+0x1b
                 libc-2.28.so`__libc_start_main+0xf3
                 _start+0x2e
(3)定位内存破环

常见的内存破坏: use after free, double free, memory overrun, out-of-bounds array write.

1
2
3
4
5
6
::umem_verify
Cache Name                      Addr             Cache Integrity     
...
umem_alloc_80                       7f3db1bba048 1 corrupt buffer
umem_alloc_96                       7f3db1bb9048 clean
...
7f3db1bba048::umem_verify
Summary for cache 'umem_alloc_80'
  buffer 7f3db1b6be80 (free) seems corrupted, at 7f3db1b6be90 //memory corrupted appear in freed address 7f3db1b6be80

通过umalog命令可以查看到申请的调用栈:

1
2
3
4
5
6
7
T-0.000000005  addr=7f3db1b6be80  umem_alloc_80 //test_UAF malloc(50) addr=7f3db1b6be80 
         libumem.so.0.0.0`umem_alloc+0x98
         libumem.so.0.0.0`malloc+0x39
         test_UAF+0x12
         main+0x20
         libc-2.28.so`__libc_start_main+0xf3
         _start+0x2e

综合信息可以知道test_UAF中出现use after free的情况;

通过以上评测结果和demo应用,相信你 已经的libumem以及mdb工具有了初步的了解,期望你来做更多的尝试…

四 更多内容关注OpenEuler


如果内存调测工具对你有用,太好了! 请传播它,让更多人使用,调试和增强它。

libumem地址:https://gitee.com/src-openeuler/libumem

配套调测工具的地址: https://github.com/openEuler-Computing/mdb

This post is licensed under CC BY 4.0 by the author.