C ++ Stack Tracing Oddity

时间:2017-03-15 19:23:20

标签: c++ visual-c++ visual-studio-2015 stack-trace

根据herehere的建议,我创建了一个类来打印带有行号的调用堆栈,只需在我的代码中的某个位置插入一个调用。它运作良好,但是,我对输出的一个方面感到困惑 - 行号。我的测试代码:

#define _WIN32 1
#define DEBUG 1

#include "C:\git\StackTrace\StackTrace.h"

int main()
{
    MyCompany::PrintStackTrace();
    return 0;
}

打印堆栈跟踪的调用在线 8 ,但是,输出表明它在线 9

*** 0: main in c:\git\tests\stacktrace\stacktrace\st_test.cpp: line: 9: address: 0x7FF629172070
*** 1: invoke_main in f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl: line: 41: address: 0x7FF629173710
*** 2: __scrt_common_main_seh in f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl: line: FD: address: 0x7FF6291734C0
*** 3: __scrt_common_main in f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl: line: 128: address: 0x7FF6291734A0
*** 4: mainCRTStartup in f:\dd\vctools\crt\vcstartup\src\startup\exe_main.cpp: line: 11: address: 0x7FF629173760
*** SymGetLineFromAddr64 returned error code 487
*** at BaseThreadInitThunk address 0x7FFA09648350
*** SymGetLineFromAddr64 returned error code 487
*** at RtlUserThreadStart address 0x7FFA0BF070B0

请帮助我理解我在这里缺少什么?

我的StackTrace.h代码:

#ifdef _WIN32
#ifdef DEBUG

#include <Windows.h>
#include <fstream>
#include <iostream>
#include <DbgHelp.h>
#include <WinBase.h>

#pragma comment(lib, "Dbghelp.lib")

using namespace std;

namespace MyCompany
{
    class StackTrace
    {
    private:
        static bool instanceFlag;
        static StackTrace *single;
        StackTrace() { }
    public:
        static StackTrace* GetInstance();
        void PrintStack();
        void PrintStack(ostream& out, int skipFrames);
        ~StackTrace()
        {
            instanceFlag = false;
        }
    };

    bool StackTrace::instanceFlag = false;
    StackTrace* StackTrace::single = NULL;

    /**
     * Returns the instance of the StackTrace class, generating the instance if needed.
     *
     * @return A pointer to the instance of the StackTrace class.
     */
    StackTrace* StackTrace::GetInstance()
    {
        if (!instanceFlag)
        {
            single = new StackTrace();
            instanceFlag = true;
            return single;
        }
        else
        {
            return single;
        }
    }

    /**
     * Prints the call stack to stdout.
     *
     * @param sourceStr
     *        The multibyte character string.
     */
    void StackTrace::PrintStack()
    {
        // If PrintStackTrace() was called with no parameters, there is one more 
        // wrapper in the stack (this one) to skip.
        int skipFrames = 3;

        PrintStack(cout, skipFrames);
    }

    /**
     * Prints the call stack to the given stream.
     *
     * @param out
     *        The already-opened stream where the call stack will be printed.
     *
     * @param skipFrames
     *        The number of frames to skip when capturing the call stack.
     */
    void StackTrace::PrintStack(ostream& out, int skipFrames = 2)
    {
        const int maxCallers = 1024; // Not worried about size limits of OS's older than Windows 7.
        void * callers_stack[maxCallers];
        unsigned short frames;
        SYMBOL_INFO * symbol;
        HANDLE process;
        DWORD displacement;

        IMAGEHLP_LINE64 *line = (IMAGEHLP_LINE64 *)malloc(sizeof(IMAGEHLP_LINE64));
        if (NULL == line)
        {
            return;
        }

        process = GetCurrentProcess();
        SymInitialize(process, NULL, TRUE);
        frames = CaptureStackBackTrace(skipFrames, maxCallers, callers_stack, NULL);

        symbol = (SYMBOL_INFO *)calloc(sizeof(SYMBOL_INFO) + 256 * sizeof(char), 1);
        if (NULL == symbol)
        {
            return;
        }

        symbol->MaxNameLen = 255;
        symbol->SizeOfStruct = sizeof(SYMBOL_INFO);

        out << uppercase;

        const unsigned short  MAX_CALLERS_SHOWN = 64;
        frames = frames < MAX_CALLERS_SHOWN ? frames : MAX_CALLERS_SHOWN;

        for (unsigned int i = 0; i < frames; i++)
        {
            DWORD64 address = (DWORD64)(callers_stack[i]);
            SymFromAddr(process, address, 0, symbol);

            if (SymGetLineFromAddr64(process, address, &displacement, line))
            {
                out << "*** " << dec << i << ": " << symbol->Name << " in " << line->FileName 
                    << ": line: " << line->LineNumber << ": address: 0x" << hex << symbol->Address << endl;
            }
            else
            {
                out << "*** SymGetLineFromAddr64 returned error code " << dec << GetLastError() << endl;
                out << "*** at " << symbol->Name << " address 0x" << hex << symbol->Address << endl;
            }
        }

        if (symbol) free(symbol);
        if (line) free(line);
    }

    /**
     * Print the call stack to stdout.
     */
    void PrintStackTrace()
    {
        StackTrace::GetInstance()->PrintStack();
    }

    /**
     * Print the call stack to the given stream.
     *
     * @param out
     *        The already-opened stream where the call stack will be printed.
     */
    void PrintStackTrace(ostream& out)
    {
        StackTrace::GetInstance()->PrintStack(out);
    }
}

#endif // DEBUG
#endif // _WIN32

1 个答案:

答案 0 :(得分:0)

你不能指望行号完全可靠 - 甚至连堆栈跟踪本身都没有。一旦优化器完成了对代码的咀嚼,它将与您实际编写的内容完全不同,并且只有很多信息可以保存在调试信息中以将其映射回源。

你必须学会​​用一粒盐阅读堆栈痕迹。