• 用内存管理器的钩子函数跟踪内存泄漏


    内存管理器的钩子函数跟踪内存泄漏

    载时请注明出处和作者联系方式
    作者联系方式:李先静 <xianjimli at hotmail dot com>

    作为Linux下的C程序员,我总是习惯在单元测试通过之后,再用valgrind把程序跑一下,看看有没有内存泄漏和内存越界等问题。可惜的是,有时valgrind并不能很好的工作,像基于DirectFB的多进程程序在valgrind下是跑不起的, 这时我们可以通过内存管理器的钩子函数来跟踪内存泄漏。

    glibc提供的内存管理器的钩子函数让你可以监控/改变内存管理函数的行为。其实glibc已经利用这个机制实现了内存泄漏检测的功能,提供了mtrace/muntrace两个函数和mtrace工具,只是不太好用,一是速度慢,二是没有backtrace。更惨的是在Fedora 7上再也找不到它了,只好自己写一个:

     

    先记录分配/释放操作:


    /*memory_trace.c*/

    #include 
    <execinfo.h>
    #include 
    <stdio.h>
    #include 
    <stdlib.h>
    #include 
    <sys/types.h>
    #include 
    <unistd.h>
    #include 
    <malloc.h>
    #include 
    <sys/stat.h>
    #include 
    <fcntl.h>
    #include 
    <string.h>

    static void  memory_trace_init(void);
    static void  memory_trace_deinit(void);
    static void *my_malloc_hook (size_t size, const void* ptr);
    static void  my_free_hook (void* ptr, const void* caller);
    static void *my_realloc_hook (void *ptr, size_t size, const void *caller);

    static void *my_malloc_hook (size_t size, const void* ptr);
    static void  my_free_hook (void* ptr, const void* caller);
    static void *my_realloc_hook (void *ptr, size_t size, const void *caller);

    static void *(*old_malloc_hook)(size_t size, const void* ptr);
    static void  (*old_free_hook)(void* ptr, const void* caller);
    static void *(*old_realloc_hook)(void *ptr, size_t size, const void *caller);

    #define BACK_TRACE_DEPTH 8
    #define CACHE_SIZE       512

    static FILE* g_memory_trace_fp = NULL;
    static int   g_memory_trace_cache_used = 0;
    /*additional 3 items: alloc/free addr size*/
    static void* g_memory_trace_cache[CACHE_SIZE][BACK_TRACE_DEPTH + 3]; 
    static void  memory_trace_flush(void);
    static void  memory_trace_write(int alloc, void* addr, int size);

    static void memory_trace_backup(void)
    {
        old_malloc_hook  
    = __malloc_hook;
        old_free_hook    
    = __free_hook;
        old_realloc_hook 
    = __realloc_hook;

        
    return;
    }


    static void memory_trace_hook(void)
    {
        __malloc_hook  
    = my_malloc_hook;
        __free_hook    
    = my_free_hook;
        __realloc_hook 
    = my_realloc_hook;

        
    return;
    }


    static void memory_trace_restore(void)
    {
        __malloc_hook  
    = old_malloc_hook;
        __free_hook    
    = old_free_hook;
        __realloc_hook 
    = old_realloc_hook;

        
    return;
    }


    static void memory_trace_init(void)
    {
        
    if(g_memory_trace_fp == NULL && getenv("MALLOC_TRACE"!= NULL)
        
    {
            
    char file_name[260= {0};
            snprintf(file_name, 
    sizeof(file_name), "/tmp/%d_memory.log", getpid());
            
    if((g_memory_trace_fp = fopen(file_name, "wb+")) != NULL)
            
    {
                memory_trace_backup();
                memory_trace_hook();
            }


            atexit(memory_trace_deinit);
        }


        
    return;
    }


    static void memory_trace_deinit(void)
    {
        
    if(g_memory_trace_fp != NULL)
        
    {
            memory_trace_restore();
            memory_trace_flush();
            fclose(g_memory_trace_fp);
            g_memory_trace_fp 
    = NULL;
        }


        
    return;
    }


    void (*__malloc_initialize_hook) (void= memory_trace_init;

    static void * my_malloc_hook (size_t size, const void *caller)
    {
        
    void *result = NULL;
        memory_trace_restore();
        result 
    = malloc (size);
        memory_trace_write(
    1, result, size);
        memory_trace_hook();
        
        
    return result;
    }


    static void my_free_hook (void *ptr, const void *caller)
    {
        memory_trace_restore();
        free (ptr);
        memory_trace_write(
    0, ptr, 0);
        memory_trace_hook();

        
    return;
    }


    static void *my_realloc_hook (void *ptr, size_t size, const void *caller)
    {
        
    void* result = NULL;

        memory_trace_restore();
        memory_trace_write(
    0, ptr, 0);
        result 
    = realloc(ptr, size);
        memory_trace_write(
    1, result, size);
        memory_trace_hook();

        
    return result;
    }


    static void memory_trace_flush_one_entry(int index)
    {
        
    int offset = 0;
        
    char buffer[512= {0};
        
    int fd = fileno(g_memory_trace_fp);
        
    int alloc  = (int)g_memory_trace_cache[index][BACK_TRACE_DEPTH]; 
        
    void* addr = g_memory_trace_cache[index][BACK_TRACE_DEPTH+1]; 
        
    int size   = (int)g_memory_trace_cache[index][BACK_TRACE_DEPTH+2];
        
    void** backtrace_buffer = g_memory_trace_cache[index];

        snprintf(buffer, 
    sizeof(buffer), "%s %p %d ", alloc ? "alloc":"free", addr, size);
        
    if(!alloc)
        
    {
            write(fd, buffer, strlen(buffer));
            
    return;
        }

        
        
    char** symbols = backtrace_symbols(backtrace_buffer, BACK_TRACE_DEPTH);
        
    if(symbols != NULL)
        
    {
            
    int i = 0;
            offset 
    = strlen(buffer);
            
    for(i = 0; i < BACK_TRACE_DEPTH; i++)
            
    {
                
    if(symbols[i] == NULL)
                
    {
                    
    break;
                }

                
    char* begin = strchr(symbols[i], '(');
                
    if(begin != NULL)
                
    {
                    
    *begin = ' ';
                    
    char* end = strchr(begin, ')');
                    
    if(end != NULL)
                    
    {
                        strcpy(end, 
    " ");
                    }

                    strncpy(buffer
    +offset, begin, sizeof(buffer)-offset);
                    offset 
    += strlen(begin);
                }

            }

            write(fd, buffer, offset);
            free(symbols);
        }


        
    return;
    }


    static void memory_trace_flush(void)
    {
        
    int i = 0;
        
    for(i = 0; i < g_memory_trace_cache_used; i++)
        
    {
            memory_trace_flush_one_entry(i);
        }

        g_memory_trace_cache_used 
    = 0;

        
    return;
    }


    static void memory_trace_write(int alloc, void* addr, int size)
    {
        
    if(g_memory_trace_cache_used >= CACHE_SIZE)
        
    {
            memory_trace_flush();
        }


        
    int i = 0;
        
    void* backtrace_buffer[BACK_TRACE_DEPTH] = {0};
        backtrace(backtrace_buffer, BACK_TRACE_DEPTH);

        
    for(i = 0; i < BACK_TRACE_DEPTH; i++)
        
    {
            g_memory_trace_cache[g_memory_trace_cache_used][i] 
    = backtrace_buffer[i];
        }

        g_memory_trace_cache[g_memory_trace_cache_used][BACK_TRACE_DEPTH] 
    = (void*)alloc;
        g_memory_trace_cache[g_memory_trace_cache_used][BACK_TRACE_DEPTH
    +1= addr;
        g_memory_trace_cache[g_memory_trace_cache_used][BACK_TRACE_DEPTH
    +2= (void*)size;

        g_memory_trace_cache_used
    ++;

        
    return;
    }


    #ifdef MEMORY_TRACE_TEST
    void test(void)
    {
        
    char* p = malloc(100);
        p 
    = malloc(123);

        free(p);

        
    return;
    }

    int main(int argc, char* argv[])
    {
        malloc(
    100);
        test();
        malloc(
    100);
        test();
        
    char* p = malloc(100);
        free(p);
        
        
    return 0;
    }

    #endif/*MEMORY_TRACE_TEST*/

    把以上代码编译成动态库,或者直接编译到功能代码一起。如果设置了MALLOC_TRACE环境变量,分配/释放会被记录到/tmp/$PID_memory.log下。

    再写个程序来分析log文件:
    /*mtrace.c*/
    #include 
    <stdio.h>

    #define MAX_ENTRY 1024*1024

    int main(int argc, char* argv[])
    {
        
    if(argc != 3)
        
    {
            printf(
    "usage: %s [log file] [out file] ", argv[0]);
            
    return 0;
        }


        FILE
    * fp     = fopen(argv[1], "r");
        FILE
    * fp_out = fopen(argv[2], "wb+");

        
    if(fp == NULL || fp_out == NULL)
        
    {
            printf(
    "open file failed ");
            
    if(fp != NULL)
            
    {
                fclose(fp);
            }

            
    if(fp_out != NULL)
            
    {
                fclose(fp_out);
            }

            
    return;
        }


        
    int   i = 0;
        
    int   n = 0;
        
    int   skip = 0;
        
    int   line_index = 0;
        
    void* addr = 0;
        
    char  line[260= {0};
        
    void* addrs_array[MAX_ENTRY] = {0};
        
    int   lines_array[MAX_ENTRY] = {0};


        
    while(fgets(line, sizeof(line), fp) != NULL)
        
    {
            
    if(line[0!= 'a' && line[0!= 'f')
            
    {
                line_index
    ++;
                
    continue;
            }


            addr 
    = NULL;
            
    if(strncmp(line, "alloc"5== 0 && n < MAX_ENTRY)
            
    {
                sscanf(line, 
    "alloc %p"&addr);
                addrs_array[n] 
    = addr;
                lines_array[n] 
    = line_index;
                n
    ++;
                
                printf(
    "a");
            }

            
    else if(strncmp(line, "free"4== 0)
            
    {
                sscanf(line, 
    "free %p"&addr);
                
    for(i = 0; i < n; i++)
                
    {
                    
    if(addrs_array[i] == addr)
                    
    {
                        lines_array[i] 
    = -1;
                        
    break;
                    }

                }


                printf(
    "f");
            }

            line_index
    ++;
        }


        printf(
    " ");
        fseek(fp, 
    00);

        i 
    = 0;
        line_index 
    = 0;
        
    while(fgets(line, sizeof(line), fp) != NULL)
        
    {
            
    if(strncmp(line, "alloc"5== 0)
            
    {
                
    if(lines_array[i] == line_index)
                
    {
                    printf(
    "leak %s", line);
                    fprintf(fp_out, 
    "*");
                    skip 
    = 0;
                    i
    ++;
                }

                
    else
                
    {
                    skip 
    = 1;
                }

            }

            
    else if(strncmp(line, "free"4== 0)
            
    {
                skip 
    = 1;
            }


            
    if(!skip)
            
    {
                fputs(line, fp_out);
            }


            line_index
    ++;
        }


        fclose(fp);
        fclose(fp_out);

        
    return 0;
    }

    把这个段代码编译成一个可执行文件mtrace,以前面记录的LOG为输入,再指定个输出文件,内存泄漏会被写到
    输出文件中,可以看到内存泄漏的地地址,大小和调用关系

    ~~end~~


  • 相关阅读:
    读取.robot文件写入excel文件中示例
    提示框、滚动条处理与JS的应用
    下拉框
    切换框架ifame
    层级定位
    定位一组元素
    Appium元素定位方法
    python+appium基本启动配置
    adb命令使用
    Python接口测试框架搭建
  • 原文地址:https://www.cnblogs.com/zhangyunlin/p/6167639.html
Copyright © 2020-2023  润新知