【问题标题】:Stackwalk get Function Name, Line number and File Name, in debugStackwalk 在调试中获取函数名、行号和文件名
【发布时间】:2019-04-18 08:40:37
【问题描述】:

快速总结

简而言之,我希望访问有关堆栈的调试信息,最好将信息传递给 Logger。

我希望信息能告诉我函数名称行号文件名

我有符号,我正在尝试访问其中的垃圾值并将它们转换为英文。但是似乎没有任何效果。

我已经注释了代码供人们阅读,看看他们是否可以帮助我有效地遍历堆栈以提取我需要的信息。

  • 到目前为止,我可以指出 SymGetModuleBase() 不只返回正数 0,根据 MSDN,如果返回 0,它会失败。这是正确的,因为它返回内存地址。

  • SymGetSymFromAddr() 无法返回 true,我假设它获取堆栈帧/函数的名称

  • SymGetLineFromAddr() 也继续失败,不返回文件中的行号位置,也不收集文件路径。

我相信这是因为process 参数无效。我会在下面详细说明。

尝试定位和解决问题

  • 我反复阅读了 MSDN 文档,感觉自己的脑袋撞到墙上了,我已经按照它所说的做了很多事情,但我觉得它只是行不通。

    李>
  • 但是我注意到在尝试此操作之前应该调用SymInitialize(),我确实调用了它。这将 GetLastError() 值从 6 ERROR_INVALID_HANDLE 更改为 0 ERROR_SUCCESS。然而,SymGetModuleBase() 仍然返回 0,不管 SymInitialize() 尽管 GetLastError() 根据 SymInitialize() 的使用报告不同的错误代码。它应该返回一个有效的虚拟内存地址,这是我认为主要问题在于代码的地方。

  • HANDLE process = ::GetCurrentProcess(); 下面代码中的这一行返回0xffffffffffffffff 如果你问我,我很怀疑。这应该返回一个伪虚拟内存地址,但对我来说它看起来像是一个错误的结果。每次我运行程序时都会发生这种情况,这导致我认为::GetCurrentProcess() 这要么有错误,要么无法正常工作。根据 MSDN,这是获取当前进程的正确最新方法,我不知道如何以另一种方式获取有效的 HANDLE 到进程。所以我无法将SymGetModuleBase()中的第一个参数传递给正确的过程,尽管我可能错了。

函数的完整代码

void Logger::WriteStackFrames(log::TextColor tc)
{
    // Initalize some memory
    DWORD                           machine = IMAGE_FILE_MACHINE_AMD64;
    HANDLE                          process = ::GetCurrentProcess();
    HANDLE                          thread = GetCurrentThread();

    // Initalize more memory
    CONTEXT                         context;
    STACKFRAME                      stack_frame;

    // Set some memory
    memset(&context, 0, sizeof(CONTEXT));
    memset(&stack_frame, 0, sizeof(STACKFRAME));

    // Capture the context
    RtlCaptureContext(&context);

    // Initalize a few things here and there
    stack_frame.AddrPC.Offset       = context.Rip;
    stack_frame.AddrPC.Mode         = AddrModeFlat;
    stack_frame.AddrStack.Offset    = context.Rsp;
    stack_frame.AddrStack.Mode      = AddrModeFlat;
    stack_frame.AddrFrame.Offset    = context.Rbp;
    stack_frame.AddrFrame.Mode      = AddrModeFlat;

    // Randomly saw this was supposed to be called prior to StackWalk so tried it
    if (!SymInitialize(process, 0, false))
    {
        wprintf(L"SymInitialize unable to find process!! Error: %d\r\n", GetLastError());
    }

    for (ULONG frame = 0; ; frame++)
    {
        // Set text color
        SetTextColor(tc);

        // Check for frames
        BOOL result = StackWalk(machine, process, thread, &stack_frame, &context, 0,
            SymFunctionTableAccess, SymGetModuleBase, 0);

        // Get memory address of base module. Returns 0 although when SymInitialize is called before it the GetLastError returns 0 without return 6
        DWORD64 module_base = SymGetModuleBase(process, stack_frame.AddrPC.Offset);
        if (module_base == 0) {
            wprintf(L"SymGetModuleBase is unable to get virutal address!! Error: %d\r\n", GetLastError());
        }

        // Initalize more memory
        MODULEINFO                  module_info;
        SecureZeroMemory(&module_info, sizeof(MODULEINFO));

        // Get the file name of the file containing the function
        TCHAR module_buffer[log::MaxPath];
        DWORD mod_file = GetModuleFileName((HINSTANCE)module_base, module_buffer, log::MaxPath);
        if ((module_base != 0) && (mod_file != 0))
        {
            module_info.module_name = module_buffer;
        }

        // Initalize more memory and clear it out
        PIMAGEHLP_SYMBOL64      symbol;
        IMAGEHLP_LINE64         line_num;
        SecureZeroMemory(&symbol, sizeof(PIMAGEHLP_SYMBOL64));
        SecureZeroMemory(&symbol, sizeof(IMAGEHLP_LINE64));

        // Get the symbol
        TCHAR symbol_buffer[log::MaxPath];
        symbol = (PIMAGEHLP_SYMBOL)symbol_buffer;
        symbol->SizeOfStruct = (sizeof(IMAGEHLP_SYMBOL) + log::MaxPath);
        symbol->MaxNameLength = 254;

        // Attempt to get name from symbol (fails)
        LPSTR name_buffer = new CHAR[254];
        if (SymGetSymFromAddr(process, stack_frame.AddrPC.Offset, 0, symbol))
        {
            name_buffer = symbol->Name;
        }

        // Set the size of something
        DWORD offset = 0;
        line_num.SizeOfStruct = sizeof(IMAGEHLP_LINE64);

        // Attempt to get the line and file name of where the symbol is
        if (SymGetLineFromAddr(process, stack_frame.AddrPC.Offset, &offset, &line_num))
        {
            module_info.line = line_num.LineNumber;
            module_info.file = line_num.FileName;
        }

        // Initalize memory
        LPWSTR console_message = new TCHAR[log::MaxMsgLength];
        LPWSTR file_message = new TCHAR[log::MaxMsgLength];

        // Set some strings
        swprintf(console_message, log::MaxMsgLength, L">> Frame %02lu: called from: %016X Stack: %016X Frame: %016X Address return: %016X\r\n",
            frame, stack_frame.AddrPC.Offset, stack_frame.AddrStack.Offset, stack_frame.AddrFrame.Offset, stack_frame.AddrReturn.Offset);
        swprintf(file_message, log::MaxMsgLength, L"Frame %02lu: called from: %016X Stack: %016X Frame: %016X Address return: %016X\r\n",
            frame, stack_frame.AddrPC.Offset, stack_frame.AddrStack.Offset, stack_frame.AddrFrame.Offset, stack_frame.AddrReturn.Offset);

        /* When the symbol can yield the name, line and file name the above strings
        will also include that information */
        // To go here . . . 

        // Write some strings
        wprintf(console_message);
        WriteAsync(file_message);

        // Delete some memory
        if (console_message) {
            delete[] console_message;   console_message = nullptr;
        }
        if (file_message) {
            delete[] file_message;  file_message = nullptr;
        }

        // If nothing else to do break loop
        if (!result) {
            break;
        }
    }
}

