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.

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