From 1244044647ed0536a50df34c282c6d79a6865724 Mon Sep 17 00:00:00 2001 From: kd-11 Date: Sun, 14 Aug 2022 03:36:53 +0300 Subject: [PATCH] util: Provide stack-trace utilities --- Utilities/stack_trace.cpp | 144 ++++++++++++++++++++++++++++++++++ Utilities/stack_trace.h | 20 +++++ rpcs3/emucore.vcxproj | 2 + rpcs3/emucore.vcxproj.filters | 6 ++ rpcs3/rpcs3.vcxproj | 4 +- 5 files changed, 174 insertions(+), 2 deletions(-) create mode 100644 Utilities/stack_trace.cpp create mode 100644 Utilities/stack_trace.h diff --git a/Utilities/stack_trace.cpp b/Utilities/stack_trace.cpp new file mode 100644 index 0000000000..5ea4021242 --- /dev/null +++ b/Utilities/stack_trace.cpp @@ -0,0 +1,144 @@ +#include "stdafx.h" +#include "stack_trace.h" + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#define DBGHELP_TRANSLATE_TCHAR +#include +#include +#else +#include +#endif + +namespace utils +{ +#ifdef _WIN32 + std::string wstr_to_utf8(LPWSTR data, int str_len) + { + if (!str_len) + { + return {}; + } + + // Calculate size + const auto length = WideCharToMultiByte(CP_UTF8, 0, data, str_len, NULL, 0, NULL, NULL); + + // Convert + std::vector out(length + 1, 0); + WideCharToMultiByte(CP_UTF8, 0, data, str_len, out.data(), length, NULL, NULL); + return out.data(); + } + + std::vector backtrace(int max_depth) + { + std::vector result = {}; + + const auto hProcess = ::GetCurrentProcess(); + const auto hThread = ::GetCurrentThread(); + + CONTEXT context{}; + RtlCaptureContext(&context); + + STACKFRAME64 stack = {}; + 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; + + while (max_depth--) + { + if (!StackWalk64( + IMAGE_FILE_MACHINE_AMD64, + hProcess, + hThread, + &stack, + &context, + NULL, + SymFunctionTableAccess64, + SymGetModuleBase64, + NULL)) + { + break; + } + + result.push_back(reinterpret_cast(stack.AddrPC.Offset)); + } + + return result; + } + + std::vector backtrace_symbols(const std::vector& stack) + { + std::vector result = {}; + std::vector symbol_buf(sizeof(SYMBOL_INFO) + sizeof(TCHAR) * 256); + + const auto hProcess = ::GetCurrentProcess(); + + auto sym = reinterpret_cast(symbol_buf.data()); + sym->SizeOfStruct = sizeof(SYMBOL_INFO); + sym->MaxNameLen = 256; + + IMAGEHLP_LINEW64 line_info{}; + line_info.SizeOfStruct = sizeof(IMAGEHLP_LINEW64); + + SymInitialize(hProcess, NULL, TRUE); + SymSetOptions(SYMOPT_LOAD_LINES); + + for (const auto& pointer : stack) + { + DWORD64 unused; + SymFromAddr(hProcess, reinterpret_cast(pointer), &unused, sym); + + if (sym->NameLen) + { + const auto function_name = wstr_to_utf8(sym->Name, static_cast(sym->NameLen)); + + // Attempt to get file and line information if available + DWORD unused2; + if (SymGetLineFromAddrW(hProcess, reinterpret_cast(pointer), &unused2, &line_info)) + { + const auto full_path = fmt::format("%s:%u %s", wstr_to_utf8(line_info.FileName, -1), line_info.LineNumber, function_name); + result.push_back(full_path); + } + else + { + result.push_back(function_name); + } + } + else + { + result.push_back(fmt::format("rpcs3@0xp", pointer)); + } + } + + return result; + } +#else + std::vector backtrace(int max_depth) + { + std::vector result(max_depth); + int depth = backtrace(result.data(), max_depth); + + result.resize(depth); + return result; + } + + std::vector backtrace_symbols(const std::vector& stack) + { + std::vector result; + result.reserve(stack.size()); + + const auto symbols = backtrace_symbols(stack.data(), static_cast(stack.size())); + for (usz i = 0; i < stack.size(); ++i) + { + result.push_back(symbols[i]); + } + + free(symbols); + return result; + } +#endif +} diff --git a/Utilities/stack_trace.h b/Utilities/stack_trace.h new file mode 100644 index 0000000000..3072ea3a88 --- /dev/null +++ b/Utilities/stack_trace.h @@ -0,0 +1,20 @@ +#pragma once +#include +#include + +namespace utils +{ + std::vector backtrace(int max_depth = 255); + std::vector backtrace_symbols(const std::vector& stack); + + FORCE_INLINE void print_trace(logs::channel& logger, int max_depth = 255) + { + const auto trace = backtrace(max_depth); + const auto lines = backtrace_symbols(trace); + + for (const auto& line : lines) + { + logger.error("%s", line); + } + } +} diff --git a/rpcs3/emucore.vcxproj b/rpcs3/emucore.vcxproj index ef8abee7e4..e59953eaa0 100644 --- a/rpcs3/emucore.vcxproj +++ b/rpcs3/emucore.vcxproj @@ -52,6 +52,7 @@ + @@ -458,6 +459,7 @@ + diff --git a/rpcs3/emucore.vcxproj.filters b/rpcs3/emucore.vcxproj.filters index d1f86e949b..25d16be05f 100644 --- a/rpcs3/emucore.vcxproj.filters +++ b/rpcs3/emucore.vcxproj.filters @@ -1090,6 +1090,9 @@ Emu\Cell\Modules + + Utilities + @@ -2173,6 +2176,9 @@ Emu\Cell\Modules + + Utilities + diff --git a/rpcs3/rpcs3.vcxproj b/rpcs3/rpcs3.vcxproj index eeb405e930..871961e9d0 100644 --- a/rpcs3/rpcs3.vcxproj +++ b/rpcs3/rpcs3.vcxproj @@ -88,7 +88,7 @@ Level3 - Ole32.lib;gdi32.lib;..\hidapi.lib;..\libusb-1.0.lib;winmm.lib;ksuser.lib;OpenAL32.lib;XAudio.lib;GLGSRender.lib;shlwapi.lib;VKGSRender.lib;vulkan-1.lib;wolfssl.lib;libcurl.lib;Wldap32.lib;glslang.lib;OSDependent.lib;OGLCompiler.lib;SPIRV.lib;MachineIndependent.lib;GenericCodeGen.lib;Advapi32.lib;user32.lib;zlib.lib;..\libpng16.lib;asmjit.lib;yaml-cpp.lib;discord-rpc.lib;emucore.lib;dxgi.lib;$(QTDIR)\lib\qtmain.lib;shell32.lib;$(QTDIR)\lib\Qt5Widgets.lib;$(QTDIR)\lib\Qt5Gui.lib;$(QTDIR)\lib\Qt5Core.lib;Qt5Core.lib;Qt5Gui.lib;Qt5Widgets.lib;Qt5WinExtras.lib;Qt5Concurrent.lib;7zlib.lib;SPIRV-Tools.lib;SPIRV-Tools-opt.lib;Qt5Multimedia.lib;Qt5MultimediaWidgets.lib;Qt5Svg.lib;libcubeb.lib;cubeb.lib;soundtouch.lib;Avrt.lib;%(AdditionalDependencies) + DbgHelp.lib;Ole32.lib;gdi32.lib;..\hidapi.lib;..\libusb-1.0.lib;winmm.lib;ksuser.lib;OpenAL32.lib;XAudio.lib;GLGSRender.lib;shlwapi.lib;VKGSRender.lib;vulkan-1.lib;wolfssl.lib;libcurl.lib;Wldap32.lib;glslang.lib;OSDependent.lib;OGLCompiler.lib;SPIRV.lib;MachineIndependent.lib;GenericCodeGen.lib;Advapi32.lib;user32.lib;zlib.lib;..\libpng16.lib;asmjit.lib;yaml-cpp.lib;discord-rpc.lib;emucore.lib;dxgi.lib;$(QTDIR)\lib\qtmain.lib;shell32.lib;$(QTDIR)\lib\Qt5Widgets.lib;$(QTDIR)\lib\Qt5Gui.lib;$(QTDIR)\lib\Qt5Core.lib;Qt5Core.lib;Qt5Gui.lib;Qt5Widgets.lib;Qt5WinExtras.lib;Qt5Concurrent.lib;7zlib.lib;SPIRV-Tools.lib;SPIRV-Tools-opt.lib;Qt5Multimedia.lib;Qt5MultimediaWidgets.lib;Qt5Svg.lib;libcubeb.lib;cubeb.lib;soundtouch.lib;Avrt.lib;%(AdditionalDependencies) ..\3rdparty\OpenAL\libs\Win64;..\3rdparty\glslang\build\hlsl\Release;..\3rdparty\glslang\build\SPIRV\Release;..\3rdparty\glslang\build\OGLCompilersDLL\Release;..\3rdparty\glslang\build\glslang\OSDependent\Windows\Release;..\3rdparty\glslang\build\glslang\Release;..\3rdparty\SPIRV\build\source\Release;..\3rdparty\SPIRV\build\source\opt\Release;..\lib\$(CONFIGURATION)-$(PLATFORM);..\3rdparty\XAudio2Redist\libs;..\3rdparty\discord-rpc\lib;$(QTDIR)\lib;%(AdditionalLibraryDirectories);$(VULKAN_SDK)\Lib "/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" %(AdditionalOptions) true @@ -139,7 +139,7 @@ $(IntDir)vc$(PlatformToolsetVersion).pdb - Ole32.lib;gdi32.lib;..\hidapi.lib;..\libusb-1.0.lib;winmm.lib;ksuser.lib;OpenAL32.lib;XAudio.lib;GLGSRender.lib;shlwapi.lib;VKGSRender.lib;vulkan-1.lib;wolfssl.lib;libcurl.lib;Wldap32.lib;glslangd.lib;OSDependentd.lib;OGLCompilerd.lib;SPIRVd.lib;MachineIndependentd.lib;GenericCodeGend.lib;Advapi32.lib;user32.lib;zlib.lib;..\libpng16.lib;asmjit.lib;yaml-cpp.lib;discord-rpc.lib;emucore.lib;dxgi.lib;$(QTDIR)\lib\qtmaind.lib;shell32.lib;$(QTDIR)\lib\Qt5Widgetsd.lib;$(QTDIR)\lib\Qt5Guid.lib;$(QTDIR)\lib\Qt5Cored.lib;Qt5Cored.lib;Qt5Guid.lib;Qt5Widgetsd.lib;Qt5WinExtrasd.lib;Qt5Concurrentd.lib;7zlib.lib;SPIRV-Tools.lib;SPIRV-Tools-opt.lib;Qt5Multimediad.lib;Qt5MultimediaWidgetsd.lib;Qt5Svgd.lib;libcubeb.lib;cubeb.lib;soundtouch.lib;Avrt.lib;%(AdditionalDependencies) + DbgHelp.lib;Ole32.lib;gdi32.lib;..\hidapi.lib;..\libusb-1.0.lib;winmm.lib;ksuser.lib;OpenAL32.lib;XAudio.lib;GLGSRender.lib;shlwapi.lib;VKGSRender.lib;vulkan-1.lib;wolfssl.lib;libcurl.lib;Wldap32.lib;glslangd.lib;OSDependentd.lib;OGLCompilerd.lib;SPIRVd.lib;MachineIndependentd.lib;GenericCodeGend.lib;Advapi32.lib;user32.lib;zlib.lib;..\libpng16.lib;asmjit.lib;yaml-cpp.lib;discord-rpc.lib;emucore.lib;dxgi.lib;$(QTDIR)\lib\qtmaind.lib;shell32.lib;$(QTDIR)\lib\Qt5Widgetsd.lib;$(QTDIR)\lib\Qt5Guid.lib;$(QTDIR)\lib\Qt5Cored.lib;Qt5Cored.lib;Qt5Guid.lib;Qt5Widgetsd.lib;Qt5WinExtrasd.lib;Qt5Concurrentd.lib;7zlib.lib;SPIRV-Tools.lib;SPIRV-Tools-opt.lib;Qt5Multimediad.lib;Qt5MultimediaWidgetsd.lib;Qt5Svgd.lib;libcubeb.lib;cubeb.lib;soundtouch.lib;Avrt.lib;%(AdditionalDependencies) ..\3rdparty\OpenAL\libs\Win64;..\3rdparty\glslang\build\hlsl\Debug;..\3rdparty\glslang\build\SPIRV\Debug;..\3rdparty\glslang\build\OGLCompilersDLL\Debug;..\3rdparty\glslang\build\glslang\OSDependent\Windows\Debug;..\3rdparty\glslang\build\glslang\Debug;..\3rdparty\SPIRV\build\source\opt\Debug;..\3rdparty\XAudio2Redist\libs;..\3rdparty\discord-rpc\lib;..\lib\$(CONFIGURATION)-$(PLATFORM);$(QTDIR)\lib;%(AdditionalLibraryDirectories);$(VULKAN_SDK)\Lib "/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" /VERBOSE %(AdditionalOptions) true