display stack trace on exceptions

This commit is contained in:
Nomango 2019-04-22 13:21:12 +08:00 committed by Nomango
parent 050b1a504e
commit 0eceb235ca
7 changed files with 1452 additions and 37 deletions

View File

@ -94,6 +94,7 @@
<ClInclude Include="third-party\ImGui\imstb_rectpack.h" /> <ClInclude Include="third-party\ImGui\imstb_rectpack.h" />
<ClInclude Include="third-party\ImGui\imstb_textedit.h" /> <ClInclude Include="third-party\ImGui\imstb_textedit.h" />
<ClInclude Include="third-party\ImGui\imstb_truetype.h" /> <ClInclude Include="third-party\ImGui\imstb_truetype.h" />
<ClInclude Include="third-party\StackWalker\StackWalker.h" />
<ClInclude Include="ui\Button.h" /> <ClInclude Include="ui\Button.h" />
<ClInclude Include="ui\Menu.h" /> <ClInclude Include="ui\Menu.h" />
<ClInclude Include="utils\DataUtil.h" /> <ClInclude Include="utils\DataUtil.h" />
@ -153,6 +154,7 @@
<ClCompile Include="third-party\ImGui\imgui_demo.cpp" /> <ClCompile Include="third-party\ImGui\imgui_demo.cpp" />
<ClCompile Include="third-party\ImGui\imgui_draw.cpp" /> <ClCompile Include="third-party\ImGui\imgui_draw.cpp" />
<ClCompile Include="third-party\ImGui\imgui_widgets.cpp" /> <ClCompile Include="third-party\ImGui\imgui_widgets.cpp" />
<ClCompile Include="third-party\StackWalker\StackWalker.cpp" />
<ClCompile Include="ui\Button.cpp" /> <ClCompile Include="ui\Button.cpp" />
<ClCompile Include="ui\Menu.cpp" /> <ClCompile Include="ui\Menu.cpp" />
<ClCompile Include="utils\DataUtil.cpp" /> <ClCompile Include="utils\DataUtil.cpp" />

View File

