zoukankan      html  css  js  c++  java
  • 内存泄漏工具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)寄存器值。

  • 相关阅读:
    P6406 [COCI2014-2015] Norma 分治+数学
    CF547D Mike and Fish 欧拉回路
    P6628 [省选联考 2020 B 卷] 丁香之路 欧拉路+最小生成树
    2020 CSP-S2 游记
    CF594D REQ 树状数组+质因数分解
    CF416E President's Path floyd
    CF1385F Removing Leaves 拓扑排序
    CF449C Jzzhu and Apples 思维题
    回溯法与八皇后问题
    codewars-7kyu:Sum of the first nth term of Series
  • 原文地址:https://www.cnblogs.com/through/p/4951473.html
Copyright © 2011-2022 走看看