Source code of Windows XP (NT5)
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.

424 lines
13 KiB

  1. /*==========================================================================
  2. *
  3. * Copyright (C) 2001 Microsoft Corporation. All Rights Reserved.
  4. *
  5. * File: MiniStack.h
  6. * Content: Reduced-overhead call stack tracking class
  7. *
  8. * History:
  9. * Date By Reason
  10. * ==== == ======
  11. * 03/27/01 RichGr Derived from CallStack.h
  12. ***************************************************************************/
  13. #ifndef __MINISTACK_H__
  14. #define __MINISTACK_H__
  15. #define _IMAGEHLP_SOURCE_
  16. #include <Imagehlp.h>
  17. #include <string.h>
  18. //**********************************************************************
  19. // Constant definitions
  20. //**********************************************************************
  21. #define MINISTACK_DEPTH 5 // Increasing this beyond 5 definitely slows things down.
  22. #define SYM_CACHE_SIZE 100 // 100 seems big enough. Old entries are automatically replaced.
  23. //**********************************************************************
  24. // Macro definitions
  25. //**********************************************************************
  26. //**********************************************************************
  27. // Structure definitions
  28. //**********************************************************************
  29. //**********************************************************************
  30. // Variable definitions
  31. //**********************************************************************
  32. //**********************************************************************
  33. // Function prototypes
  34. //**********************************************************************
  35. //
  36. // prototypes for ImageHlp.dll functions we get from LoadLibrary().
  37. //
  38. typedef DWORD (__stdcall * PIMAGEHELP_SYMGETOPTIONS)( void );
  39. typedef DWORD (__stdcall * PIMAGEHELP_SYMSETOPTIONS)( DWORD SymOptions );
  40. typedef BOOL (__stdcall * PIMAGEHELP_SYMINITIALIZE)( HANDLE hProcess, PSTR pUserSearchPath, BOOL fInvadeProcess );
  41. typedef BOOL (__stdcall * PIMAGEHELP_SYMGETSYMFROMADDR)( HANDLE hProcess, DWORD dwAddress, PDWORD pdwDisplacement, PIMAGEHLP_SYMBOL pSymbol );
  42. typedef BOOL (__stdcall * PIMAGEHELP_SYMGETSYMFROMADDR64)( HANDLE hProcess, DWORD_PTR dwAddress, PDWORD_PTR pdwDisplacement, PIMAGEHLP_SYMBOL64 pSymbol );
  43. //**********************************************************************
  44. // Class definitions
  45. //**********************************************************************
  46. class CMiniStack
  47. {
  48. public:
  49. // Member functions
  50. CMiniStack();
  51. ~CMiniStack();
  52. void GetCallStackString(char *const pOutputString);
  53. private:
  54. // Member functions
  55. void Initialize(char *const pOutputString);
  56. void NoteCurrentCallStack(PVOID pCallStack[]);
  57. // Member variables
  58. HANDLE m_hPID;
  59. BOOL m_bNotInited;
  60. volatile LONG m_nGuardInit;
  61. HINSTANCE m_hImageHelp;
  62. PIMAGEHELP_SYMGETOPTIONS m_pSymGetOptions;
  63. PIMAGEHELP_SYMSETOPTIONS m_pSymSetOptions;
  64. PIMAGEHELP_SYMINITIALIZE m_pSymInitialize;
  65. #ifndef _WIN64
  66. PIMAGEHELP_SYMGETSYMFROMADDR m_pSymGetSymFromAddr;
  67. #else
  68. PIMAGEHELP_SYMGETSYMFROMADDR64 m_pSymGetSymFromAddr;
  69. #endif // _WIN64
  70. volatile LONG m_nGuardSymTable;
  71. int m_nNextCacheSlot;
  72. BOOL m_bCacheIsFull;
  73. struct {
  74. PVOID pAddress;
  75. char szName[64];
  76. int nReadCount;
  77. } m_SymTable[SYM_CACHE_SIZE];
  78. };
  79. //**********************************************************************
  80. // Per-Process instantiations
  81. //**********************************************************************
  82. #ifdef PER_PROCESS_INSTANTIATIONS // Define this in just one source file.
  83. CMiniStack g_MiniStack;
  84. #else
  85. extern CMiniStack g_MiniStack;
  86. #endif
  87. #ifdef PER_PROCESS_INSTANTIATIONS // Define this in just one source file.
  88. //**********************************************************************
  89. // Class member function definitions
  90. //**********************************************************************
  91. // These should be moved to a new file MiniStack.cpp for DX9.
  92. // ------------------------------
  93. // CMiniStack::CMiniStack - constructor
  94. //
  95. // Entry: Nothing
  96. //
  97. // Exit: Nothing
  98. // ------------------------------
  99. CMiniStack::CMiniStack()
  100. {
  101. m_bNotInited = TRUE;
  102. m_nGuardInit = 0;
  103. m_hPID = GetCurrentProcess();
  104. m_pSymGetOptions = NULL;
  105. m_pSymSetOptions = NULL;
  106. m_pSymInitialize = NULL;
  107. m_pSymGetSymFromAddr = NULL;
  108. m_hImageHelp = NULL;
  109. memset(&m_SymTable, 0, sizeof m_SymTable);
  110. m_nNextCacheSlot = 0;
  111. m_bCacheIsFull = FALSE;
  112. return;
  113. }
  114. // ------------------------------
  115. // CMiniStack::Initialize - load ImageHlp.dll and get proc addresses.
  116. //
  117. // Entry: Nothing
  118. //
  119. // Exit: Nothing
  120. // ------------------------------
  121. void CMiniStack::Initialize(char *const pOutputString)
  122. {
  123. // Attempt to load ImageHelp.dll
  124. if ( (m_hImageHelp = LoadLibrary( "ImageHlp.dll" )) == NULL)
  125. goto FailedImageHelpLoad;
  126. m_pSymGetOptions = (PIMAGEHELP_SYMGETOPTIONS)GetProcAddress( m_hImageHelp, "SymGetOptions" );
  127. m_pSymSetOptions = (PIMAGEHELP_SYMSETOPTIONS)GetProcAddress( m_hImageHelp, "SymSetOptions" );
  128. m_pSymInitialize = (PIMAGEHELP_SYMINITIALIZE)GetProcAddress( m_hImageHelp, "SymInitialize" );
  129. #ifndef _WIN64
  130. m_pSymGetSymFromAddr = (PIMAGEHELP_SYMGETSYMFROMADDR)GetProcAddress( m_hImageHelp, "SymGetSymFromAddr" );
  131. #else // _WIN64
  132. m_pSymGetSymFromAddr = (PIMAGEHELP_SYMGETSYMFROMADDR64)GetProcAddress( m_hImageHelp, "SymGetSymFromAddr64" );
  133. #endif // _WIN64
  134. if ( m_pSymGetOptions == NULL
  135. || m_pSymSetOptions == NULL
  136. || m_pSymInitialize == NULL
  137. || m_pSymGetSymFromAddr == NULL )
  138. {
  139. goto FailedImageHelpLoad;
  140. }
  141. m_pSymSetOptions( SYMOPT_DEFERRED_LOADS | m_pSymGetOptions() );
  142. if ( m_pSymInitialize( m_hPID, NULL, TRUE ) == FALSE
  143. || m_pSymInitialize( m_hPID, NULL, FALSE ) == FALSE )
  144. {
  145. goto FailedImageHelpLoad;
  146. }
  147. m_bNotInited = FALSE;
  148. Exit:
  149. return;
  150. FailedImageHelpLoad:
  151. strcpy(pOutputString, "*** ImageHlp.dll could not be loaded ***\r\n");
  152. if (m_hImageHelp)
  153. {
  154. FreeLibrary(m_hImageHelp);
  155. m_hImageHelp = NULL;
  156. }
  157. goto Exit;
  158. }
  159. //**********************************************************************
  160. // ------------------------------
  161. // CMiniStack::CMiniStack - destructor
  162. //
  163. // Entry: Nothing
  164. //
  165. // Exit: Nothing
  166. // ------------------------------
  167. CMiniStack::~CMiniStack()
  168. {
  169. if (m_hImageHelp)
  170. {
  171. FreeLibrary(m_hImageHelp);
  172. m_hImageHelp = NULL;
  173. }
  174. return;
  175. }
  176. //**********************************************************************
  177. // ------------------------------
  178. // CMiniStack::NoteCurrentCallStack - get a call stack
  179. //
  180. // Entry: Nothing
  181. //
  182. // Exit: Nothing
  183. // ------------------------------
  184. void CMiniStack::NoteCurrentCallStack(PVOID pCallStack[])
  185. {
  186. PVOID *ppCallersEBP;
  187. PVOID pStackTop;
  188. PVOID pStackBottom;
  189. int i;
  190. ppCallersEBP = NULL;
  191. pStackTop = pStackBottom = NULL;
  192. #ifdef _X86_
  193. _asm
  194. {
  195. mov eax,dword ptr fs:[4]
  196. mov pStackTop, eax
  197. mov eax,dword ptr fs:[8]
  198. mov pStackBottom, eax
  199. mov eax,[ebp]
  200. mov ppCallersEBP,eax
  201. }
  202. __try
  203. {
  204. // This code can generate exception if it steps back too far...
  205. // Skip the 1st. stack item because it's always PerfEnterCriticalSection()
  206. // and we don't need to report it.
  207. for (i = -1; i < MINISTACK_DEPTH; i++)
  208. {
  209. if ( ppCallersEBP < pStackBottom || ppCallersEBP >= pStackTop )
  210. break;
  211. if (i >= 0)
  212. pCallStack[i] = ppCallersEBP[1];
  213. ppCallersEBP = (PVOID*)*ppCallersEBP; // get callers callers ebp
  214. }
  215. }
  216. __except( 1 ) // went too far back on the stack, rest of array is filled with zeros
  217. {
  218. // Benign access violation creating return address stack.
  219. }
  220. #endif // _X86_
  221. return;
  222. }
  223. //**********************************************************************
  224. // ------------------------------
  225. // CMiniStack::GetCallStack - return pointer to call stack string
  226. //
  227. // Entry: Pointer to destination string
  228. //
  229. // Exit: Nothing
  230. // ------------------------------
  231. void CMiniStack::GetCallStackString( char *const pOutputString )
  232. {
  233. int i, j;
  234. PVOID pCallStack[MINISTACK_DEPTH] = {0};
  235. char ImageBuffer[sizeof IMAGEHLP_SYMBOL + 64] = {0};
  236. DWORD_PTR dwFunctionDisplacement = 0;
  237. BOOL b1stStringDone = FALSE;
  238. char szMsg[50] = {0};
  239. #ifndef _WIN64
  240. IMAGEHLP_SYMBOL *const pImageHelpSymbol = (IMAGEHLP_SYMBOL*)ImageBuffer;
  241. #else // _WIN64
  242. IMAGEHLP_SYMBOL64 *const pImageHelpSymbol = (IMAGEHLP_SYMBOL64*)ImageBuffer;
  243. #endif // _WIN64
  244. strcpy(pOutputString, "STACK: ");
  245. if (m_bNotInited)
  246. {
  247. // Make sure we are the only thread attempting to call Initialize() at one time.
  248. if (InterlockedIncrement(&m_nGuardInit) == 1)
  249. Initialize(pOutputString);
  250. InterlockedDecrement(&m_nGuardInit);
  251. // If we skipped the Initialize() step or the Initialize() failed, we can just exit.
  252. if (m_bNotInited)
  253. return;
  254. }
  255. NoteCurrentCallStack(pCallStack);
  256. pImageHelpSymbol->SizeOfStruct = sizeof( *pImageHelpSymbol );
  257. pImageHelpSymbol->Flags = 0;
  258. pImageHelpSymbol->MaxNameLength = sizeof ImageBuffer - sizeof *pImageHelpSymbol - sizeof TCHAR;
  259. // Loop thru the call stack addresses and pick up the corresponding function names.
  260. for (i = 0; i < MINISTACK_DEPTH && pCallStack[i] != NULL; i++)
  261. {
  262. PVOID pAddr;
  263. char *psz;
  264. pAddr = pCallStack[i];
  265. psz = NULL;
  266. // Check to see if the address is in our Symbol table cache.
  267. // For a full array of 100, this only takes an average of 5 usecs on a P550.
  268. for (j = 0; m_SymTable[j].pAddress != NULL && j < SYM_CACHE_SIZE; j++)
  269. {
  270. if (pAddr == m_SymTable[j].pAddress)
  271. {
  272. psz = &m_SymTable[j].szName[0];
  273. m_SymTable[j].nReadCount++; // Don't worry about wraps.
  274. break;
  275. }
  276. }
  277. // It's not in the cache, so get the name from the symbol file(using ImageHlp.dll).
  278. if (psz == NULL)
  279. {
  280. pImageHelpSymbol->Address = (DWORD_PTR)pAddr;
  281. if ( m_pSymGetSymFromAddr( m_hPID, (DWORD_PTR)pCallStack[i], &dwFunctionDisplacement, pImageHelpSymbol) != FALSE )
  282. {
  283. psz = pImageHelpSymbol->Name;
  284. // We've taken a lot of time to extract the name. Now save it in our Symbol table cache.
  285. // Make sure we are the only thread attempting to update the cache at one time.
  286. // If it doesn't get updated by a particular thread, it doesn't matter - it'll get updated
  287. // some other time.
  288. if (InterlockedIncrement(&m_nGuardSymTable) == 1)
  289. {
  290. int nLastSlotFilled;
  291. // Fill slot.
  292. m_SymTable[m_nNextCacheSlot].pAddress = pAddr;
  293. m_SymTable[m_nNextCacheSlot].nReadCount = 0;
  294. // Some symbol names are invalid, so omit them.
  295. if ( !_stricmp(pImageHelpSymbol->Name, "GetModuleHandleA"))
  296. {
  297. wsprintf(szMsg, "0x%p (no symbols)", pAddr);
  298. strcpy(m_SymTable[m_nNextCacheSlot].szName, szMsg);
  299. psz = m_SymTable[m_nNextCacheSlot].szName;
  300. }
  301. else
  302. strcpy(m_SymTable[m_nNextCacheSlot].szName, pImageHelpSymbol->Name);
  303. nLastSlotFilled = m_nNextCacheSlot;
  304. // Select the next slot
  305. if ( !m_bCacheIsFull)
  306. {
  307. // This will work fine until we fill the cache table,
  308. // then we have to change our strategy and look for the
  309. // Least-Used slot.
  310. m_nNextCacheSlot++;
  311. if (m_nNextCacheSlot >= SYM_CACHE_SIZE)
  312. m_bCacheIsFull = TRUE;
  313. }
  314. // The cache is full, so we'll find the Least-Used slot and select that.
  315. if (m_bCacheIsFull)
  316. {
  317. int nLowestReadCount = 0x7fffffff;
  318. int k = 0;
  319. for (j = 0; j < SYM_CACHE_SIZE; j++)
  320. {
  321. if (j != nLastSlotFilled && m_SymTable[j].nReadCount < nLowestReadCount)
  322. {
  323. nLowestReadCount = m_SymTable[j].nReadCount;
  324. k = j;
  325. if (nLowestReadCount == 0)
  326. break;
  327. }
  328. }
  329. m_nNextCacheSlot = k;
  330. }
  331. }
  332. InterlockedDecrement(&m_nGuardSymTable);
  333. }
  334. }
  335. if (psz)
  336. {
  337. if (b1stStringDone)
  338. strcat(pOutputString, ", ");
  339. strcat(pOutputString, psz);
  340. b1stStringDone = TRUE;
  341. }
  342. }
  343. return;
  344. }
  345. #endif //#ifdef PER_PROCESS_INSTANTIATIONS // Define this in just one source file.
  346. #endif // __MINISTACK_H__