1717 * along with OpenXcom. If not, see <http://www.gnu.org/licenses/>.
1818 */
1919#include " CrossPlatform.h"
20+ #include < exception>
2021#include < algorithm>
2122#include < sstream>
2223#include < fstream>
3738#include < shlobj.h>
3839#include < shlwapi.h>
3940#include < direct.h>
41+ #include < DbgHelp.h>
4042#ifndef SHGFP_TYPE_CURRENT
4143#define SHGFP_TYPE_CURRENT 0
4244#endif
45+ #define MAX_STACK_FRAMES 100
46+ #define EXCEPTION_CODE_CXX 0xe06d7363
4347#ifndef __GNUC__
4448#pragma comment(lib, "advapi32.lib")
4549#pragma comment(lib, "shell32.lib")
4650#pragma comment(lib, "shlwapi.lib")
51+ #pragma comment(lib, "DbgHelp.lib")
4752#endif
4853#else
4954#include " Language.h"
5661#include < sys/param.h>
5762#include < sys/types.h>
5863#include < pwd.h>
64+ #ifdef BACKTRACE_LIBUNWIND
65+ #ifndef _GNU_SOURCE
66+ #define _GNU_SOURCE
5967#endif
68+ #include < dlfcn.h>
69+ #define UNW_LOCAL_ONLY
70+ #include < libunwind.h>
71+ #endif
72+ #endif
73+ #define MAX_SYMBOL_LENGTH 1024
6074#include < SDL.h>
6175#include < SDL_syswm.h>
6276
@@ -810,6 +824,13 @@ std::string getDosPath()
810824#endif
811825}
812826
827+ /* *
828+ * Sets the window titlebar icon.
829+ * For Windows, use the embedded resource icon.
830+ * For other systems, use a PNG icon.
831+ * @param winResource ID for Windows icon.
832+ * @param unixPath Path to PNG icon for Unix.
833+ */
813834void setWindowIcon (int winResource, const std::string &unixPath)
814835{
815836#ifdef _WIN32
@@ -836,5 +857,232 @@ void setWindowIcon(int winResource, const std::string &unixPath)
836857#endif
837858}
838859
860+ /* *
861+ * Logs the stack back trace leading up to this function call.
862+ */
863+ void stackTrace ()
864+ {
865+ #ifdef _WIN32
866+ static bool initialised = false ;
867+ static HANDLE process;
868+
869+ unsigned int frames;
870+ void *ip[MAX_STACK_FRAMES ];
871+ SYMBOL_INFO *sym;
872+
873+ if (!initialised)
874+ {
875+ process = GetCurrentProcess ();
876+ SymInitialize (process, NULL , true );
877+ initialised = true ;
878+ }
879+
880+ if (!process)
881+ {
882+ Log (LOG_FATAL ) << " Failed to get process for backtrace" ;
883+ return ;
884+ }
885+
886+ sym = (SYMBOL_INFO *)calloc (sizeof (SYMBOL_INFO ) + MAX_SYMBOL_LENGTH * (sizeof (char )), 1 );
887+ if (!sym)
888+ {
889+ Log (LOG_FATAL ) << " Failed to allocate symbol info for backtrace" ;
890+ return ;
891+ }
892+ sym->MaxNameLen = MAX_SYMBOL_LENGTH ;
893+ sym->SizeOfStruct = sizeof (SYMBOL_INFO );
894+
895+ /* Skip 1 frame to skip this function */
896+ frames = CaptureStackBackTrace (1 , MAX_STACK_FRAMES , ip, NULL );
897+
898+ char buf[MAX_SYMBOL_LENGTH ];
899+ for (unsigned int frame = 0 ; frame < frames; ++frame)
900+ {
901+ SymFromAddr (process, (DWORD64 )(ip[frame]), 0 , sym);
902+ sprintf (buf, " - 0x%p %s+0x%lx" , ip[frame], sym->Name , (uintptr_t )ip[frame] - (uintptr_t )sym->Address );
903+ Log (LOG_FATAL ) << buf;
904+ }
905+
906+ free (sym);
907+ #elif defined(BACKTRACE_LIBUNWIND)
908+ unw_cursor_t cursor;
909+ unw_context_t ctx;
910+ unw_word_t ip;
911+ unw_getcontext (&ctx);
912+ unw_init_local (&cursor, &ctx);
913+
914+ Log (LOG_FATAL ) << " called by:" ;
915+ char buf[MAX_SYMBOL_LENGTH ];
916+ while (unw_step (&cursor) > 0 )
917+ {
918+ Dl_info info;
919+ unw_get_reg (&cursor, UNW_REG_IP , &ip);
920+ if (ip == 0 )
921+ break ;
922+ dladdr (reinterpret_cast <void *>(ip), &info);
923+ if (info.dli_sname )
924+ {
925+ sprintf (buf, " - 0x%lx %s+0x%lx (%s)" , static_cast <uintptr_t >(ip), info.dli_sname , static_cast <uintptr_t >(ip) - reinterpret_cast <uintptr_t >(info.dli_saddr ), info.dli_fname );
926+ Log (LOG_FATAL ) << buf;
927+ continue ;
928+ }
929+ // If dladdr() failed, try libunwind
930+ unw_word_t offsetInFn;
931+ char fnName[MAX_SYMBOL_LENGTH ];
932+ if (!unw_get_proc_name (&cursor, fnName, MAX_SYMBOL_LENGTH , &offsetInFn))
933+ {
934+ sprintf (buf, " - 0x%lx %s+0x%lx (%s)" , static_cast <uintptr_t >(ip), fnName, offsetInFn, info.dli_fname );
935+ Log (LOG_FATAL ) << buf;
936+ }
937+ else
938+ {
939+ sprintf (buf, " - 0x%lx" , static_cast <uintptr_t >(ip));
940+ Log (LOG_FATAL ) << buf;
941+ }
942+ }
943+ #else
944+ // TODO: Other platform backtrace?
945+ #endif
946+ }
947+
948+ std::string timestamp ()
949+ {
950+ const int MAX_LEN = 25 , MAX_RESULT = 80 ;
951+ #ifdef _WIN32
952+ char date[MAX_LEN ], time[MAX_LEN ];
953+ if (GetDateFormatA (LOCALE_INVARIANT , 0 , 0 ,
954+ " dd'-'MM'-'yyyy" , date, MAX_LEN ) == 0 )
955+ return " Error in Now()" ;
956+ if (GetTimeFormatA (LOCALE_INVARIANT , TIME_FORCE24HOURFORMAT , 0 ,
957+ " HH'-'mm'-'ss" , time, MAX_LEN ) == 0 )
958+ return " Error in Now()" ;
959+
960+ char result[MAX_RESULT ] = { 0 };
961+ sprintf (result, " %s_%s" , date, time);
962+ #else
963+ char buffer[MAX_LEN ];
964+ time_t rawtime;
965+ struct tm *timeinfo;
966+ time (&rawtime);
967+ timeinfo = localtime (&rawtime);
968+ strftime (buffer, MAX_LEN , " %d-%m-%Y_%H-%M-%S" , timeinfo);
969+ char result[MAX_RESULT ] = { 0 };
970+ sprintf (result, " %s" , buffer);
971+ #endif
972+ return result;
973+ }
974+
975+ /* *
976+ * Logs the stack back trace and crash dump leading up to this fatal exception.
977+ * @note Based on http://stackoverflow.com/questions/22487887/why-doesnt-stack-walking-work-properly-when-using-setunhandledexceptionfilter
978+ * @return Error code.
979+ */
980+ std::string crashDump (void *info)
981+ {
982+ std::ostringstream error;
983+ #ifdef _WIN32
984+ PEXCEPTION_POINTERS exception = (PEXCEPTION_POINTERS )info;
985+ if (exception->ExceptionRecord ->ExceptionCode == EXCEPTION_CODE_CXX )
986+ {
987+ std::exception *ex = (std::exception *)exception->ExceptionRecord ->ExceptionInformation [1 ];
988+ error << ex->what ();
989+ }
990+ else
991+ {
992+ error << " 0x" << std::hex << exception->ExceptionRecord ->ExceptionCode ;
993+ }
994+ CONTEXT context = *(exception->ContextRecord );
995+ HANDLE thread = GetCurrentThread ();
996+ HANDLE process = GetCurrentProcess ();
997+ STACKFRAME64 frame;
998+ memset (&frame, 0 , sizeof (STACKFRAME64 ));
999+ DWORD image;
1000+ #ifdef _M_IX86
1001+ image = IMAGE_FILE_MACHINE_I386 ;
1002+ frame.AddrPC .Offset = context.Eip ;
1003+ frame.AddrPC .Mode = AddrModeFlat;
1004+ frame.AddrFrame .Offset = context.Ebp ;
1005+ frame.AddrFrame .Mode = AddrModeFlat;
1006+ frame.AddrStack .Offset = context.Esp ;
1007+ frame.AddrStack .Mode = AddrModeFlat;
1008+ #elif _M_X64
1009+ image = IMAGE_FILE_MACHINE_AMD64 ;
1010+ frame.AddrPC .Offset = context.Rip ;
1011+ frame.AddrPC .Mode = AddrModeFlat;
1012+ frame.AddrFrame .Offset = context.Rbp ;
1013+ frame.AddrFrame .Mode = AddrModeFlat;
1014+ frame.AddrStack .Offset = context.Rsp ;
1015+ frame.AddrStack .Mode = AddrModeFlat;
1016+ #elif _M_IA64
1017+ image = IMAGE_FILE_MACHINE_IA64 ;
1018+ frame.AddrPC .Offset = context.StIIP ;
1019+ frame.AddrPC .Mode = AddrModeFlat;
1020+ frame.AddrFrame .Offset = context.IntSp ;
1021+ frame.AddrFrame .Mode = AddrModeFlat;
1022+ frame.AddrBStore .Offset = context.RsBSP ;
1023+ frame.AddrBStore .Mode = AddrModeFlat;
1024+ frame.AddrStack .Offset = context.IntSp ;
1025+ frame.AddrStack .Mode = AddrModeFlat;
1026+ #else
1027+ #error This platform is not supported.
1028+ #endif
1029+ SYMBOL_INFO *symbol = (SYMBOL_INFO *)malloc (sizeof (SYMBOL_INFO ) + (MAX_SYMBOL_LENGTH - 1 ) * sizeof (TCHAR ));
1030+ symbol->MaxNameLen = MAX_SYMBOL_LENGTH ;
1031+ symbol->SizeOfStruct = sizeof (SYMBOL_INFO );
1032+ IMAGEHLP_LINE64 *line = (IMAGEHLP_LINE64 *)malloc (sizeof (IMAGEHLP_LINE64 ));
1033+ line->SizeOfStruct = sizeof (IMAGEHLP_LINE64 );
1034+ DWORD displacement;
1035+ Log (LOG_FATAL ) << " A fatal error has occurred: " << error.str ();
1036+ SymInitialize (process, NULL , TRUE );
1037+ while (StackWalk64 (image, process, thread, &frame, &context, NULL , NULL , NULL , NULL ))
1038+ {
1039+ if (SymFromAddr (process, frame.AddrPC .Offset , NULL , symbol))
1040+ {
1041+ if (SymGetLineFromAddr64 (process, frame.AddrPC .Offset , &displacement, line))
1042+ {
1043+ std::string filename = line->FileName ;
1044+ size_t n = filename.find_last_of (PATH_SEPARATOR );
1045+ if (n != std::string::npos)
1046+ {
1047+ filename = filename.substr (n+1 );
1048+ }
1049+ Log (LOG_FATAL ) << " 0x" << std::hex << symbol->Address << std::dec << " " << symbol->Name << " (" << filename << " :" << line->LineNumber << " )" ;
1050+ }
1051+ else
1052+ {
1053+ Log (LOG_FATAL ) << " 0x" << std::hex << symbol->Address << std::dec << " " << symbol->Name << " (SymGetLineFromAddr64 failed: " << GetLastError () << " )" ;
1054+ }
1055+ }
1056+ else
1057+ {
1058+ Log (LOG_FATAL ) << " SymFromAddr failed: " << GetLastError ();
1059+ }
1060+ }
1061+ DWORD err = GetLastError ();
1062+ if (err)
1063+ {
1064+ Log (LOG_FATAL ) << " StackWalk64 failed: " << err;
1065+ }
1066+ std::string dumpName = Options::getUserFolder ();
1067+ dumpName += timestamp () + " .dmp" ;
1068+ HANDLE dumpFile = CreateFileA (dumpName.c_str (), GENERIC_READ | GENERIC_WRITE , 0 , NULL , CREATE_ALWAYS , FILE_ATTRIBUTE_NORMAL , NULL );
1069+ MINIDUMP_EXCEPTION_INFORMATION exceptionInformation;
1070+ exceptionInformation.ThreadId = GetCurrentThreadId ();
1071+ exceptionInformation.ExceptionPointers = exception;
1072+ exceptionInformation.ClientPointers = FALSE ;
1073+ if (MiniDumpWriteDump (process, GetProcessId (process), dumpFile, MiniDumpNormal, exception ? &exceptionInformation : NULL , NULL , NULL ))
1074+ {
1075+ Log (LOG_FATAL ) << " Crash dump generated at " << dumpName;
1076+ }
1077+ else
1078+ {
1079+ Log (LOG_FATAL ) << " MiniDumpWriteDump failed: " << GetLastError ();
1080+ }
1081+ #else
1082+ // TODO: Other platform backtrace?
1083+ #endif
1084+ return error.str ();
1085+ }
1086+
8391087}
8401088}
0 commit comments