Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

706 lines
20 KiB

  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) Microsoft Corporation. All rights reserved.
  3. //-----------------------------------------------------------------------------
  4. #ifdef ASSERT_WITH_STACK
  5. #ifndef _WIN64
  6. #include "AssertWithStack.h"
  7. //
  8. //--- Macros ------------------------------------------------------------------
  9. //
  10. #define COUNT_OF(x) (sizeof(x) / sizeof(x[0]))
  11. //
  12. // Types and Constants --------------------------------------------------------
  13. //
  14. struct __SYMBOL_INFO
  15. {
  16. DWORD dwOffset;
  17. char achModule[cchMaxAssertModuleLen];
  18. char achSymbol[cchMaxAssertSymbolLen];
  19. };
  20. //--- Function Pointers to APIs in IMAGEHLP.DLL. Loaded dynamically. ---------
  21. typedef LPAPI_VERSION (__stdcall *pfnImgHlp_ImagehlpApiVersionEx)(
  22. LPAPI_VERSION AppVersion
  23. );
  24. typedef BOOL (__stdcall *pfnImgHlp_StackWalk)(
  25. DWORD MachineType,
  26. HANDLE hProcess,
  27. HANDLE hThread,
  28. LPSTACKFRAME StackFrame,
  29. LPVOID ContextRecord,
  30. PREAD_PROCESS_MEMORY_ROUTINE ReadMemoryRoutine,
  31. PFUNCTION_TABLE_ACCESS_ROUTINE FunctionTableAccessRoutine,
  32. PGET_MODULE_BASE_ROUTINE GetModuleBaseRoutine,
  33. PTRANSLATE_ADDRESS_ROUTINE TranslateAddress
  34. );
  35. typedef BOOL (__stdcall *pfnImgHlp_SymGetModuleInfo)(
  36. IN HANDLE hProcess,
  37. IN DWORD dwAddr,
  38. OUT PIMAGEHLP_MODULE ModuleInfo
  39. );
  40. typedef LPVOID (__stdcall *pfnImgHlp_SymFunctionTableAccess)(
  41. HANDLE hProcess,
  42. DWORD AddrBase
  43. );
  44. typedef BOOL (__stdcall *pfnImgHlp_SymGetSymFromAddr)(
  45. IN HANDLE hProcess,
  46. IN DWORD dwAddr,
  47. OUT PDWORD pdwDisplacement,
  48. OUT PIMAGEHLP_SYMBOL Symbol
  49. );
  50. typedef BOOL (__stdcall *pfnImgHlp_SymInitialize)(
  51. IN HANDLE hProcess,
  52. IN LPSTR UserSearchPath,
  53. IN BOOL fInvadeProcess
  54. );
  55. typedef BOOL (__stdcall *pfnImgHlp_SymUnDName)(
  56. IN PIMAGEHLP_SYMBOL sym, // Symbol to undecorate
  57. OUT LPSTR UnDecName, // Buffer to store undecorated name in
  58. IN DWORD UnDecNameLength // Size of the buffer
  59. );
  60. typedef BOOL (__stdcall *pfnImgHlp_SymLoadModule)(
  61. IN HANDLE hProcess,
  62. IN HANDLE hFile,
  63. IN PSTR ImageName,
  64. IN PSTR ModuleName,
  65. IN DWORD BaseOfDll,
  66. IN DWORD SizeOfDll
  67. );
  68. struct IMGHLPFN_LOAD
  69. {
  70. LPSTR pszFnName;
  71. LPVOID * ppvfn;
  72. };
  73. //
  74. // Globals --------------------------------------------------------------------
  75. //
  76. static BOOL g_fLoadedImageHlp = FALSE; // set to true on success
  77. static BOOL g_fLoadedImageHlpFailed = FALSE; // set to true on failure
  78. static HINSTANCE g_hinstImageHlp = NULL;
  79. static HANDLE g_hProcess = NULL;
  80. pfnImgHlp_ImagehlpApiVersionEx _ImagehlpApiVersionEx;
  81. pfnImgHlp_StackWalk _StackWalk;
  82. pfnImgHlp_SymGetModuleInfo _SymGetModuleInfo;
  83. pfnImgHlp_SymFunctionTableAccess _SymFunctionTableAccess;
  84. pfnImgHlp_SymGetSymFromAddr _SymGetSymFromAddr;
  85. pfnImgHlp_SymInitialize _SymInitialize;
  86. pfnImgHlp_SymUnDName _SymUnDName;
  87. pfnImgHlp_SymLoadModule _SymLoadModule;
  88. IMGHLPFN_LOAD ailFuncList[] =
  89. {
  90. { "ImagehlpApiVersionEx", (LPVOID*)&_ImagehlpApiVersionEx },
  91. { "StackWalk", (LPVOID*)&_StackWalk },
  92. { "SymGetModuleInfo", (LPVOID*)&_SymGetModuleInfo },
  93. { "SymFunctionTableAccess", (LPVOID*)&_SymFunctionTableAccess },
  94. { "SymGetSymFromAddr", (LPVOID*)&_SymGetSymFromAddr },
  95. { "SymInitialize", (LPVOID*)&_SymInitialize },
  96. { "SymUnDName", (LPVOID*)&_SymUnDName },
  97. { "SymLoadModule", (LPVOID*)&_SymLoadModule },
  98. };
  99. //
  100. //--- Forward declarations ----------------------------------------------------
  101. //
  102. static int Dummy1();
  103. static int Dummy2();
  104. /****************************************************************************
  105. * Dummy1 *
  106. *--------*
  107. * Description:
  108. * A placeholder function used to determine if addresses being retrieved
  109. * are for functions in this compilation unit or not.
  110. *
  111. * WARNING!! This function must be the first function in this
  112. * compilation unit
  113. ****************************************************************************/
  114. static int Dummy1()
  115. {
  116. return 1;
  117. }
  118. /****************************************************************************
  119. * IsWin95 *
  120. *---------*
  121. * Description:
  122. * Are we running on Win95 or not. Some of the logic contained here
  123. * differs on Windows 9x.
  124. *
  125. * Return:
  126. * TRUE - If we're running on a Win 9x platform
  127. * FALSE - If we're running on a non-Win 9x platform
  128. ****************************************************************************/
  129. static BOOL IsWin95()
  130. {
  131. return GetVersion() & 0x80000000;
  132. }
  133. /****************************************************************************
  134. * MagicInit *
  135. *-----------*
  136. * Description:
  137. * Initializes the symbol loading code. Currently called (if necessary)
  138. * at the beginning of each method that might need ImageHelp to be
  139. * loaded.
  140. ****************************************************************************/
  141. void MagicInit()
  142. {
  143. if (g_fLoadedImageHlp || g_fLoadedImageHlpFailed)
  144. {
  145. return;
  146. }
  147. g_hProcess = GetCurrentProcess();
  148. //
  149. // Try to load imagehlp.dll
  150. //
  151. g_hinstImageHlp = LoadLibraryA("imagehlp.dll");
  152. _ASSERT(g_hinstImageHlp);
  153. if (NULL == g_hinstImageHlp)
  154. {
  155. g_fLoadedImageHlpFailed = TRUE;
  156. return;
  157. }
  158. //
  159. // Try to get the API entrypoints in imagehlp.dll
  160. //
  161. for (int i = 0; i < COUNT_OF(ailFuncList); i++)
  162. {
  163. *(ailFuncList[i].ppvfn) = GetProcAddress(
  164. g_hinstImageHlp,
  165. ailFuncList[i].pszFnName);
  166. _ASSERT(*(ailFuncList[i].ppvfn));
  167. if (!*(ailFuncList[i].ppvfn))
  168. {
  169. g_fLoadedImageHlpFailed = TRUE;
  170. return;
  171. }
  172. }
  173. API_VERSION AppVersion = { 4, 0, API_VERSION_NUMBER, 0 };
  174. LPAPI_VERSION papiver = _ImagehlpApiVersionEx(&AppVersion);
  175. //
  176. // We assume any version 4 or greater is OK.
  177. //
  178. _ASSERT(papiver->Revision >= 4);
  179. if (papiver->Revision < 4)
  180. {
  181. g_fLoadedImageHlpFailed = TRUE;
  182. return;
  183. }
  184. g_fLoadedImageHlp = TRUE;
  185. //
  186. // Initialize imagehlp.dll
  187. //
  188. _SymInitialize(g_hProcess, NULL, FALSE);
  189. return;
  190. }
  191. /****************************************************************************
  192. * FillSymbolInfo *
  193. *----------------*
  194. * Description:
  195. * Fills in a __SYMBOL_INFO structure
  196. ****************************************************************************/
  197. void FillSymbolInfo
  198. (
  199. __SYMBOL_INFO *psi,
  200. DWORD dwAddr
  201. )
  202. {
  203. if (!g_fLoadedImageHlp)
  204. {
  205. return;
  206. }
  207. _ASSERT(psi);
  208. memset(psi, 0, sizeof(__SYMBOL_INFO));
  209. IMAGEHLP_MODULE mi;
  210. mi.SizeOfStruct = sizeof(mi);
  211. if (!_SymGetModuleInfo(g_hProcess, dwAddr, &mi))
  212. {
  213. strncpy(psi->achModule, "<no module>", sizeof(psi->achModule)-1);
  214. }
  215. else
  216. {
  217. strncpy(psi->achModule, mi.ModuleName, sizeof(psi->achModule)-1);
  218. strupr(psi->achModule);
  219. }
  220. CHAR rgchUndec[256];
  221. CHAR * pszSymbol = NULL;
  222. // Name field of IMAGEHLP_SYMBOL is dynamically sized.
  223. // Pad with space for 255 characters.
  224. union
  225. {
  226. CHAR rgchSymbol[sizeof(IMAGEHLP_SYMBOL) + 255];
  227. IMAGEHLP_SYMBOL sym;
  228. };
  229. __try
  230. {
  231. sym.SizeOfStruct = sizeof(IMAGEHLP_SYMBOL);
  232. sym.Address = dwAddr;
  233. sym.MaxNameLength = 255;
  234. if (_SymGetSymFromAddr(g_hProcess, dwAddr, &psi->dwOffset, &sym))
  235. {
  236. pszSymbol = sym.Name;
  237. if (_SymUnDName(&sym, rgchUndec, COUNT_OF(rgchUndec)-1))
  238. {
  239. pszSymbol = rgchUndec;
  240. }
  241. }
  242. else
  243. {
  244. pszSymbol = "<no symbol>";
  245. }
  246. }
  247. __except (EXCEPTION_EXECUTE_HANDLER)
  248. {
  249. pszSymbol = "<EX: no symbol>";
  250. psi->dwOffset = dwAddr - mi.BaseOfImage;
  251. }
  252. strncpy(psi->achSymbol, pszSymbol, COUNT_OF(psi->achSymbol)-1);
  253. }
  254. /****************************************************************************
  255. * FunctionTableAccess *
  256. *---------------------*
  257. * Description:
  258. * Helper for imagehlp's StackWalk API.
  259. ****************************************************************************/
  260. LPVOID __stdcall FunctionTableAccess
  261. (
  262. HANDLE hProcess,
  263. DWORD dwPCAddr
  264. )
  265. {
  266. return _SymFunctionTableAccess( hProcess, dwPCAddr );
  267. }
  268. /****************************************************************************
  269. * GetModuleBase *
  270. *---------------*
  271. * Description:
  272. * Helper for imagehlp's StackWalk API. Retrieves the base address of
  273. * the module containing the giving virtual address.
  274. *
  275. * NOTE: If the module information for the given module hasnot yet been
  276. * loaded, then it is loaded on this call.
  277. *
  278. * Return:
  279. * Base virtual address where the module containing ReturnAddress is
  280. * loaded, or 0 if the address cannot be determined.
  281. ****************************************************************************/
  282. DWORD __stdcall GetModuleBase
  283. (
  284. HANDLE hProcess,
  285. DWORD dwAddr
  286. )
  287. {
  288. IMAGEHLP_MODULE ModuleInfo;
  289. ModuleInfo.SizeOfStruct = sizeof(ModuleInfo);
  290. if (_SymGetModuleInfo(hProcess, dwAddr, &ModuleInfo))
  291. {
  292. return ModuleInfo.BaseOfImage;
  293. }
  294. else
  295. {
  296. MEMORY_BASIC_INFORMATION mbi;
  297. if (VirtualQueryEx(hProcess, (LPVOID)dwAddr, &mbi, sizeof(mbi)))
  298. {
  299. if (IsWin95() || (mbi.Type & MEM_IMAGE))
  300. {
  301. char achFile[MAX_PATH] = {0};
  302. DWORD cch;
  303. cch = GetModuleFileNameA(
  304. (HINSTANCE)mbi.AllocationBase,
  305. achFile,
  306. MAX_PATH);
  307. // Ignore the return code since we can't do anything with it.
  308. _SymLoadModule(
  309. hProcess,
  310. NULL,
  311. ((cch) ? achFile : NULL),
  312. NULL,
  313. (DWORD)mbi.AllocationBase,
  314. 0);
  315. return (DWORD)mbi.AllocationBase;
  316. }
  317. }
  318. }
  319. return 0;
  320. }
  321. /****************************************************************************
  322. * GetStackBacktrace *
  323. *-------------------*
  324. * Description:
  325. * Gets a stacktrace of the current stack, including symbols.
  326. *
  327. * Return:
  328. * The number of elements actually retrieved.
  329. ****************************************************************************/
  330. UINT GetStackBacktrace
  331. (
  332. UINT ifrStart, // How many stack elements to skip before starting.
  333. UINT cfrTotal, // How many elements to trace after starting.
  334. DWORD *pdwEip, // Array to be filled with stack addresses.
  335. __SYMBOL_INFO *psiSymbols // This array is filled with symbol information.
  336. // It should be big enough to hold cfrTotal elts.
  337. // If NULL, no symbol information is stored.
  338. )
  339. {
  340. DWORD * pdw = pdwEip;
  341. __SYMBOL_INFO * psi = psiSymbols;
  342. MagicInit();
  343. memset(pdwEip, 0, cfrTotal * sizeof(DWORD));
  344. if (psiSymbols)
  345. {
  346. memset(psiSymbols, 0, cfrTotal * sizeof(__SYMBOL_INFO));
  347. }
  348. if (!g_fLoadedImageHlp)
  349. {
  350. return 0;
  351. }
  352. HANDLE hThread;
  353. hThread = GetCurrentThread();
  354. CONTEXT context;
  355. context.ContextFlags = CONTEXT_FULL;
  356. if (GetThreadContext(hThread, &context))
  357. {
  358. STACKFRAME stkfrm;
  359. memset(&stkfrm, 0, sizeof(STACKFRAME));
  360. stkfrm.AddrPC.Mode = AddrModeFlat;
  361. DWORD dwMachType;
  362. #if defined(_M_IX86)
  363. dwMachType = IMAGE_FILE_MACHINE_I386;
  364. stkfrm.AddrPC.Offset = context.Eip; // Program Counter
  365. stkfrm.AddrStack.Offset = context.Esp; // Stack Pointer
  366. stkfrm.AddrStack.Mode = AddrModeFlat;
  367. stkfrm.AddrFrame.Offset = context.Ebp; // Frame Pointer
  368. stkfrm.AddrFrame.Mode = AddrModeFlat;
  369. #elif defined(_M_MRX000)
  370. dwMachType = IMAGE_FILE_MACHINE_R4000;
  371. stkfrm.AddrPC.Offset = context.Fir; // Program Counter
  372. #elif defined(_M_ALPHA)
  373. dwMachType = IMAGE_FILE_MACHINE_ALPHA;
  374. stkfrm.AddrPC.Offset = (unsigned long) context.Fir; // Program Counter
  375. #elif defined(_M_PPC)
  376. dwMachType = IMAGE_FILE_MACHINE_POWERPC;
  377. stkfrm.AddrPC.Offset = context.Iar; // Program Counter
  378. #elif
  379. #error("Unknown Target Machine");
  380. #endif
  381. // Ignore this function (GetStackBackTrace)
  382. ifrStart += 1;
  383. for (UINT i = 0; i < ifrStart + cfrTotal; i++)
  384. {
  385. if (!_StackWalk(dwMachType,
  386. g_hProcess,
  387. hThread,
  388. &stkfrm,
  389. &context,
  390. NULL,
  391. FunctionTableAccess,
  392. GetModuleBase,
  393. NULL))
  394. {
  395. break;
  396. }
  397. if (i >= ifrStart &&
  398. ((void*)stkfrm.AddrPC.Offset < (void*)Dummy1 ||
  399. (void*)stkfrm.AddrPC.Offset > (void*)Dummy2))
  400. {
  401. *pdw++ = stkfrm.AddrPC.Offset;
  402. if (psi)
  403. {
  404. FillSymbolInfo(psi++, stkfrm.AddrPC.Offset);
  405. }
  406. }
  407. }
  408. }
  409. return pdw - pdwEip;
  410. }
  411. /****************************************************************************
  412. * GetStringFromSymbolInfo *
  413. *-------------------------*
  414. * Description:
  415. * Actually prints the info into the string for the symbol.
  416. ****************************************************************************/
  417. void GetStringFromSymbolInfo
  418. (
  419. DWORD dwAddr,
  420. __SYMBOL_INFO *psi, // @parm Pointer to __SYMBOL_INFO. Can be NULL.
  421. CHAR *pszString // @parm Place to put string.
  422. )
  423. {
  424. _ASSERT(pszString);
  425. // <module>! <symbol> + 0x<offset> 0x<addr>\n
  426. if (psi)
  427. {
  428. wsprintfA(pszString,
  429. "%s! %s + 0x%X (0x%08X)",
  430. (psi->achModule[0]) ? psi->achModule : "<no module>",
  431. (psi->achSymbol[0]) ? psi->achSymbol : "<no symbol>",
  432. psi->dwOffset,
  433. dwAddr);
  434. }
  435. else
  436. {
  437. wsprintfA(pszString, "<symbols not available> (0x%08X)", dwAddr);
  438. }
  439. _ASSERT(strlen(pszString) < cchMaxAssertStackLevelStringLen);
  440. }
  441. /****************************************************************************
  442. * GetStringFromStackLevels *
  443. *--------------------------*
  444. * Description:
  445. * Retrieves a string from the stack frame. If more than one frame, they
  446. * are separated by newlines
  447. ****************************************************************************/
  448. void GetStringFromStackLevels
  449. (
  450. UINT ifrStart, // @parm How many stack elements to skip before starting.
  451. UINT cfrTotal, // @parm How many elements to trace after starting.
  452. // Can't be more than cfrMaxAssertStackLevels.
  453. CHAR *pszString // @parm Place to put string.
  454. // Max size will be cchMaxAssertStackLevelStringLen * cfrTotal.
  455. )
  456. {
  457. _ASSERT(pszString);
  458. _ASSERT(cfrTotal < cfrMaxAssertStackLevels);
  459. *pszString = '\0';
  460. if (cfrTotal == 0)
  461. {
  462. return;
  463. }
  464. DWORD rgdwStackAddrs[cfrMaxAssertStackLevels];
  465. __SYMBOL_INFO rgsi[cfrMaxAssertStackLevels];
  466. // Ignore this function (GetStringFromStackLevels)
  467. ifrStart += 1;
  468. UINT uiRetrieved =
  469. GetStackBacktrace(ifrStart, cfrTotal, rgdwStackAddrs, rgsi);
  470. // First level
  471. CHAR aszLevel[cchMaxAssertStackLevelStringLen];
  472. GetStringFromSymbolInfo(rgdwStackAddrs[0], &rgsi[0], aszLevel);
  473. strcpy(pszString, aszLevel);
  474. // Additional levels
  475. for (UINT i = 1; i < uiRetrieved; ++i)
  476. {
  477. strcat(pszString, "\n");
  478. GetStringFromSymbolInfo(rgdwStackAddrs[i],
  479. &rgsi[i], aszLevel);
  480. strcat(pszString, aszLevel);
  481. }
  482. _ASSERT(strlen(pszString) <= cchMaxAssertStackLevelStringLen * cfrTotal);
  483. }
  484. /****************************************************************************
  485. * GetAddrFromStackLevel *
  486. *-----------------------*
  487. * Description:
  488. * Retrieves the address of the next instruction to be executed on a
  489. * particular stack frame.
  490. *
  491. * Return:
  492. * The address of the next instruction,
  493. * 0 if there's an error.
  494. ****************************************************************************/
  495. DWORD GetAddrFromStackLevel
  496. (
  497. UINT ifrStart // How many stack elements to skip before starting.
  498. )
  499. {
  500. MagicInit();
  501. if (!g_fLoadedImageHlp)
  502. {
  503. return 0;
  504. }
  505. HANDLE hThread;
  506. hThread = GetCurrentThread();
  507. CONTEXT context;
  508. context.ContextFlags = CONTEXT_FULL;
  509. if (GetThreadContext(hThread, &context))
  510. {
  511. STACKFRAME stkfrm;
  512. memset(&stkfrm, 0, sizeof(STACKFRAME));
  513. stkfrm.AddrPC.Mode = AddrModeFlat;
  514. DWORD dwMachType;
  515. #if defined(_M_IX86)
  516. dwMachType = IMAGE_FILE_MACHINE_I386;
  517. stkfrm.AddrPC.Offset = context.Eip; // Program Counter
  518. stkfrm.AddrStack.Offset = context.Esp; // Stack Pointer
  519. stkfrm.AddrStack.Mode = AddrModeFlat;
  520. stkfrm.AddrFrame.Offset = context.Ebp; // Frame Pointer
  521. stkfrm.AddrFrame.Mode = AddrModeFlat;
  522. #elif defined(_M_MRX000)
  523. dwMachType = IMAGE_FILE_MACHINE_R4000;
  524. stkfrm.AddrPC.Offset = context.Fir; // Program Counter
  525. #elif defined(_M_ALPHA)
  526. dwMachType = IMAGE_FILE_MACHINE_ALPHA;
  527. stkfrm.AddrPC.Offset = (unsigned long) context.Fir; // Program Counter
  528. #elif defined(_M_PPC)
  529. dwMachType = IMAGE_FILE_MACHINE_POWERPC;
  530. stkfrm.AddrPC.Offset = context.Iar; // Program Counter
  531. #elif
  532. #error("Unknown Target Machine");
  533. #endif
  534. // Ignore this function (GetStackBackTrace) and the one below
  535. ifrStart += 2;
  536. for (UINT i = 0; i < ifrStart; i++)
  537. {
  538. if (!_StackWalk(dwMachType,
  539. g_hProcess,
  540. hThread,
  541. &stkfrm,
  542. &context,
  543. NULL,
  544. FunctionTableAccess,
  545. GetModuleBase,
  546. NULL))
  547. {
  548. break;
  549. }
  550. }
  551. return stkfrm.AddrPC.Offset;
  552. }
  553. return 0;
  554. }
  555. /****************************************************************************
  556. * GetStringFromAddr *
  557. *-------------------*
  558. * Description:
  559. * Returns a string from an address.
  560. ****************************************************************************/
  561. void GetStringFromAddr
  562. (
  563. DWORD dwAddr,
  564. CHAR *szString // Place to put string.
  565. // Buffer must hold at least cchMaxAssertStackLevelStringLen.
  566. )
  567. {
  568. _ASSERT(szString);
  569. __SYMBOL_INFO si;
  570. FillSymbolInfo(&si, dwAddr);
  571. wsprintfA(szString,
  572. "%s! %s + 0x%X (0x%08X)",
  573. (si.achModule[0]) ? si.achModule : "<no module>",
  574. (si.achSymbol[0]) ? si.achSymbol : "<no symbol>",
  575. si.dwOffset,
  576. dwAddr);
  577. }
  578. /****************************************************************************
  579. * MagicDeinit *
  580. *-------------*
  581. * Description:
  582. * Cleans up for the symbol loading code. Should be called before exit
  583. * to free the dynamically loaded imagehlp.dll.
  584. ****************************************************************************/
  585. void MagicDeinit(void)
  586. {
  587. if (g_hinstImageHlp)
  588. {
  589. FreeLibrary(g_hinstImageHlp);
  590. g_hinstImageHlp = NULL;
  591. g_fLoadedImageHlp = FALSE;
  592. }
  593. }
  594. static int Dummy2()
  595. {
  596. return 2;
  597. }
  598. #endif // _WIN64
  599. #endif // ASSERT_WITH_STACK