@ -40,6 +40,9 @@
<Filter Include="third-party\ImGui"> <Filter Include="third-party\ImGui">
<UniqueIdentifier>{2f24af2d-eba6-4be1-9ac4-fc58aeac6670}</UniqueIdentifier> <UniqueIdentifier>{2f24af2d-eba6-4be1-9ac4-fc58aeac6670}</UniqueIdentifier>
</Filter> </Filter>
<Filter Include="third-party\StackWalker">
<UniqueIdentifier>{1fec4835-63a1-4612-80b5-828dadf0ac63}</UniqueIdentifier>
</Filter>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="ui\Button.h"> <ClInclude Include="ui\Button.h">
@ -327,6 +330,9 @@
<ClInclude Include="imgui\imgui_impl.hpp"> <ClInclude Include="imgui\imgui_impl.hpp">
<Filter>imgui</Filter> <Filter>imgui</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="third-party\StackWalker\StackWalker.h">
<Filter>third-party\StackWalker</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="ui\Button.cpp"> <ClCompile Include="ui\Button.cpp">
@ -500,5 +506,8 @@
<ClCompile Include="imgui\imgui_impl_dx10.cpp"> <ClCompile Include="imgui\imgui_impl_dx10.cpp">
<Filter>imgui</Filter> <Filter>imgui</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="third-party\StackWalker\StackWalker.cpp">
<Filter>third-party\StackWalker</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -70,7 +70,6 @@ namespace kiwano
Logger::Logger() Logger::Logger()
: enabled_(true) : enabled_(true)
, has_console_(false)
, default_stdout_color_(0) , default_stdout_color_(0)
, default_stderr_color_(0) , default_stderr_color_(0)
, output_stream_(std::wcout.rdbuf()) , output_stream_(std::wcout.rdbuf())
@ -81,8 +80,8 @@ namespace kiwano
void Logger::ResetOutputStream() void Logger::ResetOutputStream()
{ {
has_console_ = ::GetConsoleWindow() != nullptr; bool has_console = ::GetConsoleWindow() != nullptr;
if (has_console_) if (has_console)
{ {
default_stdout_color_ = default_stderr_color_ = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY; default_stdout_color_ = default_stderr_color_ = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY;
@ -161,7 +160,7 @@ namespace kiwano
void Logger::Outputf(std::wostream& os, std::wostream& (*color)(std::wostream&), const wchar_t* prompt, const wchar_t* format, va_list args) const void Logger::Outputf(std::wostream& os, std::wostream& (*color)(std::wostream&), const wchar_t* prompt, const wchar_t* format, va_list args) const
{ {
if (enabled_ && has_console_) if (enabled_)
{ {
std::wstring output = MakeOutputStringf(prompt, format, args); std::wstring output = MakeOutputStringf(prompt, format, args);

View File

@ -115,7 +115,6 @@ namespace kiwano
private: private:
bool enabled_; bool enabled_;
bool has_console_;
WORD default_stdout_color_; WORD default_stdout_color_;
WORD default_stderr_color_; WORD default_stderr_color_;
@ -223,7 +222,7 @@ namespace kiwano
template <typename ..._Args> template <typename ..._Args>
void Logger::OutputLine(std::wostream& os, std::wostream& (*color)(std::wostream&), const wchar_t* prompt, _Args&& ... args) const void Logger::OutputLine(std::wostream& os, std::wostream& (*color)(std::wostream&), const wchar_t* prompt, _Args&& ... args) const
{ {
if (enabled_ && has_console_) if (enabled_)
{ {
Output(os, color, prompt, std::forward<_Args>(args)...); Output(os, color, prompt, std::forward<_Args>(args)...);
@ -235,7 +234,7 @@ namespace kiwano
template <typename ..._Args> template <typename ..._Args>
void Logger::Output(std::wostream& os, std::wostream& (*color)(std::wostream&), const wchar_t* prompt, _Args&& ... args) const void Logger::Output(std::wostream& os, std::wostream& (*color)(std::wostream&), const wchar_t* prompt, _Args&& ... args) const
{ {
if (enabled_ && has_console_) if (enabled_)
{ {
std::wstring output = MakeOutputString(prompt, std::forward<_Args>(args)...); std::wstring output = MakeOutputString(prompt, std::forward<_Args>(args)...);
@ -273,6 +272,12 @@ namespace kiwano
} }
} }
//
// Display stack trace on exception
//
#include "../third-party/StackWalker/StackWalker.h"
namespace kiwano namespace kiwano
{ {
inline void ThrowIfFailed(HRESULT hr) inline void ThrowIfFailed(HRESULT hr)
@ -281,6 +286,8 @@ namespace kiwano
{ {
KGE_ERROR_LOG(L"Fatal error with HRESULT of %08X", hr); KGE_ERROR_LOG(L"Fatal error with HRESULT of %08X", hr);
StackWalker{}.ShowCallstack();
static char buffer[1024 + 1]; static char buffer[1024 + 1];
sprintf_s(buffer, "Fatal error with HRESULT of %08X", hr); sprintf_s(buffer, "Fatal error with HRESULT of %08X", hr);
throw std::runtime_error(buffer); throw std::runtime_error(buffer);

View File

@ -45,45 +45,34 @@ namespace kiwano
{ {
KGE_LOG(L"Creating device resources"); KGE_LOG(L"Creating device resources");
HRESULT hr;
hwnd_ = app->GetWindow()->GetHandle(); hwnd_ = app->GetWindow()->GetHandle();
hr = hwnd_ ? S_OK : E_FAIL;
if (SUCCEEDED(hr)) ThrowIfFailed(hwnd_ ? S_OK : E_FAIL);
{
device_resources_ = nullptr; device_resources_ = nullptr;
hr = DeviceResources::Create( drawing_state_block_ = nullptr;
ThrowIfFailed(
DeviceResources::Create(
&device_resources_, &device_resources_,
hwnd_ hwnd_
); )
} );
if (SUCCEEDED(hr)) factory_ = device_resources_->GetD2DFactory();
{ device_context_ = device_resources_->GetD2DDeviceContext();
factory_ = device_resources_->GetD2DFactory();
device_context_ = device_resources_->GetD2DDeviceContext();
}
if (SUCCEEDED(hr)) ThrowIfFailed(
{ factory_->CreateDrawingStateBlock(
drawing_state_block_ = nullptr;
hr = factory_->CreateDrawingStateBlock(
&drawing_state_block_ &drawing_state_block_
); )
} );
if (SUCCEEDED(hr)) ThrowIfFailed(
{ CreateDeviceResources()
hr = CreateDeviceResources(); );
}
if (SUCCEEDED(hr)) output_size_ = app->GetWindow()->GetSize();
{
output_size_ = app->GetWindow()->GetSize();
}
ThrowIfFailed(hr);
} }
void Renderer::DestroyComponent() void Renderer::DestroyComponent()

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,189 @@
/**********************************************************************
*
* StackWalker.h
*
*
* History:
* 2005-07-27 v1 - First public release on http://www.codeproject.com/
* (for additional changes see History in 'StackWalker.cpp'!
*
**********************************************************************/
// #pragma once is supported starting with _MCS_VER 1000,
// so we need not to check the version (because we only support _MSC_VER >= 1100)!
#pragma once
#include <windows.h>
// special defines for VC5/6 (if no actual PSDK is installed):
#if _MSC_VER < 1300
typedef unsigned __int64 DWORD64, *PDWORD64;
#if defined(_WIN64)
typedef unsigned __int64 SIZE_T, *PSIZE_T;
#else
typedef unsigned long SIZE_T, *PSIZE_T;
#endif
#endif // _MSC_VER < 1300
class StackWalkerInternal; // forward
class StackWalker
{
public:
typedef enum StackWalkOptions
{
// No addition info will be retrived
// (only the address is available)
RetrieveNone = 0,
// Try to get the symbol-name
RetrieveSymbol = 1,
// Try to get the line for this symbol
RetrieveLine = 2,
// Try to retrieve the module-infos
RetrieveModuleInfo = 4,
// Also retrieve the version for the DLL/EXE
RetrieveFileVersion = 8,
// Contains all the abouve
RetrieveVerbose = 0xF,
// Generate a "good" symbol-search-path
SymBuildPath = 0x10,
// Also use the public Microsoft-Symbol-Server
SymUseSymSrv = 0x20,
// Contains all the abouve "Sym"-options
SymAll = 0x30,
// Contains all options (default)
OptionsAll = 0x3F
} StackWalkOptions;
StackWalker(
int options = OptionsAll, // 'int' is by design, to combine the enum-flags
LPCSTR szSymPath = NULL,
DWORD dwProcessId = GetCurrentProcessId(),
HANDLE hProcess = GetCurrentProcess()
);
StackWalker(DWORD dwProcessId, HANDLE hProcess);
virtual ~StackWalker();
typedef BOOL (__stdcall *PReadProcessMemoryRoutine)(
HANDLE hProcess,
DWORD64 qwBaseAddress,
PVOID lpBuffer,
DWORD nSize,
LPDWORD lpNumberOfBytesRead,
LPVOID pUserData // optional data, which was passed in "ShowCallstack"
);
BOOL LoadModules();
BOOL ShowCallstack(
HANDLE hThread = GetCurrentThread(),
const CONTEXT *context = NULL,
PReadProcessMemoryRoutine readMemoryFunction = NULL,
LPVOID pUserData = NULL // optional to identify some data in the 'readMemoryFunction'-callback
);
#if _MSC_VER >= 1300
// due to some reasons, the "STACKWALK_MAX_NAMELEN" must be declared as "public"
// in older compilers in order to use it... starting with VC7 we can declare it as "protected"
protected:
#endif
enum { STACKWALK_MAX_NAMELEN = 1024 }; // max name length for found symbols
protected:
// Entry for each Callstack-Entry
typedef struct CallstackEntry
{
DWORD64 offset; // if 0, we have no valid entry
CHAR name[STACKWALK_MAX_NAMELEN];
CHAR undName[STACKWALK_MAX_NAMELEN];
CHAR undFullName[STACKWALK_MAX_NAMELEN];
DWORD64 offsetFromSmybol;
DWORD offsetFromLine;
DWORD lineNumber;
CHAR lineFileName[STACKWALK_MAX_NAMELEN];
DWORD symType;
LPCSTR symTypeString;
CHAR moduleName[STACKWALK_MAX_NAMELEN];
DWORD64 baseOfImage;
CHAR loadedImageName[STACKWALK_MAX_NAMELEN];
} CallstackEntry;
enum CallstackEntryType {firstEntry, nextEntry, lastEntry};
virtual void OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName);
virtual void OnLoadModule(LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size, DWORD result, LPCSTR symType, LPCSTR pdbName, ULONGLONG fileVersion);
virtual void OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry);
virtual void OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr);
virtual void OnOutput(LPCSTR szText);
StackWalkerInternal *m_sw;
HANDLE m_hProcess;
DWORD m_dwProcessId;
BOOL m_modulesLoaded;
LPSTR m_szSymPath;
int m_options;
static BOOL __stdcall myReadProcMem(HANDLE hProcess, DWORD64 qwBaseAddress, PVOID lpBuffer, DWORD nSize, LPDWORD lpNumberOfBytesRead);
friend StackWalkerInternal;
};
// The "ugly" assembler-implementation is needed for systems before XP
// If you have a new PSDK and you only compile for XP and later, then you can use
// the "RtlCaptureContext"
// Currently there is no define which determines the PSDK-Version...
// So we just use the compiler-version (and assumes that the PSDK is
// the one which was installed by the VS-IDE)
// INFO: If you want, you can use the RtlCaptureContext if you only target XP and later...
// But I currently use it in x64/IA64 environments...
//#if defined(_M_IX86) && (_WIN32_WINNT <= 0x0500) && (_MSC_VER < 1400)
#if defined(_M_IX86)
#ifdef CURRENT_THREAD_VIA_EXCEPTION
// TODO: The following is not a "good" implementation,
// because the callstack is only valid in the "__except" block...
#define GET_CURRENT_CONTEXT(c, contextFlags) \
do { \
memset(&c, 0, sizeof(CONTEXT)); \
EXCEPTION_POINTERS *pExp = NULL; \
__try { \
throw 0; \
} __except( ( (pExp = GetExceptionInformation()) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_EXECUTE_HANDLER)) {} \
if (pExp != NULL) \
memcpy(&c, pExp->ContextRecord, sizeof(CONTEXT)); \
c.ContextFlags = contextFlags; \
} while(0);
#else
// The following should be enough for walking the callstack...
#define GET_CURRENT_CONTEXT(c, contextFlags) \
do { \
memset(&c, 0, sizeof(CONTEXT)); \
c.ContextFlags = contextFlags; \
__asm call x \
__asm x: pop eax \
__asm mov c.Eip, eax \
__asm mov c.Ebp, ebp \
__asm mov c.Esp, esp \
} while(0);
#endif
#else
// The following is defined for x86 (XP and higher), x64 and IA64:
#define GET_CURRENT_CONTEXT(c, contextFlags) \
do { \
memset(&c, 0, sizeof(CONTEXT)); \
c.ContextFlags = contextFlags; \
RtlCaptureContext(&c); \
} while(0);
#endif