Stackwalk在调试中获取函数名称,行号和文件名

时间:2019-04-18 08:40:37

标签: c++ windows debugging stack names

快速摘要

在坚果壳中,我希望访问有关堆栈的调试信息,最好是将信息传递给Logger。

我希望这些信息告诉我功能名称行号文件名

我有这些符号,并且我试图访问它们中的垃圾值并将其变成英语。但是似乎没有任何作用。

我已对代码进行了注释,以供人们阅读并查看他们是否可以帮助我有效地走动栈以获取所需的信息。

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

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

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

我相信这是由于process参数无效。我将在下面详细说明。

尝试查找并解决问题

  • 我已经反复阅读了MSDN文档,感觉就像是我的头撞墙一样,我已经做了很多说的事,而且感觉好像不起作用。

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

  • 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上找到,那将是非常棒的。因此,对于一个很长的问题,但我觉得我应该提供我所知道的一切。

2 个答案:

答案 0 :(得分:1)

::GetCurrentProcess() = 0xffffffffffffffff

不是可疑的。

答案 1 :(得分:0)

我在您的代码上尝试了一些变体,从这里到那里拉位-最终,由于我使用的是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;
        }
    }
}