dbghelpplug.c 50 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896
  1. /*
  2. * 2008 January 19
  3. *
  4. * The author disclaims copyright to this source code. In place of
  5. * a legal notice, here is a blessing:
  6. *
  7. * May you do good and not evil.
  8. * May you find forgiveness for yourself and forgive others.
  9. * May you share freely, never taking more than you give.
  10. *
  11. ********************************************************************
  12. *
  13. * DONE:
  14. * - Command line
  15. * - Windows version
  16. * - Exception
  17. * - Registers
  18. * - Stacktrace (frame number starting at 0)
  19. * + Functions:
  20. * - address
  21. * - name
  22. * - offset in the function
  23. * - line number and source file
  24. * - source code of the line (external source)
  25. * - function parameters
  26. * - local function variables
  27. * + Variables/parameters:
  28. * - variable name
  29. * - variable type (char/wchar, integers, floats, enums, arrays,
  30. * pointers, structs, unions, ...)
  31. * - readability of memory
  32. * - value of char/wchar
  33. * - value of integers
  34. * - value of floats
  35. * - value of enums (hex number)
  36. * - values of arrays
  37. * - address of pointers
  38. * - value of simple pointers (not pointing to another pointer)
  39. * - show (char*) and (wchar*) as nul-terminated strings
  40. * - show chars/wchar escaped
  41. * + Modules:
  42. * - base address
  43. * - file
  44. * - version
  45. * - size
  46. *
  47. * TODO:
  48. * + Variables/parameters:
  49. * - structure members
  50. * - union members
  51. * - globals
  52. * - Portability to MinGW
  53. *
  54. * $Id$
  55. */
  56. #ifdef _WIN32
  57. /////////////////////////////////////////////////////////////////////
  58. // Include files
  59. //
  60. #include <assert.h>
  61. #define WIN32_LEAN_AND_MEAN
  62. #include <windows.h>
  63. #define _NO_CVCONST_H
  64. #include <dbghelp.h>
  65. #include <stdio.h>
  66. #include <stdlib.h>
  67. #include <time.h>
  68. /////////////////////////////////////////////////////////////////////
  69. // Types from Cvconst.h (DIA SDK)
  70. //
  71. #ifdef _NO_CVCONST_H
  72. typedef enum _BasicType
  73. {
  74. btNoType = 0,
  75. btVoid = 1,
  76. btChar = 2,
  77. btWChar = 3,
  78. btInt = 6,
  79. btUInt = 7,
  80. btFloat = 8,
  81. btBCD = 9,
  82. btBool = 10,
  83. btLong = 13,
  84. btULong = 14,
  85. btCurrency = 25,
  86. btDate = 26,
  87. btVariant = 27,
  88. btComplex = 28,
  89. btBit = 29,
  90. btBSTR = 30,
  91. btHresult = 31
  92. } BasicType;
  93. typedef enum _UdtKind
  94. {
  95. UdtStruct,
  96. UdtClass,
  97. UdtUnion
  98. } UdtKind;
  99. typedef enum _SymTag {
  100. SymTagNull = 0,
  101. SymTagExe = 1,
  102. SymTagCompiland = 2,
  103. SymTagCompilandDetails = 3,
  104. SymTagCompilandEnv = 4,
  105. SymTagFunction = 5,
  106. SymTagBlock = 6,
  107. SymTagData = 7,
  108. SymTagAnnotation = 8,
  109. SymTagLabel = 9,
  110. SymTagPublicSymbol = 10,
  111. SymTagUDT = 11,
  112. SymTagEnum = 12,
  113. SymTagFunctionType = 13,
  114. SymTagPointerType = 14,
  115. SymTagArrayType = 15,
  116. SymTagBaseType = 16,
  117. SymTagTypedef = 17,
  118. SymTagBaseClass = 18,
  119. SymTagFriend = 19,
  120. SymTagFunctionArgType = 20,
  121. SymTagFuncDebugStart = 21,
  122. SymTagFuncDebugEnd = 22,
  123. SymTagUsingNamespace = 23,
  124. SymTagVTableShape = 24,
  125. SymTagVTable = 25,
  126. SymTagCustom = 26,
  127. SymTagThunk = 27,
  128. SymTagCustomType = 28,
  129. SymTagManagedType = 29,
  130. SymTagDimension = 30
  131. } SymTag;
  132. #endif /* _NO_CVCONST_H */
  133. /////////////////////////////////////////////////////////////////////
  134. // dbghelp function prototypes
  135. //
  136. typedef BOOL (WINAPI *MINIDUMPWRITEDUMP)(
  137. HANDLE hProcess,
  138. DWORD ProcessId,
  139. HANDLE hFile,
  140. MINIDUMP_TYPE DumpType,
  141. CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
  142. CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
  143. CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam
  144. );
  145. typedef BOOL (WINAPI *SYMINITIALIZE)(
  146. HANDLE hProcess,
  147. PSTR UserSearchPath,
  148. BOOL fInvadeProcess
  149. );
  150. typedef DWORD (WINAPI *SYMSETOPTIONS)(
  151. DWORD SymOptions
  152. );
  153. typedef DWORD (WINAPI *SYMGETOPTIONS)(
  154. VOID
  155. );
  156. typedef BOOL (WINAPI *SYMCLEANUP)(
  157. HANDLE hProcess
  158. );
  159. typedef BOOL (WINAPI *SYMGETTYPEINFO)(
  160. HANDLE hProcess,
  161. DWORD64 ModBase,
  162. ULONG TypeId,
  163. IMAGEHLP_SYMBOL_TYPE_INFO GetType,
  164. PVOID pInfo
  165. );
  166. typedef BOOL (WINAPI *SYMGETLINEFROMADDR)(
  167. HANDLE hProcess,
  168. DWORD dwAddr,
  169. PDWORD pdwDisplacement,
  170. PIMAGEHLP_LINE Line
  171. );
  172. typedef BOOL (WINAPI *SYMENUMSYMBOLS)(
  173. HANDLE hProcess,
  174. ULONG64 BaseOfDll,
  175. PCSTR Mask,
  176. PSYM_ENUMERATESYMBOLS_CALLBACK EnumSymbolsCallback,
  177. PVOID UserContext
  178. );
  179. typedef BOOL (WINAPI *SYMSETCONTEXT)(
  180. HANDLE hProcess,
  181. PIMAGEHLP_STACK_FRAME StackFrame,
  182. PIMAGEHLP_CONTEXT Context
  183. );
  184. typedef BOOL (WINAPI *SYMFROMADDR)(
  185. HANDLE hProcess,
  186. DWORD64 Address,
  187. PDWORD64 Displacement,
  188. PSYMBOL_INFO Symbol
  189. );
  190. typedef BOOL (WINAPI *STACKWALK)(
  191. DWORD MachineType,
  192. HANDLE hProcess,
  193. HANDLE hThread,
  194. LPSTACKFRAME StackFrame,
  195. PVOID ContextRecord,
  196. PREAD_PROCESS_MEMORY_ROUTINE ReadMemoryRoutine,
  197. PFUNCTION_TABLE_ACCESS_ROUTINE FunctionTableAccessRoutine,
  198. PGET_MODULE_BASE_ROUTINE GetModuleBaseRoutine,
  199. PTRANSLATE_ADDRESS_ROUTINE TranslateAddress
  200. );
  201. typedef PVOID (WINAPI *SYMFUNCTIONTABLEACCESS)(
  202. HANDLE hProcess,
  203. DWORD AddrBase
  204. );
  205. typedef DWORD (WINAPI *SYMGETMODULEBASE)(
  206. HANDLE hProcess,
  207. DWORD dwAddr
  208. );
  209. /////////////////////////////////////////////////////////////////////
  210. // Custom info
  211. /// Internal structure used to pass some data around
  212. typedef struct _InternalData {
  213. // PrintStacktrace
  214. FILE* log_file;
  215. STACKFRAME* pStackframe;
  216. HANDLE hProcess;
  217. DWORD nr_of_frame;
  218. // PrintFunctionDetail
  219. BOOL as_arg_list;
  220. BOOL log_params;
  221. BOOL log_locals;
  222. BOOL log_globals;
  223. DWORD nr_of_var;
  224. // PrintDataInfo
  225. ULONG64 modBase;
  226. } InterData;
  227. /// dbghelp dll filename
  228. #define DBGHELP_DLL "dbghelp.dll"
  229. // Default report filename, used when the module path is unavailable
  230. #define DBG_DEFAULT_FILENAME "athena"
  231. // Extended information printed in the console
  232. #define DBG_EXTENDED_INFORMATION \
  233. "Please report the crash in the bug tracker:\n" \
  234. "http://www.eathena.ws/board/index.php?autocom=bugtracker\n"
  235. /////////////////////////////////////////////////////////////////////
  236. // Global variables
  237. HANDLE dbghelp_dll = INVALID_HANDLE_VALUE;
  238. MINIDUMPWRITEDUMP MiniDumpWriteDump_ = NULL;
  239. SYMINITIALIZE SymInitialize_ = NULL;
  240. SYMSETOPTIONS SymSetOptions_ = NULL;
  241. SYMGETOPTIONS SymGetOptions_ = NULL;
  242. SYMCLEANUP SymCleanup_ = NULL;
  243. SYMGETTYPEINFO SymGetTypeInfo_ = NULL;
  244. SYMGETLINEFROMADDR SymGetLineFromAddr_ = NULL;
  245. SYMENUMSYMBOLS SymEnumSymbols_ = NULL;
  246. SYMSETCONTEXT SymSetContext_ = NULL;
  247. SYMFROMADDR SymFromAddr_ = NULL;
  248. STACKWALK StackWalk_ = NULL;
  249. SYMFUNCTIONTABLEACCESS SymFunctionTableAccess_ = NULL;
  250. SYMGETMODULEBASE SymGetModuleBase_ = NULL;
  251. /////////////////////////////////////////////////////////////////////
  252. // Code
  253. /// Writes the minidump to file. The callback function will at the
  254. /// same time write the list of modules to the log file.
  255. ///
  256. /// @param file Filename of the minidump
  257. /// @param ptrs Exception info
  258. /// @param module_callback Callback for MiniDumpWriteDump
  259. /// @param log_file Log file
  260. static VOID
  261. Dhp__WriteMinidumpFile(
  262. const char* file,
  263. PEXCEPTION_POINTERS ptrs,
  264. MINIDUMP_CALLBACK_ROUTINE module_callback,
  265. FILE* log_file)
  266. {
  267. // open minidump file
  268. HANDLE minidump_file = CreateFileA(
  269. file,
  270. GENERIC_WRITE,
  271. 0,
  272. NULL,
  273. CREATE_ALWAYS,
  274. FILE_ATTRIBUTE_NORMAL,
  275. NULL
  276. );
  277. if( minidump_file != INVALID_HANDLE_VALUE )
  278. {
  279. MINIDUMP_EXCEPTION_INFORMATION expt_info;
  280. MINIDUMP_CALLBACK_INFORMATION dump_cb_info;
  281. expt_info.ThreadId = GetCurrentThreadId();
  282. expt_info.ExceptionPointers = ptrs;
  283. expt_info.ClientPointers = FALSE;
  284. dump_cb_info.CallbackRoutine = module_callback;
  285. dump_cb_info.CallbackParam = (void*)log_file;
  286. if( module_callback != NULL && log_file != NULL )
  287. fprintf(log_file, "\n\nLoaded modules:\n");
  288. MiniDumpWriteDump_(
  289. GetCurrentProcess(),
  290. GetCurrentProcessId(),
  291. minidump_file,
  292. MiniDumpNormal,
  293. ptrs ? &expt_info : NULL,
  294. NULL,
  295. &dump_cb_info
  296. );
  297. CloseHandle(minidump_file);
  298. }
  299. }
  300. /// Prints module information to the log file.
  301. /// Used as a callback to MiniDumpWriteDump.
  302. ///
  303. /// @param data Log file
  304. /// @param callback_input
  305. /// @param callback_output
  306. /// @return
  307. static BOOL CALLBACK
  308. Dhp__PrintModuleInfoCallback(
  309. void* data,
  310. CONST PMINIDUMP_CALLBACK_INPUT callback_input,
  311. PMINIDUMP_CALLBACK_OUTPUT callback_output)
  312. {
  313. if( data != NULL &&
  314. callback_input != NULL &&
  315. callback_input->CallbackType == ModuleCallback)
  316. {
  317. FILE* log_file = (FILE*)data;
  318. MINIDUMP_MODULE_CALLBACK module = callback_input->Module;
  319. fprintf(log_file, "0x%p", module.BaseOfImage);
  320. fprintf(log_file, " %ws", module.FullPath, log_file);
  321. fprintf(log_file, " (%d.%d.%d.%d, %d bytes)\n",
  322. HIWORD(module.VersionInfo.dwFileVersionMS),
  323. LOWORD(module.VersionInfo.dwFileVersionMS),
  324. HIWORD(module.VersionInfo.dwFileVersionLS),
  325. LOWORD(module.VersionInfo.dwFileVersionLS),
  326. module.SizeOfImage);
  327. }
  328. return TRUE;
  329. }
  330. /// Prints details about the current process, platform and exception
  331. /// information to the log file.
  332. ///
  333. /// @param exception Exception info
  334. /// @param context Exception context
  335. /// @param log_file Log file
  336. static VOID
  337. Dhp__PrintProcessInfo(
  338. EXCEPTION_RECORD* exception,
  339. CONTEXT* context,
  340. FILE* log_file)
  341. {
  342. OSVERSIONINFOA oi;
  343. LPSTR cmd_line;
  344. fprintf(log_file,
  345. "\nProcess info:\n");
  346. // print the command line
  347. cmd_line = GetCommandLineA();
  348. if( cmd_line )
  349. fprintf(log_file,
  350. "Cmd line: %s\n",
  351. cmd_line);
  352. // print information about the OS
  353. oi.dwOSVersionInfoSize = sizeof(oi);
  354. GetVersionExA(&oi);
  355. fprintf(log_file,
  356. "Platform: Windows OS version %d.%d build %d %s\n",
  357. oi.dwMajorVersion, oi.dwMinorVersion, oi.dwBuildNumber, oi.szCSDVersion);
  358. // print the exception code
  359. if( exception )
  360. {
  361. fprintf(log_file,
  362. "\nException:\n"
  363. "0x%x",
  364. exception->ExceptionCode);
  365. switch( exception->ExceptionCode )
  366. {
  367. #define PRINT(x) case x: fprintf(log_file, " "#x); break
  368. PRINT(EXCEPTION_ACCESS_VIOLATION);
  369. PRINT(EXCEPTION_DATATYPE_MISALIGNMENT);
  370. PRINT(EXCEPTION_BREAKPOINT);
  371. PRINT(EXCEPTION_SINGLE_STEP);
  372. PRINT(EXCEPTION_ARRAY_BOUNDS_EXCEEDED);
  373. PRINT(EXCEPTION_FLT_DENORMAL_OPERAND);
  374. PRINT(EXCEPTION_FLT_DIVIDE_BY_ZERO);
  375. PRINT(EXCEPTION_FLT_INEXACT_RESULT);
  376. PRINT(EXCEPTION_FLT_INVALID_OPERATION);
  377. PRINT(EXCEPTION_FLT_OVERFLOW);
  378. PRINT(EXCEPTION_FLT_STACK_CHECK);
  379. PRINT(EXCEPTION_FLT_UNDERFLOW);
  380. PRINT(EXCEPTION_INT_DIVIDE_BY_ZERO);
  381. PRINT(EXCEPTION_INT_OVERFLOW);
  382. PRINT(EXCEPTION_PRIV_INSTRUCTION);
  383. PRINT(EXCEPTION_IN_PAGE_ERROR);
  384. PRINT(EXCEPTION_ILLEGAL_INSTRUCTION);
  385. PRINT(EXCEPTION_NONCONTINUABLE_EXCEPTION);
  386. PRINT(EXCEPTION_STACK_OVERFLOW);
  387. PRINT(EXCEPTION_INVALID_DISPOSITION);
  388. PRINT(EXCEPTION_GUARD_PAGE);
  389. PRINT(EXCEPTION_INVALID_HANDLE);
  390. #undef PRINT
  391. }
  392. // print where the fault occured
  393. fprintf(log_file, " at location 0x%p", exception->ExceptionAddress);
  394. // if the exception was an access violation, print additional information
  395. if( exception->ExceptionCode == EXCEPTION_ACCESS_VIOLATION && exception->NumberParameters >= 2 )
  396. fprintf(log_file, " %s location 0x%p", exception->ExceptionInformation[0] ? "writing to" : "reading from", exception->ExceptionInformation[1]);
  397. fprintf(log_file, "\n");
  398. }
  399. // print the register info
  400. if( context )
  401. {
  402. #if defined(_M_IX86)
  403. fprintf(log_file,
  404. "\nRegisters:\n");
  405. if( context->ContextFlags & CONTEXT_INTEGER )
  406. {
  407. fprintf(log_file,
  408. "eax=%08x ebx=%08x ecx=%08x edx=%08x esi=%08x edi=%08x\n",
  409. context->Eax, context->Ebx, context->Ecx,
  410. context->Edx, context->Esi, context->Edi);
  411. }
  412. if( context->ContextFlags & CONTEXT_CONTROL )
  413. {
  414. fprintf(log_file,
  415. "eip=%08x esp=%08x ebp=%08x iopl=%1x %s %s %s %s %s %s %s %s %s %s\n",
  416. context->Eip, context->Esp, context->Ebp,
  417. (context->EFlags >> 12) & 3, // IOPL level value
  418. context->EFlags & 0x00100000 ? "vip" : " ", // VIP (virtual interrupt pending)
  419. context->EFlags & 0x00080000 ? "vif" : " ", // VIF (virtual interrupt flag)
  420. context->EFlags & 0x00000800 ? "ov" : "nv", // VIF (virtual interrupt flag)
  421. context->EFlags & 0x00000400 ? "dn" : "up", // OF (overflow flag)
  422. context->EFlags & 0x00000200 ? "ei" : "di", // IF (interrupt enable flag)
  423. context->EFlags & 0x00000080 ? "ng" : "pl", // SF (sign flag)
  424. context->EFlags & 0x00000040 ? "zr" : "nz", // ZF (zero flag)
  425. context->EFlags & 0x00000010 ? "ac" : "na", // AF (aux carry flag)
  426. context->EFlags & 0x00000004 ? "po" : "pe", // PF (parity flag)
  427. context->EFlags & 0x00000001 ? "cy" : "nc"); // CF (carry flag)
  428. }
  429. if( context->ContextFlags & CONTEXT_SEGMENTS )
  430. {
  431. fprintf(log_file,
  432. "cs=%04x ss=%04x ds=%04x es=%04x fs=%04x gs=%04x",
  433. context->SegCs,
  434. context->SegSs,
  435. context->SegDs,
  436. context->SegEs,
  437. context->SegFs,
  438. context->SegGs,
  439. context->EFlags);
  440. if( context->ContextFlags & CONTEXT_CONTROL )
  441. fprintf(log_file,
  442. " efl=%08x",
  443. context->EFlags);
  444. fprintf(log_file, "\n");
  445. }
  446. else if( context->ContextFlags & CONTEXT_CONTROL )
  447. fprintf(log_file,
  448. " efl=%08x\n",
  449. context->EFlags);
  450. #else /* defined(_M_IX86) */
  451. //TODO add more processors
  452. #endif
  453. }
  454. }
  455. /// Prints the typename of the symbol.
  456. ///
  457. /// @param typeIndex Type index of the symbol
  458. /// @param symtag Symbol tag
  459. /// @param withParens If brackets are printed around the typename
  460. /// @param interData Inter data
  461. static VOID
  462. Dhp__PrintTypeName(
  463. DWORD typeIndex,
  464. DWORD symtag,
  465. BOOL withParens,
  466. InterData* interData)
  467. {
  468. // inter data
  469. FILE* log_file;
  470. HANDLE hProcess;
  471. ULONG64 modBase;
  472. //
  473. assert( interData != NULL );
  474. log_file = interData->log_file;
  475. hProcess = interData->hProcess;
  476. modBase = interData->modBase;
  477. if( withParens )
  478. fprintf(log_file, "(");
  479. switch( symtag )
  480. {
  481. case SymTagEnum:
  482. {
  483. WCHAR* pwszTypeName;
  484. if( SymGetTypeInfo_(hProcess, modBase, typeIndex, TI_GET_SYMNAME, &pwszTypeName) )
  485. {
  486. fprintf(log_file, "enum %ls", pwszTypeName);
  487. LocalFree(pwszTypeName);
  488. }
  489. else
  490. fprintf(log_file, "enum <symname not found>");
  491. }
  492. break;
  493. case SymTagBaseType:
  494. {
  495. DWORD basetype;
  496. ULONG64 length = 0;
  497. SymGetTypeInfo_(hProcess, modBase, typeIndex, TI_GET_LENGTH, &length);
  498. if( !SymGetTypeInfo_(hProcess, modBase, typeIndex, TI_GET_BASETYPE, &basetype) )
  499. {
  500. fprintf(log_file, "<basetype not found>");
  501. break;
  502. }
  503. switch( basetype )
  504. {
  505. case btVoid: fprintf(log_file, "void"); break;
  506. case btChar: fprintf(log_file, "char"); break;
  507. case btWChar: fprintf(log_file, "wchar"); break;
  508. case btULong: fprintf(log_file, "unsigned "); // next
  509. case btLong: fprintf(log_file, "long"); break;
  510. case btUInt: fprintf(log_file, "unsigned "); // next
  511. case btInt:
  512. if( length == sizeof(char) ) fprintf(log_file, "char");
  513. else if( length == sizeof(short) ) fprintf(log_file, "short");
  514. else if( length == sizeof(int) ) fprintf(log_file, "int");
  515. else if( length == sizeof(long long) ) fprintf(log_file, "long long");
  516. else fprintf(log_file, "<int%d>", length*8);
  517. break;
  518. case btFloat:
  519. if( length == sizeof(float) ) fprintf(log_file, "float");
  520. else if( length == sizeof(double) ) fprintf(log_file, "double");
  521. else if( length == sizeof(long double) ) fprintf(log_file, "long double");
  522. else fprintf(log_file, "<float%d>", length*8);
  523. break;
  524. default: fprintf(log_file, "<TODO name of basetype %d %d>", basetype, length); break;
  525. }
  526. }
  527. break;
  528. case SymTagPointerType:
  529. {
  530. DWORD subtype;
  531. DWORD subtag;
  532. if( !SymGetTypeInfo_(hProcess, modBase, typeIndex, TI_GET_TYPE, &subtype) )
  533. fprintf(log_file, "<type not found>*");
  534. else if( !SymGetTypeInfo_(hProcess, modBase, subtype, TI_GET_SYMTAG, &subtag) )
  535. fprintf(log_file, "<symtag not found>*");
  536. else
  537. {
  538. Dhp__PrintTypeName(subtype, subtag, FALSE, interData);
  539. fprintf(log_file, "*");
  540. }
  541. }
  542. break;
  543. case SymTagArrayType:
  544. {
  545. DWORD childTypeIndex;
  546. DWORD childSymtag;
  547. // print root type
  548. childTypeIndex = typeIndex;
  549. childSymtag = symtag;
  550. for( ; ; )
  551. {
  552. if( !SymGetTypeInfo_( hProcess, modBase, childTypeIndex, TI_GET_TYPE, &childTypeIndex) )
  553. {
  554. fprintf(log_file, "<child type not found>");
  555. break;
  556. }
  557. if( !SymGetTypeInfo_( hProcess, modBase, childTypeIndex, TI_GET_SYMTAG, &childSymtag) )
  558. {
  559. fprintf(log_file, "<child symtag not found>");
  560. break;
  561. }
  562. if( childSymtag != SymTagArrayType )
  563. {
  564. Dhp__PrintTypeName(childTypeIndex, childSymtag, FALSE, interData);
  565. break;
  566. }
  567. // next dimension
  568. }
  569. // print dimensions
  570. childTypeIndex = typeIndex;
  571. childSymtag = symtag;
  572. for( ; childSymtag == SymTagArrayType ; )
  573. {
  574. DWORD childCount;
  575. if( !SymGetTypeInfo_( hProcess, modBase, childTypeIndex, TI_GET_COUNT, &childCount) )
  576. fprintf(log_file, "[<count not found>]");
  577. else
  578. fprintf(log_file, "[%u]", childCount);
  579. if( !SymGetTypeInfo_( hProcess, modBase, childTypeIndex, TI_GET_TYPE, &childTypeIndex) )
  580. {
  581. fprintf(log_file, "<child type not found>");
  582. break;
  583. }
  584. if( !SymGetTypeInfo_( hProcess, modBase, childTypeIndex, TI_GET_SYMTAG, &childSymtag) )
  585. {
  586. fprintf(log_file, "<child symtag not found>");
  587. break;
  588. }
  589. // next dimension
  590. }
  591. }
  592. break;
  593. default:
  594. {
  595. WCHAR* pSymname;
  596. DWORD udtkind;
  597. if( SymGetTypeInfo_( hProcess, modBase, typeIndex, TI_GET_UDTKIND, &udtkind) )
  598. {
  599. switch( (UdtKind)udtkind )
  600. {
  601. case UdtStruct: fprintf(log_file, "struct "); break;
  602. case UdtClass: fprintf(log_file, "class "); break;
  603. case UdtUnion: fprintf(log_file, "union "); break;
  604. default: fprintf(log_file, "<unknown udtkind %d> ", udtkind); break;
  605. }
  606. }
  607. if( SymGetTypeInfo_( hProcess, modBase, typeIndex, TI_GET_SYMNAME, &pSymname ) )
  608. {
  609. fprintf(log_file, "%ls", pSymname);
  610. LocalFree( pSymname );
  611. }
  612. else
  613. fprintf(log_file, "<TODO typename of symtag %d>", symtag); break;
  614. }
  615. break;
  616. }
  617. if( withParens )
  618. fprintf(log_file, ")");
  619. }
  620. /// Prints the bytes in the target location.
  621. ///
  622. /// @param log_file Log file
  623. /// @param p Pointer to the data
  624. /// @param length Length of the data in bytes
  625. static VOID
  626. Dhp__PrintValueBytes(
  627. FILE* log_file,
  628. BYTE* p,
  629. ULONG64 length)
  630. {
  631. ULONG64 i;
  632. fprintf(log_file, "<bytes:");
  633. for( i = 0; i < length; ++i )
  634. {
  635. fprintf(log_file, "%02X", p[i]);
  636. }
  637. fprintf(log_file, ">");
  638. }
  639. /// Prints a wide string/char value.
  640. ///
  641. /// @param log_file Log file
  642. /// @param p Pointer to the value
  643. /// @param length Length of the value in bytes
  644. static VOID
  645. Dhp__PrintValueWideChars(
  646. FILE* log_file,
  647. WCHAR* wstr,
  648. ULONG64 length,
  649. BOOL isString)
  650. {
  651. ULONG64 i;
  652. char* buf;
  653. char delim;
  654. length /= sizeof(WCHAR);
  655. delim = ( isString || length > 1 ? '\"' : '\'' );
  656. fprintf(log_file, "%c", delim);
  657. buf = (char *)LocalAlloc(LMEM_FIXED, MB_CUR_MAX+1);
  658. buf[MB_CUR_MAX] = '\0';
  659. for( i = 0; i < length; ++i )
  660. {
  661. int n;
  662. switch( wstr[i] )
  663. {
  664. case L'\"': fprintf(log_file, "\\\""); break;
  665. case L'\'': fprintf(log_file, "\\\'"); break;
  666. case L'\\': fprintf(log_file, "\\\\"); break;
  667. case L'\a': fprintf(log_file, "\\a"); break;
  668. case L'\b': fprintf(log_file, "\\b"); break;
  669. case L'\f': fprintf(log_file, "\\f"); break;
  670. case L'\n': fprintf(log_file, "\\n"); break;
  671. case L'\r': fprintf(log_file, "\\r"); break;
  672. case L'\t': fprintf(log_file, "\\t"); break;
  673. case L'\v': fprintf(log_file, "\\v"); break;
  674. default:
  675. if( iswprint(wstr[i]) && (n=wctomb(buf, wstr[i])) > 0 )
  676. {
  677. buf[n] = '\0';
  678. fprintf(log_file, "%s", buf);
  679. }
  680. else fprintf(log_file, "\\u%04X", wstr[i]);
  681. break;
  682. }
  683. }
  684. LocalFree(buf);
  685. fprintf(log_file, "%c", delim);
  686. }
  687. /// Prints a string/char value.
  688. ///
  689. /// @param log_file Log file
  690. /// @param p Pointer to the value
  691. /// @param length Length of the value in bytes
  692. static VOID
  693. Dhp__PrintValueChars(
  694. FILE* log_file,
  695. char* str,
  696. ULONG64 length,
  697. BOOL isString)
  698. {
  699. ULONG64 i;
  700. char delim;
  701. length /= sizeof(char);
  702. delim = ( isString || length > 1 ? '\"' : '\'' );
  703. fprintf(log_file, "%c", delim);
  704. for( i = 0; i < length; ++i )
  705. {
  706. switch( str[i] )
  707. {
  708. case '\"': fprintf(log_file, "\\\""); break;
  709. case '\'': fprintf(log_file, "\\\'"); break;
  710. case '\\': fprintf(log_file, "\\\\"); break;
  711. case '\a': fprintf(log_file, "\\a"); break;
  712. case '\b': fprintf(log_file, "\\b"); break;
  713. case '\f': fprintf(log_file, "\\f"); break;
  714. case '\n': fprintf(log_file, "\\n"); break;
  715. case '\r': fprintf(log_file, "\\r"); break;
  716. case '\t': fprintf(log_file, "\\t"); break;
  717. case '\v': fprintf(log_file, "\\v"); break;
  718. default:
  719. if( isprint((unsigned char)str[i]) ) fprintf(log_file, "%c", str[i]);
  720. else fprintf(log_file, "\\x%02X", (unsigned char)str[i]);
  721. break;
  722. }
  723. }
  724. fprintf(log_file, "%c", delim);
  725. }
  726. /// Prints a float value.
  727. ///
  728. /// @param log_file Log file
  729. /// @param p Pointer to the value
  730. /// @param length Length of the value in bytes
  731. static VOID
  732. Dhp__PrintValueFloat(
  733. FILE* log_file,
  734. VOID* p,
  735. ULONG64 length)
  736. {
  737. if( length == sizeof(float) ) fprintf(log_file, "%f", *(float*)p);
  738. else if( length == sizeof(double) ) fprintf(log_file, "%lf", *(double*)p);
  739. else if( length == sizeof(long double) ) fprintf(log_file, "%Lf", *(long double*)p);
  740. else
  741. {
  742. fprintf(log_file, "<unexpected length %I64u>", length);
  743. Dhp__PrintValueBytes(log_file, (BYTE*)p, length);
  744. }
  745. }
  746. /// Prints a hex value.
  747. ///
  748. /// @param log_file Log file
  749. /// @param p Pointer to the value
  750. /// @param length Length of the value in bytes
  751. static VOID
  752. Dhp__PrintValueHex(
  753. FILE* log_file,
  754. VOID* p,
  755. ULONG64 length)
  756. {
  757. if( length == sizeof(UINT32) ) fprintf(log_file, "0x%I32X", *(UINT32*)p);
  758. else if( length == sizeof(UINT64) ) fprintf(log_file, "0x%I64X", *(UINT64*)p);
  759. else if( length == sizeof(char) ) fprintf(log_file, "0x%X", *(unsigned char*)p);
  760. else if( length == sizeof(short) ) fprintf(log_file, "0x%X", *(unsigned short*)p);
  761. else if( length == sizeof(int) ) fprintf(log_file, "0x%x", *(unsigned int*)p);
  762. else if( length == sizeof(long) ) fprintf(log_file, "0x%lX", *(unsigned long*)p);
  763. else if( length == sizeof(long long) ) fprintf(log_file, "0x%llX", *(unsigned long long*)p);
  764. else
  765. {
  766. fprintf(log_file, "<unexpected length %I64u>", length);
  767. Dhp__PrintValueBytes(log_file, (BYTE*)p, length);
  768. }
  769. }
  770. /// Prints an unsigned integer value.
  771. ///
  772. /// @param log_file Log file
  773. /// @param p Pointer to the value
  774. /// @param length Length of the value in bytes
  775. static VOID
  776. Dhp__PrintValueUnsigned(
  777. FILE* log_file,
  778. VOID* p,
  779. ULONG64 length)
  780. {
  781. if( length == sizeof(INT32) ) fprintf(log_file, "%I32u", *(INT32*)p);
  782. else if( length == sizeof(INT64) ) fprintf(log_file, "%I64u", *(INT64*)p);
  783. else if( length == sizeof(char) ) fprintf(log_file, "%u", *(unsigned char*)p);
  784. else if( length == sizeof(short) ) fprintf(log_file, "%u", *(unsigned short*)p);
  785. else if( length == sizeof(int) ) fprintf(log_file, "%u", *(unsigned int*)p);
  786. else if( length == sizeof(long) ) fprintf(log_file, "%lu", *(unsigned long*)p);
  787. else if( length == sizeof(long long) ) fprintf(log_file, "%llu", *(unsigned long long*)p);
  788. else
  789. {
  790. fprintf(log_file, "<unexpected length %I64u>", length);
  791. Dhp__PrintValueBytes(log_file, (BYTE*)p, length);
  792. }
  793. }
  794. /// Prints a signed integer value.
  795. ///
  796. /// @param log_file Log file
  797. /// @param p Pointer to the value
  798. /// @param length Length of the value in bytes
  799. static VOID
  800. Dhp__PrintValueSigned(
  801. FILE* log_file,
  802. VOID* p,
  803. ULONG64 length)
  804. {
  805. if( length == sizeof(INT32) ) fprintf(log_file, "%I32d", *(INT32*)p);
  806. else if( length == sizeof(INT64) ) fprintf(log_file, "%I64d", *(INT64*)p);
  807. else if( length == sizeof(char) ) fprintf(log_file, "%d", *(signed char*)p);
  808. else if( length == sizeof(short) ) fprintf(log_file, "%d", *(signed short*)p);
  809. else if( length == sizeof(int) ) fprintf(log_file, "%d", *(signed int*)p);
  810. else if( length == sizeof(long) ) fprintf(log_file, "%ld", *(signed long*)p);
  811. else if( length == sizeof(long long) ) fprintf(log_file, "%lld", *(signed long long*)p);
  812. else
  813. {
  814. fprintf(log_file, "<unexpected length %I64u>", length);
  815. Dhp__PrintValueBytes(log_file, (BYTE*)p, length);
  816. }
  817. }
  818. /// Prints a nul-terminated wide string value.
  819. /// Checks if the memory can be read from.
  820. ///
  821. /// @param log_file Log file
  822. /// @param str Target string
  823. static VOID
  824. Dhp__PrintValueCWideString(
  825. FILE* log_file,
  826. WCHAR* str)
  827. {
  828. ULONG64 length = 0;
  829. // check if memory is readable
  830. __try
  831. {
  832. while( str[length] != L'\0' )
  833. ++length;
  834. }
  835. __except( EXCEPTION_EXECUTE_HANDLER )
  836. {
  837. if( length ) Dhp__PrintValueWideChars(log_file, str, length*sizeof(WCHAR), TRUE); // print readable part
  838. fprintf(log_file, "<invalid memory>");
  839. return;
  840. }
  841. // print string
  842. Dhp__PrintValueWideChars(log_file, str, length*sizeof(WCHAR), TRUE);
  843. }
  844. /// Prints a nul-terminated string value.
  845. /// Checks if the memory can be read from.
  846. ///
  847. /// @param log_file Log file
  848. /// @param str Target string
  849. static VOID
  850. Dhp__PrintValueCString(
  851. FILE* log_file,
  852. char* str)
  853. {
  854. ULONG64 length = 0;
  855. assert( log_file != NULL );
  856. // check if memory is readable
  857. __try
  858. {
  859. while( str[length] != '\0' )
  860. ++length;
  861. }
  862. __except( EXCEPTION_EXECUTE_HANDLER )
  863. {
  864. if( length ) Dhp__PrintValueChars(log_file, str, length*sizeof(char), TRUE); // print readable part
  865. fprintf(log_file, "<invalid memory>");
  866. return;
  867. }
  868. // print string
  869. Dhp__PrintValueChars(log_file, str, length*sizeof(char), TRUE);
  870. }
  871. // forward declaration of Dhp__PrintDataContents
  872. static VOID Dhp__PrintDataContents(DWORD typeIndex, PVOID pVariable, InterData* interData);
  873. /// Prints the value of the data symbol.
  874. /// Checks if the memory can be read from.
  875. ///
  876. /// @param typeIndex Type index of the symbol
  877. /// @param symtag Symbol tag
  878. /// @param pVariable Address to the symbol contents
  879. /// @param pInterData Inter data
  880. static VOID
  881. Dhp__PrintDataValue(
  882. DWORD typeIndex,
  883. DWORD symtag,
  884. PVOID pVariable,
  885. InterData* pInterData)
  886. {
  887. // inter data
  888. FILE* log_file;
  889. DWORD64 modBase;
  890. HANDLE hProcess;
  891. //
  892. ULONG64 length = 0;
  893. DWORD basetype;
  894. BOOL isValid = TRUE;
  895. assert( pInterData != NULL );
  896. log_file = pInterData->log_file;
  897. modBase = pInterData->modBase;
  898. hProcess = pInterData->hProcess;
  899. if( !SymGetTypeInfo_(hProcess, modBase, typeIndex, TI_GET_LENGTH, &length) )
  900. {
  901. fprintf(log_file, "<unknown data length>");
  902. return;
  903. }
  904. // check if memory is readable
  905. __try
  906. {
  907. BYTE* p = (BYTE*)pVariable;
  908. ULONG i;
  909. BYTE b = 0;
  910. for( i = 0; i < length; ++i )
  911. b += p[i]; // add to make sure it's not optimized out in release mode
  912. }
  913. __except( EXCEPTION_EXECUTE_HANDLER )
  914. {
  915. fprintf(log_file, "<invalid memory>");
  916. return;
  917. }
  918. switch( symtag )
  919. {
  920. case SymTagBaseType:
  921. {
  922. if( !SymGetTypeInfo_(hProcess, modBase, typeIndex, TI_GET_BASETYPE, &basetype) )
  923. {
  924. fprintf(log_file, "<basetype not found>");
  925. Dhp__PrintValueBytes(log_file, (BYTE*)pVariable, length);
  926. break;
  927. }
  928. switch( basetype )
  929. {
  930. case btInt:
  931. case btLong:
  932. Dhp__PrintValueSigned(log_file, pVariable, length);
  933. break;
  934. case btUInt:
  935. case btULong:
  936. Dhp__PrintValueUnsigned(log_file, pVariable, length);
  937. break;
  938. case btFloat:
  939. Dhp__PrintValueFloat(log_file, pVariable, length);
  940. break;
  941. case btChar:
  942. {
  943. if( length == sizeof(char) ) fprintf(log_file, "%u ", *(unsigned char*)pVariable);
  944. Dhp__PrintValueChars(log_file, (char*)pVariable, length, FALSE);
  945. }
  946. break;
  947. case btWChar:
  948. {
  949. if( length == sizeof(WCHAR) ) fprintf(log_file, "%u ", *(WCHAR*)pVariable);
  950. Dhp__PrintValueWideChars(log_file, (WCHAR*)pVariable, length, FALSE);
  951. }
  952. break;
  953. case btVoid:
  954. if( length > 0 ) Dhp__PrintValueBytes(log_file, (BYTE*)pVariable, length);
  955. break;
  956. default:
  957. fprintf(log_file, "<TODO value of basetype %d>", basetype);
  958. Dhp__PrintValueBytes(log_file, (BYTE*)pVariable, length);
  959. break;
  960. }
  961. }
  962. break;
  963. case SymTagEnum:
  964. Dhp__PrintValueHex(log_file, pVariable, length);
  965. break;
  966. case SymTagPointerType:
  967. {
  968. DWORD childTypeIndex;
  969. DWORD childSymtag;
  970. fprintf(log_file, "0x%p", *(void**)pVariable);
  971. if( SymGetTypeInfo_(hProcess, modBase, typeIndex, TI_GET_TYPE, &childTypeIndex) &&
  972. SymGetTypeInfo_(hProcess, modBase, childTypeIndex, TI_GET_SYMTAG, &childSymtag) &&
  973. childSymtag != SymTagPointerType )
  974. {
  975. DWORD childBasetype;
  976. // child isn't a pointer, print the contents
  977. fprintf(log_file, " ");
  978. if( childSymtag == SymTagBaseType &&
  979. SymGetTypeInfo_(hProcess, modBase, childTypeIndex, TI_GET_BASETYPE, &childBasetype) &&
  980. (childBasetype == btChar || childBasetype == btWChar) )
  981. {
  982. // string or wide string
  983. if( childBasetype == btChar ) Dhp__PrintValueCString(log_file, *(char**)pVariable);
  984. else if( childBasetype == btWChar ) Dhp__PrintValueCWideString(log_file, *(WCHAR**)pVariable);
  985. else fprintf(log_file, "<unexpected child basetype %d>", childBasetype);
  986. break;
  987. }
  988. Dhp__PrintDataValue(childTypeIndex, childSymtag, *(PVOID*)pVariable, pInterData);
  989. }
  990. }
  991. break;
  992. case SymTagArrayType:
  993. {
  994. DWORD childTypeIndex;
  995. DWORD childSymtag;
  996. DWORD count;
  997. DWORD i;
  998. if( !SymGetTypeInfo_( hProcess, modBase, typeIndex, TI_GET_TYPE, &childTypeIndex) )
  999. {
  1000. fprintf(log_file, "<child type not found>");
  1001. Dhp__PrintValueBytes(log_file, (BYTE*)pVariable, length);
  1002. break;
  1003. }
  1004. if( !SymGetTypeInfo_( hProcess, modBase, childTypeIndex, TI_GET_SYMTAG, &childSymtag) )
  1005. {
  1006. fprintf(log_file, "<child symtag not found>");
  1007. Dhp__PrintValueBytes(log_file, (BYTE*)pVariable, length);
  1008. break;
  1009. }
  1010. if( !SymGetTypeInfo_( hProcess, modBase, typeIndex, TI_GET_COUNT, &count) )
  1011. {
  1012. fprintf(log_file, "<count not found>");
  1013. Dhp__PrintValueBytes(log_file, (BYTE*)pVariable, length);
  1014. break;
  1015. }
  1016. // print values
  1017. fprintf(log_file, "{");
  1018. for( i = 0; i < count; ++i )
  1019. {
  1020. BYTE* pData = pVariable;
  1021. pData += i*(length/count);
  1022. if( i > 0 ) fprintf(log_file, ",");
  1023. Dhp__PrintDataValue(childTypeIndex, childSymtag, pData, pInterData);
  1024. }
  1025. fprintf(log_file, "}");
  1026. }
  1027. break;
  1028. default:
  1029. #if 0
  1030. {//## TODO show children of structs/unions
  1031. TI_FINDCHILDREN_PARAMS* children;
  1032. DWORD childCount;
  1033. DWORD i;
  1034. // count children
  1035. if( !SymGetTypeInfo_(hProcess, modBase, typeIndex, TI_GET_CHILDRENCOUNT, &childCount) )
  1036. {
  1037. fprintf(log_file, "<child count not found>");
  1038. Dhp__PrintValueBytes(log_file, (BYTE*)pVariable, length);
  1039. break;
  1040. }
  1041. // Prepare to get an array of "TypeIds", representing each of the children.
  1042. // SymGetTypeInfo(TI_FINDCHILDREN) expects more memory than just a
  1043. // TI_FINDCHILDREN_PARAMS struct has. Use derivation to accomplish this.
  1044. children = (TI_FINDCHILDREN_PARAMS*)LocalAlloc(LMEM_FIXED, sizeof(TI_FINDCHILDREN_PARAMS)+childCount*sizeof(ULONG));
  1045. children->Count = childCount;
  1046. children->Start= 0;
  1047. // Get the array of TypeIds, one for each child type
  1048. if( !SymGetTypeInfo_(hProcess, modBase, typeIndex, TI_FINDCHILDREN, &children) )
  1049. {
  1050. fprintf(log_file, "<children not found>");
  1051. Dhp__PrintValueBytes(log_file, (BYTE*)pVariable, length);
  1052. LocalFree(children);
  1053. return;
  1054. }
  1055. // Iterate through each of the children
  1056. fprintf(log_file, "{");
  1057. for( i = 0; i < childCount; ++i )
  1058. {
  1059. DWORD childOffset;
  1060. DWORD childTypeid;
  1061. WCHAR* childName;
  1062. DWORD_PTR pData;
  1063. if( i > 0 ) fprintf(log_file, ",");
  1064. // Get the offset of the child member, relative to its parent
  1065. if( !SymGetTypeInfo_(hProcess, modBase, children->ChildId[i], TI_GET_OFFSET, &childOffset) )
  1066. {
  1067. fprintf(log_file, "<child offset not found>");
  1068. continue;
  1069. }
  1070. // Get the real "TypeId" of the child.
  1071. if( !SymGetTypeInfo_(hProcess, modBase, children->ChildId[i], TI_GET_TYPEID, &childTypeid) )
  1072. {
  1073. fprintf(log_file, "<child typeid not found>");
  1074. continue;
  1075. }
  1076. // Calculate the address of the member
  1077. pData = (DWORD_PTR)pVariable;
  1078. pData += childOffset;
  1079. // print name of the child
  1080. if( !SymGetTypeInfo_(hProcess, modBase, childTypeid, TI_GET_SYMNAME, &childName) )
  1081. {
  1082. fprintf(log_file, "<child symname not found>");
  1083. continue;
  1084. }
  1085. fprintf(log_file, "%ws=", childName);
  1086. LocalFree(childName);
  1087. // print contents of the child
  1088. Dhp__PrintDataContents(childTypeid, (PVOID)pData, interData);
  1089. }
  1090. fprintf(log_file, "}");
  1091. LocalFree(children);
  1092. }
  1093. #endif
  1094. Dhp__PrintValueBytes(log_file, (BYTE*)pVariable, length);
  1095. break;
  1096. }
  1097. }
  1098. /// Prints the contents of the data symbol. (type and value)
  1099. ///
  1100. /// @param typeIndex Type index of the symbol
  1101. /// @param pVariable Address of the symbol contents
  1102. /// @param pInterData Inter data
  1103. static VOID
  1104. Dhp__PrintDataContents(
  1105. DWORD typeIndex,
  1106. PVOID pVariable,
  1107. InterData* pInterData)
  1108. {
  1109. // inter data
  1110. FILE* log_file;
  1111. HANDLE hProcess;
  1112. DWORD64 modBase;
  1113. //
  1114. DWORD symtag;
  1115. assert( pInterData != NULL );
  1116. log_file = pInterData->log_file;
  1117. hProcess = pInterData->hProcess;
  1118. modBase = pInterData->modBase;
  1119. if( SymGetTypeInfo_(hProcess, modBase, typeIndex, TI_GET_SYMTAG, &symtag) )
  1120. {
  1121. // print type
  1122. Dhp__PrintTypeName(typeIndex, symtag, TRUE, pInterData);
  1123. // print value
  1124. Dhp__PrintDataValue(typeIndex, symtag, pVariable, pInterData);
  1125. }
  1126. else
  1127. fprintf(log_file, "<symtag not found>");
  1128. }
  1129. /// Prints information about the data symbol.
  1130. ///
  1131. /// @param pSymInfo Symbol info
  1132. /// @param pInterData Inter data
  1133. static VOID
  1134. Dhp__PrintDataInfo(
  1135. PSYMBOL_INFO pSymInfo,
  1136. InterData* pInterData)
  1137. {
  1138. // inter data
  1139. FILE* log_file;
  1140. STACKFRAME* pStackframe;
  1141. BOOL as_arg_list;
  1142. BOOL log_params;
  1143. BOOL log_locals;
  1144. BOOL log_globals;
  1145. int nr_of_var;
  1146. // my data
  1147. DWORD_PTR pVariable = 0;
  1148. enum{ UNKNOWN, PARAM, LOCAL, GLOBAL } scope = UNKNOWN;
  1149. assert( pSymInfo != NULL );
  1150. assert( pInterData != NULL );
  1151. assert( pSymInfo->Tag == SymTagData );
  1152. log_file = pInterData->log_file;
  1153. pStackframe = pInterData->pStackframe;
  1154. as_arg_list = pInterData->as_arg_list;
  1155. log_params = pInterData->log_params;
  1156. log_locals = pInterData->log_locals;
  1157. log_globals = pInterData->log_globals;
  1158. nr_of_var = pInterData->nr_of_var;
  1159. // Determine the scope and address of the variable
  1160. if( pSymInfo->Flags & SYMFLAG_REGREL )
  1161. {
  1162. pVariable = pStackframe->AddrFrame.Offset;
  1163. pVariable += (DWORD_PTR)pSymInfo->Address;
  1164. if( pSymInfo->Flags & SYMFLAG_PARAMETER )
  1165. scope = PARAM; // parameter
  1166. else if( pSymInfo->Flags & SYMFLAG_LOCAL )
  1167. {
  1168. scope = LOCAL; // local
  1169. #if defined(_M_IX86)
  1170. if( (LONG64)pSymInfo->Address > 0) scope = PARAM; // parameter as local (bug in DBGHELP 5.1)
  1171. #endif
  1172. }
  1173. }
  1174. else if( pSymInfo->Flags & SYMFLAG_REGISTER )
  1175. {
  1176. scope = ( pSymInfo->Flags & SYMFLAG_PARAMETER ? PARAM : LOCAL ); // register, optimized out(?)
  1177. }
  1178. else
  1179. {
  1180. pVariable = (DWORD_PTR)pSymInfo->Address;
  1181. scope = GLOBAL; // It must be a global variable
  1182. }
  1183. // check if we should to log the variable
  1184. if( (scope == PARAM && log_params) ||
  1185. (scope == LOCAL && log_locals) ||
  1186. (scope == GLOBAL && log_globals) )
  1187. {
  1188. // print prefix and name
  1189. if( as_arg_list )
  1190. fprintf(log_file, "%s%s=", (nr_of_var ? ", " : ""), pSymInfo->Name);
  1191. else
  1192. fprintf(log_file, "\t%s = ", pSymInfo->Name);
  1193. // print value
  1194. if( !(pSymInfo->Flags & SYMFLAG_REGREL) && (pSymInfo->Flags & SYMF_REGISTER) )
  1195. fprintf(log_file, "<value optimized out>");
  1196. else
  1197. {
  1198. pInterData->modBase = pSymInfo->ModBase;
  1199. Dhp__PrintDataContents(pSymInfo->TypeIndex, (PVOID)pVariable, pInterData);
  1200. }
  1201. // print postfix
  1202. if( !as_arg_list )
  1203. fprintf(log_file, "\n");
  1204. pInterData->nr_of_var = ++nr_of_var;
  1205. }
  1206. }
  1207. /// Prints information about the symbol.
  1208. ///
  1209. /// @param pSymInfo Symbol info
  1210. /// @param pInterData Inter data
  1211. static VOID
  1212. Dhp__PrintSymbolInfo(
  1213. PSYMBOL_INFO pSymInfo,
  1214. InterData* pInterData)
  1215. {
  1216. assert( pSymInfo != NULL );
  1217. assert( pInterData != NULL );
  1218. switch( pSymInfo->Tag )
  1219. {
  1220. case SymTagData: Dhp__PrintDataInfo( pSymInfo, pInterData ); break;
  1221. default: /*fprintf(pInterData->log_file, "<unsupported symtag %d>", pSymInfo->Tag);*/ break;
  1222. }
  1223. }
  1224. /// Prints the details of one symbol to the log file.
  1225. /// Used as a callback for SymEnumSymbols.
  1226. ///
  1227. /// @param pSymInfo Symbol info
  1228. /// @param symSize Size of the symbol info structure
  1229. /// @param pData Inter data
  1230. static BOOL WINAPI
  1231. Dhp__EnumSymbolsCallback(
  1232. PSYMBOL_INFO pSymInfo,
  1233. ULONG symSize,
  1234. PVOID pData)
  1235. {
  1236. if( pSymInfo == NULL )
  1237. return TRUE; // try other symbols
  1238. if( pData == NULL )
  1239. {
  1240. printf("Dhp__EnumSymbolsCallback: pData is NULL\n");
  1241. return FALSE;
  1242. }
  1243. Dhp__PrintSymbolInfo(pSymInfo, (InterData*)pData);
  1244. return TRUE;
  1245. }
  1246. /// Prints the source code of the target line.
  1247. /// Searches for the target file in the original path,
  1248. /// in the last src folder of the original path (relative)
  1249. /// and in the current directory.
  1250. ///
  1251. /// @param filename Original source file
  1252. /// @param line Target line
  1253. /// @param log_file Log file
  1254. static VOID
  1255. Dhp__PrintSourceLine(
  1256. FILE* log_file,
  1257. char* filename,
  1258. DWORD line)
  1259. {
  1260. char path[MAX_PATH*3];
  1261. char pathBuffer[MAX_PATH+1];
  1262. char* p;
  1263. assert( filename != NULL );
  1264. assert( log_file != NULL );
  1265. // generate search paths
  1266. strcpy(path, filename); // original path
  1267. p = strrchr(path, '\\');
  1268. if( p )
  1269. {
  1270. memcpy(p, ";\0", 2);
  1271. p = strstr(filename, "\\src\\");
  1272. if( p )
  1273. {
  1274. while( strstr(p+1, "\\src\\") )
  1275. p = strstr(p+1, "\\src\\");
  1276. strcat(path, p+1); // last src folder path
  1277. p = strrchr(path, '\\');
  1278. memcpy(p, ";\0", 2);
  1279. }
  1280. filename = strrchr(filename, '\\')+1;
  1281. }
  1282. else
  1283. *path = '\0'; // no path
  1284. strcat(path, "."); // current directoy
  1285. // search for file and line
  1286. if( SearchPathA(path, filename, NULL, MAX_PATH, pathBuffer, NULL) )
  1287. {
  1288. char code[1024+1];
  1289. DWORD i = 1;
  1290. FILE* fp;
  1291. fp = fopen(pathBuffer, "rt");
  1292. if( fp == NULL )
  1293. return;
  1294. code[1024] = '\0';
  1295. while( fgets(code, 1024, fp) )
  1296. {
  1297. if( i == line )
  1298. {// found line
  1299. char* term = strchr(code, '\n');
  1300. if( term && term != code && term[-1] == '\r' ) --term;
  1301. if( term ) *term = '\0';
  1302. fprintf(log_file, "%d\t%s\n", line, code);
  1303. break;
  1304. }
  1305. if( strchr(code, '\n') )
  1306. ++i;
  1307. }
  1308. fclose(fp);
  1309. }
  1310. }
  1311. /// Prints details of one function to the log file.
  1312. ///
  1313. /// @param interData Inter data
  1314. static VOID
  1315. Dhp__PrintFunctionDetails(
  1316. InterData* pInterData)
  1317. {
  1318. // inter data
  1319. HANDLE hProcess;
  1320. STACKFRAME* pStackframe;
  1321. FILE* log_file;
  1322. int nr_of_frame;
  1323. //
  1324. PSYMBOL_INFO pSymbolInfo;
  1325. DWORD64 funcDisplacement=0;
  1326. IMAGEHLP_STACK_FRAME imagehlpStackFrame;
  1327. IMAGEHLP_LINE imagehlpLine;
  1328. DWORD lineDisplacement=0;
  1329. assert( pInterData != NULL );
  1330. hProcess = pInterData->hProcess;
  1331. pStackframe = pInterData->pStackframe;
  1332. log_file = pInterData->log_file;
  1333. nr_of_frame = pInterData->nr_of_frame;
  1334. // frame info
  1335. fprintf(log_file,
  1336. "#%d 0x%p",
  1337. nr_of_frame, (void*)(DWORD_PTR)pStackframe->AddrPC.Offset);
  1338. // restrict symbol enumeration to this frame only
  1339. ZeroMemory(&imagehlpStackFrame, sizeof(IMAGEHLP_STACK_FRAME));
  1340. imagehlpStackFrame.InstructionOffset = pStackframe->AddrPC.Offset;
  1341. SymSetContext_(hProcess, &imagehlpStackFrame, 0);
  1342. // function name and displacement
  1343. pSymbolInfo = (PSYMBOL_INFO)LocalAlloc(LMEM_FIXED, sizeof(SYMBOL_INFO)+1024);
  1344. pSymbolInfo->SizeOfStruct = sizeof(SYMBOL_INFO);
  1345. pSymbolInfo->MaxNameLen = 1024;
  1346. if( SymFromAddr_(hProcess, pStackframe->AddrPC.Offset, &funcDisplacement, pSymbolInfo) == TRUE )
  1347. {
  1348. fprintf(log_file,
  1349. " in %.1024s+0x%I64X (",
  1350. pSymbolInfo->Name, funcDisplacement);
  1351. // log all function parameters
  1352. pInterData->as_arg_list = TRUE;
  1353. pInterData->log_params = TRUE;
  1354. pInterData->log_locals = FALSE;
  1355. pInterData->log_globals = FALSE;
  1356. pInterData->nr_of_var = 0;
  1357. SymEnumSymbols_(hProcess, 0, 0, Dhp__EnumSymbolsCallback, pInterData);
  1358. fprintf(log_file,
  1359. ")");
  1360. }
  1361. else
  1362. fprintf(log_file,
  1363. "in <unknown function>");
  1364. // find the source line for this function.
  1365. imagehlpLine.SizeOfStruct = sizeof(IMAGEHLP_LINE);
  1366. if( SymGetLineFromAddr_(hProcess, pStackframe->AddrPC.Offset, &lineDisplacement, &imagehlpLine) != 0 )
  1367. {
  1368. char* filename = imagehlpLine.FileName;
  1369. DWORD line = imagehlpLine.LineNumber;
  1370. fprintf(log_file,
  1371. " at %s:%d\n",
  1372. filename, line);
  1373. Dhp__PrintSourceLine(log_file, filename, line);
  1374. }
  1375. else
  1376. fprintf(log_file,
  1377. "\n");
  1378. // log all function local variables
  1379. pInterData->as_arg_list = FALSE;
  1380. pInterData->log_params = FALSE;
  1381. pInterData->log_locals = TRUE;
  1382. pInterData->log_globals = FALSE;
  1383. pInterData->nr_of_var = 0;
  1384. SymEnumSymbols_(hProcess, 0, 0, Dhp__EnumSymbolsCallback, pInterData);
  1385. pInterData->nr_of_frame = ++nr_of_frame;
  1386. LocalFree(pSymbolInfo);
  1387. }
  1388. /// Walks over the stack and prints all relevant information to the log file.
  1389. ///
  1390. /// @param context Exception context
  1391. /// @param log_file Log file
  1392. static VOID
  1393. Dhp__PrintStacktrace(
  1394. CONTEXT *context,
  1395. FILE *log_file)
  1396. {
  1397. HANDLE hProcess = GetCurrentProcess();
  1398. STACKFRAME stackframe;
  1399. DWORD machine;
  1400. CONTEXT ctx;
  1401. InterData interData;
  1402. int skip = 0;
  1403. int i;
  1404. assert( log_file != NULL );
  1405. fprintf(log_file,
  1406. "\nStacktrace:\n");
  1407. // Use thread information - if not supplied.
  1408. if( context == NULL )
  1409. {
  1410. // If no context is supplied, skip 1 frame
  1411. skip = 1;
  1412. ctx.ContextFlags = CONTEXT_FULL;
  1413. if( GetThreadContext(GetCurrentThread(), &ctx) )
  1414. context = &ctx;
  1415. }
  1416. if( context == NULL )
  1417. return;
  1418. // Write the stack trace
  1419. ZeroMemory(&stackframe, sizeof(STACKFRAME));
  1420. stackframe.AddrPC.Mode = AddrModeFlat;
  1421. stackframe.AddrStack.Mode = AddrModeFlat;
  1422. stackframe.AddrFrame.Mode = AddrModeFlat;
  1423. #if defined(_M_IX86)
  1424. machine = IMAGE_FILE_MACHINE_I386;
  1425. stackframe.AddrPC.Offset = context->Eip;
  1426. stackframe.AddrStack.Offset = context->Esp;
  1427. stackframe.AddrFrame.Offset = context->Ebp;
  1428. #else /* defined(_M_IX86) */
  1429. #error FIXME add more processors
  1430. some compilers don't stop on #error, this line makes sure it errors out
  1431. #endif
  1432. interData.hProcess = hProcess;
  1433. interData.log_file = log_file;
  1434. interData.pStackframe = &stackframe;
  1435. interData.nr_of_frame = 0;
  1436. for( i = 0; ; ++i )
  1437. {
  1438. if( !StackWalk_(machine, hProcess, GetCurrentThread(),
  1439. &stackframe, context, NULL, SymFunctionTableAccess_,
  1440. SymGetModuleBase_, NULL))
  1441. {
  1442. break;
  1443. }
  1444. if( i >= skip )
  1445. {
  1446. // Check that the address is not zero.
  1447. // Sometimes StackWalk returns TRUE with a frame of zero.
  1448. if( stackframe.AddrPC.Offset != 0 )
  1449. Dhp__PrintFunctionDetails(&interData);
  1450. }
  1451. }
  1452. }
  1453. typedef BOOL (WINAPI *ISDEBUGGERPRESENT)(void);
  1454. /// Checks if a debugger is attached to this process
  1455. ///
  1456. /// @return TRUE is a debugger is present
  1457. static BOOL
  1458. Dhp__IsDebuggerPresent()
  1459. {
  1460. HANDLE kernel32_dll;
  1461. ISDEBUGGERPRESENT IsDebuggerPresent_;
  1462. BOOL result;
  1463. kernel32_dll = LoadLibraryA("kernel32.dll");
  1464. if( kernel32_dll == NULL )
  1465. return FALSE;
  1466. IsDebuggerPresent_ = (ISDEBUGGERPRESENT)GetProcAddress(kernel32_dll, "IsDebuggerPresent");
  1467. if( IsDebuggerPresent_ )
  1468. result = IsDebuggerPresent_();
  1469. else
  1470. result = FALSE;
  1471. FreeLibrary(kernel32_dll);
  1472. return result;
  1473. }
  1474. /// Loads the dbghelp.dll library.
  1475. ///
  1476. /// @return TRUE is sucessfull
  1477. static BOOL
  1478. Dhp__LoadDbghelpDll()
  1479. {
  1480. dbghelp_dll = LoadLibraryA(DBGHELP_DLL);
  1481. if( dbghelp_dll != INVALID_HANDLE_VALUE )
  1482. {
  1483. DWORD opts;
  1484. // load the functions
  1485. MiniDumpWriteDump_ = (MINIDUMPWRITEDUMP)GetProcAddress(dbghelp_dll, "MiniDumpWriteDump");
  1486. SymInitialize_ = (SYMINITIALIZE)GetProcAddress(dbghelp_dll, "SymInitialize");
  1487. SymSetOptions_ = (SYMSETOPTIONS)GetProcAddress(dbghelp_dll, "SymSetOptions");
  1488. SymGetOptions_ = (SYMGETOPTIONS)GetProcAddress(dbghelp_dll, "SymGetOptions");
  1489. SymCleanup_ = (SYMCLEANUP)GetProcAddress(dbghelp_dll, "SymCleanup");
  1490. SymGetTypeInfo_ = (SYMGETTYPEINFO)GetProcAddress(dbghelp_dll, "SymGetTypeInfo");
  1491. SymGetLineFromAddr_ = (SYMGETLINEFROMADDR)GetProcAddress(dbghelp_dll, "SymGetLineFromAddr");
  1492. SymEnumSymbols_ = (SYMENUMSYMBOLS)GetProcAddress(dbghelp_dll, "SymEnumSymbols");
  1493. SymSetContext_ = (SYMSETCONTEXT)GetProcAddress(dbghelp_dll, "SymSetContext");
  1494. SymFromAddr_ = (SYMFROMADDR)GetProcAddress(dbghelp_dll, "SymFromAddr");
  1495. StackWalk_ = (STACKWALK)GetProcAddress(dbghelp_dll, "StackWalk");
  1496. SymFunctionTableAccess_ = (SYMFUNCTIONTABLEACCESS)GetProcAddress(dbghelp_dll, "SymFunctionTableAccess");
  1497. SymGetModuleBase_ = (SYMGETMODULEBASE)GetProcAddress(dbghelp_dll, "SymGetModuleBase");
  1498. if( MiniDumpWriteDump_ &&
  1499. SymInitialize_ && SymSetOptions_ && SymGetOptions_ &&
  1500. SymCleanup_ && SymGetTypeInfo_ && SymGetLineFromAddr_ &&
  1501. SymEnumSymbols_ && SymSetContext_ && SymFromAddr_ && StackWalk_ &&
  1502. SymFunctionTableAccess_ && SymGetModuleBase_ )
  1503. {
  1504. // initialize the symbol loading code
  1505. opts = SymGetOptions_();
  1506. // Set the 'load lines' option to retrieve line number information.
  1507. // Set the 'deferred loads' option to map the debug info in memory only when needed.
  1508. SymSetOptions_(opts | SYMOPT_LOAD_LINES | SYMOPT_DEFERRED_LOADS);
  1509. // Initialize the dbghelp DLL with the default path and automatic
  1510. // module enumeration (and loading of symbol tables) for this process.
  1511. SymInitialize_(GetCurrentProcess(), NULL, TRUE);
  1512. return TRUE;
  1513. }
  1514. }
  1515. if( dbghelp_dll )
  1516. {
  1517. FreeLibrary(dbghelp_dll);
  1518. dbghelp_dll = NULL;
  1519. }
  1520. return FALSE;
  1521. }
  1522. /// Unloads the dbghelp.dll library.
  1523. static VOID
  1524. Dhp__UnloadDbghlpDll()
  1525. {
  1526. SymCleanup_(GetCurrentProcess());
  1527. FreeLibrary(dbghelp_dll);
  1528. dbghelp_dll = NULL;
  1529. }
  1530. /// Creates the report and minidump files.
  1531. /// Puts the resulting pathnames in the arguments.
  1532. /// The buffers must be at least MAX_PATH+1 in size.
  1533. ///
  1534. /// @param out_lpszLogFileName Buffer for the report filename
  1535. /// @param out_lpszDmpFileName Buffer for the minidump filename
  1536. /// @return TRUE if the files were created
  1537. static BOOL
  1538. Dhp__CreateFiles(
  1539. char* out_logFileName,
  1540. char* out_dmpFileName)
  1541. {
  1542. #define LEN_TIMESTAMP 14 // "YYYYMMDDhhmmss"
  1543. #define LEN_EXT 4 // ".rpt" or ".dmp"
  1544. char baseFileName[MAX_PATH+1];
  1545. char timestamp[LEN_TIMESTAMP+1];
  1546. FILE* fp;
  1547. time_t now;
  1548. // Generate base filename for the report/minidump
  1549. ZeroMemory(baseFileName, sizeof(baseFileName));
  1550. if( GetModuleFileName(NULL, baseFileName, MAX_PATH-LEN_TIMESTAMP-LEN_EXT) )
  1551. {
  1552. char* pTerm = strrchr(baseFileName, '\\');
  1553. if( pTerm == NULL ) pTerm = baseFileName;
  1554. pTerm = strrchr(pTerm, '.');
  1555. if( pTerm ) *pTerm = '\0'; // remove extension
  1556. }
  1557. else if( GetTempPathA(MAX_PATH-6-LEN_TIMESTAMP-LEN_EXT, baseFileName) )
  1558. {// in temp folder
  1559. strcat(baseFileName, DBG_DEFAULT_FILENAME);
  1560. }
  1561. else
  1562. {// in current folder
  1563. strcpy(baseFileName, DBG_DEFAULT_FILENAME);
  1564. }
  1565. time(&now);
  1566. #if 0
  1567. szTimestamp[0] = '\0';
  1568. #else
  1569. strftime(timestamp, sizeof(timestamp), "%Y%m%d%H%M%S", localtime(&now));
  1570. #endif
  1571. timestamp[LEN_TIMESTAMP] = '\0';
  1572. sprintf(out_logFileName, "%s%s.rpt", baseFileName, timestamp);
  1573. fp = fopen(out_logFileName, "w");
  1574. if( fp == NULL )
  1575. return FALSE; // failed to create log file
  1576. fclose(fp);
  1577. sprintf(out_dmpFileName, "%s%s.dmp", baseFileName, timestamp);
  1578. fp = fopen(out_dmpFileName, "w");
  1579. if( fp == NULL)
  1580. return FALSE; // failed to create dump file
  1581. fclose(fp);
  1582. return TRUE; // success
  1583. #undef LEN_EXT
  1584. #undef LEN_TIMESTAMP
  1585. }
  1586. /// Unhandled exception handler. Where the magic starts... ;D
  1587. ///
  1588. /// @param ptrs Exception information
  1589. /// @return What to do with the exception
  1590. LONG WINAPI
  1591. Dhp__UnhandledExceptionFilter(PEXCEPTION_POINTERS ptrs)
  1592. {
  1593. char szLogFileName[MAX_PATH+1];
  1594. char szDmpFileName[MAX_PATH+1];
  1595. FILE* log_file;
  1596. // check if the crash handler was already loaded (crash while handling the crash)
  1597. if( dbghelp_dll != INVALID_HANDLE_VALUE )
  1598. return EXCEPTION_CONTINUE_SEARCH;
  1599. // don't log anything if we're running inside a debugger ...
  1600. if( Dhp__IsDebuggerPresent() == TRUE )
  1601. return EXCEPTION_CONTINUE_SEARCH;
  1602. // ... or if we can't load dbghelp.dll ...
  1603. if( Dhp__LoadDbghelpDll() == FALSE )
  1604. return EXCEPTION_CONTINUE_SEARCH;
  1605. // ... or if we can't create the log files
  1606. if( Dhp__CreateFiles(szLogFileName, szDmpFileName) == FALSE )
  1607. return EXCEPTION_CONTINUE_SEARCH;
  1608. // open log file
  1609. log_file = fopen(szLogFileName, "wt");
  1610. // print information about the process
  1611. Dhp__PrintProcessInfo(
  1612. ptrs ? ptrs->ExceptionRecord : NULL,
  1613. ptrs ? ptrs->ContextRecord : NULL,
  1614. log_file);
  1615. // print the stacktrace
  1616. Dhp__PrintStacktrace(
  1617. ptrs ? ptrs->ContextRecord : NULL,
  1618. log_file);
  1619. // write the minidump file and use the callback to print the list of modules to the log file
  1620. Dhp__WriteMinidumpFile(
  1621. szDmpFileName,
  1622. ptrs,
  1623. Dhp__PrintModuleInfoCallback,
  1624. log_file);
  1625. fclose(log_file);
  1626. Dhp__UnloadDbghlpDll();
  1627. // inform the user
  1628. fprintf(stderr,
  1629. "\n"
  1630. "This application has halted due to an unexpected error.\n"
  1631. "A crash report and minidump file were saved to disk, you can find them here:\n"
  1632. "%s\n"
  1633. "%s\n"
  1634. DBG_EXTENDED_INFORMATION
  1635. "\n"
  1636. "NOTE: The crash report and minidump files can contain sensitive information\n"
  1637. "(filenames, partial file content, usernames and passwords etc.)\n",
  1638. szLogFileName,
  1639. szDmpFileName);
  1640. // terminate the application
  1641. return EXCEPTION_EXECUTE_HANDLER;
  1642. }
  1643. /////////////////////////////////////////////////////////////////////
  1644. // DLL stuff
  1645. #if !defined(DBG_EMBEDDED)
  1646. /// Previous exception filter.
  1647. static LPTOP_LEVEL_EXCEPTION_FILTER previousFilter;
  1648. #if defined(__GNUC__)
  1649. // GNU : define DLL load/unload functions
  1650. static void Dhp__OnStartup(void) __attribute__((constructor));
  1651. static void Dhp__OnExit(void) __attribute__((destructor));
  1652. #endif /* defined(__GNUC__) */
  1653. /// Installs as the unhandled exception handler.
  1654. void Dhp__OnStartup(void)
  1655. {
  1656. // Install the unhandled exception filter function
  1657. previousFilter = SetUnhandledExceptionFilter(Dhp__UnhandledExceptionFilter);
  1658. }
  1659. /// Uninstalls the handler.
  1660. void Dhp__OnExit(void)
  1661. {
  1662. SetUnhandledExceptionFilter(previousFilter);
  1663. }
  1664. #if !defined(__GNUC__)
  1665. // Windows : invoke DLL load/unload functions
  1666. BOOL APIENTRY DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
  1667. {
  1668. switch( dwReason )
  1669. {
  1670. case DLL_PROCESS_ATTACH: Dhp__OnStartup(); break;
  1671. case DLL_PROCESS_DETACH: Dhp__OnExit(); break;
  1672. }
  1673. return TRUE;
  1674. }
  1675. #endif /* !defined(__GNUC__) */
  1676. #endif /* !defined(DBG_EMBEDDED) */
  1677. #endif /* _WIN32 */