内存泄漏工具VLD1.0_要点分析
0X01 关闭FPO优化
// Frame pointer omission (FPO) optimization should be turned off for this // entire file. The release VLD libs don't include FPO debug information, so FPO // optimization will interfere with stack walking. #pragma optimize ("y", off) |
Release版本的VLD库不包含FPO调试信息,进而影响栈回溯,所以文件开头关闭FPO优化;
0X02 内存泄漏检测器全局对象
// The one and only VisualLeakDetector object instance. This is placed in the // "compiler" initialization area, so that it gets constructed during C runtime // initialization and before any user global objects are constructed. Also, // disable the warning about us using the "compiler" initialization area. #pragma warning (disable:4074) #pragma init_seg (compiler) VisualLeakDetector visualleakdetector; |
对于同一个文件来说,全局变量的构造顺序是按照全局变量的声明顺序来构造的。但对于不同文件来说,不同文件间的全局变量构造顺序是不确定的。在C++标准中,只要求“处于同一编译但与的全局对象按其声明顺序初始化并倒序析构”,但并没有对处于不同编译单元的全局对象的初始化顺序做出要求。
通过init_seg预处理器指令中,:
#pragma init_seg(compiler) #pragma init_seg(lib) #pragma init_seg(user) #pragma init_seg("user_defined_segment_name") |
编译器、库、用户和用户定义段,按顺序依次初始化(但据实测,用户和用户定义段的优先级相同);
注:一个源文件中只能出现一次init_seg指令;且编译器段是微软保留给C/C++运行时库用的,一般情况下我们不应该使用它。
VLD为了保证尽可能的全面诊断内存泄漏,势必需要在其他全局变量构造前完成初始化。VLD就利用init_seg了处理器指令,让VLD的VisualLeakDetector在绝大多数情况下早于其他全局变量进行构造和初始化,即上面的实现。
#pragma warning (disable:4074):用于屏蔽C4074编译器警告,否则编译时会提示:warning C4074: initializers put in compiler reserved initialization area
0X03 设置内存开辟钩子
VisualLeakDetector::VisualLeakDetector () { 。。。 。。。 else if (linkdebughelplibrary()) { // 显示加载dbghelp.dll中必要的API // Register our allocation hook function with the debug heap. m_poldhook = _CrtSetAllocHook(allochook); report("Visual Leak Detector Version "VLD_VERSION" installed ("VLD_LIBTYPE").\n"); reportconfig(); 。。。 。。。 } } |
0x04 钩子函数
// 该函数不用考虑线程安全问题,CRT有调试堆有锁定 int VisualLeakDetector::allochook (int type, void *pdata, size_t size, int use, long request, const unsigned char *file, int line) { static bool inallochook = false; int status = true; // 1. 防止递归;2.不处理CRT内部使用的内存块 if (inallochook || (use == _CRT_BLOCK)) { if (visualleakdetector.m_poldhook) { status = visualleakdetector.m_poldhook(type, pdata, size, use, request, file, line); } return status; } inallochook = true; // Call the appropriate handler for the type of operation. switch (type) { case _HOOK_ALLOC: visualleakdetector.hookmalloc(request); break; case _HOOK_FREE: visualleakdetector.hookfree(pdata); break; case _HOOK_REALLOC: visualleakdetector.hookrealloc(pdata, request); break; default: visualleakdetector.report("WARNING: Visual Leak Detector: in allochook(): Unhandled allocation type (%d).\n", type); break; } if (visualleakdetector.m_poldhook) { status = visualleakdetector.m_poldhook(type, pdata, size, use, request, file, line); } inallochook = false; return status; } |
0X05 栈回溯
#if defined(_M_IX86) || defined(_M_X64) #pragma auto_inline(off) DWORD_PTR VisualLeakDetector::getprogramcounterx86x64 () { DWORD_PTR programcounter; // Get the return address out of the current stack frame __asm mov AXREG, [BPREG + SIZEOFPTR] // Put the return address into the variable we'll return __asm mov [programcounter], AXREG return programcounter; } #pragma auto_inline(on) #endif // defined(_M_IX86) || defined(_M_X64) void VisualLeakDetector::getstacktrace (CallStack *callstack) { DWORD architecture; CONTEXT context; unsigned int count = 0; STACKFRAME64 frame; DWORD_PTR framepointer; DWORD_PTR programcounter; // Get the required values for initialization of the STACKFRAME64 structure // to be passed to StackWalk64(). Required fields are AddrPC and AddrFrame. #if defined(_M_IX86) || defined(_M_X64) architecture = X86X64ARCHITECTURE; programcounter = getprogramcounterx86x64(); __asm mov [framepointer], BPREG // Get the frame pointer (aka base pointer) #else // If you want to retarget Visual Leak Detector to another processor // architecture then you'll need to provide architecture-specific code to // retrieve the current frame pointer and program counter in order to initialize // the STACKFRAME64 structure below. #error "Visual Leak Detector is not supported on this architecture." #endif // defined(_M_IX86) || defined(_M_X64) // Initialize the STACKFRAME64 structure. memset(&frame, 0x0, sizeof(frame)); frame.AddrPC.Offset = programcounter; frame.AddrPC.Mode = AddrModeFlat; frame.AddrFrame.Offset = framepointer; frame.AddrFrame.Mode = AddrModeFlat; // Walk the stack. while (count < _VLD_maxtraceframes) { count++; if (!pStackWalk64(architecture, m_process, m_thread, &frame, &context, NULL, pSymFunctionTableAccess64, pSymGetModuleBase64, NULL)) { // Couldn't trace back through any more frames. break; } if (frame.AddrFrame.Offset == 0) { // End of stack. break; } // Push this frame's program counter onto the provided CallStack. callstack->push_back((DWORD_PTR)frame.AddrPC.Offset); } } |
StackWalk64进行栈回溯需要精确的知道栈从哪里开始。这需要提供当前的栈帧地址和当前的EIP(RIP)寄存器值,而通过GetThreadContext获取的当前线程上下文对于一个正在运行的线程是不可靠的。所以这里通过了内联汇编代码实现,即getprogramcounterx86x64()函数的作用,其返回函数的返回地址,当函数返回时即当前的EIP(RIP)寄存器值。
内存泄漏工具VLD1.0_要点分析的更多相关文章
- VS2015 定位内存泄漏工具vld
介绍一款在vs2015开发环境定位内存泄漏工具:Visual Leak Detector ,具体的使用方法如下: 1. 安装vld-2.5-setup.exe (下载链接地址后面会给出),安装过程会 ...
- 【腾讯优测干货分享】Android内存泄漏的简单检查与分析方法
本文来自于Dev Club 开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/57d14047603a5bf1242ad01b 导语 内存泄漏问题大约是An ...
- Linux下内存泄漏工具【转】
转自:http://www.cnblogs.com/guochaoxxl/p/6970090.html 概述 内存泄漏(memory leak)指由于疏忽或错误造成程序未能释放已经不再使用的内存的情况 ...
- Linux下内存泄漏工具
概述 内存泄漏(memory leak)指由于疏忽或错误造成程序未能释放已经不再使用的内存的情况,在大型的.复杂的应用程序中,内存泄漏是常见的问题.当以前分配的一片内存不再需要使用或无法访问时,但是却 ...
- js的内存泄漏场景、监控以及分析
内存泄漏 Q:什么是内存泄漏? 字面上的意思,申请的内存没有及时回收掉,被泄漏了 Q:为什么会发生内存泄漏? 虽然前端有垃圾回收机制,但当某块无用的内存,却无法被垃圾回收机制认为是垃圾时,也就发生内存 ...
- Valgrind 内存泄漏工具
Valgrind 是一款 Linux下(支持 x86.x86_64和ppc32)程序的内存调试工具,它可以对编译后的二进制程序进行内存使用监测(C语言中的malloc和free,以及C++中的new和 ...
- malloc钩子和内存泄漏工具mtrace、Valgrind
一:malloc钩子函数 static void* (* old_malloc_hook) (size_t,const void *);static void (* old_free_hook)(vo ...
- 安卓 内存 泄漏 工具 LeakCanary 使用
韩梦飞沙 yue31313 韩亚飞 han_meng_fei_sha 313134555@qq.com LeakCanary是Square开源了一个内存泄露自动探测神器 .这是项目的github仓库地 ...
- Logcat中报内存泄漏MemoryLeak的一次分析
有时候运行APP的时候Logcat中会报错,提示资源没有释放,Memory leak, 于是打开Android Studio在Android Monitor工具栏点开,有Logcat和Monitors ...
随机推荐
- ios 添加多个target 管理 多个版本文件
1. 添加一个Target 这里是添加一个Test 项目 这里添加新的target Test与Release 也是同上的操作
- bootstrap 之 列表组件使用
列表是几乎所有网站都会用到的一个组件,正好bootstrap也给我们提供了这个组件的样式,下面我给大家简单介绍一下bootstrap中的列表组件的用法! 首先,重提一下引用bootstrap的核心文件 ...
- jquery mobile 入门级实战1
第一步:使用CDN接入jquery mobile CDN的全称是Content Delivery Network,即内容分发网络.其基本思路是尽可能避开互联网上有可能影响数据传输速度和稳定性的瓶颈和环 ...
- K - Digital Roots(第二季水)
Description The digital root of a positive integer is found by summing the digits of the integer. If ...
- Oracle表分区[转]
废话少说,直接讲分区语法. Oracle表分区分为四种:范围分区,散列分区,列表分区和复合分区. 一:范围分区 就是根据数据库表中某一字段的值的范围来划分分区,例如: create table gra ...
- 1002 Fire Net
用递归实现各种情况的枚举,可以看做是考察DPS的简单实现. #include <stdio.h> ][]; int place(int x,int y){ int i; ;i--){ ) ...
- python 数据类型之数值型
1.在python在数值的类型大概包括int float complex decimal 1.int 在3.x中它不在有最大值的限制. 2.float它更加连近于C语言中的double 3.compl ...
- 再次优化NGINX+php-fpm上传
上次写了一篇nginx+php-fpm优化上传,一位博友留言介绍了,第三方nginx upload module http://www.grid.net.ru/nginx/upload.en.html ...
- Oracle EBS-SQL (INV-8):检查物料入库明细信息.sql
select a.vendor_number 供应商编码 ,a.vendor_name 供应商名称 ,a.item_nu ...
- 自己封装的一个简易的二维表类SimpleTable
在QT中,QTableWidget处理二维表格的功能很强大(QTableView更强大),但有时我们只想让它显示少量数据(文字和图片),这时,使用QTableWidget就有点不方便了(个人感觉).所 ...