Stackwalk получить имя функции, номер строки и имя файла, в отладке - PullRequest
1 голос
/ 18 апреля 2019

Краткое резюме

В скорлупе ореха я хочу получить доступ к отладочной информации, касающейся стека, предпочтительно для передачи информации в 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__, но это не то, что я ищу.

Результаты должны быть свернуты из нижнего стека, показывая адреса памяти вызывающего ПК, стека и фрейма. Это работает.

Однако он также должен показать мне, какое имя функции, номер строки и путь к файлу. Это не работает.

К вашему сведению: я понимаю, что мне нужно добавить код к генерации строки и вывести ее, но код не способен получать информацию для строк, так что она еще не закодирована.

Пожалуйста, если кто-нибудь может мне помочь, было бы здорово, если бы весь код был сосредоточен вокруг файла Windows "DbgHelp.h", и большая часть информации доступна на MSDN. Так что на длинный вопрос, но я чувствовал, что должен предоставить все, что я знаю.

1 Ответ

1 голос
/ 07 июня 2019
::GetCurrentProcess() = 0xffffffffffffffff

не вызывает подозрений.

...