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.

566 lines
14 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. //
  5. // Copyright (C) Microsoft Corporation, 1998 - 1998
  6. //
  7. // File: dbg_.cpp
  8. //
  9. //--------------------------------------------------------------------------
  10. // We want to continue using _vsnwprintf() below so we silence
  11. // the deprecation warnings.
  12. #define STRSAFE_NO_DEPRECATE
  13. #include <strsafe.h>
  14. /////////////////////////////////////////////////////////////////////
  15. // debug helpers
  16. #if defined(_USE_MTFRMWK_TRACE) || defined(_USE_MTFRMWK_ASSERT)
  17. #ifndef _MTFRMWK_INI_FILE
  18. #define _MTFRMWK_INI_FILE (L"\\system32\\mtfrmwk.ini")
  19. #endif
  20. UINT GetInfoFromIniFile(LPCWSTR lpszSection, LPCWSTR lpszKey, INT nDefault = 0)
  21. {
  22. static LPCWSTR lpszFile = _MTFRMWK_INI_FILE;
  23. WCHAR szFilePath[2*MAX_PATH];
  24. UINT nLen = ::GetSystemWindowsDirectory(szFilePath, 2*MAX_PATH);
  25. if (nLen == 0)
  26. return nDefault;
  27. // NOTICE-2002/04/18-artm Part of fix for ntraid#ntbug9-540061.
  28. // We need strsafe.h for this file, and there was a dangerous fctn
  29. // here that was flagged as deprecated (I've replaced with StringCchCat()).
  30. HRESULT hr = StringCchCat(szFilePath, 2*MAX_PATH, lpszFile);
  31. if (FAILED(hr))
  32. {
  33. return nDefault;
  34. }
  35. // ISSUE-2002/03/08-JeffJon-Since this function is deprecated we should move
  36. // to using the registry to turn on the debugging flags
  37. return ::GetPrivateProfileInt(lpszSection, lpszKey, nDefault, szFilePath);
  38. }
  39. #endif // defined(_USE_MTFRMWK_TRACE) || defined(_USE_MTFRMWK_ASSERT)
  40. #if defined(_USE_MTFRMWK_TRACE)
  41. DWORD g_dwTrace = ::GetInfoFromIniFile(L"Debug", L"Trace");
  42. void MtFrmwkTrace(LPCTSTR lpszFormat, ...)
  43. {
  44. if (g_dwTrace == 0)
  45. return;
  46. va_list args;
  47. va_start(args, lpszFormat);
  48. int nBuf;
  49. WCHAR szBuffer[512];
  50. ZeroMemory(szBuffer, sizeof(szBuffer));
  51. nBuf = _vsnwprintf(szBuffer, sizeof(szBuffer)/sizeof(WCHAR) - 1, lpszFormat, args);
  52. // was there an error? was the expanded string too long?
  53. ASSERT(nBuf >= 0);
  54. ::OutputDebugString(szBuffer);
  55. va_end(args);
  56. }
  57. #endif
  58. #if defined(DBG)
  59. void MtFrmwkLogFile(LPCTSTR lpszFormat, ...)
  60. {
  61. va_list args;
  62. va_start(args, lpszFormat);
  63. int nBuf;
  64. WCHAR szBuffer[512];
  65. ZeroMemory(szBuffer, sizeof(szBuffer));
  66. nBuf = _vsnwprintf(szBuffer, sizeof(szBuffer)/sizeof(WCHAR) - 1, lpszFormat, args);
  67. // If the string is truncated, we should still show it.
  68. CLogFile* _dlog = CLogFile::GetInstance();
  69. if (_dlog)
  70. {
  71. _dlog->writeln(szBuffer);
  72. }
  73. va_end(args);
  74. }
  75. void MtFrmwkLogFileIfLog(BOOL bLog, LPCTSTR lpszFormat, ...)
  76. {
  77. if (bLog)
  78. {
  79. va_list args;
  80. va_start(args, lpszFormat);
  81. int nBuf;
  82. WCHAR szBuffer[512];
  83. ZeroMemory(szBuffer, sizeof(szBuffer));
  84. nBuf = _vsnwprintf(szBuffer, sizeof(szBuffer)/sizeof(WCHAR) - 1, lpszFormat, args);
  85. // If the string is truncated, we should still show it.
  86. CLogFile* _dlog = CLogFile::GetInstance();
  87. if (_dlog)
  88. {
  89. _dlog->writeln(szBuffer);
  90. }
  91. va_end(args);
  92. }
  93. }
  94. #endif
  95. //
  96. // Copied and modified from burnslib on 12-07-1999 by JeffJon
  97. // Needed file logging on DnsSetup call from DCPromo.
  98. // I wanted it to behave like the DCPromo log but including all of
  99. // burnslib required too many alterations in the debugging behavior
  100. // already in place.
  101. //
  102. extern CString LOGFILE_NAME = _T("");
  103. static CLogFile* log_instance = 0;
  104. //
  105. // # of spaces per indentation level
  106. //
  107. static const int TAB = 2;
  108. static int margin = 0;
  109. //
  110. // index to Thread Local Storage slot where the per-thread debug state is
  111. // kept. Initialized in Startup
  112. //
  113. static DWORD tls_index = 0;
  114. CLogFile* CLogFile::GetInstance()
  115. {
  116. if (!log_instance && !LOGFILE_NAME.IsEmpty())
  117. {
  118. log_instance = new CLogFile(LOGFILE_NAME);
  119. }
  120. return log_instance;
  121. }
  122. void CLogFile::KillInstance()
  123. {
  124. delete log_instance;
  125. log_instance = 0;
  126. }
  127. BOOL PathExists(PCWSTR pszPath)
  128. {
  129. DWORD attrs = GetFileAttributes(pszPath);
  130. if (attrs != 0xFFFFFFFF)
  131. {
  132. return TRUE;
  133. }
  134. return FALSE;
  135. }
  136. HANDLE OpenFile(PCWSTR pszPath)
  137. {
  138. //
  139. // remove the last element of the path to form the parent directory
  140. //
  141. HANDLE handle = ::CreateFile(pszPath,
  142. GENERIC_WRITE,
  143. 0,
  144. 0,
  145. OPEN_ALWAYS,
  146. FILE_ATTRIBUTE_NORMAL,
  147. 0);
  148. return handle;
  149. }
  150. PCWSTR GetSystemRootDirectory()
  151. {
  152. static CString SYSTEMROOT;
  153. WCHAR buf[MAX_PATH + 1];
  154. DWORD result = ::GetWindowsDirectory(buf, MAX_PATH + 1);
  155. ASSERT(result != 0 && result <= MAX_PATH);
  156. if (result == 0 || result > MAX_PATH)
  157. {
  158. return NULL;
  159. }
  160. SYSTEMROOT = buf;
  161. return (PCWSTR)SYSTEMROOT;
  162. }
  163. // locate the log file with the highest-numbered extension, then add 1 and
  164. // return the result.
  165. int DetermineNextLogNumber(PCWSTR logDir, PCWSTR logBaseName)
  166. {
  167. ASSERT(logDir != NULL);
  168. ASSERT(logBaseName != NULL);
  169. int largest = 0;
  170. CString filespec = CString(logDir) + L"\\" + CString(logBaseName) + L".*.log";
  171. WIN32_FIND_DATA findData;
  172. HANDLE ff = ::FindFirstFile(filespec, &findData);
  173. if (ff != INVALID_HANDLE_VALUE)
  174. {
  175. for (;;)
  176. {
  177. CString current = findData.cFileName;
  178. // grab the text between the dots: "nnn" in foo.nnn.ext
  179. // first dot
  180. int pos = current.Find(L".");
  181. if (pos == -1)
  182. {
  183. continue;
  184. }
  185. CString extension = current.Right(current.GetLength() - pos - 1);
  186. // second dot
  187. pos = extension.Find(L".");
  188. if (pos == -1)
  189. {
  190. continue;
  191. }
  192. extension = extension.Left(pos);
  193. long i = 0;
  194. i = wcstol(extension, L'\0', 10);
  195. largest = max(i, largest);
  196. if (!::FindNextFile(ff, &findData))
  197. {
  198. ::FindClose(ff);
  199. break;
  200. }
  201. }
  202. }
  203. // roll over after 255
  204. return (++largest & 0xFF);
  205. }
  206. // Determine the name of the log file. If a log file of that name already
  207. // exists, rename the existing file to a numbered backup. Create the new
  208. // log file, return a handle to it.
  209. //
  210. HANDLE OpenNewLogFile(PCWSTR pszLogBaseName, CString& logName)
  211. {
  212. CString logDir = CString(GetSystemRootDirectory()) + L"\\debug";
  213. int i = DetermineNextLogNumber(logDir, pszLogBaseName);
  214. CString szCount;
  215. szCount.Format(L"%d", i);
  216. logName = logDir + L"\\" + pszLogBaseName + L"." + szCount + L".log";
  217. HANDLE result = OpenFile(logName);
  218. return result;
  219. }
  220. // Create a new log.
  221. //
  222. // logBaseName - base name of the log. If logging-to-file is active, then a
  223. // file in the %windir%\debug folder will be created/used. The name of the
  224. // file is of the form %windir%\debug\logBaseName.log. If a file by that name
  225. // already exists, then the existing file will be renamed
  226. // %windir%\debug\logBaseName.xxx.log, where xxx is an integer 1 greater than
  227. // the last so-numbered file in that directory.
  228. CLogFile::CLogFile(PCWSTR pszLogBaseName)
  229. :
  230. szBase_name(pszLogBaseName),
  231. file_handle(INVALID_HANDLE_VALUE),
  232. trace_line_number(0)
  233. {
  234. ASSERT(pszLogBaseName != NULL);
  235. if (pszLogBaseName != NULL)
  236. {
  237. CString logName;
  238. file_handle = OpenNewLogFile(pszLogBaseName, logName);
  239. if (file_handle != INVALID_HANDLE_VALUE)
  240. {
  241. CString szOpeningFile = L"opening log file ";
  242. szOpeningFile += logName;
  243. writeln(szOpeningFile);
  244. }
  245. }
  246. SYSTEMTIME localtime;
  247. ::GetLocalTime(&localtime);
  248. CString szTime;
  249. szTime.Format(L"%d/%d/%d %d:%d:%d.%d",
  250. localtime.wMonth,
  251. localtime.wDay,
  252. localtime.wYear,
  253. localtime.wHour,
  254. localtime.wMinute,
  255. localtime.wSecond,
  256. localtime.wMilliseconds);
  257. writeln(szTime);
  258. }
  259. CLogFile::~CLogFile()
  260. {
  261. if (IsOpen())
  262. {
  263. writeln(L"closing log file");
  264. ::CloseHandle(file_handle);
  265. file_handle = INVALID_HANDLE_VALUE;
  266. }
  267. }
  268. // guarded by caller
  269. void CLogFile::indent()
  270. {
  271. //
  272. // indent by adding to the margin
  273. //
  274. margin += TAB;
  275. }
  276. BOOL CLogFile::IsOpen() const
  277. {
  278. return file_handle != INVALID_HANDLE_VALUE;
  279. }
  280. // guarded by caller
  281. void CLogFile::outdent()
  282. {
  283. //
  284. // outdent by subtracting from the margin
  285. //
  286. ASSERT(margin >= TAB);
  287. margin = max(0, margin - TAB);
  288. }
  289. void ConvertStringToANSI(PCWSTR pszWide, PSTR* ppAnsi)
  290. {
  291. ASSERT(ppAnsi);
  292. ASSERT(pszWide);
  293. if (!pszWide || !ppAnsi)
  294. {
  295. return;
  296. }
  297. *ppAnsi = NULL;
  298. //
  299. // determine the size of the buffer required to hold the ANSI string
  300. //
  301. // This function assumes pszWide is NULL terminated.
  302. // REVIEWED-2002/03/08-JeffJon-This is proper usage
  303. int bufsize =
  304. ::WideCharToMultiByte(
  305. CP_ACP,
  306. 0,
  307. pszWide,
  308. -1, // Let WCtoMB determine length of pszWide.
  309. 0, 0, 0, 0);
  310. if (bufsize > 0)
  311. {
  312. *ppAnsi = new CHAR[bufsize];
  313. if (*ppAnsi == NULL)
  314. {
  315. return;
  316. }
  317. memset(*ppAnsi, 0, bufsize);
  318. // We need to pass the size of the buffer *including* space for NULL
  319. // to WideCharToMultiByte().
  320. size_t result = ::WideCharToMultiByte(CP_ACP,
  321. 0,
  322. pszWide,
  323. -1, // Let WCtoMB determine length of pszWide.
  324. *ppAnsi,
  325. bufsize,
  326. 0,
  327. 0);
  328. ASSERT(result);
  329. if (!result)
  330. {
  331. *ppAnsi = NULL;
  332. }
  333. }
  334. }
  335. //
  336. // Spews output to the log according to the current logging type and
  337. // output options in effect.
  338. //
  339. // type - log output type of this output spewage.
  340. //
  341. // text - the spewage. This is prefaced with the log name, thread id, spewage
  342. // line number, and current indentation.
  343. //
  344. void CLogFile::writeln(PCWSTR pszText)
  345. {
  346. CString white(L' ',margin);
  347. // Format the line with thread ID, line number, whitespace, and the text
  348. CString t = LOGFILE_NAME;
  349. t.Format(L" t:0x%x %3d ", ::GetCurrentThreadId(), trace_line_number);
  350. t += white;
  351. t += pszText;
  352. if (IsOpen())
  353. {
  354. ASSERT(file_handle != INVALID_HANDLE_VALUE);
  355. ASSERT(!t.IsEmpty());
  356. PSTR pAnsi;
  357. ConvertStringToANSI(t, &pAnsi);
  358. if (pAnsi)
  359. {
  360. // NTRAID#NTBUG9-657626-2002/07/11-sburns
  361. size_t bytesToWrite = sizeof(CHAR) * strlen(pAnsi);
  362. DWORD bytes_written = 0;
  363. BOOL success =::WriteFile(file_handle,
  364. pAnsi,
  365. static_cast<ULONG>(bytesToWrite),
  366. &bytes_written,
  367. 0);
  368. ASSERT(success);
  369. ASSERT(bytes_written == bytesToWrite);
  370. delete[] pAnsi;
  371. }
  372. }
  373. trace_line_number++;
  374. }
  375. CScopeTracer::CScopeTracer(BOOL bLog, PCWSTR pszMessage_)
  376. :
  377. szMessage(pszMessage_),
  378. m_bLog(bLog)
  379. {
  380. // build this string once, instead of using the string literal in the
  381. // below expression (which would implicitly build the string on each
  382. // evaluation of that expression) as a slight performance gain.
  383. static const CString ENTER(L"Enter ");
  384. if (m_bLog)
  385. {
  386. CLogFile* li = CLogFile::GetInstance();
  387. li->writeln(ENTER + szMessage);
  388. li->indent();
  389. }
  390. }
  391. CScopeTracer::~CScopeTracer()
  392. {
  393. // build this string once, instead of using the string literal in the
  394. // below expression (which would implicitly build the string on each
  395. // evaluation of that expression) as a slight performance gain.
  396. static const CString EXIT(L"Exit ");
  397. if (m_bLog)
  398. {
  399. CLogFile* li = CLogFile::GetInstance();
  400. li->outdent();
  401. li->writeln(EXIT + szMessage);
  402. }
  403. }
  404. #if defined(_USE_MTFRMWK_ASSERT)
  405. DWORD g_dwAssert = ::GetInfoFromIniFile(L"Debug", L"Assert");
  406. BOOL MtFrmwkAssertFailedLine(LPCSTR lpszFileName, int nLine)
  407. {
  408. if (g_dwAssert == 0)
  409. return FALSE;
  410. WCHAR szMessage[_MAX_PATH*2];
  411. // assume the debugger or auxiliary port
  412. // NOTICE-2002/04/18-artm Part of fix for ntraid#ntbug9-540061.
  413. HRESULT hr = StringCchPrintfW(
  414. szMessage, // destination buffer,
  415. _MAX_PATH*2, // size of dest. buffer, including null
  416. _T("Assertion Failed: File %hs, Line %d\n"),
  417. lpszFileName,
  418. nLine);
  419. // If the string is truncated, we should still show it.
  420. OutputDebugString(szMessage);
  421. // display the assert
  422. int nCode = ::MessageBox(NULL, szMessage, _T("Assertion Failed!"),
  423. MB_TASKMODAL|MB_ICONHAND|MB_ABORTRETRYIGNORE|MB_SETFOREGROUND);
  424. OutputDebugString(L"after message box\n");
  425. if (nCode == IDIGNORE)
  426. {
  427. return FALSE; // ignore
  428. }
  429. if (nCode == IDRETRY)
  430. {
  431. return TRUE; // will cause DebugBreak
  432. }
  433. abort(); // should not return
  434. return TRUE;
  435. }
  436. #endif // _USE_MTFRMWK_ASSERT