ExceptionHandler.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814
  1. // ExceptionHandler.cpp Version 1.4
  2. //
  3. // Copyright © 1998 Bruce Dawson
  4. //
  5. // This source file contains the exception handler for recording error
  6. // information after crashes. See ExceptionHandler.h for information
  7. // on how to hook it in.
  8. //
  9. // Author: Bruce Dawson
  10. // [email protected]
  11. //
  12. // Modified by: Hans Dietrich
  13. // [email protected]
  14. //
  15. // Version 1.4: - Added invocation of XCrashReport.exe
  16. //
  17. // Version 1.3: - Added minidump output
  18. //
  19. // Version 1.1: - reformatted output for XP-like error report
  20. // - added ascii output to stack dump
  21. //
  22. // A paper by the original author can be found at:
  23. // http://www.cygnus-software.com/papers/release_debugging.html
  24. //
  25. ///////////////////////////////////////////////////////////////////////////////
  26. // Disable warnings generated by the Windows header files.
  27. #pragma warning(disable : 4514)
  28. #pragma warning(disable : 4201)
  29. #define _WIN32_WINDOWS 0x0500 // for IsDebuggerPresent
  30. #include "windows.h"
  31. #include <tchar.h>
  32. #include "GetWinVer.h"
  33. #include "miniversion.h"
  34. #include "../nu/ns_wc.h"
  35. #include "minidump.h"
  36. #include ".\settings.h"
  37. #include "api__gen_crasher.h"
  38. extern char *winampVersion;
  39. extern Settings settings;
  40. #ifndef _countof
  41. #define _countof(array) (sizeof(array)/sizeof(array[0]))
  42. #endif
  43. const int NumCodeBytes = 16; // Number of code bytes to record.
  44. const int MaxStackDump = 3072; // Maximum number of DWORDS in stack dumps.
  45. const int StackColumns = 4; // Number of columns in stack dump.
  46. #define ONEK 1024
  47. #define SIXTYFOURK (64*ONEK)
  48. #define ONEM (ONEK*ONEK)
  49. #define ONEG (ONEK*ONEK*ONEK)
  50. ///////////////////////////////////////////////////////////////////////////////
  51. // lstrrchr (avoid the C Runtime )
  52. static TCHAR * lstrrchr(LPCTSTR string, int ch)
  53. {
  54. TCHAR *start = (TCHAR *)string;
  55. while (string && *string++) /* find end of string */
  56. ;
  57. /* search towards front */
  58. while (--string != start && *string != (TCHAR) ch)
  59. ;
  60. if (*string == (TCHAR) ch) /* char found ? */
  61. return (TCHAR *)string;
  62. return NULL;
  63. }
  64. ///////////////////////////////////////////////////////////////////////////////
  65. // hprintf behaves similarly to printf, with a few vital differences.
  66. // It uses wvsprintf to do the formatting, which is a system routine,
  67. // thus avoiding C run time interactions. For similar reasons it
  68. // uses WriteFile rather than fwrite.
  69. // The one limitation that this imposes is that wvsprintf, and
  70. // therefore hprintf, cannot handle floating point numbers.
  71. // Too many calls to WriteFile can take a long time, causing
  72. // confusing delays when programs crash. Therefore I implemented
  73. // a simple buffering scheme for hprintf
  74. #define HPRINTF_BUFFER_SIZE (8*1024) // must be at least 2048
  75. static wchar_t hprintf_buffer[HPRINTF_BUFFER_SIZE]; // wvsprintf never prints more than one K.
  76. static int hprintf_index = 0;
  77. ///////////////////////////////////////////////////////////////////////////////
  78. // hflush
  79. static void hflush(HANDLE LogFile)
  80. {
  81. if (hprintf_index > 0)
  82. {
  83. DWORD NumBytes = 0;
  84. WriteFile(LogFile, hprintf_buffer, lstrlenW(hprintf_buffer)*2, &NumBytes, 0);
  85. hprintf_index = 0;
  86. }
  87. }
  88. ///////////////////////////////////////////////////////////////////////////////
  89. // hprintf
  90. static void hprintf(HANDLE LogFile, const wchar_t *Format, ...)
  91. {
  92. if (hprintf_index > (HPRINTF_BUFFER_SIZE-1024))
  93. {
  94. DWORD NumBytes = 0;
  95. WriteFile(LogFile, hprintf_buffer, lstrlen(hprintf_buffer)*2, &NumBytes, 0);
  96. hprintf_index = 0;
  97. }
  98. va_list arglist;
  99. va_start( arglist, Format);
  100. hprintf_index += vswprintf(&hprintf_buffer[hprintf_index], Format, arglist);
  101. va_end( arglist);
  102. }
  103. #include <strsafe.h>
  104. ///////////////////////////////////////////////////////////////////////////////
  105. // DumpMiniDump
  106. static BOOL DumpMiniDump(HANDLE hFile, PEXCEPTION_POINTERS excpInfo)
  107. {
  108. if (excpInfo == NULL)
  109. {
  110. // Generate exception to get proper context in dump
  111. __try
  112. {
  113. //OutputDebugString(_T("raising exception\r\n"));
  114. RaiseException(EXCEPTION_BREAKPOINT, 0, 0, NULL);
  115. }
  116. __except(DumpMiniDump(hFile, GetExceptionInformation()), EXCEPTION_CONTINUE_EXECUTION)
  117. {
  118. }
  119. }
  120. else
  121. {
  122. //OutputDebugString(_T("writing minidump\r\n"));
  123. MINIDUMP_EXCEPTION_INFORMATION eInfo = {0};
  124. eInfo.ThreadId = GetCurrentThreadId();
  125. eInfo.ExceptionPointers = excpInfo;
  126. eInfo.ClientPointers = FALSE;
  127. // try to load dbghelpdll
  128. HMODULE hm = NULL;
  129. // first from app folder
  130. wchar_t szDbgHelpPath[_MAX_PATH] = {0};
  131. if (GetModuleFileNameW( NULL, szDbgHelpPath, _MAX_PATH ))
  132. {
  133. wchar_t *pSlash = wcsrchr( szDbgHelpPath, L'\\' );
  134. if (pSlash)
  135. {
  136. StringCchCopy( pSlash+1, _MAX_PATH, L"dbghelp.dll");
  137. hm = LoadLibraryW( szDbgHelpPath );
  138. }
  139. }
  140. if (!hm)
  141. {
  142. // load any version we can
  143. hm = LoadLibraryW(L"dbghelp.dll");
  144. }
  145. if (hm)
  146. {
  147. BOOL (WINAPI* MiniDumpWriteDump)(
  148. HANDLE hProcess,
  149. DWORD ProcessId,
  150. HANDLE hFile,
  151. MINIDUMP_TYPE DumpType,
  152. PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
  153. PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
  154. PMINIDUMP_CALLBACK_INFORMATION CallbackParam
  155. ) = NULL;
  156. //OutputDebugString(_T("Found dbghelp.dll, searching for MiniDumpWriteDump\r\n"));
  157. *(FARPROC*)&MiniDumpWriteDump = GetProcAddress(hm, "MiniDumpWriteDump");
  158. if (MiniDumpWriteDump)
  159. {
  160. //OutputDebugString(_T("Calling MiniDumpWriteDump\r\n"));
  161. BOOL ret = MiniDumpWriteDump(
  162. GetCurrentProcess(),
  163. GetCurrentProcessId(),
  164. hFile,
  165. (MINIDUMP_TYPE)settings.dumpType,
  166. excpInfo ? &eInfo : NULL,
  167. NULL,
  168. NULL);
  169. //OutputDebugString(_T("MiniDumpWriteDump finished\r\n"));
  170. if (!ret)
  171. {
  172. DWORD le = GetLastError();
  173. wchar_t tmp[256] = {0};
  174. StringCchPrintfW(tmp, 256, L"call failed with error code: %d", le);
  175. //OutputDebugString(tmp);
  176. }
  177. return ret;
  178. }
  179. }
  180. }
  181. return FALSE;
  182. }
  183. ///////////////////////////////////////////////////////////////////////////////
  184. // FormatTime
  185. //
  186. // Format the specified FILETIME to output in a human readable format,
  187. // without using the C run time.
  188. static void FormatTime(LPTSTR output, FILETIME TimeToPrint)
  189. {
  190. output[0] = _T('\0');
  191. WORD Date, Time;
  192. if (FileTimeToLocalFileTime(&TimeToPrint, &TimeToPrint) &&
  193. FileTimeToDosDateTime(&TimeToPrint, &Date, &Time))
  194. {
  195. StringCchPrintf(output, 100, _T("%d/%d/%d %02d:%02d:%02d"),
  196. (Date / 32) & 15, Date & 31, (Date / 512) + 1980,
  197. (Time >> 11), (Time >> 5) & 0x3F, (Time & 0x1F) * 2);
  198. }
  199. }
  200. ///////////////////////////////////////////////////////////////////////////////
  201. // DumpModuleInfo
  202. //
  203. // Print information about a code module (DLL or EXE) such as its size,
  204. // location, time stamp, etc.
  205. static bool DumpModuleInfo(HANDLE LogFile, HINSTANCE ModuleHandle, int nModuleNo)
  206. {
  207. bool rc = false;
  208. wchar_t szModName[MAX_PATH*2] = {0};
  209. __try
  210. {
  211. if (GetModuleFileName(ModuleHandle, szModName, MAX_PATH*2) > 0)
  212. {
  213. // If GetModuleFileName returns greater than zero then this must
  214. // be a valid code module address. Therefore we can try to walk
  215. // our way through its structures to find the link time stamp.
  216. IMAGE_DOS_HEADER *DosHeader = (IMAGE_DOS_HEADER*)ModuleHandle;
  217. if (IMAGE_DOS_SIGNATURE != DosHeader->e_magic)
  218. return false;
  219. IMAGE_NT_HEADERS *NTHeader = (IMAGE_NT_HEADERS*)((char *)DosHeader
  220. + DosHeader->e_lfanew);
  221. if (IMAGE_NT_SIGNATURE != NTHeader->Signature)
  222. return false;
  223. // open the code module file so that we can get its file date and size
  224. HANDLE ModuleFile = CreateFile(szModName, GENERIC_READ,
  225. FILE_SHARE_READ, 0, OPEN_EXISTING,
  226. FILE_ATTRIBUTE_NORMAL, 0);
  227. TCHAR TimeBuffer[100] = {0};
  228. DWORD FileSize = 0;
  229. if (ModuleFile != INVALID_HANDLE_VALUE)
  230. {
  231. FileSize = GetFileSize(ModuleFile, 0);
  232. FILETIME LastWriteTime;
  233. if (GetFileTime(ModuleFile, 0, 0, &LastWriteTime))
  234. {
  235. FormatTime(TimeBuffer, LastWriteTime);
  236. }
  237. CloseHandle(ModuleFile);
  238. }
  239. hprintf(LogFile, _T("Module %d\r\n"), nModuleNo);
  240. hprintf(LogFile, _T("%s\r\n"), szModName);
  241. hprintf(LogFile, _T("Image Base: 0x%08x Image Size: 0x%08x\r\n"),
  242. NTHeader->OptionalHeader.ImageBase,
  243. NTHeader->OptionalHeader.SizeOfImage),
  244. hprintf(LogFile, _T("Checksum: 0x%08x Time Stamp: 0x%08x\r\n"),
  245. NTHeader->OptionalHeader.CheckSum,
  246. NTHeader->FileHeader.TimeDateStamp);
  247. hprintf(LogFile, _T("File Size: %-10d File Time: %s\r\n"),
  248. FileSize, TimeBuffer);
  249. hprintf(LogFile, _T("Version Information:\r\n"));
  250. CMiniVersion ver(szModName);
  251. TCHAR szBuf[200] = {0};
  252. WORD dwBuf[4] = {0};
  253. ver.GetCompanyName(szBuf, _countof(szBuf)-1);
  254. hprintf(LogFile, _T(" Company: %s\r\n"), szBuf);
  255. ver.GetProductName(szBuf, _countof(szBuf)-1);
  256. hprintf(LogFile, _T(" Product: %s\r\n"), szBuf);
  257. ver.GetFileDescription(szBuf, _countof(szBuf)-1);
  258. hprintf(LogFile, _T(" FileDesc: %s\r\n"), szBuf);
  259. ver.GetFileVersion(dwBuf);
  260. hprintf(LogFile, _T(" FileVer: %d.%d.%d.%d\r\n"),
  261. dwBuf[0], dwBuf[1], dwBuf[2], dwBuf[3]);
  262. ver.GetProductVersion(dwBuf);
  263. hprintf(LogFile, _T(" ProdVer: %d.%d.%d.%d\r\n"),
  264. dwBuf[0], dwBuf[1], dwBuf[2], dwBuf[3]);
  265. ver.Release();
  266. hprintf(LogFile, _T("\r\n"));
  267. rc = true;
  268. }
  269. }
  270. // Handle any exceptions by continuing from this point.
  271. __except(EXCEPTION_EXECUTE_HANDLER)
  272. {
  273. //OutputDebugString(L"DumpModuleInfo exception");
  274. }
  275. return rc;
  276. }
  277. ///////////////////////////////////////////////////////////////////////////////
  278. // DumpModuleList
  279. //
  280. // Scan memory looking for code modules (DLLs or EXEs). VirtualQuery is used
  281. // to find all the blocks of address space that were reserved or committed,
  282. // and ShowModuleInfo will display module information if they are code
  283. // modules.
  284. static void DumpModuleList(HANDLE LogFile)
  285. {
  286. SYSTEM_INFO SystemInfo;
  287. GetSystemInfo(&SystemInfo);
  288. //OutputDebugString(L"Dumping modules list");
  289. const size_t PageSize = SystemInfo.dwPageSize;
  290. // Set NumPages to the number of pages in the 4GByte address space,
  291. // while being careful to avoid overflowing ints
  292. const size_t NumPages = 4 * size_t(ONEG / PageSize);
  293. size_t pageNum = 0;
  294. void *LastAllocationBase = 0;
  295. int nModuleNo = 1;
  296. while (pageNum < NumPages)
  297. {
  298. MEMORY_BASIC_INFORMATION MemInfo;
  299. if (VirtualQuery((void *)(pageNum * PageSize), &MemInfo, sizeof(MemInfo)))
  300. {
  301. if (MemInfo.RegionSize > 0)
  302. {
  303. // Adjust the page number to skip over this block of memory
  304. pageNum += MemInfo.RegionSize / PageSize;
  305. if (MemInfo.State == MEM_COMMIT && MemInfo.AllocationBase > LastAllocationBase)
  306. {
  307. // Look for new blocks of committed memory, and try
  308. // recording their module names - this will fail
  309. // gracefully if they aren't code modules
  310. LastAllocationBase = MemInfo.AllocationBase;
  311. if (DumpModuleInfo(LogFile, (HINSTANCE)LastAllocationBase, nModuleNo))
  312. {
  313. nModuleNo++;
  314. }
  315. }
  316. }
  317. else
  318. pageNum += SIXTYFOURK / PageSize;
  319. }
  320. else
  321. pageNum += SIXTYFOURK / PageSize;
  322. // If VirtualQuery fails we advance by 64K because that is the
  323. // granularity of address space doled out by VirtualAlloc()
  324. }
  325. }
  326. ///////////////////////////////////////////////////////////////////////////////
  327. // DumpSystemInformation
  328. //
  329. // Record information about the user's system, such as processor type, amount
  330. // of memory, etc.
  331. static void DumpSystemInformation(HANDLE LogFile)
  332. {
  333. FILETIME CurrentTime;
  334. GetSystemTimeAsFileTime(&CurrentTime);
  335. TCHAR szTimeBuffer[100] = {0};
  336. FormatTime(szTimeBuffer, CurrentTime);
  337. hprintf(LogFile, _T("Error occurred at %s.\r\n"), szTimeBuffer);
  338. TCHAR szModuleName[MAX_PATH*2] = {0};
  339. if (GetModuleFileName(0, szModuleName, _countof(szModuleName)-2) <= 0)
  340. StringCbCopy(szModuleName, sizeof(szModuleName), _T("Unknown"));
  341. TCHAR szUserName[200] = {0};
  342. DWORD UserNameSize = _countof(szUserName)-2;
  343. if (!GetUserName(szUserName, &UserNameSize))
  344. StringCbCopy(szUserName, sizeof(szUserName), _T("Unknown"));
  345. hprintf(LogFile, _T("%s, run by %s.\r\n"), szModuleName, szUserName);
  346. // print out operating system
  347. TCHAR szWinVer[50] = {0}, szMajorMinorBuild[50] = {0};
  348. int nWinVer = 0;
  349. GetWinVer(szWinVer, &nWinVer, szMajorMinorBuild);
  350. hprintf(LogFile, _T("Operating system: %s (%s).\r\n"),
  351. szWinVer, szMajorMinorBuild);
  352. SYSTEM_INFO SystemInfo;
  353. GetSystemInfo(&SystemInfo);
  354. hprintf(LogFile, _T("%d processor(s), type %d.\r\n"),
  355. SystemInfo.dwNumberOfProcessors, SystemInfo.dwProcessorType);
  356. MEMORYSTATUS MemInfo;
  357. MemInfo.dwLength = sizeof(MemInfo);
  358. GlobalMemoryStatus(&MemInfo);
  359. // Print out info on memory, rounded up.
  360. hprintf(LogFile, _T("%d%% memory in use.\r\n"), MemInfo.dwMemoryLoad);
  361. hprintf(LogFile, _T("%d MBytes physical memory.\r\n"), (MemInfo.dwTotalPhys +
  362. ONEM - 1) / ONEM);
  363. hprintf(LogFile, _T("%d MBytes physical memory free.\r\n"),
  364. (MemInfo.dwAvailPhys + ONEM - 1) / ONEM);
  365. hprintf(LogFile, _T("%d MBytes paging file.\r\n"), (MemInfo.dwTotalPageFile +
  366. ONEM - 1) / ONEM);
  367. hprintf(LogFile, _T("%d MBytes paging file free.\r\n"),
  368. (MemInfo.dwAvailPageFile + ONEM - 1) / ONEM);
  369. hprintf(LogFile, _T("%d MBytes user address space.\r\n"),
  370. (MemInfo.dwTotalVirtual + ONEM - 1) / ONEM);
  371. hprintf(LogFile, _T("%d MBytes user address space free.\r\n"),
  372. (MemInfo.dwAvailVirtual + ONEM - 1) / ONEM);
  373. }
  374. ///////////////////////////////////////////////////////////////////////////////
  375. // GetExceptionDescription
  376. //
  377. // Translate the exception code into something human readable
  378. static const TCHAR *GetExceptionDescription(DWORD ExceptionCode)
  379. {
  380. struct ExceptionNames
  381. {
  382. DWORD ExceptionCode;
  383. TCHAR * ExceptionName;
  384. };
  385. #if 0 // from winnt.h
  386. #define STATUS_WAIT_0 ((DWORD )0x00000000L)
  387. #define STATUS_ABANDONED_WAIT_0 ((DWORD )0x00000080L)
  388. #define STATUS_USER_APC ((DWORD )0x000000C0L)
  389. #define STATUS_TIMEOUT ((DWORD )0x00000102L)
  390. #define STATUS_PENDING ((DWORD )0x00000103L)
  391. #define STATUS_SEGMENT_NOTIFICATION ((DWORD )0x40000005L)
  392. #define STATUS_GUARD_PAGE_VIOLATION ((DWORD )0x80000001L)
  393. #define STATUS_DATATYPE_MISALIGNMENT ((DWORD )0x80000002L)
  394. #define STATUS_BREAKPOINT ((DWORD )0x80000003L)
  395. #define STATUS_SINGLE_STEP ((DWORD )0x80000004L)
  396. #define STATUS_ACCESS_VIOLATION ((DWORD )0xC0000005L)
  397. #define STATUS_IN_PAGE_ERROR ((DWORD )0xC0000006L)
  398. #define STATUS_INVALID_HANDLE ((DWORD )0xC0000008L)
  399. #define STATUS_NO_MEMORY ((DWORD )0xC0000017L)
  400. #define STATUS_ILLEGAL_INSTRUCTION ((DWORD )0xC000001DL)
  401. #define STATUS_NONCONTINUABLE_EXCEPTION ((DWORD )0xC0000025L)
  402. #define STATUS_INVALID_DISPOSITION ((DWORD )0xC0000026L)
  403. #define STATUS_ARRAY_BOUNDS_EXCEEDED ((DWORD )0xC000008CL)
  404. #define STATUS_FLOAT_DENORMAL_OPERAND ((DWORD )0xC000008DL)
  405. #define STATUS_FLOAT_DIVIDE_BY_ZERO ((DWORD )0xC000008EL)
  406. #define STATUS_FLOAT_INEXACT_RESULT ((DWORD )0xC000008FL)
  407. #define STATUS_FLOAT_INVALID_OPERATION ((DWORD )0xC0000090L)
  408. #define STATUS_FLOAT_OVERFLOW ((DWORD )0xC0000091L)
  409. #define STATUS_FLOAT_STACK_CHECK ((DWORD )0xC0000092L)
  410. #define STATUS_FLOAT_UNDERFLOW ((DWORD )0xC0000093L)
  411. #define STATUS_INTEGER_DIVIDE_BY_ZERO ((DWORD )0xC0000094L)
  412. #define STATUS_INTEGER_OVERFLOW ((DWORD )0xC0000095L)
  413. #define STATUS_PRIVILEGED_INSTRUCTION ((DWORD )0xC0000096L)
  414. #define STATUS_STACK_OVERFLOW ((DWORD )0xC00000FDL)
  415. #define STATUS_CONTROL_C_EXIT ((DWORD )0xC000013AL)
  416. #define STATUS_FLOAT_MULTIPLE_FAULTS ((DWORD )0xC00002B4L)
  417. #define STATUS_FLOAT_MULTIPLE_TRAPS ((DWORD )0xC00002B5L)
  418. #define STATUS_ILLEGAL_VLM_REFERENCE ((DWORD )0xC00002C0L)
  419. #endif
  420. ExceptionNames ExceptionMap[] =
  421. {
  422. {0x40010005, _T("a Control-C")},
  423. {0x40010008, _T("a Control-Break")},
  424. {0x80000002, _T("a Datatype Misalignment")},
  425. {0x80000003, _T("a Breakpoint")},
  426. {0xc0000005, _T("an Access Violation")},
  427. {0xc0000006, _T("an In Page Error")},
  428. {0xc0000017, _T("a No Memory")},
  429. {0xc000001d, _T("an Illegal Instruction")},
  430. {0xc0000025, _T("a Noncontinuable Exception")},
  431. {0xc0000026, _T("an Invalid Disposition")},
  432. {0xc000008c, _T("a Array Bounds Exceeded")},
  433. {0xc000008d, _T("a Float Denormal Operand")},
  434. {0xc000008e, _T("a Float Divide by Zero")},
  435. {0xc000008f, _T("a Float Inexact Result")},
  436. {0xc0000090, _T("a Float Invalid Operation")},
  437. {0xc0000091, _T("a Float Overflow")},
  438. {0xc0000092, _T("a Float Stack Check")},
  439. {0xc0000093, _T("a Float Underflow")},
  440. {0xc0000094, _T("an Integer Divide by Zero")},
  441. {0xc0000095, _T("an Integer Overflow")},
  442. {0xc0000096, _T("a Privileged Instruction")},
  443. {0xc00000fD, _T("a Stack Overflow")},
  444. {0xc0000142, _T("a DLL Initialization Failed")},
  445. {0xe06d7363, _T("a Microsoft C++ Exception")},
  446. };
  447. for (int i = 0; i < _countof(ExceptionMap); i++)
  448. if (ExceptionCode == ExceptionMap[i].ExceptionCode)
  449. return ExceptionMap[i].ExceptionName;
  450. return _T("an Unknown exception type");
  451. }
  452. ///////////////////////////////////////////////////////////////////////////////
  453. // GetFilePart
  454. static TCHAR * GetFilePart(LPCTSTR source)
  455. {
  456. TCHAR *result = lstrrchr(source, _T('\\'));
  457. if (result)
  458. result++;
  459. else
  460. result = (TCHAR *)source;
  461. return result;
  462. }
  463. #ifdef _M_IX86
  464. ///////////////////////////////////////////////////////////////////////////////
  465. // DumpStack
  466. static void DumpStack(HANDLE LogFile, DWORD *pStack)
  467. {
  468. hprintf(LogFile, _T("\r\n\r\nStack:\r\n"));
  469. __try
  470. {
  471. // Esp contains the bottom of the stack, or at least the bottom of
  472. // the currently used area.
  473. DWORD* pStackTop;
  474. __asm
  475. {
  476. // Load the top (highest address) of the stack from the
  477. // thread information block. It will be found there in
  478. // Win9x and Windows NT.
  479. mov eax, fs:[4]
  480. mov pStackTop, eax
  481. }
  482. if (pStackTop > pStack + MaxStackDump)
  483. pStackTop = pStack + MaxStackDump;
  484. int Count = 0;
  485. DWORD* pStackStart = pStack;
  486. int nDwordsPrinted = 0;
  487. while (pStack + 1 <= pStackTop)
  488. {
  489. if ((Count % StackColumns) == 0)
  490. {
  491. pStackStart = pStack;
  492. nDwordsPrinted = 0;
  493. hprintf(LogFile, _T("0x%08x: "), pStack);
  494. }
  495. hprintf(LogFile, _T("%08x "), pStack);
  496. if ((++Count % StackColumns) == 0 || pStack + 2 > pStackTop)
  497. {
  498. nDwordsPrinted++;
  499. int n = nDwordsPrinted;
  500. while (n < 4)
  501. {
  502. hprintf(LogFile, _T(" "));
  503. n++;
  504. }
  505. for (int i = 0; i < nDwordsPrinted; i++)
  506. {
  507. DWORD dwStack = *pStackStart;
  508. for (int j = 0; j < 4; j++)
  509. {
  510. char c = (char)(dwStack & 0xFF);
  511. if (c < 0x20 || c > 0x7E)
  512. c = '.';
  513. #ifdef _UNICODE
  514. WCHAR w = (WCHAR)c;
  515. hprintf(LogFile, _T("%c"), w);
  516. #else
  517. hprintf(LogFile, _T("%c"), c);
  518. #endif
  519. dwStack = dwStack >> 8;
  520. }
  521. pStackStart++;
  522. }
  523. hprintf(LogFile, _T("\r\n"));
  524. }
  525. else
  526. {
  527. // hprintf(LogFile, _T("%08x "), *pStack);
  528. nDwordsPrinted++;
  529. }
  530. pStack++;
  531. }
  532. hprintf(LogFile, _T("\r\n"));
  533. }
  534. __except(EXCEPTION_EXECUTE_HANDLER)
  535. {
  536. hprintf(LogFile, _T("Exception encountered during stack dump.\r\n"));
  537. }
  538. }
  539. ///////////////////////////////////////////////////////////////////////////////
  540. // DumpRegisters
  541. static void DumpRegisters(HANDLE LogFile, PCONTEXT Context)
  542. {
  543. // Print out the register values in an XP error window compatible format.
  544. hprintf(LogFile, _T("\r\n"));
  545. hprintf(LogFile, _T("Context:\r\n"));
  546. hprintf(LogFile, _T("EDI: 0x%08x ESI: 0x%08x EAX: 0x%08x\r\n"),
  547. Context->Edi, Context->Esi, Context->Eax);
  548. hprintf(LogFile, _T("EBX: 0x%08x ECX: 0x%08x EDX: 0x%08x\r\n"),
  549. Context->Ebx, Context->Ecx, Context->Edx);
  550. hprintf(LogFile, _T("EIP: 0x%08x EBP: 0x%08x SegCs: 0x%08x\r\n"),
  551. Context->Eip, Context->Ebp, Context->SegCs);
  552. hprintf(LogFile, _T("EFlags: 0x%08x ESP: 0x%08x SegSs: 0x%08x\r\n"),
  553. Context->EFlags, Context->Esp, Context->SegSs);
  554. }
  555. #endif
  556. BOOL CreateLog(PEXCEPTION_POINTERS pExceptPtrs, LPCWSTR lpszMessage)
  557. {
  558. HANDLE hLogFile = CreateFile(settings.logPath, GENERIC_WRITE, 0, 0,
  559. CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH, 0);
  560. if (hLogFile == INVALID_HANDLE_VALUE)
  561. {
  562. //OutputDebugString(_T("Error creating exception report\r\n"));
  563. return FALSE;
  564. }
  565. // add BOM
  566. WORD wBOM = 0xFEFF;
  567. DWORD num = 0;
  568. WriteFile(hLogFile, &wBOM, sizeof(WORD), &num, NULL);
  569. // Append to the error log
  570. SetFilePointer(hLogFile, 0, 0, FILE_END);
  571. wchar_t line[1024] = {0};
  572. wchar_t msgBody[4*1024] = {0};
  573. wchar_t winampVersionWide[1024] = {0};
  574. MultiByteToWideCharSZ(CP_ACP, 0, winampVersion, -1, winampVersionWide, 1024);
  575. StringCchPrintf(line, 1024, L"Winamp client version: %s\r\n", winampVersionWide);
  576. StringCchCopy(msgBody, 4*1024, line);
  577. PEXCEPTION_RECORD Exception = pExceptPtrs->ExceptionRecord;
  578. PCONTEXT Context = pExceptPtrs->ContextRecord;
  579. TCHAR szCrashModulePathName[MAX_PATH*2] = {0};
  580. TCHAR *pszCrashModuleFileName = _T("Unknown");
  581. #ifdef _M_IX86
  582. MEMORY_BASIC_INFORMATION MemInfo;
  583. // VirtualQuery can be used to get the allocation base associated with a
  584. // code address, which is the same as the ModuleHandle. This can be used
  585. // to get the filename of the module that the crash happened in.
  586. if (VirtualQuery((void*)Context->Eip, &MemInfo, sizeof(MemInfo)) &&
  587. (GetModuleFileName((HINSTANCE)MemInfo.AllocationBase,
  588. szCrashModulePathName,
  589. sizeof(szCrashModulePathName)-2) > 0))
  590. {
  591. //OutputDebugString(szCrashModulePathName);
  592. pszCrashModuleFileName = GetFilePart(szCrashModulePathName);
  593. }
  594. #endif
  595. // Print out the beginning of the error log in a Win95 error window
  596. // compatible format.
  597. TCHAR szModuleName[MAX_PATH*2] = {0};
  598. if (GetModuleFileName(0, szModuleName, _countof(szModuleName)-2) <= 0)
  599. StringCbCopy(szModuleName, sizeof(szModuleName), _T("Unknown"));
  600. TCHAR *pszFilePart = GetFilePart(szModuleName);
  601. // Extract the file name portion and remove it's file extension
  602. TCHAR szFileName[MAX_PATH*2] = {0};
  603. StringCbCopy(szFileName, sizeof(szFileName), pszFilePart);
  604. TCHAR *lastperiod = lstrrchr(szFileName, _T('.'));
  605. if (lastperiod)
  606. lastperiod[0] = 0;
  607. #ifdef _M_IX86
  608. StringCchPrintf(line, 1024, L"%s caused %s (0x%08x) \r\nin module %s at %04x:%08x.\r\n\r\n",
  609. szFileName, GetExceptionDescription(Exception->ExceptionCode),
  610. Exception->ExceptionCode,
  611. pszCrashModuleFileName, Context->SegCs, Context->Eip);
  612. #endif
  613. StringCchCat(msgBody, 4*1024, line);
  614. StringCchPrintf(line, 1024, L"Exception handler called in %s.\r\n", lpszMessage);
  615. StringCchCat(msgBody, 4*1024, line);
  616. hprintf(hLogFile, L"%s", msgBody);
  617. wchar_t *p = msgBody, *end = msgBody + wcslen(msgBody);
  618. while(p != end)
  619. {
  620. if (*p == L'\r') *p = 1;
  621. if (*p == L'\n') *p = 2;
  622. p++;
  623. }
  624. settings.WriteBody(msgBody);
  625. if (settings.logSystem)
  626. {
  627. DumpSystemInformation(hLogFile);
  628. // If the exception was an access violation, print out some additional
  629. // information, to the error log and the debugger.
  630. if (Exception->ExceptionCode == STATUS_ACCESS_VIOLATION &&
  631. Exception->NumberParameters >= 2)
  632. {
  633. TCHAR szDebugMessage[1000] = {0};
  634. const TCHAR* readwrite = _T("Read from");
  635. if (Exception->ExceptionInformation[0])
  636. readwrite = _T("Write to");
  637. StringCchPrintf(szDebugMessage, 1000, _T("%s location %08x caused an access violation.\r\n"),
  638. readwrite, Exception->ExceptionInformation[1]);
  639. hprintf(hLogFile, _T("%s"), szDebugMessage);
  640. }
  641. }
  642. if (settings.logRegistry)
  643. {
  644. #ifdef _M_IX86
  645. DumpRegisters(hLogFile, Context);
  646. #endif
  647. // Print out the bytes of code at the instruction pointer. Since the
  648. // crash may have been caused by an instruction pointer that was bad,
  649. // this code needs to be wrapped in an exception handler, in case there
  650. // is no memory to read. If the dereferencing of code[] fails, the
  651. // exception handler will print '??'.
  652. #ifdef _M_IX86
  653. hprintf(hLogFile, _T("\r\nBytes at CS:EIP:\r\n"));
  654. BYTE * code = (BYTE *)Context->Eip;
  655. for (int codebyte = 0; codebyte < NumCodeBytes; codebyte++)
  656. {
  657. __try
  658. {
  659. hprintf(hLogFile, _T("%02x "), code[codebyte]);
  660. }
  661. __except(EXCEPTION_EXECUTE_HANDLER)
  662. {
  663. hprintf(hLogFile, _T("?? "));
  664. }
  665. }
  666. #endif
  667. }
  668. if (settings.logStack)
  669. {
  670. // Time to print part or all of the stack to the error log. This allows
  671. // us to figure out the call stack, parameters, local variables, etc.
  672. // Esp contains the bottom of the stack, or at least the bottom of
  673. // the currently used area
  674. #ifdef _M_IX86
  675. DWORD* pStack = (DWORD *)Context->Esp;
  676. DumpStack(hLogFile, pStack);
  677. #endif
  678. }
  679. if (settings.logModule)
  680. {
  681. DumpModuleList(hLogFile);
  682. }
  683. hprintf(hLogFile, _T("\r\n===== [end of log file] =====\r\n"));
  684. hflush(hLogFile);
  685. CloseHandle(hLogFile);
  686. return TRUE;
  687. }
  688. BOOL CreateDump(PEXCEPTION_POINTERS pExceptPtrs)
  689. {
  690. BOOL retCode = FALSE;
  691. // Create the file
  692. //OutputDebugString(_T("CreateFile: "));
  693. //OutputDebugString(settings.dumpPath);
  694. HANDLE hMiniDumpFile = CreateFile(
  695. settings.dumpPath,
  696. GENERIC_WRITE,
  697. 0,
  698. NULL,
  699. CREATE_ALWAYS,
  700. FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH,
  701. NULL);
  702. // Write the minidump to the file
  703. if (hMiniDumpFile != INVALID_HANDLE_VALUE)
  704. {
  705. retCode = DumpMiniDump(hMiniDumpFile, pExceptPtrs);
  706. // Close file
  707. CloseHandle(hMiniDumpFile);
  708. if (!retCode) DeleteFile(settings.dumpPath);
  709. }
  710. return retCode;
  711. }