利用Strace工具分析内存泄漏问题

问题背景
实际项目中可能会遇见内存泄漏等比较棘手的问题,这时可以用strace工具分析内存泄漏的原因。内存泄漏一般是申请了的资源没有得到及时的释放,所有用strace工具调查malloc或者new对应的系统函数mmap和free或者delete对应的系统函数munmap的调用次数,即可得知内存泄漏,正常情况下mmap和munmap的调用次数的相对应的。
利用strace工具也可以打印出相应系统调用的线程号,根据线程号信息进行定位和分析也不失为一种调查方式。

一.内存泄漏代码

straceMemleak.c

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/prctl.h>
#include <sys/types.h>
#include <sys/syscall.h>

#define MMAP_PRINT_THRESHOLD 131049 

void * thread1(void* args)
{ 
    prctl(PR_SET_NAME,"THREAD1");
    while(1){ 
        sleep(1);
        malloc(MMAP_PRINT_THRESHOLD);
        printf("[pid: %d tid: %ld] this is thread1!\n", getpid(), syscall(SYS_gettid));
    }
}
 
void* thread2(void* args)
{ 
    prctl(PR_SET_NAME,"THREAD2");
    while(1){ 
        sleep(2);
        malloc(131000);
        printf("[pid: %d tid: %ld] this is thread2!\n", getpid(), syscall(SYS_gettid));
    }
}
 
int main()
{ 
    pthread_t pid1, pid2;
 
    if(pthread_create(&pid1, NULL, thread1, NULL))
    { 
        return -1;
    }
 
    if(pthread_create(&pid2, NULL, thread2, NULL))
    { 
        return -1;
    }
 
    while(1){ 
        sleep(3);
    }
 
    return 0;
}

gcc straceMemleak.c -o straceMemleak -lpthread

二.利用strace工具检测内存泄漏
1.运行strace命令

strace -tt -f -e "brk,mmap,munmap" ./straceMemleak
-tt 在每行输出的前面,显示毫秒级别的时间
-f 跟踪目标进程,以及目标进程创建的所有子进程
-e 控制要跟踪的事件和跟踪行为,比如指定要跟踪的系统调用名称

2.根据进程名字获取进程和该进程下的线程名

danny@danny:~/Learing$ ps -A | egrep straceMemleak
 7931 pts/5    00:00:00 straceMemleak
 
danny@danny:~/Learing$ ps -A -T | grep 7931
 7931  7931 pts/5    00:00:00 straceMemleak
 7931  7932 pts/5    00:00:00 THREAD1
 7931  7933 pts/5    00:00:00 THREAD2

3.strace输出的结果

