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