ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

3.2.5 手写内存泄漏检测组件

3.2.5 手写内存泄漏检测组件 1.内存泄漏a.是否有内存泄漏 b.在哪里有内存泄漏2.try-catch调用malloc 没有调用free#includestdio.h#includestdlib.hintmain(){void*p1malloc(5);void*p2malloc(10);void*p3malloc(15);free(p1);free(p3);return0;}上面这个程序造成了内存泄漏void*_malloc(size_tsize){printf(_malloc\n);}void_free(void*ptr){printf(_free\n);}#definemalloc(size)_malloc(size)#definefree(ptr)_free(ptr)intmain(){void*p1malloc(5);void*p2malloc(10);void*p3malloc(15);free(p1);free(p3);return0;}可以根据打印值判断malloc和free个数不匹配为了得到更详细的信息我们可以将文件名和行号打印出来void*_malloc(size_tsize,constchar*filename,intline){void*ptrmalloc(size);printf([]_malloc: %p, filename: %s, line: %d\n,ptr,filename,line);returnptr;}void_free(void*ptr,constchar*filename,intline){printf([-]_free, filename: %s, line: %d\n,filename,line);free(ptr);}#definemalloc(size)_malloc(size,__FILE__,__LINE__)#definefree(ptr)_free(ptr,__FILE__,__LINE__)为了输出信息更直观我们可以在malloc时创建一个文件free时删除这个文件最后看看是否有文件生成就可知道是否发生内存泄漏void*_malloc(size_tsize,constchar*filename,intline){void*ptrmalloc(size);charbuff[128]{0};sprintf(buff,./mem/%p.mem,ptr);FILE*fpfopen(buff,w);fprintf(fp,[]addr: %p, filename: %s, line: %d\n,ptr,buff,line);fflush(fp);fclose(fp);//printf([]_malloc: %p, filename: %s, line: %d\n, ptr, filename, line);returnptr;}void_free(void*ptr,constchar*filename,intline){charbuff[128]{0};sprintf(buff,./mem/%p.mem,ptr);if(unlink(buff)0){printf(double free: %p\n,ptr);return;}//printf([-]_free, filename: %s, line: %d\n, filename, line);returnfree(ptr);}进入mem目录可以看到0x5601fae8f4b0.mem查看文件内容可以看到更详细的泄漏内容也可以使用hook技术typedefvoid*(*malloc_t)(size_tsize);malloc_tmalloc_fNULL;typedefvoid(*free_t)(void*ptr);free_tfree_fNULL;void*malloc(size_tsize){printf(malloc \n);}voidfree(void*ptr){printf(free \n);}voidinit_hook(void){if(!malloc_f){malloc_fdlsym(RTLD_NEXT,malloc);}if(!free_f){free_fdlsym(RTLD_NEXT,free);}}intmain(){init_hook();void*p1malloc(5);void*p2malloc(10);void*p3malloc(15);free(p1);free(p3);return0;}但是运行该程序会发现发生了segmentation fault这时因为printf本身也会调用malloc造成了递归调用。intenable_malloc_hook1;intenable_free_hook1;void*malloc(size_tsize){void*ptrNULL;if(enable_malloc_hook){enable_malloc_hook0;printf(malloc \n);ptrmalloc_f(size);enable_malloc_hook1;}else{ptrmalloc_f(size);}returnptr;}voidfree(void*ptr){if(enable_free_hook){enable_free_hook0;printf(free \n);enable_free_hook1;}else{free_f(ptr);}}可以做一个修改让printf调用只调用一次。这样就不会递归调用了。我们仍然可以在malloc时创建一个文件在free时删除相应的文件void*malloc(size_tsize){void*ptrNULL;if(enable_malloc_hook){enable_malloc_hook0;printf(malloc \n);ptrmalloc_f(size);charfilename[128]{0};sprintf(filename,./mem/%p.mem,ptr);FILE*fpfopen(filename,w);fprintf(fp,[]addr: %p, filename: %s, line: %d,ptr,__FILE__,__LINE__);enable_malloc_hook1;}else{ptrmalloc_f(size);}returnptr;}但是这时你会发现我们打印的行号信息始终不变而之前是宏定义会在使用的地方展开但是这里是一个函数所以行号不会跟随使用的地方发生变化。解决这个问题可以使用__builtin_return_address这个函数是编译器自带的函数void*malloc(size_tsize){void*ptrNULL;if(enable_malloc_hook){enable_malloc_hook0;printf(malloc \n);ptrmalloc_f(size);//0参数表示上一级 1向上反二级//main - f1() - f2() - f3() {}void*caller__builtin_return_address(0);charfilename[128]{0};sprintf(filename,./mem/%p.mem,ptr);FILE*fpfopen(filename,w);fprintf(fp,[]caller: %p, addr: %p, size: %ld,caller,ptr,size);fflush(fp);enable_malloc_hook1;}else{ptrmalloc_f(size);}returnptr;}voidfree(void*ptr){if(enable_free_hook){enable_free_hook0;charbuff[128]{0};sprintf(buff,./mem/%p.mem,ptr);if(unlink(buff)0){printf(double free: %p\n,ptr);return;}free_f(ptr);enable_free_hook1;}else{free_f(ptr);}}这时再查看文件内容虽然打印出了地址但还是不直观这里用到一个工具addr2line可以通过addr2line -f -e ./memleak -a 0x5595d3a06bcc查看文件和行号还有一种通过libc的方式更底层的方式因为malloc底层调用的是__libc_mallocexternvoid*__libc_malloc(size_tsize);externvoid__libc_free(void*ptr);intenable_malloc_hook1;intenable_free_hook1;void*malloc(size_tsize){void*ptrNULL;if(enable_malloc_hook){enable_malloc_hook0;printf(malloc \n);ptr__libc_malloc(size);//0参数表示上一级 1向上反二级//main - f1() - f2() - f3() {}void*caller__builtin_return_address(0);charfilename[128]{0};sprintf(filename,./mem/%p.mem,ptr);FILE*fpfopen(filename,w);fprintf(fp,[]caller: %p, addr: %p, size: %ld,caller,ptr,size);fflush(fp);enable_malloc_hook1;}else{ptr__libc_malloc(size);}returnptr;}voidfree(void*ptr){if(enable_free_hook){enable_free_hook0;charbuff[128]{0};sprintf(buff,./mem/%p.mem,ptr);if(unlink(buff)0){printf(double free: %p\n,ptr);return;}__libc_free(ptr);enable_free_hook1;}else{__libc_free(ptr);}}try-catch#includestdio.h#includesetjmp.hjmp_buf env;voidfunc(intidx){printf(func -- idx: %d\n,idx);longjmp(env,idx);}intmain(){intcountsetjmp(env);if(count0){printf(count - %d\n,count);func(count);}elseif(count1){printf(count - %d\n,count);func(count);}elseif(count2){printf(count - %d\n,count);func(count);}elseif(count3){printf(count - %d\n,count);func(count);}printf(other count : %d\n,count);return0;}程序输出count - 0 func -- idx: 1 count - 1 func -- idx: 2 count - 2 func -- idx: 3 count - 3 func -- idx: 4 other count : 4#includestdio.h#includesetjmp.h#defineTRYintcountsetjmp(env);if(count0)#defineCATCH(x)elseif(count(x))#defineTHROW(idx)longjmp(env,(idx))#defineFINALLYjmp_buf env;voidfunc(intidx){printf(func -- idx: %d\n,idx);THROW(idx);}intmain(){TRY{printf(count - %d\n,count);func(count);}CATCH(1){printf(count - %d\n,count);func(count);}CATCH(2){printf(count - %d\n,count);func(count);}CATCH(3){printf(count - %d\n,count);func(count);}FINALLY{printf(other count : %d\n,count);}return0;}但同时存在两个问题try/catch嵌套用单链表头插法线程安全问题pthread_key_t文章参考于零声教育的C/Clinux服务期高级架构系统教程学习:https://ke.qq.com/course/417774?flowToken1020253
返回列表