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.

1257 lines
29 KiB

  1. // Copyright (c) 1997-1999 Microsoft Corporation
  2. //
  3. // Debugging tools
  4. //
  5. // 8-13-97 sburns
  6. #include "headers.hxx"
  7. #ifdef LOGGING_BUILD
  8. //
  9. // Logging build only
  10. //
  11. static Burnslib::Log* logInstance = 0;
  12. // # of spaces per indentation level
  13. static const int TAB = 2;
  14. Burnslib::Log*
  15. Burnslib::Log::GetInstance()
  16. {
  17. do
  18. {
  19. if (!logInstance)
  20. {
  21. static bool initialized = false;
  22. if (initialized)
  23. {
  24. ASSERT(false);
  25. break;
  26. }
  27. // this might fail with a thrown exception if Log::Log can't
  28. // init it's critical section. In that case, we can't allocate
  29. // an instance. We don't handle that exception, but let it
  30. // propagate to the caller
  31. // ISSUE-2002/03/06-sburns we should handle the exception and
  32. // leave the log uninitialized
  33. logInstance = new Burnslib::Log(RUNTIME_NAME);
  34. initialized = true;
  35. }
  36. }
  37. while (0);
  38. return logInstance;
  39. }
  40. // read the debugging options from the registry
  41. void
  42. Burnslib::Log::ReadLogFlags()
  43. {
  44. do
  45. {
  46. static String keyname =
  47. String(REG_ADMIN_RUNTIME_OPTIONS) + RUNTIME_NAME;
  48. HKEY hKey = 0;
  49. LONG result =
  50. ::RegCreateKeyEx(
  51. HKEY_LOCAL_MACHINE,
  52. keyname.c_str(),
  53. 0,
  54. 0,
  55. REG_OPTION_NON_VOLATILE,
  56. // ISSUE-2002/03/05-sburns should be KEY_SET_VALUE | KEY_READ
  57. KEY_ALL_ACCESS,
  58. // ISSUE-2002/03/05-sburns should set a restrictive ACL here
  59. // see NTRAID#NTBUG9-535841-2002/03/05-sburns
  60. 0,
  61. &hKey,
  62. 0);
  63. if (result != ERROR_SUCCESS)
  64. {
  65. break;
  66. }
  67. static const wchar_t* FLAG_VALUE_NAME = L"LogFlags";
  68. DWORD dataSize = sizeof(flags);
  69. // REVIEWED-2002/03/05-sburns no null-termination issue here
  70. result =
  71. ::RegQueryValueEx(
  72. hKey,
  73. FLAG_VALUE_NAME,
  74. 0,
  75. 0,
  76. reinterpret_cast<BYTE*>(&flags),
  77. &dataSize);
  78. if (result != ERROR_SUCCESS)
  79. {
  80. flags = DEFAULT_LOGGING_OPTIONS;
  81. // create the value for convenience
  82. result =
  83. // REVIEWED-2002/03/05-sburns no null-termination issue here
  84. ::RegSetValueEx(
  85. hKey,
  86. FLAG_VALUE_NAME,
  87. 0,
  88. REG_DWORD,
  89. reinterpret_cast<BYTE*>(&flags),
  90. dataSize);
  91. }
  92. ::RegCloseKey(hKey);
  93. }
  94. while (0);
  95. }
  96. Burnslib::Log::~Log()
  97. {
  98. WriteLn(Burnslib::Log::OUTPUT_LOGS, L"closing log");
  99. ::DeleteCriticalSection(&critsec);
  100. ::TlsFree(logfileMarginTlsIndex);
  101. if (IsOpen())
  102. {
  103. ::CloseHandle(fileHandle);
  104. fileHandle = INVALID_HANDLE_VALUE;
  105. ::CloseHandle(spewviewHandle);
  106. spewviewHandle = INVALID_HANDLE_VALUE;
  107. }
  108. }
  109. void
  110. Burnslib::Log::Cleanup()
  111. {
  112. delete logInstance;
  113. logInstance = 0;
  114. }
  115. // Returns the string name of the platform
  116. String
  117. OsName(OSVERSIONINFO& info)
  118. {
  119. switch (info.dwPlatformId)
  120. {
  121. case VER_PLATFORM_WIN32s:
  122. {
  123. return L"Win32s on Windows 3.1";
  124. }
  125. case VER_PLATFORM_WIN32_WINDOWS:
  126. {
  127. switch (info.dwMinorVersion)
  128. {
  129. case 0:
  130. {
  131. return L"Windows 95";
  132. }
  133. case 1:
  134. {
  135. return L"Windows 98";
  136. }
  137. default:
  138. {
  139. return L"Windows 9X";
  140. }
  141. }
  142. }
  143. case VER_PLATFORM_WIN32_NT:
  144. {
  145. return L"Windows NT";
  146. }
  147. // case VER_PLATFORM_WIN32_CE:
  148. // {
  149. // return L"Windows CE";
  150. // }
  151. default:
  152. {
  153. ASSERT(false);
  154. break;
  155. }
  156. }
  157. return L"Some Unknown Windows Version";
  158. }
  159. // locate the log file with the highest-numbered extension, then add 1 and
  160. // return the result.
  161. int
  162. DetermineNextLogNumber(const String& logDir, const String& logBaseName)
  163. {
  164. ASSERT(!logDir.empty());
  165. ASSERT(!logBaseName.empty());
  166. int largest = 0;
  167. String filespec = logDir + L"\\" + logBaseName + L".*.log";
  168. WIN32_FIND_DATA findData;
  169. HANDLE ff = ::FindFirstFile(filespec.c_str(), &findData);
  170. if (ff != INVALID_HANDLE_VALUE)
  171. {
  172. for (;;)
  173. {
  174. String current = findData.cFileName;
  175. // grab the text between the dots: "nnn" in foo.nnn.ext
  176. // first dot
  177. size_t pos = current.find(L".");
  178. if (pos == String::npos)
  179. {
  180. continue;
  181. }
  182. String extension = current.substr(pos + 1);
  183. // second dot
  184. pos = extension.find(L".");
  185. if (pos == String::npos)
  186. {
  187. continue;
  188. }
  189. extension = extension.substr(0, pos);
  190. int i = 0;
  191. extension.convert(i);
  192. largest = max(i, largest);
  193. if (!::FindNextFile(ff, &findData))
  194. {
  195. BOOL success = ::FindClose(ff);
  196. ASSERT(success);
  197. break;
  198. }
  199. }
  200. }
  201. // roll over after 255
  202. return (++largest & 0xFF);
  203. }
  204. // Writes the Unicode BOM to the first two bytes of the
  205. // file specified by handle
  206. HRESULT
  207. WriteUnicodeBOMToFile(HANDLE handle)
  208. {
  209. ASSERT(handle != INVALID_HANDLE_VALUE);
  210. HRESULT hr = S_OK;
  211. static const int BYTES_TO_WRITE = 2;
  212. BYTE buffer[BYTES_TO_WRITE];
  213. buffer[0] = 0xFF;
  214. buffer[1] = 0xFE;
  215. DWORD numberOfBytesWritten = 0;
  216. BOOL succeeded =
  217. ::WriteFile(
  218. handle,
  219. buffer,
  220. BYTES_TO_WRITE,
  221. &numberOfBytesWritten,
  222. 0);
  223. if (!succeeded)
  224. {
  225. hr = HRESULT_FROM_WIN32(::GetLastError());
  226. }
  227. ASSERT(SUCCEEDED(hr));
  228. ASSERT(numberOfBytesWritten == BYTES_TO_WRITE);
  229. return hr;
  230. }
  231. HANDLE
  232. OpenLogFile(const String& logName)
  233. {
  234. HANDLE result =
  235. ::CreateFile(
  236. logName.c_str(),
  237. // ISSUE-2002/03/06-sburns don't need read, do we?
  238. GENERIC_READ | GENERIC_WRITE,
  239. // Leave shared read so that we can look at the file while the
  240. // program is still running.
  241. FILE_SHARE_READ,
  242. 0,
  243. OPEN_ALWAYS,
  244. FILE_ATTRIBUTE_NORMAL,
  245. 0);
  246. // don't assert that the create worked: the current user may not have
  247. // rights to open the log file.
  248. if (result != INVALID_HANDLE_VALUE)
  249. {
  250. LARGE_INTEGER li;
  251. LARGE_INTEGER newpos;
  252. // REVIEWED-2002/03/06-sburns correct byte counts passed
  253. ::ZeroMemory(&li, sizeof li);
  254. ::ZeroMemory(&newpos, sizeof newpos);
  255. BOOL success = ::SetFilePointerEx(result, li, &newpos, FILE_END);
  256. ASSERT(success);
  257. // NTRAID#NTBUG9-494875-2001/11/14-JeffJon
  258. // If the newpos is at the beginning of the file, this means
  259. // its a new file and we should write the Unicode BOM so
  260. // that apps know the file is in Unicode.
  261. if (newpos.QuadPart == 0)
  262. {
  263. HRESULT unused = WriteUnicodeBOMToFile(result);
  264. ASSERT(SUCCEEDED(unused));
  265. }
  266. }
  267. return result;
  268. }
  269. // Determine the name of the log file. If a log file of that name already
  270. // exists, rename the existing file to a numbered backup. Create the new
  271. // log file, return a handle to it.
  272. HANDLE
  273. OpenNewLogFile(const String& logBaseName, String& logName)
  274. {
  275. wchar_t buf[MAX_PATH + 2];
  276. ::ZeroMemory(buf, (MAX_PATH + 2) * sizeof wchar_t);
  277. // +1 per MSDN docs.
  278. // NTRAID#NTBUG9-550315-2002/03/28-sburns
  279. UINT err = ::GetSystemWindowsDirectory(buf, MAX_PATH + 1);
  280. ASSERT(err != 0 && err <= MAX_PATH);
  281. String logDir = String(buf) + L"\\debug";
  282. logName = logDir + L"\\" + logBaseName + L".log";
  283. if (::GetFileAttributes(logName.c_str()) != 0xFFFFFFFF)
  284. {
  285. // file already exists. rename the existing file
  286. int logNumber = DetermineNextLogNumber(logDir, logBaseName);
  287. String newName =
  288. logDir
  289. + L"\\"
  290. + logBaseName
  291. + String::format(L".%1!03d!.log", logNumber);
  292. if (::GetFileAttributes(newName.c_str()) != 0xFFFFFFFF)
  293. {
  294. // could exist, as the log numbers roll over
  295. BOOL success = ::DeleteFile(newName.c_str());
  296. ASSERT(success);
  297. }
  298. // Don't assert that the move is successful. The user may not have
  299. // rights to rename the file. If this is the case, then we will attempt
  300. // to re-open the old log file and append to it.
  301. // REVIEWED-2002/03/06-sburns keeping the same ACLs as the source
  302. // is what we want
  303. ::MoveFile(logName.c_str(), newName.c_str());
  304. }
  305. HANDLE result = OpenLogFile(logName);
  306. return result;
  307. }
  308. // Opens the specified log file or creates one if it doesn't
  309. // already exist.
  310. HANDLE
  311. AppendLogFile(const String& logBaseName, String& logName)
  312. {
  313. wchar_t buf[MAX_PATH + 1];
  314. UINT err = ::GetSystemWindowsDirectory(buf, MAX_PATH);
  315. ASSERT(err != 0 && err <= MAX_PATH);
  316. String logDir = String(buf) + L"\\debug";
  317. logName = logDir + L"\\" + logBaseName + L".log";
  318. return OpenLogFile(logName);
  319. }
  320. // This wrapper function required to make prefast shut up.
  321. void
  322. ExceptionPropagatingInitializeCriticalSection(LPCRITICAL_SECTION critsec)
  323. {
  324. __try
  325. {
  326. // REVIEWED-2002/03/06-sburns propagating the exception that might
  327. // occur in low memory is what we want.
  328. ::InitializeCriticalSection(critsec);
  329. }
  330. // propagate the exception to our caller. This will cause Log::Log
  331. // to abort prematurely, and jump to the handler in Log::GetInstance
  332. __except (EXCEPTION_CONTINUE_SEARCH)
  333. {
  334. }
  335. }
  336. // Create a new log.
  337. //
  338. // logBaseName - base name of the log. If logging-to-file is active, then a
  339. // file in the %windir%\debug folder will be created/used. The name of the
  340. // file is of the form %windir%\debug\logBaseName.log. If a file by that name
  341. // already exists, then the existing file will be renamed
  342. // %windir%\debug\logBaseName.xxx.log, where xxx is an integer 1 greater than
  343. // the last so-numbered file in that directory.
  344. Burnslib::Log::Log(const String& logBaseName)
  345. :
  346. baseName(logBaseName),
  347. fileHandle(INVALID_HANDLE_VALUE),
  348. flags(0),
  349. spewviewHandle(INVALID_HANDLE_VALUE),
  350. spewviewPipeName(),
  351. traceLineNumber(0),
  352. logfileMarginTlsIndex(0)
  353. {
  354. ASSERT(!logBaseName.empty());
  355. ReadLogFlags();
  356. ExceptionPropagatingInitializeCriticalSection(&critsec);
  357. // create thread-local storage for per-thread debugging state. We keep
  358. // the output margin in the slot.
  359. logfileMarginTlsIndex = ::TlsAlloc();
  360. ASSERT(logfileMarginTlsIndex != 0xFFFFFFFF);
  361. // spewview setup is done on-demand in WriteLn, so that spewview will
  362. // be used as soon as the server sets up the connection.
  363. if (ShouldLogToFile())
  364. {
  365. String logName;
  366. fileHandle = OpenNewLogFile(logBaseName, logName);
  367. WriteLn(
  368. Burnslib::Log::OUTPUT_HEADER,
  369. String::format(L"opening log file %1", logName.c_str()));
  370. }
  371. else if (ShouldAppendLogToFile())
  372. {
  373. String logName;
  374. fileHandle = AppendLogFile(logBaseName, logName);
  375. WriteLn(
  376. Burnslib::Log::OUTPUT_HEADER,
  377. String::format(L"appending to log file %1", logName.c_str()));
  378. }
  379. WriteHeader();
  380. }
  381. void
  382. Burnslib::Log::WriteHeaderModule(HMODULE moduleHandle)
  383. {
  384. do
  385. {
  386. wchar_t filename[MAX_PATH + 1];
  387. // REVIEWED-2002/03/06-sburns correct byte count passed.
  388. ::ZeroMemory(filename, sizeof filename);
  389. // ISSUE-2002/03/06-sburns This call is problematic for large paths:
  390. // it may truncate the result
  391. if (::GetModuleFileNameW(moduleHandle, filename, MAX_PATH) == 0)
  392. {
  393. break;
  394. }
  395. WriteLn(Burnslib::Log::OUTPUT_HEADER, filename);
  396. // add the timestamp of the file
  397. WIN32_FILE_ATTRIBUTE_DATA attr;
  398. // REVIEWED-2002/03/06-sburns correct byte count passed
  399. ::ZeroMemory(&attr, sizeof attr);
  400. if (
  401. ::GetFileAttributesEx(
  402. filename,
  403. GetFileExInfoStandard,
  404. &attr) == 0)
  405. {
  406. break;
  407. }
  408. FILETIME localtime;
  409. ::FileTimeToLocalFileTime(&attr.ftLastWriteTime, &localtime);
  410. SYSTEMTIME systime;
  411. ::FileTimeToSystemTime(&localtime, &systime);
  412. WriteLn(
  413. Burnslib::Log::OUTPUT_HEADER,
  414. String::format(
  415. L"file timestamp %1!02u!/%2!02u!/%3!04u! "
  416. // NTRAID#NTBUG9-550381-2002/03/05-sburns
  417. L"%4!02u!:%5!02u!:%6!02u!.%7!03u!",
  418. systime.wMonth,
  419. systime.wDay,
  420. systime.wYear,
  421. systime.wHour,
  422. systime.wMinute,
  423. systime.wSecond,
  424. systime.wMilliseconds));
  425. }
  426. while (0);
  427. }
  428. void
  429. Burnslib::Log::WriteHeader()
  430. {
  431. // Log the name and timestamp of the file that created the process.
  432. WriteHeaderModule(0);
  433. // Log the name and timestamp of the file that corresponds to the
  434. // resource module handle, if there is one.
  435. if (hResourceModuleHandle)
  436. {
  437. WriteHeaderModule(hResourceModuleHandle);
  438. }
  439. SYSTEMTIME localtime;
  440. ::GetLocalTime(&localtime);
  441. WriteLn(
  442. Burnslib::Log::OUTPUT_HEADER,
  443. String::format(
  444. L"local time %1!02u!/%2!02u!/%3!04u! "
  445. // NTRAID#NTBUG9-550381-2002/03/05-sburns
  446. L"%4!02u!:%5!02u!:%6!02u!.%7!03u!",
  447. localtime.wMonth,
  448. localtime.wDay,
  449. localtime.wYear,
  450. localtime.wHour,
  451. localtime.wMinute,
  452. localtime.wSecond,
  453. localtime.wMilliseconds));
  454. OSVERSIONINFO info;
  455. info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  456. BOOL success = ::GetVersionEx(&info);
  457. ASSERT(success);
  458. // Get the whistler build lab version
  459. String labInfo;
  460. do
  461. {
  462. HKEY key = 0;
  463. LONG err =
  464. ::RegOpenKeyEx(
  465. HKEY_LOCAL_MACHINE,
  466. L"Software\\Microsoft\\Windows NT\\CurrentVersion",
  467. 0,
  468. KEY_READ,
  469. &key);
  470. if (err != ERROR_SUCCESS)
  471. {
  472. break;
  473. }
  474. wchar_t buf[MAX_PATH + 1];
  475. // REVIEWED-2002/03/12-sburns correct byte count passed.
  476. ::ZeroMemory(buf, sizeof buf);
  477. DWORD type = 0;
  478. // -2 to guarantee terminating null
  479. // NTRAID#NTBUG9-541959-2002/03/12-sburns
  480. DWORD bufSize = sizeof(buf) - 2;
  481. err =
  482. ::RegQueryValueEx(
  483. key,
  484. L"BuildLab",
  485. 0,
  486. &type,
  487. reinterpret_cast<BYTE*>(buf),
  488. // REVIEWED-2002/03/12-sburns correct byte count passed.
  489. &bufSize);
  490. if (err != ERROR_SUCCESS)
  491. {
  492. break;
  493. }
  494. labInfo = buf;
  495. }
  496. while (0);
  497. WriteLn(
  498. Burnslib::Log::OUTPUT_HEADER,
  499. String::format(
  500. L"running %1 %2!d!.%3!d! build %4!d! %5 (BuildLab:%6) "
  501. #if defined(_M_IX86)
  502. L"i386",
  503. #elif defined(_M_AMD64)
  504. L"amd64",
  505. #elif defined(_M_IA64)
  506. L"ia64",
  507. #else
  508. #error( "unknown target machine" );
  509. #endif
  510. OsName(info).c_str(),
  511. info.dwMajorVersion,
  512. info.dwMinorVersion,
  513. info.dwBuildNumber,
  514. info.szCSDVersion,
  515. labInfo.c_str()));
  516. WriteLn(
  517. Burnslib::Log::OUTPUT_HEADER,
  518. String::format(
  519. L"logging flags %1!08X!",
  520. flags));
  521. }
  522. HRESULT
  523. Log::AdjustLogMargin(int delta)
  524. {
  525. // extract the current value in this thread's slot
  526. HRESULT hr = S_OK;
  527. do
  528. {
  529. PVOID margin = ::TlsGetValue(logfileMarginTlsIndex);
  530. if (!margin)
  531. {
  532. DWORD err = ::GetLastError();
  533. if (err != NO_ERROR)
  534. {
  535. hr = HRESULT_FROM_WIN32(err);
  536. break;
  537. }
  538. }
  539. // indent by adding to the margin
  540. ULONG_PTR marginTemp = reinterpret_cast<ULONG_PTR>(margin);
  541. // marginTemp will always be >= 0, as it is unsigned.
  542. marginTemp += delta;
  543. margin = reinterpret_cast<PVOID>(marginTemp);
  544. // save the new margin in this thread's slot
  545. BOOL succeeded = ::TlsSetValue(logfileMarginTlsIndex, margin);
  546. if (!succeeded)
  547. {
  548. DWORD lastErr = ::GetLastError();
  549. hr = HRESULT_FROM_WIN32(lastErr);
  550. break;
  551. }
  552. }
  553. while (0);
  554. return hr;
  555. }
  556. // guarded by caller
  557. void
  558. Burnslib::Log::Indent()
  559. {
  560. HRESULT hr = AdjustLogMargin(TAB);
  561. ASSERT(SUCCEEDED(hr));
  562. }
  563. // guarded by caller
  564. void
  565. Burnslib::Log::Outdent()
  566. {
  567. HRESULT hr = AdjustLogMargin(-TAB);
  568. ASSERT(SUCCEEDED(hr));
  569. }
  570. size_t
  571. Burnslib::Log::GetLogMargin()
  572. {
  573. PVOID margin = ::TlsGetValue(logfileMarginTlsIndex);
  574. if (!margin)
  575. {
  576. DWORD err = ::GetLastError();
  577. if (err != NO_ERROR)
  578. {
  579. ASSERT(false);
  580. return 0;
  581. }
  582. }
  583. return
  584. static_cast<size_t>(
  585. reinterpret_cast<ULONG_PTR>(margin));
  586. }
  587. // I've commented out the spewview stuff because it's so rarely used.
  588. //
  589. // // Examine the registry key for the name of the pipe, append our debugging
  590. // // baseName to make it specific to this binary, and return the result. Return
  591. // // an empty string on error (most likely cause: the registry keys are not yet
  592. // // present)
  593. // //
  594. // // baseName - the logfile base name used to identify this binary.
  595. //
  596. // String
  597. // DetermineSpewviewPipeName(const String& baseName)
  598. // {
  599. // String result;
  600. //
  601. // do
  602. // {
  603. // HKEY key = 0;
  604. // LONG err =
  605. // ::RegOpenKeyEx(
  606. // HKEY_LOCAL_MACHINE,
  607. // (String(REG_ADMIN_RUNTIME_OPTIONS) + L"Spewview\\" + baseName).c_str(),
  608. // 0,
  609. // KEY_READ,
  610. // &key);
  611. // if (err != ERROR_SUCCESS)
  612. // {
  613. // break;
  614. // }
  615. //
  616. // wchar_t buf[MAX_PATH + 1];
  617. //
  618. // // REVIEWED-2002/03/12-sburns correct byte count passed.
  619. //
  620. // ::ZeroMemory(buf, sizeof buf);
  621. //
  622. // DWORD type = 0;
  623. //
  624. // // -2 to guarantee terminating null
  625. // // NTRAID#NTBUG9-541959-2002/03/12-sburns
  626. //
  627. // DWORD bufSize = sizeof(buf) - 2;
  628. //
  629. // err =
  630. // ::RegQueryValueEx(
  631. // key,
  632. // L"Server",
  633. // 0,
  634. // &type,
  635. // reinterpret_cast<BYTE*>(buf),
  636. //
  637. // // REVIEWED-2002/03/12-sburns correct byte count passed.
  638. //
  639. // &bufSize);
  640. // if (err != ERROR_SUCCESS)
  641. // {
  642. // break;
  643. // }
  644. //
  645. // result =
  646. // L"\\\\"
  647. // + String(buf)
  648. // + L"\\pipe\\spewview\\"
  649. // + baseName;
  650. // }
  651. // while (0);
  652. //
  653. // return result;
  654. // }
  655. //
  656. //
  657. //
  658. // // Attempts to connect to the spewview application that is running elsewhere.
  659. // // Sets the spewviewHandle parameter to a valid handle on success, or
  660. // // INVALID_HANDLE_VALUE on error.
  661. // //
  662. // // baseName - the logfile base name used to identify this binary.
  663. // //
  664. // // spewviewPipeName - if empty, set to the name of the pipe. If not empty,
  665. // // used as the name of the pipe that the handle will be opened on.
  666. // //
  667. // // spewviewHandle - handle to the named pipe that spewage will be written to.
  668. // // On success, this is set to a valid handle. On failure, this is set to
  669. // // INVALID_HANDLE_VALUE.
  670. //
  671. // void
  672. // AttemptConnectToSpewPipe(
  673. // const String& baseName,
  674. // String& spewviewPipeName,
  675. // HANDLE& spewviewHandle)
  676. // {
  677. // ASSERT(!baseName.empty());
  678. // ASSERT(spewviewHandle == INVALID_HANDLE_VALUE);
  679. //
  680. // spewviewHandle = INVALID_HANDLE_VALUE;
  681. //
  682. // // only attempt to determine the pipe name as long as we haven't been
  683. // // successful determining it so far.
  684. //
  685. // if (spewviewPipeName.empty())
  686. // {
  687. // spewviewPipeName = DetermineSpewviewPipeName(baseName);
  688. // }
  689. //
  690. // do
  691. // {
  692. // // wait a very short time for the pipe to become available
  693. //
  694. // BOOL err = ::WaitNamedPipe(spewviewPipeName.c_str(), 500);
  695. // if (!err)
  696. // {
  697. // // pipe not available
  698. //
  699. // // DWORD lastErr = ::GetLastError();
  700. //
  701. // break;
  702. // }
  703. //
  704. // spewviewHandle =
  705. // ::CreateFile(
  706. // spewviewPipeName.c_str(),
  707. // GENERIC_WRITE,
  708. // 0,
  709. // 0,
  710. // OPEN_EXISTING,
  711. // FILE_ATTRIBUTE_NORMAL,
  712. // 0);
  713. //
  714. // if (spewviewHandle == INVALID_HANDLE_VALUE)
  715. // {
  716. // // DWORD lastErr = ::GetLastError();
  717. //
  718. // break;
  719. // }
  720. // }
  721. // while (0);
  722. //
  723. // return;
  724. // }
  725. String
  726. Burnslib::Log::ComposeSpewLine(const String& text)
  727. {
  728. // This needs to be a wchar_t*, not a String, as this function will be
  729. // called by Log::~Log, which is called when the process is being cleaned
  730. // up. Part of the cleanup is to delete all static objects created since
  731. // the program started, in reverse order of construction. This includes
  732. // the InitializationGuard instances.
  733. //
  734. // The final invocation of the Initialization dtor will cause the single
  735. // Log instance to be deleted, which will log a message, which will call
  736. // this routine, so SPEW_FMT better still exist. If SPEW_FMT were an
  737. // object, it would be instanciated after the InitializationGuard objects,
  738. // and destroyed before them, and so would not exist at that point.
  739. //
  740. // (you may rightly suspect that I discovered this after I had declared
  741. // SPEW_FMT a String instance.)
  742. static const wchar_t* SPEW_FMT =
  743. L"%1 " // base name
  744. L"%2!03X!." // process id
  745. L"%3!03X! " // thread id
  746. L"%4!04X! " // spew line number
  747. L"%5" // time of day
  748. L"%6" // run time (time since process start)
  749. L"%7" // margin whitespace
  750. L"%8" // text
  751. L"\r\n";
  752. size_t margin = GetLogMargin();
  753. String white(margin, L' ');
  754. String tod;
  755. if (ShouldLogTimeOfDay())
  756. {
  757. SYSTEMTIME localtime;
  758. ::GetLocalTime(&localtime);
  759. tod =
  760. String::format(
  761. L"%1!02u!:%2!02u!:%3!02u!.%4!03u! ",
  762. localtime.wHour,
  763. localtime.wMinute,
  764. localtime.wSecond,
  765. localtime.wMilliseconds);
  766. }
  767. String rt;
  768. if (ShouldLogRunTime())
  769. {
  770. static DWORD MILLIS_PER_SECOND = 1000;
  771. static DWORD MILLIS_PER_MINUTE = 60000;
  772. static DWORD MILLIS_PER_HOUR = 3600000;
  773. static DWORD MILLIS_PER_DAY = 86400000;
  774. DWORD tics = ::GetTickCount();
  775. unsigned days = tics / MILLIS_PER_DAY;
  776. tics -= days * MILLIS_PER_DAY;
  777. unsigned hours = tics / MILLIS_PER_HOUR;
  778. tics -= hours * MILLIS_PER_HOUR;
  779. unsigned minutes = tics / MILLIS_PER_MINUTE;
  780. tics -= minutes * MILLIS_PER_MINUTE;
  781. unsigned seconds = tics / MILLIS_PER_SECOND;
  782. tics -= seconds * MILLIS_PER_SECOND;
  783. rt =
  784. String::format(
  785. L"%1!02u!:%2!02u!:%3!02u!:%4!02u!.%5!04u! ",
  786. days,
  787. hours,
  788. minutes,
  789. seconds,
  790. tics);
  791. }
  792. String t =
  793. String::format(
  794. SPEW_FMT,
  795. RUNTIME_NAME,
  796. ::GetCurrentProcessId(),
  797. ::GetCurrentThreadId(),
  798. traceLineNumber,
  799. tod.c_str(),
  800. rt.c_str(),
  801. white.c_str(),
  802. text.c_str() );
  803. return t;
  804. }
  805. // Spews output to the log according to the current logging type and
  806. // output options in effect.
  807. //
  808. // type - log output type of this output spewage.
  809. //
  810. // text - the spewage. This is prefaced with the log name, thread id, spewage
  811. // line number, and current indentation.
  812. void
  813. Burnslib::Log::UnguardedWriteLn(DWORD type, const String& text)
  814. {
  815. // guarded by caller
  816. // CODEWORK: could circumvent this with a registry change notification
  817. // (re-read only if changed)
  818. // ReadLogFlags();
  819. if (
  820. !ShouldLogToFile()
  821. && !ShouldLogToDebugger()
  822. && !ShouldLogToSpewView() )
  823. {
  824. // nothing to do
  825. return;
  826. }
  827. if (type & DebugType())
  828. {
  829. String t = ComposeSpewLine(text);
  830. if (ShouldLogToDebugger())
  831. {
  832. ::OutputDebugString(t.c_str());
  833. }
  834. if (ShouldLogToFile())
  835. {
  836. if (IsOpen())
  837. {
  838. // write disk output as unicode text.
  839. DWORD bytesToWrite =
  840. static_cast<DWORD>(t.length() * sizeof(wchar_t));
  841. DWORD bytesWritten = 0;
  842. BOOL success =
  843. // REVIEWED-2002/03/06-sburns correct byte count passed
  844. ::WriteFile(
  845. fileHandle,
  846. reinterpret_cast<void*>(const_cast<wchar_t*>(t.data())),
  847. bytesToWrite,
  848. &bytesWritten,
  849. 0);
  850. // NTRAID#NTBUG9-465946-2001/09/10-sburns
  851. // ASSERT(success);
  852. #ifdef DBG
  853. if (success)
  854. {
  855. ASSERT(bytesToWrite == bytesWritten);
  856. }
  857. #endif
  858. }
  859. }
  860. // I've commented out the spewview stuff because it's so rarely used.
  861. // if (ShouldLogToSpewView())
  862. // {
  863. // if (spewviewHandle == INVALID_HANDLE_VALUE)
  864. // {
  865. // AttemptConnectToSpewPipe(
  866. // baseName,
  867. // spewviewPipeName,
  868. // spewviewHandle);
  869. // }
  870. //
  871. // if (spewviewHandle != INVALID_HANDLE_VALUE)
  872. // {
  873. // // the connect attempt worked, and we have a valid handle.
  874. //
  875. // DWORD bytesToWrite =
  876. // static_cast<DWORD>(t.length() * sizeof(wchar_t));
  877. // DWORD bytesWritten = 0;
  878. //
  879. // BOOL result =
  880. // ::WriteFile(
  881. // spewviewHandle,
  882. // t.c_str(),
  883. // bytesToWrite,
  884. // &bytesWritten,
  885. // 0);
  886. // if (!result)
  887. // {
  888. // // write failed, so disconnect. On the next call to this
  889. // // function, we will attempt to reconnect.
  890. //
  891. // ::CloseHandle(spewviewHandle);
  892. // spewviewHandle = INVALID_HANDLE_VALUE;
  893. // }
  894. // }
  895. // }
  896. ++traceLineNumber;
  897. }
  898. }
  899. void
  900. Burnslib::Log::WriteLn(
  901. WORD type,
  902. const String& text)
  903. {
  904. ::EnterCriticalSection(&critsec);
  905. UnguardedWriteLn(type, text);
  906. ::LeaveCriticalSection(&critsec);
  907. }
  908. Burnslib::Log::ScopeTracer::ScopeTracer(
  909. DWORD type_,
  910. const String& message_)
  911. :
  912. message(message_),
  913. type(type_)
  914. {
  915. // build this string once, instead of using the string literal in the
  916. // below expression (which would implicitly build the string on each
  917. // evaluation of that expression) as a slight performance gain.
  918. static const String ENTER(L"Enter ");
  919. Burnslib::Log* li = Burnslib::Log::GetInstance();
  920. ASSERT(li);
  921. if (!li)
  922. {
  923. return;
  924. }
  925. if (type & li->DebugType())
  926. {
  927. ::EnterCriticalSection(&li->critsec);
  928. li->UnguardedWriteLn(type, ENTER + message);
  929. li->Indent();
  930. ::LeaveCriticalSection(&li->critsec);
  931. }
  932. }
  933. Burnslib::Log::ScopeTracer::~ScopeTracer()
  934. {
  935. // build this string once, instead of using the string literal in the
  936. // below expression (which would implicitly build the string on each
  937. // evaluation of that expression) as a slight performance gain.
  938. static const String EXIT(L"Exit ");
  939. Burnslib::Log* li = Burnslib::Log::GetInstance();
  940. ASSERT(li);
  941. if (!li)
  942. {
  943. return;
  944. }
  945. DWORD dt = li->DebugType();
  946. if ((type & dt))
  947. {
  948. ::EnterCriticalSection(&li->critsec);
  949. li->Outdent();
  950. if (OUTPUT_SCOPE_EXIT & dt)
  951. {
  952. li->UnguardedWriteLn(type, EXIT + message);
  953. }
  954. ::LeaveCriticalSection(&li->critsec);
  955. }
  956. }
  957. #endif // LOGGING_BUILD