danny@danny:~/Learing$ strace -tt -f -e "brk,mmap,munmap" ./straceMemleak 
17:48:56.984837 brk(NULL)               = 0x5573b0801000
17:48:56.985534 mmap(NULL, 121229, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fd70f889000
17:48:56.985941 mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd70f887000
17:48:56.986045 mmap(NULL, 2221184, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fd70f45f000
17:48:56.986191 mmap(0x7fd70f678000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x19000) = 0x7fd70f678000
17:48:56.986294 mmap(0x7fd70f67a000, 13440, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fd70f67a000
17:48:56.986663 mmap(NULL, 4131552, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fd70f06e000
17:48:56.986809 mmap(0x7fd70f455000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1e7000) = 0x7fd70f455000
17:48:56.986906 mmap(0x7fd70f45b000, 15072, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fd70f45b000
17:48:56.987093 mmap(NULL, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd70f884000
17:48:56.987627 munmap(0x7fd70f889000, 121229) = 0
17:48:56.988126 mmap(NULL, 8392704, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK, -1, 0) = 0x7fd70e86d000
17:48:56.988365 brk(NULL)               = 0x5573b0801000
17:48:56.988440 brk(0x5573b0822000)     = 0x5573b0822000
strace: Process 7932 attached

-----------------------------------------------------------------------------------------------------------------------
[pid  7931] 17:48:56.988721 mmap(NULL, 8392704, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK, -1, 0) = 0x7fd70e06c000
strace: Process 7933 attached

//thread 7932 thread1 申请134217728 bytes空间,但是后面只是释放了33112064 bytes。
//也是存在内存泄漏的情况
[pid  7932] 17:48:57.989124 mmap(NULL, 134217728, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = 0x7fd70606c000
[pid  7932] 17:48:57.989378 munmap(0x7fd70606c000, 33112064) = 0


[pid  7932] 17:48:57.989485 munmap(0x7fd70c000000, 33996800) = 0
[pid: 7931 tid: 7932] this is thread1!

//7933 是线程thread2,申请了134217728bytes,并没有释放,实际上每次都在泄漏,
[pid  7933] 17:48:58.989476 mmap(NULL, 134217728, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = 0x7fd700000000


[pid  7933] 17:48:58.989768 munmap(0x7fd704000000, 67108864) = 0
[pid  7932] 17:48:58.990158 mmap(NULL, 135168, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0[pid: 7931 tid: 7933] this is thread2!
) = 0x7fd70f863000
[pid: 7931 tid: 7932] this is thread1!
[pid  7932] 17:48:59.990739 mmap(NULL, 135168, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd70f842000
[pid: 7931 tid: 7932] this is thread1!
[pid: 7931 tid: 7933] this is thread2!
[pid  7932] 17:49:00.991201 mmap(NULL, 135168, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd70f821000
[pid: 7931 tid: 7932] this is thread1!
[pid  7932] 17:49:01.992148 mmap(NULL, 135168, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd70f800000
[pid: 7931 tid: 7932] this is thread1!
[pid: 7931 tid: 7933] this is thread2!
[pid  7932] 17:49:02.993053 mmap(NULL, 135168, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd70f7df000
[pid: 7931 tid: 7932] this is thread1!
[pid  7932] 17:49:03.993679 mmap(NULL, 135168, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd70f7be000
[pid: 7931 tid: 7932] this is thread1!
[pid: 7931 tid: 7933] this is thread2!
[pid  7932] 17:49:04.994598 mmap(NULL, 135168, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd70f79d000
[pid: 7931 tid: 7932] this is thread1!
[pid  7932] 17:49:05.995194 mmap(NULL, 135168, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd70f77c000
[pid: 7931 tid: 7932] this is thread1!
[pid: 7931 tid: 7933] this is thread2!
[pid  7932] 17:49:06.996079 mmap(NULL, 135168, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd70f75b000
[pid: 7931 tid: 7932] this is thread1!
[pid  7932] 17:49:07.997138 mmap(NULL, 135168, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd70f73a000
[pid: 7931 tid: 7932] this is thread1!
[pid: 7931 tid: 7933] this is thread2!
[pid  7932] 17:49:08.998367 mmap(NULL, 135168, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd70f719000
[pid: 7931 tid: 7932] this is thread1!
[pid  7932] 17:49:09.999430 mmap(NULL, 135168, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd70f6f8000
[pid: 7931 tid: 7932] this is thread1!
[pid: 7931 tid: 7933] this is thread2!
[pid  7932] 17:49:11.000469 mmap(NULL, 135168, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd70f6d7000
[pid: 7931 tid: 7932] this is thread1!
[pid  7932] 17:49:12.001381 mmap(NULL, 135168, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd70f6b6000
[pid: 7931 tid: 7932] this is thread1!
[pid: 7931 tid: 7933] this is thread2!
^Cstrace: Process 7931 detached
strace: Process 7932 detached
strace: Process 7933 detached
------------------------------------------------------------------------------------------------------------------------

4.分析结论

1.当malloc的数据量达到131049 bytes 约等于128kb时,每次malloc后都会打印mmap相关信息;
2.当malloc的数据量未达到131049 bytes时,如131000 bytes,只是在开始时打印mmap相关信息,实际上每次都是在泄漏的;
3.实际项目中可以通过线程号和相关线程的打印信息定位到是那个线程在泄漏。
    原文作者:权艺
    原文地址: https://blog.csdn.net/Danny_llp/article/details/120891399
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