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.

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