美文网首页
Linux C语言memory leak的调试方法

Linux C语言memory leak的调试方法

作者: CodingCode | 来源:发表于2023-09-30 06:24 被阅读0次

    使用Linux的PRELOAD重载malloc/free的函数实现,然后可以记录下来所有的malloc/free操作。

    定义PRELOAD库的源文件

    $ cat mymalloc.c
    #define _GNU_SOURCE
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <time.h>
    
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <dlfcn.h>
    
    #include <string.h>
    #include <stdarg.h>
    #include <pthread.h>
    
    static pthread_mutex_t mymem_lock = PTHREAD_MUTEX_INITIALIZER;
    
    //typedef void *(*malloc_t)(size_t size);
    //static malloc_t malloc_f = NULL;
      static void * (*malloc_f)(size_t) = NULL;
      static void * (*realloc_f)(void *, size_t) = NULL;
    
    //typedef void (*free_t)(void* p);
    //static free_t free_f = NULL;
      static void (*free_f)(void *) = NULL;
    
    void *malloc(size_t sz) {
        pthread_mutex_lock(&mymem_lock);
    
        if (malloc_f == NULL) {
            malloc_f = dlsym(RTLD_NEXT, "malloc");
        }
    
        void * mem = malloc_f(sz);
    
        pthread_mutex_unlock(&mymem_lock);
        return mem;
    }
    
    void *realloc(void *ptr, size_t sz) {
        pthread_mutex_lock(&mymem_lock);
    
        if (realloc_f == NULL) {
            realloc_f = dlsym(RTLD_NEXT, "realloc");
        }
    
        void * mem = realloc_f(ptr, sz);
    
        pthread_mutex_unlock(&mymem_lock);
        return mem;
    }
    
    void free(void *mem) {
        if (mem != NULL) {
            pthread_mutex_lock(&mymem_lock);
            if (free_f == NULL) {
                free_f =  dlsym(RTLD_NEXT, "free");
            }
    
            free_f(mem);
    
            pthread_mutex_unlock(&mymem_lock);
        }
    }
    

    编译运行:

    $ gcc -o libmymalloc.so -g -shared -fPIC -ldl mymalloc.c
    
    $ LD_PRELOAD=/path/to/libmymalloc.so ./yourprogram
    

    这样yourprogram就会使用mymalloc里面定义的malloc和free,而不会使用系统库的malloc和free。

    可以增强的地方:

    1. 把malloc和free的请求打印出来
    static void logmallocfree(const char * fmt, ...) {
        va_list args;
        char fmtbuffer[80] = { '\0' };
        va_start(args, fmt);
        vsnprintf(fmtbuffer, 80, fmt, args);
        va_end(args);
    
        char logbuffer[120] = { '\0' };
        snprintf(logbuffer, 120, "ts=%d, pid=%d, %s\n", time(NULL), getpid(), fmtbuffer);
    
        int fd = open("mymalloc.log", O_CREAT | O_WRONLY | O_APPEND, S_IRUSR | S_IWUSR);
        write(fd, logbuffer, strlen(logbuffer));
        close(fd);
    }
    
    void *malloc(size_t sz) {
        ...
        void * mem = malloc_f(sz);
        logmallocfree("mymalloc=%p, size=%d", mem, sz);
        ...
    }
    

    这里把每次请求的时间戳,进程号,以及分配的地址和大小都打印到日志文件中。

    另一可以增强的地方,对于满足条件(现在只是基于大小一致)的malloc把调用栈打出来,遗憾的是找不到办法打印出栈,只能让进程sleep一段时间,然后这期间用pstack来dump出来。

    static int monitoralloc() {
        int sz = 0;
        char buffer[80] = { '\0' };
    
        int fd = open("mymalloc.cfg", O_RDONLY);
        if (fd != -1) {
            if (read(fd, buffer, sizeof(buffer)) != -1) {
               sz = atoi(buffer);
            }
            close(fd);
        }
    
        return sz;
    }
    
    
    void *malloc(size_t sz) {
        ...
        if (sz == monitoralloc()) {
            logmallocfree("mymalloc sleep when size=%d", sz);
            sleep(10);
        }
        ...
    }
    

    monitoralloc从配置文件读取一个数字,表示要monitor如果请求大小是这个值,那么就sleep(10),并打印出一句提示,当用户看到提示出来后,就调用pstack。

    另外这里要注意的是,因为重载了malloc/free,所以在mymalloc.c里面就不能再直接或者间接调用malloc/free了,所以想访问文件的很多libc函数都不能用了,必须用最基础的系统IO函数,而不是libc的函数来访问文件,因为libc的函数也会使用malloc/free倒是嵌套循环。

    相关文章

      网友评论

          本文标题:Linux C语言memory leak的调试方法

          本文链接:https://www.haomeiwen.com/subject/klldbdtx.html