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.

688 lines
19 KiB

  1. //+---------------------------------------------------------------------------
  2. // Copyright (C) 1991-1994, Microsoft Corporation.
  3. //
  4. // File: assert.cpp
  5. //
  6. // Contents: Debugging output routines
  7. //
  8. // History: 23-Jul-91 KyleP Created.
  9. // 09-Oct-91 KevinRo Major changes and comments added
  10. // 18-Oct-91 vich moved debug print routines out
  11. // 10-Jun-92 BryanT Switched to w4crt.h instead of wchar.h
  12. // 7-Oct-94 BruceFo Ripped out all kernel, non-FLAT,
  13. // DLL-specific, non-Win32 functionality.
  14. // Now it's basically "print to the
  15. // debugger" code.
  16. // 20-Oct-95 EricB Set component debug level in the
  17. // registry.
  18. //
  19. //----------------------------------------------------------------------------
  20. #include "headers.hxx"
  21. #pragma hdrstop
  22. #include <shlwapi.h>
  23. #include <objbase.h>
  24. #include <basetyps.h>
  25. #include <tchar.h>
  26. #if DBG==1
  27. #include "malloc.h"
  28. //
  29. // Globals
  30. //
  31. ULONG g_AdminAssertLevel = ASSRT_MESSAGE | ASSRT_BREAK | ASSRT_POPUP;
  32. BOOL g_fInitializedTickCount = FALSE;
  33. ULONG g_ulTickCountAtStart;
  34. DECLARE_HEAPCHECKING;
  35. //
  36. // Forward declration of local functions
  37. //
  38. LPSTR AnsiPathFindFileName(LPSTR pPath);
  39. void smprintf(ULONG ulCompMask, ULONG cchIndent, LPTSTR pszComp, LPTSTR ppszfmt, va_list pargs);
  40. static int w4smprintf(LPTSTR format, va_list arglist);
  41. //+---------------------------------------------------------------------------
  42. //
  43. // Function: w4dprintf
  44. //
  45. // Synopsis: Calls w4smprintf to output a formatted message.
  46. //
  47. //----------------------------------------------------------------------------
  48. static int __cdecl w4dprintf(LPTSTR format, ...)
  49. {
  50. int ret;
  51. va_list va;
  52. va_start(va, format);
  53. ret = w4smprintf(format, va);
  54. va_end(va);
  55. return ret;
  56. }
  57. //+---------------------------------------------------------------------------
  58. //
  59. // Function: w4smprintf
  60. //
  61. // Synopsis: Calls OutputDebugStringA to output a formatted message.
  62. //
  63. //----------------------------------------------------------------------------
  64. static int w4smprintf(LPTSTR format, va_list arglist)
  65. {
  66. int ret;
  67. TCHAR szMessageBuf[2048]; // this is the message buffer
  68. ret = wvnsprintf(szMessageBuf, // JonN 1/23/01 290502
  69. ARRAYLEN(szMessageBuf),
  70. format,
  71. arglist);
  72. OutputDebugString(szMessageBuf);
  73. return ret;
  74. }
  75. //+------------------------------------------------------------
  76. // Function: smprintf
  77. //
  78. // Synopsis: Prints debug output using a pointer to the
  79. // variable information. Used primarily by the
  80. // xxDebugOut macros
  81. //
  82. // Arguements:
  83. // ulCompMask -- Component level mask used to determine
  84. // output ability
  85. // pszComp -- String const of component prefix.
  86. // ppszfmt -- Pointer to output format and data
  87. //
  88. //-------------------------------------------------------------
  89. void smprintf(
  90. ULONG ulCompMask,
  91. ULONG cchIndent,
  92. LPTSTR pszComp,
  93. LPTSTR ppszfmt,
  94. va_list pargs)
  95. {
  96. if (ulCompMask & DEB_FORCE)
  97. {
  98. if (ulCompMask & DEB_ELAPSEDTIME)
  99. {
  100. ULONG ulTicksNow = GetTickCount();
  101. if (!g_fInitializedTickCount)
  102. {
  103. g_fInitializedTickCount = TRUE;
  104. g_ulTickCountAtStart = ulTicksNow;
  105. }
  106. ULONG ulDelta;
  107. if (g_ulTickCountAtStart > ulTicksNow)
  108. {
  109. ulDelta = ulTicksNow + ((ULONG)-1) - g_ulTickCountAtStart;
  110. }
  111. else
  112. {
  113. ulDelta = ulTicksNow - g_ulTickCountAtStart;
  114. }
  115. w4dprintf(_T("%04u.%03u "), ulDelta / 1000, ulDelta % 1000);
  116. }
  117. if (! (ulCompMask & DEB_NOCOMPNAME))
  118. {
  119. DWORD pid = GetCurrentProcessId();
  120. DWORD tid = GetCurrentThreadId();
  121. w4dprintf(_T("%x.%03x> %s: "), pid, tid, pszComp);
  122. }
  123. if (cchIndent)
  124. {
  125. TCHAR tzFmt[] = _T("%999s");
  126. wsprintf(tzFmt, _T("%%%us"), cchIndent);
  127. w4dprintf(tzFmt, _T(""));
  128. }
  129. w4smprintf(ppszfmt, pargs);
  130. }
  131. }
  132. //+----------------------------------------------------------------------------
  133. //
  134. // Admin debugging library inititalization.
  135. //
  136. // To set a non-default debug info level outside of the debugger, create the
  137. // below registry key and in it create a value whose name is the component's
  138. // debugging tag name (the "comp" parameter to the DECLARE_INFOLEVEL macro) and
  139. // whose data is the desired infolevel in REG_DWORD format.
  140. //-----------------------------------------------------------------------------
  141. #define CURRENT_VERSION_KEY _T("SOFTWARE\\Microsoft\\Windows\\CurrentVersion")
  142. #define ADMINDEBUGKEY _T("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\AdminDebug")
  143. #define ADMINDEBUG _T("AdminDebug")
  144. //+----------------------------------------------------------------------------
  145. // Function: CheckInit
  146. //
  147. // Synopsis: Performs debugging library initialization
  148. // including reading the registry for the desired infolevel
  149. //
  150. //-----------------------------------------------------------------------------
  151. void CheckInit(LPTSTR pInfoLevelString, ULONG * pulInfoLevel)
  152. {
  153. HKEY hKey;
  154. LONG lRet;
  155. DWORD dwSize;
  156. *pulInfoLevel = DEF_INFOLEVEL;
  157. lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  158. ADMINDEBUGKEY,
  159. 0,
  160. KEY_READ,
  161. &hKey);
  162. if (lRet == ERROR_FILE_NOT_FOUND)
  163. {
  164. HKEY hkCV;
  165. lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, CURRENT_VERSION_KEY, 0,
  166. KEY_ALL_ACCESS, &hkCV);
  167. if (lRet == ERROR_SUCCESS)
  168. {
  169. lRet = RegCreateKeyEx(hkCV, ADMINDEBUG, 0, _T(""),
  170. REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, NULL);
  171. RegCloseKey(hkCV);
  172. }
  173. }
  174. if (lRet == ERROR_SUCCESS)
  175. {
  176. dwSize = sizeof(ULONG);
  177. lRet = RegQueryValueEx(hKey, pInfoLevelString, NULL, NULL,
  178. (LPBYTE)pulInfoLevel, &dwSize);
  179. if (lRet != ERROR_SUCCESS)
  180. {
  181. lRet = RegSetValueEx(hKey, pInfoLevelString, 0, REG_DWORD,
  182. (CONST BYTE *)pulInfoLevel, sizeof(ULONG));
  183. }
  184. RegCloseKey(hKey);
  185. }
  186. }
  187. // Returns a pointer to the last component of a path string.
  188. //
  189. // in:
  190. // path name, either fully qualified or not
  191. //
  192. // returns:
  193. // pointer into the path where the path is. if none is found
  194. // returns a poiter to the start of the path
  195. //
  196. // c:\foo\bar -> bar
  197. // c:\foo -> foo
  198. // c:\foo\ -> c:\foo\ (REVIEW: is this case busted?)
  199. // c:\ -> c:\ (REVIEW: this case is strange)
  200. // c: -> c:
  201. // foo -> foo
  202. LPSTR AnsiPathFindFileName(LPSTR pPath)
  203. {
  204. LPSTR pT;
  205. for (pT = pPath; *pPath; pPath = CharNextA(pPath))
  206. {
  207. if ((pPath[0] == '\\' || pPath[0] == ':')
  208. && pPath[1] && (pPath[1] != '\\'))
  209. pT = pPath + 1;
  210. }
  211. return (LPSTR)pT; // const -> non const
  212. }
  213. /////////////////////////////////////////////////////////////////////////////
  214. /////////////////////////////////////////////////////////////////////////////
  215. ////////////// ASSERT CODE //////////////////////////////////////////////
  216. /////////////////////////////////////////////////////////////////////////////
  217. /////////////////////////////////////////////////////////////////////////////
  218. //+------------------------------------------------------------
  219. // Function: PopUpError
  220. //
  221. // Synopsis: Displays a dialog box using provided text,
  222. // and presents the user with the option to
  223. // continue or cancel.
  224. //
  225. // Arguments:
  226. // szMsg -- The string to display in main body of dialog
  227. // iLine -- Line number of file in error
  228. // szFile -- Filename of file in error
  229. //
  230. // Returns:
  231. // IDCANCEL -- User selected the CANCEL button
  232. // IDOK -- User selected the OK button
  233. //-------------------------------------------------------------
  234. int PopUpError(LPTSTR szMsg, int iLine, LPSTR szFile)
  235. {
  236. //
  237. // Create caption
  238. //
  239. static TCHAR szAssertCaption[128];
  240. //
  241. // get process
  242. //
  243. static CHAR szModuleName[128];
  244. LPSTR pszModuleName;
  245. if (GetModuleFileNameA(NULL, szModuleName, 128))
  246. {
  247. pszModuleName = szModuleName;
  248. }
  249. else
  250. {
  251. pszModuleName = "Unknown";
  252. }
  253. LPSTR pProcess = AnsiPathFindFileName(pszModuleName);
  254. wnsprintf(szAssertCaption, ARRAYLEN(szAssertCaption), _T("%hs: Assertion Failed"), pProcess);
  255. //
  256. // Create details.
  257. //
  258. TCHAR szDetails[1024];
  259. DWORD tid = GetCurrentThreadId();
  260. DWORD pid = GetCurrentProcessId();
  261. wnsprintf(szDetails, ARRAYLEN(szDetails), _T(" Assertion:\t %s\n\n") \
  262. _T(" File: \t\t %hs\n") \
  263. _T(" Line: \t\t %d\n\n") \
  264. _T(" Module: \t %hs\n") \
  265. _T(" Thread ID:\t %d.%d\n"),
  266. szMsg, szFile, iLine, pszModuleName, pid, tid);
  267. int id = MessageBox(NULL,
  268. szDetails,
  269. szAssertCaption,
  270. MB_SETFOREGROUND
  271. | MB_DEFAULT_DESKTOP_ONLY
  272. | MB_TASKMODAL
  273. | MB_ICONEXCLAMATION
  274. | MB_OKCANCEL);
  275. //
  276. // If id == 0, then an error occurred. There are two possibilities
  277. // that can cause the error: Access Denied, which means that this
  278. // process does not have access to the default desktop, and everything
  279. // else (usually out of memory).
  280. //
  281. if (0 == id)
  282. {
  283. if (GetLastError() == ERROR_ACCESS_DENIED)
  284. {
  285. //
  286. // Retry this one with the SERVICE_NOTIFICATION flag on. That
  287. // should get us to the right desktop.
  288. //
  289. id = MessageBox(NULL,
  290. szMsg,
  291. szAssertCaption,
  292. MB_SETFOREGROUND
  293. | MB_TASKMODAL
  294. | MB_ICONEXCLAMATION
  295. | MB_OKCANCEL);
  296. }
  297. }
  298. return id;
  299. }
  300. //+---------------------------------------------------------------------------
  301. //
  302. // Function: _asdprintf
  303. //
  304. // Synopsis: Calls smprintf to output a formatted message.
  305. //
  306. // History: 18-Oct-91 vich Created
  307. //
  308. //----------------------------------------------------------------------------
  309. inline void __cdecl _asdprintf(LPTSTR pszfmt, ...)
  310. {
  311. va_list va;
  312. va_start(va, pszfmt);
  313. smprintf(DEB_FORCE, 0, _T("Assert"), pszfmt, va);
  314. va_end(va);
  315. }
  316. //+---------------------------------------------------------------------------
  317. //
  318. // Function: AdminAssertEx, private
  319. //
  320. // Synopsis: Display assertion information
  321. //
  322. // Effects: Called when an assertion is hit.
  323. //
  324. //----------------------------------------------------------------------------
  325. void AdminAssertEx(LPSTR szFile, int iLine, LPTSTR szMessage)
  326. {
  327. if (g_AdminAssertLevel & ASSRT_MESSAGE)
  328. {
  329. DWORD tid = GetCurrentThreadId();
  330. LPSTR pszFileName = AnsiPathFindFileName(szFile);
  331. _asdprintf(_T("%s <%hs, l %u, thread %d>\n"),
  332. szMessage, pszFileName, iLine, tid);
  333. }
  334. if (g_AdminAssertLevel & ASSRT_POPUP)
  335. {
  336. int id = PopUpError(szMessage,iLine,szFile);
  337. if (id == IDCANCEL)
  338. {
  339. DebugBreak();
  340. }
  341. }
  342. else if (g_AdminAssertLevel & ASSRT_BREAK)
  343. {
  344. DebugBreak();
  345. }
  346. }
  347. //____________________________________________________________________________
  348. //____________________________________________________________________________
  349. //________________ _________________________________________
  350. //________________ class CDbg _________________________________________
  351. //________________ _________________________________________
  352. //____________________________________________________________________________
  353. //____________________________________________________________________________
  354. CDbg::CDbg(LPTSTR str)
  355. :
  356. m_InfoLevelString(str),
  357. m_flInfoLevel(DEF_INFOLEVEL),
  358. m_flOutputOptions(0)
  359. {
  360. ULONG flRegistry = 0;
  361. CheckInit(m_InfoLevelString, &flRegistry);
  362. m_flInfoLevel = flRegistry & DEB_FORCE;
  363. m_flOutputOptions = flRegistry & ~DEB_FORCE;
  364. }
  365. CDbg::~CDbg()
  366. {
  367. TlsFree(CDbg::s_idxTls);
  368. CDbg::s_idxTls = 0xFFFFFFFF;
  369. }
  370. void __cdecl CDbg::Trace(LPSTR pszfmt, ...)
  371. {
  372. #ifdef UNICODE
  373. ULONG convert = static_cast<ULONG>(strlen(pszfmt)) + 1;
  374. // NTRAID#NTBUG9-2002/05/17-623723-ericb: prefast
  375. PWSTR ptcfmt = (PWSTR)malloc(convert * sizeof(WCHAR));
  376. if (!ptcfmt)
  377. {
  378. OutputDebugString(TEXT("Trace: out of memory!"));
  379. return;
  380. }
  381. ptcfmt[0] = '\0';
  382. (void) MultiByteToWideChar(CP_ACP, 0, pszfmt, -1, ptcfmt, convert);
  383. #else
  384. LPTSTR ptcfmt = pszfmt;
  385. #endif
  386. if (m_flInfoLevel & DEB_TRACE)
  387. {
  388. va_list va;
  389. va_start (va, pszfmt);
  390. ULONG cchIndent = _GetIndent();
  391. smprintf(DEB_TRACE, cchIndent, m_InfoLevelString, ptcfmt, va);
  392. va_end(va);
  393. }
  394. #ifdef UNICODE
  395. free(ptcfmt);
  396. #endif
  397. }
  398. void __cdecl CDbg::Trace(PWSTR pwzfmt, ...)
  399. {
  400. #ifndef UNICODE
  401. int convert = wcslen(pwzfmt) + 1;
  402. // NTRAID#NTBUG9-2002/05/17-623723-ericb: prefast
  403. LPSTR ptcfmt = (LPSTR)malloc(convert * sizeof(CHAR));
  404. if (!ptcfmt)
  405. {
  406. OutputDebugString(TEXT("Trace: out of memory!"));
  407. return;
  408. }
  409. ptcfmt[0] = '\0';
  410. (void) WideCharToMultiByte(CP_ACP, 0, pwzfmt, -1, ptcfmt, convert, NULL, NULL);
  411. #else
  412. LPTSTR ptcfmt = pwzfmt;
  413. #endif
  414. if (m_flInfoLevel & DEB_TRACE)
  415. {
  416. va_list va;
  417. va_start (va, pwzfmt);
  418. ULONG cchIndent = _GetIndent();
  419. smprintf(DEB_TRACE, cchIndent, m_InfoLevelString, ptcfmt, va);
  420. va_end(va);
  421. }
  422. #ifndef UNICODE
  423. free(ptcfmt);
  424. #endif
  425. }
  426. void __cdecl CDbg::DebugOut(ULONG fDebugMask, LPSTR pszfmt, ...)
  427. {
  428. #ifdef UNICODE
  429. ULONG convert = static_cast<ULONG>(strlen(pszfmt)) + 1;
  430. // NTRAID#NTBUG9-2002/05/17-623723-ericb: prefast
  431. PWSTR ptcfmt = (PWSTR)malloc(convert * sizeof(WCHAR));
  432. if (!ptcfmt)
  433. {
  434. OutputDebugString(TEXT("DebugOut: out of memory!"));
  435. return;
  436. }
  437. ptcfmt[0] = '\0';
  438. (void) MultiByteToWideChar(CP_ACP, 0, pszfmt, -1, ptcfmt, convert);
  439. #else
  440. LPTSTR ptcfmt = pszfmt;
  441. #endif
  442. va_list va;
  443. va_start (va, pszfmt);
  444. ULONG cchIndent = _GetIndent();
  445. smprintf(m_flOutputOptions | (m_flInfoLevel & fDebugMask)
  446. | (fDebugMask & DEB_NOCOMPNAME),
  447. cchIndent,
  448. m_InfoLevelString,
  449. ptcfmt,
  450. va);
  451. va_end(va);
  452. #ifdef UNICODE
  453. free(ptcfmt);
  454. #endif
  455. }
  456. void __cdecl CDbg::DebugOut(ULONG fDebugMask, PWSTR pwzfmt, ...)
  457. {
  458. #ifndef UNICODE
  459. int convert = wcslen(pwzfmt) + 1;
  460. // NTRAID#NTBUG9-2002/05/17-623723-ericb: prefast
  461. LPSTR ptcfmt = (LPSTR)malloc(convert * sizeof(CHAR));
  462. if (!ptcfmt)
  463. {
  464. OutputDebugString(TEXT("DebugOut: out of memory!"));
  465. return;
  466. }
  467. ptcfmt[0] = '\0';
  468. (void) WideCharToMultiByte(CP_ACP, 0, pwzfmt, -1, ptcfmt, convert, NULL, NULL);
  469. #else
  470. LPTSTR ptcfmt = pwzfmt;
  471. #endif
  472. va_list va;
  473. va_start (va, pwzfmt);
  474. ULONG cchIndent = _GetIndent();
  475. smprintf(m_flOutputOptions | (m_flInfoLevel & fDebugMask)
  476. | (fDebugMask & DEB_NOCOMPNAME),
  477. cchIndent,
  478. m_InfoLevelString,
  479. ptcfmt,
  480. va);
  481. va_end(va);
  482. #ifndef UNICODE
  483. free(ptcfmt);
  484. #endif
  485. }
  486. void CDbg::DebugErrorX(LPSTR file, ULONG line, LONG err)
  487. {
  488. if (m_flInfoLevel & DEB_ERROR)
  489. {
  490. file = AnsiPathFindFileName(file);
  491. this->DebugOut(DEB_ERROR, "error<0x%08x> %hs, l %u\n",
  492. err, file, line);
  493. }
  494. }
  495. void CDbg::DebugErrorL(LPSTR file, ULONG line, LONG err)
  496. {
  497. if (m_flInfoLevel & DEB_ERROR)
  498. {
  499. file = AnsiPathFindFileName(file);
  500. this->DebugOut(DEB_ERROR, "error<%uL> %hs, l %u\n", err, file, line);
  501. }
  502. }
  503. void CDbg::DebugMsg(LPSTR file, ULONG line, LPSTR msg)
  504. {
  505. file = AnsiPathFindFileName(file);
  506. this->DebugOut(DEB_FORCE, "asrt %hs, l %u, <%s>\n", file, line, msg);
  507. }
  508. void CDbg::DebugMsg(LPSTR file, ULONG line, PWSTR msg)
  509. {
  510. file = AnsiPathFindFileName(file);
  511. this->DebugOut(DEB_FORCE, _T("asrt %hs, l %u, <%s>\n"), file, line, msg);
  512. }
  513. void CDbg::AssertEx(LPSTR pszFile, int iLine, LPTSTR pszMsg)
  514. {
  515. #if 0
  516. LPTSTR ptcMsg = NULL;
  517. #ifdef UNICODE
  518. int convert = strlen(pszMsg) + 1;
  519. // NTRAID#NTBUG9-2002/05/17-623723-ericb: prefast
  520. ptcMsg = (PWSTR)malloc(convert * sizeof(WCHAR));
  521. if (!ptcMsg)
  522. {
  523. OutputDebugString(TEXT("AssertEx: out of memory!"));
  524. return;
  525. }
  526. ptcMsg[0] = '\0';
  527. (void) MultiByteToWideChar(CP_ACP, 0, pszMsg, -1, ptcMsg, convert);
  528. #else
  529. ptcMsg = pszMsg;
  530. #endif
  531. AdminAssertEx(pszFile, iLine, ptcMsg);
  532. #ifdef UNICODE
  533. free(ptcMsg);
  534. #endif
  535. #endif //0
  536. AdminAssertEx(pszFile, iLine, pszMsg);
  537. }
  538. ULONG
  539. CDbg::_GetIndent()
  540. {
  541. ULONG cchIndent = 0;
  542. if (s_idxTls != 0xFFFFFFFF)
  543. {
  544. cchIndent = static_cast<ULONG>
  545. (reinterpret_cast<ULONG_PTR>
  546. (TlsGetValue(s_idxTls)));
  547. }
  548. return cchIndent;
  549. }
  550. void CDbg::IncIndent()
  551. {
  552. if (s_idxTls != 0xFFFFFFFF)
  553. {
  554. ULONG_PTR cchIndent = reinterpret_cast<ULONG_PTR>(TlsGetValue(s_idxTls));
  555. cchIndent++;
  556. TlsSetValue(s_idxTls, reinterpret_cast<PVOID>(cchIndent));
  557. }
  558. }
  559. void CDbg::DecIndent()
  560. {
  561. if (s_idxTls != 0xFFFFFFFF)
  562. {
  563. ULONG_PTR cchIndent = reinterpret_cast<ULONG_PTR>(TlsGetValue(s_idxTls));
  564. cchIndent--;
  565. TlsSetValue(s_idxTls, reinterpret_cast<PVOID>(cchIndent));
  566. }
  567. }
  568. #endif // DBG==1