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.

499 lines
11 KiB

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