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.

726 lines
18 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. //
  5. // Copyright (C) Microsoft Corporation, 1997 - 1999
  6. //
  7. // File: debug.cpp
  8. //
  9. //--------------------------------------------------------------------------
  10. /*----------------------------------------------------------------------------
  11. / Title;
  12. / debug.cpp
  13. /
  14. / Authors;
  15. / Jeffrey Saathoff (jeffreys)
  16. /
  17. / Notes;
  18. / Provides printf style debug output
  19. /----------------------------------------------------------------------------*/
  20. #include "pch.h"
  21. #include <stdio.h>
  22. #include <comctrlp.h>
  23. #pragma hdrstop
  24. #ifdef DEBUG
  25. DWORD g_dwTraceMask = 0;
  26. DWORD g_tlsDebug = 0xffffffffL;
  27. #define MAX_CALL_DEPTH 64
  28. class CDebugStack
  29. {
  30. private:
  31. DWORD m_dwThreadID;
  32. LONG m_cDepth;
  33. struct
  34. {
  35. BOOL fTracedYet;
  36. LPCTSTR pszFunctionName;
  37. DWORD dwMask;
  38. }
  39. m_CallStack[MAX_CALL_DEPTH];
  40. public:
  41. CDebugStack() : m_dwThreadID(GetCurrentThreadId()), m_cDepth(-1)
  42. { ZeroMemory(&m_CallStack, sizeof(m_CallStack)); }
  43. public:
  44. void _Indent(LONG iDepth, LPCTSTR pszFormat, ...);
  45. void _vIndent(LONG iDepth, LPCTSTR pszFormat, va_list *pva);
  46. BOOL _TraceProlog(LONG iDepth, BOOL fForce);
  47. void _TraceEnter(DWORD dwMask, LPCTSTR pName);
  48. void _TraceLeave(void);
  49. void _Trace(BOOL bForce, LPCTSTR pszFormat, ...);
  50. void _vTrace(BOOL bForce, LPCTSTR pszFormat, va_list *pva);
  51. void _TraceGUID(LPCTSTR pPrefix, REFGUID rGUID);
  52. void _TraceAssert(int iLine, LPTSTR pFilename, LPTSTR pCondition);
  53. };
  54. typedef CDebugStack *PDEBUGSTACK;
  55. class CDebugStackHolder
  56. {
  57. private:
  58. HDPA m_hDebugStackList;
  59. CRITICAL_SECTION m_csStackList;
  60. public:
  61. CDebugStackHolder() : m_hDebugStackList(NULL) { InitializeCriticalSection(&m_csStackList); }
  62. ~CDebugStackHolder();
  63. public:
  64. void Add(PDEBUGSTACK pDebugStack);
  65. void Remove(PDEBUGSTACK pDebugStack);
  66. };
  67. typedef CDebugStackHolder *PDEBUGSTACKHOLDER;
  68. PDEBUGSTACKHOLDER g_pStackHolder = NULL;
  69. /*-----------------------------------------------------------------------------
  70. / _Indent
  71. / -------
  72. / Output to the debug stream indented by n columns.
  73. /
  74. / In:
  75. / i = column to indent to.
  76. / pszFormat -> string to be indented
  77. /
  78. / Out:
  79. / -
  80. /----------------------------------------------------------------------------*/
  81. void CDebugStack::_Indent(LONG iDepth, LPCTSTR pszFormat, ...)
  82. {
  83. va_list va;
  84. va_start(va, pszFormat);
  85. _vIndent(iDepth, pszFormat, &va);
  86. va_end(va);
  87. }
  88. void CDebugStack::_vIndent(LONG iDepth, LPCTSTR pszFormat, va_list *pva)
  89. {
  90. TCHAR szStringBuffer[2048];
  91. szStringBuffer[0] = TEXT('\0');
  92. wsprintf(szStringBuffer, TEXT("%08x "), m_dwThreadID);
  93. iDepth = min(iDepth, MAX_CALL_DEPTH - 1);
  94. for ( ; iDepth > 0 ; iDepth-- )
  95. lstrcat(szStringBuffer, TEXT(" "));
  96. wvsprintf(szStringBuffer + lstrlen(szStringBuffer), pszFormat, *pva);
  97. lstrcat(szStringBuffer, TEXT("\n"));
  98. OutputDebugString(szStringBuffer);
  99. }
  100. /*-----------------------------------------------------------------------------
  101. / _TraceProlog
  102. / -------------
  103. / Handle the prolog to a prefix string, including outputting the
  104. / function name if we haven't already.
  105. /
  106. / In:
  107. / iDepth = depth in the call stack
  108. / fForce = ignore flags
  109. /
  110. / Out:
  111. / BOOL if trace output should be made
  112. /----------------------------------------------------------------------------*/
  113. BOOL CDebugStack::_TraceProlog(LONG iDepth, BOOL fForce)
  114. {
  115. if ( iDepth < 0 || iDepth >= MAX_CALL_DEPTH )
  116. return FALSE;
  117. if ( (g_dwTraceMask & m_CallStack[iDepth].dwMask) || fForce )
  118. {
  119. if ( !m_CallStack[iDepth].fTracedYet )
  120. {
  121. if ( iDepth > 0 )
  122. _TraceProlog(iDepth-1, TRUE);
  123. _Indent(iDepth, m_CallStack[iDepth].pszFunctionName);
  124. m_CallStack[iDepth].fTracedYet = TRUE;
  125. }
  126. return TRUE;
  127. }
  128. return FALSE;
  129. }
  130. /*-----------------------------------------------------------------------------
  131. / _TraceEnter
  132. / ------------
  133. / Set the debugging call stack up to indicate which function we are in.
  134. /
  135. / In:
  136. / pName -> function name to be displayed in subsequent trace output.
  137. /
  138. / Out:
  139. / -
  140. /----------------------------------------------------------------------------*/
  141. void CDebugStack::_TraceEnter(DWORD dwMask, LPCTSTR pName)
  142. {
  143. m_cDepth++;
  144. if ( m_cDepth < MAX_CALL_DEPTH )
  145. {
  146. if ( !pName )
  147. pName = TEXT("<no name>"); // no function name given
  148. m_CallStack[m_cDepth].fTracedYet = FALSE;
  149. m_CallStack[m_cDepth].pszFunctionName = pName;
  150. m_CallStack[m_cDepth].dwMask = dwMask;
  151. //if ( m_cDepth > 0 )
  152. // _TraceProlog(m_cDepth-1, FALSE);
  153. }
  154. }
  155. /*-----------------------------------------------------------------------------
  156. / _TraceLeave
  157. / ------------
  158. / On exit from a function this will adjust the function stack pointer to
  159. / point to our previous function. If no trace output has been made then
  160. / we will output the function name on a single line (to indicate we went somewhere).
  161. /
  162. / In:
  163. / -
  164. / Out:
  165. / -
  166. /----------------------------------------------------------------------------*/
  167. void CDebugStack::_TraceLeave(void)
  168. {
  169. //_TraceProlog(m_cDepth, FALSE);
  170. //if ( !m_cDepth && m_CallStack[0].fTracedYet )
  171. // OutputDebugString(TEXT("\n"));
  172. m_cDepth = max(m_cDepth-1, -1); // account for underflow
  173. }
  174. /*-----------------------------------------------------------------------------
  175. / _Trace
  176. / -------
  177. / Perform printf formatting to the debugging stream. We indent the output
  178. / and stream the function name as required to give some indication of
  179. / call stack depth.
  180. /
  181. / In:
  182. / pszFormat -> printf style formatting string
  183. / ... = arguments as required for the formatting
  184. /
  185. / Out:
  186. / -
  187. /----------------------------------------------------------------------------*/
  188. void CDebugStack::_Trace(BOOL bForce, LPCTSTR pszFormat, ...)
  189. {
  190. va_list va;
  191. va_start(va, pszFormat);
  192. _vTrace(bForce, pszFormat, &va);
  193. va_end(va);
  194. }
  195. void CDebugStack::_vTrace(BOOL bForce, LPCTSTR pszFormat, va_list *pva)
  196. {
  197. if ( _TraceProlog(m_cDepth, bForce) || bForce )
  198. _vIndent(m_cDepth+1, pszFormat, pva);
  199. }
  200. /*-----------------------------------------------------------------------------
  201. / _TraceGUID
  202. / -----------
  203. / Given a GUID output it into the debug string, first we try and map it
  204. / to a name (ie. IShellFolder), if that didn't work then we convert it
  205. / to its human readable form.
  206. /
  207. / In:
  208. / pszPrefix -> prefix string
  209. / lpGuid -> guid to be streamed
  210. /
  211. / Out:
  212. / -
  213. /----------------------------------------------------------------------------*/
  214. #ifdef UNICODE
  215. #define MAP_GUID(x) &x, TEXT(""L#x)
  216. #else
  217. #define MAP_GUID(x) &x, TEXT(""#x)
  218. #endif
  219. #define MAP_GUID2(x,y) MAP_GUID(x), MAP_GUID(y)
  220. const struct
  221. {
  222. const GUID* m_pGUID;
  223. LPCTSTR m_pName;
  224. }
  225. _guid_map[] =
  226. {
  227. MAP_GUID(IID_IUnknown),
  228. MAP_GUID(IID_IClassFactory),
  229. MAP_GUID(IID_IDropTarget),
  230. MAP_GUID(IID_IDataObject),
  231. MAP_GUID(IID_IPersist),
  232. MAP_GUID(IID_IOleWindow),
  233. MAP_GUID2(IID_INewShortcutHookA, IID_INewShortcutHookW),
  234. MAP_GUID(IID_IShellBrowser),
  235. MAP_GUID(IID_IShellView),
  236. MAP_GUID(IID_IContextMenu),
  237. MAP_GUID(IID_IShellIcon),
  238. MAP_GUID(IID_IShellFolder),
  239. MAP_GUID(IID_IShellExtInit),
  240. MAP_GUID(IID_IShellPropSheetExt),
  241. MAP_GUID(IID_IPersistFolder),
  242. MAP_GUID2(IID_IExtractIconA, IID_IExtractIconW),
  243. MAP_GUID2(IID_IShellLinkA, IID_IShellLinkW),
  244. MAP_GUID2(IID_IShellCopyHookA, IID_IShellCopyHookW),
  245. MAP_GUID2(IID_IFileViewerA, IID_IFileViewerW),
  246. MAP_GUID(IID_ICommDlgBrowser),
  247. MAP_GUID(IID_IEnumIDList),
  248. MAP_GUID(IID_IFileViewerSite),
  249. MAP_GUID(IID_IContextMenu2),
  250. MAP_GUID2(IID_IShellExecuteHookA, IID_IShellExecuteHookW),
  251. MAP_GUID(IID_IPropSheetPage),
  252. MAP_GUID(IID_IShellView2),
  253. MAP_GUID(IID_IUniformResourceLocator),
  254. };
  255. void CDebugStack::_TraceGUID(LPCTSTR pPrefix, REFGUID rGUID)
  256. {
  257. TCHAR szGUID[40];
  258. LPCTSTR pName = NULL;
  259. int i;
  260. for ( i = 0 ; i < ARRAYSIZE(_guid_map); i++ )
  261. {
  262. if ( IsEqualGUID(rGUID, *_guid_map[i].m_pGUID) )
  263. {
  264. pName = _guid_map[i].m_pName;
  265. break;
  266. }
  267. }
  268. if ( !pName )
  269. {
  270. SHStringFromGUID(rGUID, szGUID, ARRAYSIZE(szGUID));
  271. pName = szGUID;
  272. }
  273. _Trace(FALSE, TEXT("%s %s"), pPrefix, pName);
  274. }
  275. /*-----------------------------------------------------------------------------
  276. / _TraceAssert
  277. / -------------
  278. / Our assert handler, always prints messages but only breaks if enabled
  279. / in the trace mask.
  280. /
  281. / In:
  282. / iLine = line
  283. / pFilename -> filename of the file we asserted in
  284. / pCondition -> assert condition that failed
  285. /
  286. / Out:
  287. / -
  288. /----------------------------------------------------------------------------*/
  289. void CDebugStack::_TraceAssert(int iLine, LPTSTR pFilename, LPTSTR pCondition)
  290. {
  291. // nb: TRUE --> asserts always displayed
  292. _Trace(TRUE, TEXT("Assert failed: \"%s\""), pCondition);
  293. _Trace(TRUE, TEXT("File: %s, Line %d"), pFilename, iLine);
  294. if ( g_dwTraceMask & TRACE_COMMON_ASSERT )
  295. DebugBreak();
  296. }
  297. /*-----------------------------------------------------------------------------
  298. / ~CDebugStackHolder
  299. / ------------------
  300. / Free any DebugStack objects that exist
  301. /
  302. / In:
  303. / -
  304. /
  305. / Out:
  306. / -
  307. /----------------------------------------------------------------------------*/
  308. int CALLBACK
  309. _DeleteCB(LPVOID pVoid, LPVOID /*pData*/)
  310. {
  311. PDEBUGSTACK pDebugStack = (PDEBUGSTACK)pVoid;
  312. if (pDebugStack)
  313. {
  314. //pDebugStack->_Trace(TRUE, TEXT("~CDebugStackHolder destroying DebugStack"));
  315. delete pDebugStack;
  316. }
  317. return 1;
  318. }
  319. CDebugStackHolder::~CDebugStackHolder()
  320. {
  321. EnterCriticalSection(&m_csStackList);
  322. if (NULL != m_hDebugStackList)
  323. {
  324. DPA_DestroyCallback(m_hDebugStackList, _DeleteCB, NULL);
  325. m_hDebugStackList = NULL;
  326. }
  327. LeaveCriticalSection(&m_csStackList);
  328. DeleteCriticalSection(&m_csStackList);
  329. }
  330. /*-----------------------------------------------------------------------------
  331. / CDebugStackHolder::Add
  332. / ----------------------
  333. / Saves the DebugStack object in a list
  334. /
  335. / In:
  336. / PDEBUGSTACK pointer to the thread's debug stack object
  337. /
  338. / Out:
  339. / -
  340. /----------------------------------------------------------------------------*/
  341. void
  342. CDebugStackHolder::Add(PDEBUGSTACK pDebugStack)
  343. {
  344. EnterCriticalSection(&m_csStackList);
  345. if (NULL == m_hDebugStackList)
  346. m_hDebugStackList = DPA_Create(4);
  347. if (NULL != m_hDebugStackList)
  348. DPA_AppendPtr(m_hDebugStackList, pDebugStack);
  349. LeaveCriticalSection(&m_csStackList);
  350. }
  351. /*-----------------------------------------------------------------------------
  352. / CDebugStackHolder::Remove
  353. / -------------------------
  354. / Removes the DebugStack object from the list
  355. /
  356. / In:
  357. / PDEBUGSTACK pointer to the thread's debug stack object
  358. /
  359. / Out:
  360. / -
  361. /----------------------------------------------------------------------------*/
  362. void
  363. CDebugStackHolder::Remove(PDEBUGSTACK pDebugStack)
  364. {
  365. EnterCriticalSection(&m_csStackList);
  366. if (NULL != m_hDebugStackList)
  367. {
  368. int iStack = DPA_GetPtrIndex(m_hDebugStackList, pDebugStack);
  369. if (-1 != iStack)
  370. DPA_DeletePtr(m_hDebugStackList, iStack);
  371. }
  372. LeaveCriticalSection(&m_csStackList);
  373. }
  374. /*-----------------------------------------------------------------------------
  375. / GetThreadStack
  376. / --------------
  377. / Create (if necessary) and return the per-thread debug stack object.
  378. /
  379. / In:
  380. / -
  381. /
  382. / Out:
  383. / PDEBUGSTACK pointer to the thread's debug stack object
  384. /----------------------------------------------------------------------------*/
  385. PDEBUGSTACK GetThreadStack()
  386. {
  387. PDEBUGSTACK pDebugStack;
  388. if (0xffffffffL == g_tlsDebug)
  389. return NULL;
  390. pDebugStack = (PDEBUGSTACK)TlsGetValue(g_tlsDebug);
  391. if (!pDebugStack)
  392. {
  393. pDebugStack = new CDebugStack;
  394. TlsSetValue(g_tlsDebug, pDebugStack);
  395. if (!g_pStackHolder)
  396. g_pStackHolder = new CDebugStackHolder;
  397. if (g_pStackHolder)
  398. g_pStackHolder->Add(pDebugStack);
  399. }
  400. return pDebugStack;
  401. }
  402. /*-----------------------------------------------------------------------------
  403. / DoTraceSetMask
  404. / --------------
  405. / Adjust the trace mask to reflect the state given.
  406. /
  407. / In:
  408. / dwMask = mask for enabling / disable trace output
  409. /
  410. / Out:
  411. / -
  412. /----------------------------------------------------------------------------*/
  413. void DoTraceSetMask(DWORD dwMask)
  414. {
  415. g_dwTraceMask = dwMask;
  416. }
  417. /*-----------------------------------------------------------------------------
  418. / DoTraceSetMaskFromRegKey
  419. / ------------------------
  420. / Pick up the TraceMask value from the given registry key and
  421. / set the trace mask using that.
  422. /
  423. / In:
  424. / hkRoot = handle of open key
  425. / pszSubKey = name of subkey to open
  426. /
  427. / Out:
  428. / -
  429. /----------------------------------------------------------------------------*/
  430. void DoTraceSetMaskFromRegKey(HKEY hkRoot, LPCTSTR pszSubKey)
  431. {
  432. HKEY hKey;
  433. if (ERROR_SUCCESS == RegOpenKey(hkRoot, pszSubKey, &hKey))
  434. {
  435. DWORD dwTraceMask = 0;
  436. DWORD cbTraceMask = sizeof(dwTraceMask);
  437. RegQueryValueEx(hKey,
  438. TEXT("TraceMask"),
  439. NULL,
  440. NULL,
  441. (LPBYTE)&dwTraceMask,
  442. &cbTraceMask);
  443. DoTraceSetMask(dwTraceMask);
  444. RegCloseKey(hKey);
  445. }
  446. }
  447. /*-----------------------------------------------------------------------------
  448. / DoTraceSetMaskFromCLSID
  449. / -----------------------
  450. / Pick up the TraceMask value from the given CLSID value and
  451. / set the trace mask using that.
  452. /
  453. / In:
  454. / rCLSID = CLSID to query the value from
  455. /
  456. / Out:
  457. / -
  458. /----------------------------------------------------------------------------*/
  459. void DoTraceSetMaskFromCLSID(REFCLSID rCLSID)
  460. {
  461. TCHAR szClsidKey[48] = TEXT("CLSID\\");
  462. int nLength = lstrlen(szClsidKey);
  463. if (!SHStringFromGUID(rCLSID, szClsidKey + nLength, ARRAYSIZE(szClsidKey) - nLength))
  464. return;
  465. DoTraceSetMaskFromRegKey(HKEY_CLASSES_ROOT, szClsidKey);
  466. }
  467. /*-----------------------------------------------------------------------------
  468. / DoTraceEnter
  469. / ------------
  470. / Set the debugging call stack up to indicate which function we are in.
  471. /
  472. / In:
  473. / pName -> function name to be displayed in subsequent trace output.
  474. /
  475. / Out:
  476. / -
  477. /----------------------------------------------------------------------------*/
  478. void DoTraceEnter(DWORD dwMask, LPCTSTR pName)
  479. {
  480. PDEBUGSTACK pDebugStack = GetThreadStack();
  481. if (pDebugStack)
  482. pDebugStack->_TraceEnter(dwMask, pName);
  483. }
  484. /*-----------------------------------------------------------------------------
  485. / DoTraceLeave
  486. / ------------
  487. / On exit from a function this will adjust the function stack pointer to
  488. / point to our previous function. If no trace output has been made then
  489. / we will output the function name on a single line (to indicate we went somewhere).
  490. /
  491. / In:
  492. / -
  493. / Out:
  494. / -
  495. /----------------------------------------------------------------------------*/
  496. void DoTraceLeave(void)
  497. {
  498. PDEBUGSTACK pDebugStack = GetThreadStack();
  499. if (pDebugStack)
  500. pDebugStack->_TraceLeave();
  501. }
  502. /*-----------------------------------------------------------------------------
  503. / DoTrace
  504. / -------
  505. / Perform printf formatting to the debugging stream. We indent the output
  506. / and stream the function name as required to give some indication of
  507. / call stack depth.
  508. /
  509. / In:
  510. / pszFormat -> printf style formatting string
  511. / ... = arguments as required for the formatting
  512. /
  513. / Out:
  514. / -
  515. /----------------------------------------------------------------------------*/
  516. void DoTrace(LPCTSTR pszFormat, ...)
  517. {
  518. PDEBUGSTACK pDebugStack = GetThreadStack();
  519. va_list va;
  520. if (pDebugStack)
  521. {
  522. va_start(va, pszFormat);
  523. pDebugStack->_vTrace(FALSE, pszFormat, &va);
  524. va_end(va);
  525. }
  526. }
  527. /*-----------------------------------------------------------------------------
  528. / DoTraceGuid
  529. / -----------
  530. / Given a GUID output it into the debug string, first we try and map it
  531. / to a name (ie. IShellFolder), if that didn't work then we convert it
  532. / to its human readable form.
  533. /
  534. / In:
  535. / pszPrefix -> prefix string
  536. / lpGuid -> guid to be streamed
  537. /
  538. / Out:
  539. / -
  540. /----------------------------------------------------------------------------*/
  541. void DoTraceGUID(LPCTSTR pPrefix, REFGUID rGUID)
  542. {
  543. PDEBUGSTACK pDebugStack = GetThreadStack();
  544. if (pDebugStack)
  545. pDebugStack->_TraceGUID(pPrefix, rGUID);
  546. }
  547. /*-----------------------------------------------------------------------------
  548. / DoTraceAssert
  549. / -------------
  550. / Our assert handler, out faults it the trace mask as enabled assert
  551. / faulting.
  552. /
  553. / In:
  554. / iLine = line
  555. / pFilename -> filename of the file we asserted in
  556. /
  557. / Out:
  558. / -
  559. /----------------------------------------------------------------------------*/
  560. void DoTraceAssert(int iLine, LPTSTR pFilename, LPTSTR pCondition)
  561. {
  562. PDEBUGSTACK pDebugStack = GetThreadStack();
  563. if (pDebugStack)
  564. pDebugStack->_TraceAssert(iLine, pFilename, pCondition);
  565. }
  566. /*-----------------------------------------------------------------------------
  567. / DebugThreadDetach
  568. / DebugProcessAttach
  569. / DebugProcessDetach
  570. / -------------
  571. / These must be called from DllMain
  572. /
  573. / In:
  574. / -
  575. /
  576. / Out:
  577. / -
  578. /----------------------------------------------------------------------------*/
  579. void DebugThreadDetach(void)
  580. {
  581. PDEBUGSTACK pDebugStack;
  582. if (0xffffffffL == g_tlsDebug)
  583. return;
  584. pDebugStack = (PDEBUGSTACK)TlsGetValue(g_tlsDebug);
  585. if (pDebugStack)
  586. {
  587. if (g_pStackHolder)
  588. g_pStackHolder->Remove(pDebugStack);
  589. delete pDebugStack;
  590. TlsSetValue(g_tlsDebug, NULL);
  591. }
  592. }
  593. void DebugProcessAttach(void)
  594. {
  595. g_tlsDebug = TlsAlloc();
  596. }
  597. void DebugProcessDetach(void)
  598. {
  599. DebugThreadDetach();
  600. if (NULL != g_pStackHolder)
  601. {
  602. delete g_pStackHolder;
  603. g_pStackHolder = NULL;
  604. }
  605. if (0xffffffffL != g_tlsDebug)
  606. {
  607. TlsFree(g_tlsDebug);
  608. g_tlsDebug = 0xffffffffL;
  609. }
  610. }
  611. #endif // DEBUG