我希望达到的目标

虽然我意识到这只能在调试模式下工作,但我知道我可以使用 __LINE__ __FUNCTION__ __FILE__ 宏编写宏,但这不是我想要的。

结果应该是从底部堆栈开始的结果,显示调用 PC、堆栈和帧的内存地址。这行得通。

但是它也应该告诉我函数的名称、行号和文件路径。这不起作用。

仅供参考:我意识到我需要将代码添加到生成字符串并输出它,但代码无法获取字符串的信息,因此尚未编码。

如果有人可以帮助我,那就太好了,所有代码都集中在“DbgHelp.h”windows 文件中,并且大多数信息都可以在 MSDN 上找到。所以对于很长的问题,但我觉得我应该提供我所知道的一切。

【问题讨论】:

  • GetCurrentProcess 返回 -1 (0xffffffffffffffff),这并不可疑。
  • 线程也将是 0xffffffffffffffffe,这似乎意味着“你自己的线程”

标签: c++ windows debugging stack names


【解决方案1】:
::GetCurrentProcess() = 0xffffffffffffffff

没有可疑。

【讨论】:

    【解决方案2】:

    我尝试了对您的代码从这里和那里提取位的一些变体 - 最后我无法让它工作,因为我使用的是 clang/mingw 并且它没有生成 .pdb 文件。但是,当您使用 MSVC 时,代码可能对您有用。无论如何,在这里,以防万一它有帮助

    我还注意到您将机器类型硬编码为 AMD - 我认为这对您来说是正确的,但在下面我发现了一个 ifdef,它为其他拱门设置了它。

    #include <windows.h>
    #include <excpt.h>
    #include <imagehlp.h>
    #include <binutils/bfd.h>
    #include <psapi.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <stdarg.h>
    #include <string.h>
    #include <stdbool.h>
    #include <psapi.h>
    #include <dbghelp.h>
    
    #define MAX_SYMBOL_LEN 1024
    
    typedef struct CallstackEntry
    {
        DWORD64 offset; // if 0, we have no valid entry
        CHAR    name[MAX_SYMBOL_LEN];
        CHAR    undName[MAX_SYMBOL_LEN];
        CHAR    undFullName[MAX_SYMBOL_LEN];
        DWORD64 offsetFromSmybol;
        DWORD   offsetFromLine;
        DWORD   lineNumber;
        CHAR    lineFileName[MAX_SYMBOL_LEN];
        DWORD   symType;
        LPCSTR  symTypeString;
        CHAR    moduleName[MAX_SYMBOL_LEN];
        DWORD64 baseOfImage;
        CHAR    loadedImageName[MAX_SYMBOL_LEN];
    } CallstackEntry;
    
    typedef enum CallstackEntryType
    {
        firstEntry,
        nextEntry,
        lastEntry
    } CallstackEntryType;
    
    void _backtrace (void)
    {
        HANDLE process = ::GetCurrentProcess();
        HANDLE thread = GetCurrentThread();
    
        if (!SymInitialize(process, 0, true)) {
            wprintf(L"SymInitialize unable to find process!! Error: %d\r\n",~
                    GetLastError());
        }
       DWORD symOptions = SymGetOptions();
        symOptions |= SYMOPT_LOAD_LINES;
        symOptions |= SYMOPT_FAIL_CRITICAL_ERRORS;
        symOptions = SymSetOptions(symOptions);
    
        char szSearchPath[MAX_SYMBOL_LEN] = {0};
        SymGetSearchPath(process, szSearchPath, MAX_SYMBOL_LEN);
    
        char  szUserName[MAX_SYMBOL_LEN] = {0};
        DWORD dwSize = MAX_SYMBOL_LEN;
        GetUserNameA(szUserName, &dwSize);
    
        CHAR   search_path_debug[MAX_SYMBOL_LEN];
        size_t maxLen = MAX_SYMBOL_LEN;
    #if _MSC_VER >= 1400
        maxLen = _TRUNCATE;
    #endif
        _snprintf_s(search_path_debug, maxLen,~
                    "SymInit: Symbol-SearchPath: '%s', symOptions: %d, UserName: '%s'\n",
                    szSearchPath, symOptions, szUserName);
        search_path_debug[MAX_SYMBOL_LEN - 1] = 0;
        printf(search_path_debug);
    
        // Initalize more memory
        CONTEXT context;
        memset(&context, 0, sizeof(CONTEXT));
        context.ContextFlags = CONTEXT_FULL;
        RtlCaptureContext(&context);
    
        // Initalize a few things here and there
        STACKFRAME stack;
        memset(&stack, 0, sizeof(STACKFRAME));
        stack.AddrPC.Offset       = context.Rip;
        stack.AddrPC.Mode         = AddrModeFlat;
        stack.AddrStack.Offset    = context.Rsp;
        stack.AddrStack.Mode      = AddrModeFlat;
        stack.AddrFrame.Offset    = context.Rbp;
        stack.AddrFrame.Mode      = AddrModeFlat;
    
    #ifdef _M_IX86
        auto machine = IMAGE_FILE_MACHINE_I386;
    #elif _M_X64
        auto machine = IMAGE_FILE_MACHINE_AMD64;
    #elif _M_IA64
        auto machine = IMAGE_FILE_MACHINE_IA64;
    #else
    #error "platform not supported!"
    #endif
        for (ULONG frame = 0; ; frame++) {
            BOOL result = StackWalk(machine,~
                                    process,~
                                    thread,~
                                    &stack,
                                    &context,
                                    0,
                                    SymFunctionTableAccess,~
                                    SymGetModuleBase,~
                                    0);
    
            CallstackEntry csEntry;
            csEntry.offset = stack.AddrPC.Offset;
            csEntry.name[0] = 0;
            csEntry.undName[0] = 0;
            csEntry.undFullName[0] = 0;
            csEntry.offsetFromSmybol = 0;
            csEntry.offsetFromLine = 0;
            csEntry.lineFileName[0] = 0;
            csEntry.lineNumber = 0;
            csEntry.loadedImageName[0] = 0;
            csEntry.moduleName[0] = 0;
    
            IMAGEHLP_SYMBOL64 symbol {};
            symbol.SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
            symbol.MaxNameLength = MAX_SYMBOL_LEN;
    
            // Initalize more memory and clear it out
            if (SymGetSymFromAddr64(process,~
                                    stack.AddrPC.Offset,
                                    &csEntry.offsetFromSmybol,~
                                    &symbol)) {
            }
    
            IMAGEHLP_LINE64 line {};
            line.SizeOfStruct = sizeof(line);
    
            if (SymGetLineFromAddr64(process,~
                                     stack.AddrPC.Offset,
                                     &csEntry.offsetFromLine,~
                                     &line)) {
            }
            printf("Frame %lu:\n"
                   "    Symbol name:    %s\n"
                   "    PC address:     0x%08LX\n"
                   "    Stack address:  0x%08LX\n"
                   "    Frame address:  0x%08LX\n"
                   "\n",
                   frame,
                   symbol.Name,
                   (ULONG64)stack.AddrPC.Offset,
                   (ULONG64)stack.AddrStack.Offset,
                   (ULONG64)stack.AddrFrame.Offset
               );
    
            // If nothing else to do break loop
            if (!result) {
                break;
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2014-08-17
      • 1970-01-01
      • 2014-01-28
      • 2012-02-11
      • 1970-01-01
      • 2012-08-24
      • 1970-01-01
      • 1970-01-01
      • 2014-01-26
      相关资源
      最近更新 更多