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.

730 lines
18 KiB

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