2022-08-22 22:21:23 +02:00
# include "Common/precompiled.h"
# include "Cafe/CafeSystem.h"
2023-04-06 06:08:14 +02:00
# include "ExceptionHandler.h"
2022-08-22 22:21:23 +02:00
# include <Windows.h>
# include <Dbghelp.h>
# include <shellapi.h>
2023-04-06 06:08:14 +02:00
# include "config/ActiveSettings.h"
# include "config/CemuConfig.h"
2022-08-22 22:21:23 +02:00
# include "Cafe/OS/libs/coreinit/coreinit_Thread.h"
# include "Cafe/HW/Espresso/PPCState.h"
2023-02-19 15:41:49 +01:00
# include "Cafe/HW/Espresso/Debugger/GDBStub.h"
2022-08-22 22:21:23 +02:00
LONG handleException_SINGLE_STEP ( PEXCEPTION_POINTERS pExceptionInfo )
{
return EXCEPTION_CONTINUE_SEARCH ;
}
# include <boost/algorithm/string.hpp>
BOOL CALLBACK MyMiniDumpCallback ( PVOID pParam , const PMINIDUMP_CALLBACK_INPUT pInput , PMINIDUMP_CALLBACK_OUTPUT pOutput )
{
if ( ! pInput | | ! pOutput )
return FALSE ;
switch ( pInput - > CallbackType )
{
case IncludeModuleCallback :
case IncludeThreadCallback :
case ThreadCallback :
case ThreadExCallback :
return TRUE ;
case ModuleCallback :
if ( ! ( pOutput - > ModuleWriteFlags & ModuleReferencedByMemory ) )
pOutput - > ModuleWriteFlags & = ~ ModuleWriteModule ;
return TRUE ;
}
return FALSE ;
}
bool CreateMiniDump ( CrashDump dump , EXCEPTION_POINTERS * pep )
{
if ( dump = = CrashDump : : Disabled )
return true ;
2022-10-11 23:03:26 -07:00
fs : : path p = ActiveSettings : : GetUserDataPath ( " crashdump " ) ;
2022-08-22 22:21:23 +02:00
std : : error_code ec ;
fs : : create_directories ( p , ec ) ;
if ( ec )
return false ;
const auto now = std : : chrono : : system_clock : : now ( ) ;
const auto temp_time = std : : chrono : : system_clock : : to_time_t ( now ) ;
const auto & time = * std : : gmtime ( & temp_time ) ;
p / = fmt : : format ( " crash_{:04d}{:02d}{:02d}_{:02d}{:02d}{:02d}.dmp " , 1900 + time . tm_year , time . tm_mon + 1 , time . tm_mday , time . tm_year , time . tm_hour , time . tm_min , time . tm_sec ) ;
const auto hFile = CreateFileW ( p . wstring ( ) . c_str ( ) , GENERIC_READ | GENERIC_WRITE , 0 , nullptr , CREATE_ALWAYS , FILE_ATTRIBUTE_NORMAL , nullptr ) ;
if ( hFile = = INVALID_HANDLE_VALUE )
return false ;
MINIDUMP_EXCEPTION_INFORMATION mdei ;
mdei . ThreadId = GetCurrentThreadId ( ) ;
mdei . ExceptionPointers = pep ;
mdei . ClientPointers = FALSE ;
MINIDUMP_CALLBACK_INFORMATION mci ;
mci . CallbackRoutine = ( MINIDUMP_CALLBACK_ROUTINE ) MyMiniDumpCallback ;
mci . CallbackParam = nullptr ;
MINIDUMP_TYPE mdt ;
if ( dump = = CrashDump : : Full )
{
mdt = ( MINIDUMP_TYPE ) ( MiniDumpWithPrivateReadWriteMemory |
MiniDumpWithDataSegs |
MiniDumpWithHandleData |
MiniDumpWithFullMemoryInfo |
MiniDumpWithThreadInfo |
MiniDumpWithUnloadedModules ) ;
}
else
{
mdt = ( MINIDUMP_TYPE ) ( MiniDumpWithIndirectlyReferencedMemory | MiniDumpScanMemory ) ;
}
const auto result = MiniDumpWriteDump ( GetCurrentProcess ( ) , GetCurrentProcessId ( ) , hFile , mdt , & mdei , nullptr , & mci ) ;
CloseHandle ( hFile ) ;
return result ! = FALSE ;
}
void DumpThreadStackTrace ( )
{
HANDLE process = GetCurrentProcess ( ) ;
SymInitialize ( process , NULL , TRUE ) ;
char dumpLine [ 1024 * 4 ] ;
void * stack [ 100 ] ;
const unsigned short frames = CaptureStackBackTrace ( 0 , 40 , stack , NULL ) ;
SYMBOL_INFO * symbol = ( SYMBOL_INFO * ) calloc ( sizeof ( SYMBOL_INFO ) + 256 * sizeof ( char ) , 1 ) ;
symbol - > MaxNameLen = 255 ;
symbol - > SizeOfStruct = sizeof ( SYMBOL_INFO ) ;
2023-04-06 06:08:14 +02:00
CrashLog_WriteHeader ( " Stack trace " ) ;
2022-08-22 22:21:23 +02:00
for ( unsigned int i = 0 ; i < frames ; i + + )
{
DWORD64 stackTraceOffset = ( DWORD64 ) stack [ i ] ;
SymFromAddr ( process , stackTraceOffset , 0 , symbol ) ;
sprintf ( dumpLine , " 0x%016I64x " , ( uint64 ) ( size_t ) stack [ i ] ) ;
cemuLog_writePlainToLog ( dumpLine ) ;
// module name
HMODULE stackModule ;
if ( GetModuleHandleExA ( GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS , ( LPCSTR ) stackTraceOffset , & stackModule ) )
{
char moduleName [ 512 ] ;
moduleName [ 0 ] = ' \0 ' ;
GetModuleFileNameA ( stackModule , moduleName , 512 ) ;
sint32 moduleNameStartIndex = std : : max ( ( sint32 ) 0 , ( sint32 ) strlen ( moduleName ) - 1 ) ;
while ( moduleNameStartIndex > 0 )
{
if ( moduleName [ moduleNameStartIndex ] = = ' \\ ' | | moduleName [ moduleNameStartIndex ] = = ' / ' )
{
moduleNameStartIndex + + ;
break ;
}
moduleNameStartIndex - - ;
}
DWORD64 moduleAddress = ( DWORD64 ) GetModuleHandleA ( moduleName ) ;
sint32 relativeOffset = 0 ;
if ( moduleAddress ! = 0 )
relativeOffset = stackTraceOffset - moduleAddress ;
sprintf ( dumpLine , " +0x%08x %-16s " , relativeOffset , moduleName + moduleNameStartIndex ) ;
cemuLog_writePlainToLog ( dumpLine ) ;
}
else
{
sprintf ( dumpLine , " +0x00000000 %-16s " , " NULL " ) ;
cemuLog_writePlainToLog ( dumpLine ) ;
}
// function name
sprintf ( dumpLine , " %s \n " , symbol - > Name ) ;
cemuLog_writePlainToLog ( dumpLine ) ;
}
free ( symbol ) ;
}
void createCrashlog ( EXCEPTION_POINTERS * e , PCONTEXT context )
{
2023-04-06 06:08:14 +02:00
if ( ! CrashLog_Create ( ) )
return ; // give up if crashlog was already created
2022-08-22 22:21:23 +02:00
const auto crash_dump = GetConfig ( ) . crash_dump . GetValue ( ) ;
const auto dump_written = CreateMiniDump ( crash_dump , e ) ;
if ( ! dump_written )
cemuLog_writeLineToLog ( fmt : : format ( " couldn't write minidump {:#x} " , GetLastError ( ) ) , false , true ) ;
char dumpLine [ 1024 * 4 ] ;
// info about Cemu version
2022-08-31 12:04:09 +02:00
sprintf ( dumpLine , " \n Crashlog for %s \n " , BUILD_VERSION_WITH_NAME_STRING ) ;
2022-08-22 22:21:23 +02:00
cemuLog_writePlainToLog ( dumpLine ) ;
SYSTEMTIME sysTime ;
GetSystemTime ( & sysTime ) ;
sprintf ( dumpLine , " Date: %02d-%02d-%04d %02d:%02d:%02d \n \n " , ( sint32 ) sysTime . wDay , ( sint32 ) sysTime . wMonth , ( sint32 ) sysTime . wYear , ( sint32 ) sysTime . wHour , ( sint32 ) sysTime . wMinute , ( sint32 ) sysTime . wSecond ) ;
cemuLog_writePlainToLog ( dumpLine ) ;
DumpThreadStackTrace ( ) ;
// info about exception
if ( e - > ExceptionRecord )
{
HMODULE exceptionModule ;
if ( GetModuleHandleExA ( GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS , ( LPCSTR ) ( e - > ExceptionRecord - > ExceptionAddress ) , & exceptionModule ) )
{
char moduleName [ 512 ] ;
moduleName [ 0 ] = ' \0 ' ;
GetModuleFileNameA ( exceptionModule , moduleName , 512 ) ;
sint32 moduleNameStartIndex = std : : max ( ( sint32 ) 0 , ( sint32 ) strlen ( moduleName ) - 1 ) ;
while ( moduleNameStartIndex > 0 )
{
if ( moduleName [ moduleNameStartIndex ] = = ' \\ ' | | moduleName [ moduleNameStartIndex ] = = ' / ' )
{
moduleNameStartIndex + + ;
break ;
}
moduleNameStartIndex - - ;
}
sprintf ( dumpLine , " Exception 0x%08x at 0x%I64x(+0x%I64x) in module %s \n " , ( uint32 ) e - > ExceptionRecord - > ExceptionCode , ( uint64 ) e - > ExceptionRecord - > ExceptionAddress , ( uint64 ) e - > ExceptionRecord - > ExceptionAddress - ( uint64 ) exceptionModule , moduleName + moduleNameStartIndex ) ;
cemuLog_writePlainToLog ( dumpLine ) ;
}
else
{
sprintf ( dumpLine , " Exception 0x%08x at 0x%I64x \n " , ( uint32 ) e - > ExceptionRecord - > ExceptionCode , ( uint64 ) e - > ExceptionRecord - > ExceptionAddress ) ;
cemuLog_writePlainToLog ( dumpLine ) ;
}
}
sprintf ( dumpLine , " cemu.exe at 0x%I64x \n " , ( uint64 ) GetModuleHandle ( NULL ) ) ;
cemuLog_writePlainToLog ( dumpLine ) ;
// register info
sprintf ( dumpLine , " \n " ) ;
cemuLog_writePlainToLog ( dumpLine ) ;
sprintf ( dumpLine , " RAX=%016I64x RBX=%016I64x RCX=%016I64x RDX=%016I64x \n " , context - > Rax , context - > Rbx , context - > Rcx , context - > Rdx ) ;
cemuLog_writePlainToLog ( dumpLine ) ;
sprintf ( dumpLine , " RSP=%016I64x RBP=%016I64x RDI=%016I64x RSI=%016I64x \n " , context - > Rsp , context - > Rbp , context - > Rdi , context - > Rsi ) ;
cemuLog_writePlainToLog ( dumpLine ) ;
sprintf ( dumpLine , " R8 =%016I64x R9 =%016I64x R10=%016I64x R11=%016I64x \n " , context - > R8 , context - > R9 , context - > R10 , context - > R11 ) ;
cemuLog_writePlainToLog ( dumpLine ) ;
sprintf ( dumpLine , " R12=%016I64x R13=%016I64x R14=%016I64x R15=%016I64x \n " , context - > R12 , context - > R13 , context - > R14 , context - > R15 ) ;
cemuLog_writePlainToLog ( dumpLine ) ;
2023-04-06 06:08:14 +02:00
CrashLog_SetOutputChannels ( false , true ) ;
ExceptionHandler_LogGeneralInfo ( ) ;
CrashLog_SetOutputChannels ( true , true ) ;
2022-08-22 22:21:23 +02:00
cemuLog_waitForFlush ( ) ;
// save log with the dump
if ( dump_written & & crash_dump ! = CrashDump : : Disabled )
{
const auto now = std : : chrono : : system_clock : : now ( ) ;
const auto temp_time = std : : chrono : : system_clock : : to_time_t ( now ) ;
const auto & time = * std : : gmtime ( & temp_time ) ;
2022-10-11 23:03:26 -07:00
fs : : path p = ActiveSettings : : GetUserDataPath ( " crashdump " ) ;
2022-08-22 22:21:23 +02:00
p / = fmt : : format ( " log_{:04d}{:02d}{:02d}_{:02d}{:02d}{:02d}.txt " , 1900 + time . tm_year , time . tm_mon + 1 , time . tm_mday , time . tm_year , time . tm_hour , time . tm_min , time . tm_sec ) ;
std : : error_code ec ;
2022-10-11 23:03:26 -07:00
fs : : copy_file ( ActiveSettings : : GetUserDataPath ( " log.txt " ) , p , ec ) ;
2022-08-22 22:21:23 +02:00
}
2022-08-26 19:41:42 +02:00
exit ( 0 ) ;
2022-08-22 22:21:23 +02:00
return ;
}
bool logCrashlog ;
int crashlogThread ( void * exceptionInfoRawPtr )
{
PEXCEPTION_POINTERS pExceptionInfo = ( PEXCEPTION_POINTERS ) exceptionInfoRawPtr ;
createCrashlog ( pExceptionInfo , pExceptionInfo - > ContextRecord ) ;
logCrashlog = true ;
return 0 ;
}
2023-02-19 15:41:49 +01:00
void debugger_handleSingleStepException ( uint64 dr6 ) ;
2022-08-22 22:21:23 +02:00
LONG WINAPI VectoredExceptionHandler ( PEXCEPTION_POINTERS pExceptionInfo )
{
if ( pExceptionInfo - > ExceptionRecord - > ExceptionCode = = EXCEPTION_SINGLE_STEP )
{
LONG r = handleException_SINGLE_STEP ( pExceptionInfo ) ;
if ( r ! = EXCEPTION_CONTINUE_SEARCH )
return r ;
2023-02-19 15:41:49 +01:00
if ( GetBits ( pExceptionInfo - > ContextRecord - > Dr6 , 0 , 1 ) | | GetBits ( pExceptionInfo - > ContextRecord - > Dr6 , 1 , 1 ) )
debugger_handleSingleStepException ( pExceptionInfo - > ContextRecord - > Dr6 ) ;
else if ( GetBits ( pExceptionInfo - > ContextRecord - > Dr6 , 2 , 1 ) | | GetBits ( pExceptionInfo - > ContextRecord - > Dr6 , 3 , 1 ) )
g_gdbstub - > HandleAccessException ( pExceptionInfo - > ContextRecord - > Dr6 ) ;
2022-08-22 22:21:23 +02:00
return EXCEPTION_CONTINUE_EXECUTION ;
}
return EXCEPTION_CONTINUE_SEARCH ;
}
LONG WINAPI cemu_unhandledExceptionFilter ( EXCEPTION_POINTERS * pExceptionInfo )
{
createCrashlog ( pExceptionInfo , pExceptionInfo - > ContextRecord ) ;
return EXCEPTION_NONCONTINUABLE_EXCEPTION ;
}
2023-04-06 06:08:14 +02:00
void ExceptionHandler_Init ( )
2022-08-22 22:21:23 +02:00
{
SetUnhandledExceptionFilter ( cemu_unhandledExceptionFilter ) ;
AddVectoredExceptionHandler ( 1 , VectoredExceptionHandler ) ;
SetErrorMode ( SEM_FAILCRITICALERRORS ) ;
}