【问题标题】:C++ Stack Tracing OddityC++ 堆栈跟踪异常
【发布时间】:2017-08-06 17:33:54
【问题描述】:

根据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

【问题讨论】:

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


    【解决方案1】:

    你不能指望行号是完全可靠的——甚至堆栈跟踪本身也不行。优化器完成对您的代码的咀嚼后,它看起来与您实际编写的内容非常不同,并且只有这么多信息可以保留在调试信息中以将其映射回源代码。

    你必须学会​​用一粒盐来阅读堆栈跟踪。

    【讨论】:

    • 编译器应该在优化时跟踪行号。
    • @Barmar 确实如此。但只是在一定程度上。 特别是在优化时不能可靠地做到这一点。如果您怀疑我,请尝试在最高优化级别(包括调试信息)使用 gcc、clang 或 VS 构建任何重要的程序,然后使其在某个深堆栈的地方崩溃并查看核心转储。跨度>
    • 如果我正在构建一个 DEBUG 二进制文件并通过 /Od 关闭优化,会发生什么情况?
    • @Jon 那么您通常可以更加信任堆栈跟踪(尽管即使在这种情况下编译器有时仍然会更改内容)。在任何情况下;编译器在 语句之后将某些内容分配给该行是很常见的(根据我的经验)。因此,看到“foo()”报告为“分号后的 foo.cc 行”,我一点也不感到惊讶。
    • 所以,对于一个普通的 VC2015 C++ WIn32 控制台应用程序项目,构建上面的代码,你看不到任何差异的原因,除了编译器做了一些有趣的技巧(IOW,这不是什么我做了还是没做)?
    猜你喜欢
    • 2011-01-05
    • 2010-09-13
    • 1970-01-01
    • 2011-06-01
    • 2010-10-16
    相关资源
    最近更新 更多