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.

1235 lines
36 KiB

  1. /*++
  2. Copyright (c) 1998 Microsoft Corporation
  3. Module Name:
  4. vs_trace.cxx
  5. Abstract:
  6. This module defines the global debug\trace facilities used by the
  7. Long Term Storage service.
  8. Previous name: bsdebug.cxx
  9. Author:
  10. Revision History:
  11. Name Date Comments
  12. ssteiner 06/03/98 Made numerious changes and removed iostream
  13. dependencies, added a few new registry entries and
  14. added serialization.
  15. aoltean 06/06/99 Taken from atl30\atlbase.h in order to avoid linking ATL with BSCommon.lib
  16. ssteiner 05/15/00 Fixed bug #116688. Added file locking to prevent multiple processes from
  17. interferring with writing to the trace file. Added code to place a UNICODE
  18. BOM at the beginning of the trace file.
  19. --*/
  20. //
  21. // ***** Includes *****
  22. //
  23. #pragma warning(disable:4290)
  24. #pragma warning(disable:4127)
  25. #include <wtypes.h>
  26. #pragma warning( disable: 4201 ) // C4201: nonstandard extension used : nameless struct/union
  27. #include <winioctl.h>
  28. #pragma warning( default: 4201 ) // C4201: nonstandard extension used : nameless struct/union
  29. #include <winbase.h>
  30. #include <wchar.h>
  31. #include <string.h>
  32. #include <iostream.h>
  33. #include <fstream.h>
  34. #include <stdio.h>
  35. #include <process.h>
  36. #include <stdlib.h>
  37. #include <time.h>
  38. #include <errno.h>
  39. #include <vssmsg.h>
  40. // Enabling asserts in ATL and VSS
  41. #include "vs_assert.hxx"
  42. #include <oleauto.h>
  43. #include <stddef.h>
  44. #pragma warning( disable: 4127 ) // warning C4127: conditional expression is constant
  45. #include <atlconv.h>
  46. #include <atlbase.h>
  47. #include <ntverp.h>
  48. #include "vs_inc.hxx"
  49. #include "vs_idl.hxx"
  50. ////////////////////////////////////////////////////////////////////////
  51. // Standard foo for file name aliasing. This code block must be after
  52. // all includes of VSS header files.
  53. //
  54. #ifdef VSS_FILE_ALIAS
  55. #undef VSS_FILE_ALIAS
  56. #endif
  57. #define VSS_FILE_ALIAS "TRCTRCC"
  58. //
  59. ////////////////////////////////////////////////////////////////////////
  60. //
  61. // The following global, g_cDbgTrace must be declared BEFORE any of our
  62. // objects including _Module, since some of our static objects have destructors
  63. // that call trace methods. The following pragma ensures that this
  64. // module's static objects are initialized before any of our other
  65. // static objects, assuming they don't use this same pragma.
  66. //
  67. #pragma warning(disable:4073) // ignore init_seg warning
  68. #pragma init_seg(lib)
  69. CBsDbgTrace g_cDbgTrace;
  70. static VOID MakeFileUnicode(
  71. IN HANDLE hFile
  72. );
  73. /////////////////////////////////////////////////////////////////////////////
  74. // constants
  75. //
  76. const WCHAR VSS_TRACINGKEYPATH[] =
  77. L"SYSTEM\\CurrentControlSet\\Services\\VSS\\Debug\\Tracing";
  78. const WCHAR SETUP_KEY[] =
  79. L"SYSTEM\\Setup";
  80. const WCHAR SETUP_INPROGRESS_REG[] =
  81. L"SystemSetupInProgress";
  82. const DWORD SETUP_INPROGRESS_VALUE = 1;
  83. /////////////////////////////////////////////////////////////////////////////
  84. // Globals
  85. //
  86. //
  87. // NOTE: g_cDbgTrace, the global instance of this class is declared in
  88. // ltss\modules\ltssvc\src\ltssvc.cxx since we have to make sure
  89. // this object is the last one being destructed, otherwise possible
  90. // calls to this object will fail.
  91. //
  92. //
  93. // Define a TLS var, stores the CLTermStg & intention list index.
  94. // The index is a counter that is incremented and set for each thread
  95. // coming into the service, in the CLTermStg::FinalConstruct method.
  96. // The counter is also incremented and set for each intention list
  97. // thread that is created by the service.
  98. //
  99. // WARNING
  100. //
  101. //
  102. //_declspec( thread ) DWORD CBsDbgTrace::m_dwContextNum = 0;
  103. //
  104. // Queries a registry value name and if found sets dwValue to the value.
  105. // If the value name is not found, dwValue remains unchanged.
  106. //
  107. static DWORD
  108. QuerySetValue (
  109. IN CRegKey &cRegKey,
  110. IN OUT DWORD &dwValue,
  111. IN LPCWSTR pwszValueName
  112. )
  113. {
  114. DWORD dwReadValue = 0;
  115. DWORD dwResult = cRegKey.QueryValue( dwReadValue, pwszValueName );
  116. if ( dwResult == ERROR_SUCCESS )
  117. dwValue = dwReadValue;
  118. return dwResult;
  119. }
  120. //
  121. // Queries a registry value name and if found sets bValue to the value.
  122. // If the value name is not found, bValue remains unchanged.
  123. //
  124. static DWORD
  125. QuerySetValue (
  126. IN CRegKey &cRegKey,
  127. IN OUT BOOL &bValue,
  128. IN LPCWSTR pwszValueName
  129. )
  130. {
  131. DWORD dwReadValue = 0;
  132. DWORD dwResult = cRegKey.QueryValue( dwReadValue, pwszValueName );
  133. if ( dwResult == ERROR_SUCCESS )
  134. bValue = (BOOL)(dwReadValue != 0);
  135. return dwResult;
  136. }
  137. //
  138. // Queries a registry value name and if found sets wsValue to the value.
  139. // If the value name is not found, wsValue remains unchanged.
  140. //
  141. static DWORD
  142. QuerySetValue (
  143. IN CRegKey &cRegKey,
  144. OUT LPWSTR &wsValue, // If allocated, must be freed before calling with ::VssFreeString
  145. IN LPCWSTR pwszValueName
  146. )
  147. {
  148. WCHAR pszValueBuffer[_MAX_PATH];
  149. DWORD dwCount = _MAX_PATH;
  150. DWORD dwResult = cRegKey.QueryValue( pszValueBuffer, pwszValueName, &dwCount );
  151. BS_ASSERT(wsValue == NULL);
  152. if ( dwResult == ERROR_SUCCESS )
  153. ::VssDuplicateStr(wsValue, pszValueBuffer);
  154. return dwResult;
  155. }
  156. //
  157. // ***** class definitions *****
  158. //
  159. CBsDbgTrace::CBsDbgTrace()
  160. /*++
  161. Routine Description:
  162. Constructor method. Default values are given to operational
  163. parameters and overwritten using values from the registry if
  164. set. Also prints out the trace file banner.
  165. Arguments:
  166. NONE
  167. Return Value:
  168. NONE
  169. --*/
  170. {
  171. m_bInitialized = false;
  172. m_bTracingEnabled = false;
  173. m_pcs = NULL;
  174. Initialize( TRUE );
  175. }
  176. CBsDbgTrace::~CBsDbgTrace()
  177. /*++
  178. Routine Description:
  179. Destructor method. Prints out the last record in the NTMS
  180. Arguments:
  181. LONG Indent - NOT USED YET [todo] this is the indentation indicator
  182. LONG Level - this is the debug trace level
  183. Return Value:
  184. BOOL
  185. --*/
  186. {
  187. if ( !m_bInitialized )
  188. return;
  189. if ( m_bTracingEnabled ) {
  190. //
  191. // Write out a finished tracing message
  192. //
  193. m_pcs->Enter();
  194. BsDebugTraceAlways( 0, DEBUG_TRACE_ALL, ( L"****************************************************************" ) );
  195. BsDebugTraceAlways( 0, DEBUG_TRACE_ALL, ( L"** TRACING FINISHED - ProcessId: 0x%x, ContextId: 0x%x",
  196. m_dwCurrentProcessId, m_dwContextId ) );
  197. WCHAR pwszCurrentTime[128];
  198. time_t ltime;
  199. struct tm *pToday;
  200. time( &ltime );
  201. pToday = localtime( &ltime );
  202. wcsftime( pwszCurrentTime, sizeof pwszCurrentTime, L"%c", pToday );
  203. BsDebugTraceAlways( 0, DEBUG_TRACE_ALL, ( L"** Current time: %s", pwszCurrentTime ) );
  204. BsDebugTraceAlways( 0, DEBUG_TRACE_ALL, ( L"** Elapsed time: %d seconds", ltime- m_lTimeStarted ) );
  205. BsDebugTraceAlways( 0, DEBUG_TRACE_ALL, ( L"****************************************************************" ) );
  206. m_pcs->Leave();
  207. //
  208. // Make sure the file is flushed before leaving
  209. //
  210. if ( m_bTraceToFile ) {
  211. m_hTraceFile = ::CreateFile( m_pwszTraceFileName?
  212. m_pwszTraceFileName :
  213. BS_DBG_TRACE_FILE_NAME_DFLT,
  214. GENERIC_WRITE,
  215. FILE_SHARE_READ,
  216. NULL,
  217. OPEN_ALWAYS,
  218. FILE_ATTRIBUTE_NORMAL,
  219. NULL );
  220. if ( m_hTraceFile != INVALID_HANDLE_VALUE ) {
  221. ::FlushFileBuffers( m_hTraceFile );
  222. ::CloseHandle( m_hTraceFile );
  223. }
  224. }
  225. }
  226. ::VssFreeString(m_pwszTraceFileName);
  227. //
  228. // Delete the critical section
  229. //
  230. delete m_pcs;
  231. m_pcs = NULL;
  232. m_bInitialized = FALSE;
  233. }
  234. //
  235. // In certain cases the global trace object doesn't seem to get it's constructor called.
  236. // To fix this problem, this function was added to perform the initialization of the
  237. // object. This function is called both in the constructor and the set context call
  238. // which all DLLs that use the trace class call.
  239. //
  240. VOID
  241. CBsDbgTrace::Initialize(
  242. IN BOOL bInConstructor
  243. )
  244. {
  245. if ( !m_bInitialized )
  246. {
  247. try
  248. {
  249. //
  250. // Get the critical section created first
  251. //
  252. m_pcs = new CBsCritSec;
  253. if ( m_pcs == NULL )
  254. throw E_OUTOFMEMORY;
  255. m_bInitialized = TRUE;
  256. m_bTracingEnabled = FALSE;
  257. m_bTraceToFile = BS_DBG_TRACE_TO_FILE_DFLT;
  258. m_bTraceToDebugger = BS_DBG_TRACE_TO_DEBUGGER_DFLT;
  259. m_bTraceEnterExit = BS_DBG_TRACE_ENTER_EXIT_DFLT;
  260. m_dwTraceLevel = BS_DBG_TRACE_LEVEL_DFLT;
  261. m_bTraceFileLineInfo = BS_DBG_TRACE_FILE_LINE_INFO_DFLT;
  262. m_bTraceTimestamp = BS_DBG_TRACE_TIMESTAMP_DFLT;
  263. m_pwszTraceFileName = NULL;
  264. m_bForceFlush = BS_DBG_TRACE_FORCE_FLUSH_DFLT;
  265. m_dwTraceIndent = 0;
  266. m_bInTrace = FALSE;
  267. m_hTraceFile = INVALID_HANDLE_VALUE;
  268. m_dwLineNum = 0;
  269. m_dwCurrentProcessId = GetCurrentProcessId();
  270. m_bIsDuringSetup = FALSE;
  271. LARGE_INTEGER liTimer;
  272. if ( ::QueryPerformanceCounter( &liTimer ) )
  273. {
  274. // Got high performance counter, use the low part
  275. m_dwContextId = liTimer.LowPart;
  276. }
  277. else
  278. {
  279. m_dwContextId = ::GetTickCount();
  280. }
  281. ReadRegistry();
  282. BsDebugTraceAlways( 0, DEBUG_TRACE_ALL, ( L"****************************************************************" ) );
  283. BsDebugTraceAlways( 0, DEBUG_TRACE_ALL, ( L"** TRACING STARTED - ProcessId: 0x%x, ContextId: 0x%x",
  284. m_dwCurrentProcessId, m_dwContextId ) );
  285. if ( !bInConstructor )
  286. BsDebugTraceAlways( 0, DEBUG_TRACE_ALL, ( L"** N.B. NOT INITIALIZED BY THE CONSTRUCTOR" ) );
  287. WCHAR pwszCurrentTime[128];
  288. struct tm *pToday;
  289. time( &m_lTimeStarted );
  290. pToday = localtime( &m_lTimeStarted );
  291. wcsftime( pwszCurrentTime, sizeof pwszCurrentTime, L"%c", pToday );
  292. BsDebugTraceAlways( 0, DEBUG_TRACE_ALL, ( L"** Current time: %s", pwszCurrentTime ) );
  293. BsDebugTraceAlways( 0, DEBUG_TRACE_ALL, ( L"** Product version: %d.%d.%d.%d", VER_PRODUCTVERSION ) );
  294. BsDebugTraceAlways( 0, DEBUG_TRACE_ALL, ( L"****************************************************************" ) );
  295. }
  296. catch(...)
  297. {
  298. delete m_pcs;
  299. m_pcs = NULL;
  300. m_bInitialized = false;
  301. m_bTracingEnabled = false;
  302. }
  303. }
  304. }
  305. BOOL
  306. CBsDbgTrace::IsDuringSetup()
  307. {
  308. return m_bIsDuringSetup;
  309. }
  310. HRESULT
  311. CBsDbgTrace::ReadRegistry()
  312. /*++
  313. Routine Description:
  314. Tries to read debug specific values from the registry and adds
  315. the values if they don't exist.
  316. Arguments:
  317. NONE
  318. Return Value:
  319. HRESULT
  320. --*/
  321. {
  322. DWORD dwRes;
  323. CRegKey cRegKeySetup;
  324. CRegKey cRegKeyTracing;
  325. m_bTracingEnabled = FALSE;
  326. m_bIsDuringSetup = FALSE;
  327. //
  328. // Open the Setup key
  329. //
  330. dwRes = cRegKeySetup.Open( HKEY_LOCAL_MACHINE, SETUP_KEY, KEY_READ );
  331. if ( dwRes == ERROR_SUCCESS ) {
  332. DWORD dwSetupInProgress = 0;
  333. QuerySetValue( cRegKeySetup, dwSetupInProgress, SETUP_INPROGRESS_REG );
  334. m_bIsDuringSetup = ( dwSetupInProgress == SETUP_INPROGRESS_VALUE );
  335. }
  336. //
  337. // Open the VSS tracing key
  338. //
  339. dwRes = cRegKeyTracing.Open( HKEY_LOCAL_MACHINE, VSS_TRACINGKEYPATH, KEY_READ );
  340. if ( dwRes == ERROR_SUCCESS ) {
  341. // The name of the optional trace file
  342. QuerySetValue( cRegKeyTracing, m_pwszTraceFileName, BS_DBG_TRACE_FILE_NAME_REG );
  343. // The trace level determines what type of traciung will occur. Zero
  344. // indicates that no tracing will occur, and is the default.
  345. QuerySetValue( cRegKeyTracing, m_dwTraceLevel, BS_DBG_TRACE_LEVEL_REG );
  346. // The TraceEnterExit flag determines whether or not function entry & exit
  347. // information is output to the trace file & the debug output stream.
  348. QuerySetValue( cRegKeyTracing, m_bTraceEnterExit, BS_DBG_TRACE_ENTER_EXIT_REG );
  349. // The TraceToFile flag determines whether or not trace information is output to
  350. // the trace file. If this value is FALSE, no output is sent to the trace file.
  351. QuerySetValue( cRegKeyTracing, m_bTraceToFile, BS_DBG_TRACE_TO_FILE_REG );
  352. // The TraceToDebugger flag determines whether or not trace information is output
  353. // to the debugger. If this value is FALSE, no output is sent to the debugger.
  354. QuerySetValue( cRegKeyTracing, m_bTraceToDebugger, BS_DBG_TRACE_TO_DEBUGGER_REG );
  355. // The Timestamp flag determines whether or not timestamp
  356. // information is output to the trace file & the debug output stream.
  357. QuerySetValue( cRegKeyTracing, m_bTraceTimestamp, BS_DBG_TRACE_TIMESTAMP_REG );
  358. // The FileLineInfo flag determines whether or not the module file name
  359. // and line number information is output to the trace file & the debug
  360. // output stream.
  361. QuerySetValue( cRegKeyTracing, m_bTraceFileLineInfo, BS_DBG_TRACE_FILE_LINE_INFO_REG );
  362. // The TraceForceFlush flag specifies whether or not after each trace message is
  363. // written to the trace file a forced flush occurs. If enabled, no trace records
  364. // are ever lost, however, performance is greatly reduced.
  365. QuerySetValue( cRegKeyTracing, m_bForceFlush, BS_DBG_TRACE_FORCE_FLUSH_REG );
  366. // Determine if tracing should be enabled
  367. if ( m_bTraceToDebugger || m_bTraceToFile )
  368. m_bTracingEnabled = TRUE;
  369. }
  370. return S_OK;
  371. }
  372. HRESULT
  373. CBsDbgTrace::PrePrint(
  374. IN LPCWSTR pwszSourceFileName,
  375. IN DWORD dwLineNum,
  376. IN DWORD dwIndent,
  377. IN DWORD dwLevel,
  378. IN LPCWSTR pwszFunctionName,
  379. IN BOOL bTraceEnter
  380. )
  381. /*++
  382. Routine Description:
  383. Acquires the critical section so that other threads are
  384. now serialized. Opens the trace file if necessary.
  385. N.B. Any A/V's in this code can cause a hang since the SEH translator function
  386. calls these trace functions.
  387. Arguments:
  388. pszSourceFileName - Source file name of the module whose
  389. code called this method.
  390. dwLineNum - Line number in the source
  391. dwIndent - Number to increase or decrease the indendation level
  392. dwLevel - Trace level that specifies for which component
  393. the code resides in.
  394. pwszFunctionName - For entry/exit tracing. Specifies the
  395. function name constains a call the a trace macro.
  396. bTraceEnter - True if this is a entry trace.
  397. Return Value:
  398. HRESULT
  399. --*/
  400. {
  401. m_pcs->Enter();
  402. //
  403. // Assume the trace macros have already filtered out traces based
  404. // on m_bTracingEnabled and on the active trace level.
  405. //
  406. if ( m_bTracingEnabled && (dwLevel & m_dwTraceLevel) != 0) {
  407. if ( pwszSourceFileName == NULL )
  408. m_pwszSourceFileName = L"(Unknown source file)";
  409. else
  410. {
  411. //
  412. // Keep only two levels deep of directory components
  413. //
  414. LPCWSTR pwszTemp = pwszSourceFileName + ::wcslen( pwszSourceFileName ) - 1;
  415. for ( int i = 0; pwszTemp > pwszSourceFileName && i < 3; ++i )
  416. {
  417. do
  418. {
  419. --pwszTemp;
  420. }
  421. while( *pwszTemp != L'\\' && pwszTemp > pwszSourceFileName ) ;
  422. }
  423. if ( pwszTemp > pwszSourceFileName )
  424. m_pwszSourceFileName = pwszTemp + 1;
  425. else
  426. m_pwszSourceFileName = pwszSourceFileName;
  427. }
  428. m_pwszFunctionName = pwszFunctionName;
  429. m_dwLineNum = dwLineNum;
  430. m_bTraceEnter = bTraceEnter;
  431. BS_ASSERT( m_hTraceFile == INVALID_HANDLE_VALUE );
  432. if ( m_bTraceToFile ) {
  433. m_hTraceFile = ::CreateFile( m_pwszTraceFileName?
  434. m_pwszTraceFileName :
  435. BS_DBG_TRACE_FILE_NAME_DFLT,
  436. GENERIC_WRITE,
  437. FILE_SHARE_READ | FILE_SHARE_WRITE,
  438. NULL,
  439. OPEN_ALWAYS,
  440. FILE_ATTRIBUTE_NORMAL,
  441. NULL );
  442. if ( m_hTraceFile == INVALID_HANDLE_VALUE ) {
  443. //
  444. // Error opening the file, print a message to the debugger if debugger
  445. // tracing is enabled
  446. //
  447. Print( L"CBsDbgTrace::PrePrint: TRACING ERROR: Unable to open trace file, dwRet: %u", ::GetLastError() );
  448. } else {
  449. //
  450. // Now lock the process from other processes and threads that are concurrently
  451. // accessing the file. Just lock the first byte of the file.
  452. //
  453. OVERLAPPED ovStart = { NULL, NULL, { 0, 0 }, 0 };
  454. if ( !::LockFileEx( m_hTraceFile,
  455. LOCKFILE_EXCLUSIVE_LOCK,
  456. 0,
  457. 1,
  458. 0,
  459. &ovStart ) ) {
  460. //
  461. // Tracing to file will be skipped for this record. This should
  462. // never happen in practice.
  463. //
  464. ::CloseHandle( m_hTraceFile );
  465. m_hTraceFile = INVALID_HANDLE_VALUE;
  466. //
  467. // Try printing a trace message that will get to the debugger if debugger
  468. // tracing is enabled
  469. //
  470. Print( L"CBsDbgTrace::PrePrint: TRACING ERROR: Unable to lock trace file, skipping trace record, dwRet: %u", ::GetLastError() );
  471. } else {
  472. //
  473. // If the file is new (empty) put the UNICODE BOM at the beginning of the file
  474. //
  475. LARGE_INTEGER liPointer;
  476. if ( ::GetFileSizeEx( m_hTraceFile, &liPointer ) ) {
  477. if ( liPointer.QuadPart == 0 )
  478. ::MakeFileUnicode( m_hTraceFile );
  479. }
  480. //
  481. // Now move the file pointer to the end of the file
  482. //
  483. liPointer.QuadPart = 0;
  484. if ( !::SetFilePointerEx( m_hTraceFile,
  485. liPointer,
  486. NULL,
  487. FILE_END ) ) {
  488. //
  489. // Don't write to the file since it might overwrite valid records.
  490. // Tracing to file will be skipped for this record. This should
  491. // never happen in practice.
  492. //
  493. ::CloseHandle( m_hTraceFile );
  494. m_hTraceFile = INVALID_HANDLE_VALUE;
  495. //
  496. // Try printing a trace message that will get to the debugger if debugger
  497. // tracing is enabled
  498. //
  499. Print( L"CBsDbgTrace::PrePrint: TRACING ERROR: Unable to set end of file, skipping trace record, dwRet: %u", ::GetLastError() );
  500. }
  501. }
  502. }
  503. }
  504. m_bInTrace = TRUE;
  505. }
  506. return S_OK;
  507. UNREFERENCED_PARAMETER( dwIndent );
  508. }
  509. HRESULT
  510. CBsDbgTrace::PostPrint(
  511. IN DWORD dwIndent
  512. )
  513. /*++
  514. Routine Description:
  515. Releases the critical section so that other threads
  516. can now call perform tracing. Closes the trace file
  517. and resets variables.
  518. Arguments:
  519. dwIndent - Number to increase or decrease the indendation level
  520. Return Value:
  521. HRESULT
  522. --*/
  523. {
  524. if ( m_hTraceFile != INVALID_HANDLE_VALUE ) {
  525. OVERLAPPED ovStart = { NULL, NULL, { 0, 0 }, 0 };
  526. if ( !::UnlockFileEx( m_hTraceFile,
  527. 0,
  528. 1,
  529. 0,
  530. &ovStart ) ) {
  531. Print( L"CBsDbgTrace::PrePrint: TRACING ERROR: Unable to unlock trace file, dwRet: %u", ::GetLastError() );
  532. }
  533. if ( m_bForceFlush )
  534. ::FlushFileBuffers( m_hTraceFile );
  535. ::CloseHandle( m_hTraceFile );
  536. m_hTraceFile = INVALID_HANDLE_VALUE;
  537. }
  538. m_pwszSourceFileName = NULL;
  539. m_pwszFunctionName = NULL;
  540. m_dwLineNum = 0;
  541. m_bInTrace = FALSE;
  542. m_pcs->Leave();
  543. return S_OK;
  544. UNREFERENCED_PARAMETER( dwIndent );
  545. }
  546. HRESULT _cdecl
  547. CBsDbgTrace::Print(
  548. IN LPCWSTR pwszFormatStr,
  549. IN ...
  550. )
  551. /*++
  552. Routine Description:
  553. Formats the trace message out to the trace file and/or debugger.
  554. Arguments:
  555. pwszFormatStr - printf style format string
  556. ... - Arguments for the message
  557. Return Value:
  558. HRESULT
  559. --*/
  560. {
  561. va_list pArg;
  562. if ( m_bInTrace ) {
  563. if ( m_bTraceTimestamp )
  564. swprintf( m_pwszOutBuf,
  565. L"[%010u,",
  566. GetTickCount() );
  567. else
  568. swprintf( m_pwszOutBuf,
  569. L"[-," );
  570. swprintf( m_pwszOutBuf + wcslen( m_pwszOutBuf ),
  571. L"0x%06x:0x%04x:0x%08x] ",
  572. m_dwCurrentProcessId,
  573. GetCurrentThreadId(),
  574. m_dwContextId );
  575. if ( m_bTraceFileLineInfo )
  576. swprintf( m_pwszOutBuf + wcslen( m_pwszOutBuf ),
  577. L"%s(%04u): ",
  578. m_pwszSourceFileName,
  579. m_dwLineNum );
  580. OutputString();
  581. //
  582. // read the variable length parameter list into a formatted string
  583. //
  584. va_start( pArg, pwszFormatStr );
  585. _vsnwprintf( m_pwszOutBuf, BS_DBG_OUT_BUF_SIZE-1, pwszFormatStr, pArg );
  586. va_end( pArg );
  587. OutputString();
  588. //
  589. // Finish up with a carriage return.
  590. //
  591. wcscpy( m_pwszOutBuf, L"\r\n" );
  592. OutputString();
  593. }
  594. return S_OK;
  595. }
  596. HRESULT _cdecl
  597. CBsDbgTrace::PrintEnterExit(
  598. IN LPCWSTR pwszFormatStr,
  599. IN ...
  600. )
  601. /*++
  602. Routine Description:
  603. Formats the entry/exit trace message out to the trace file and/or debugger.
  604. Arguments:
  605. pwszFormatStr - printf style format string
  606. ... - Arguments for the message
  607. Return Value:
  608. HRESULT
  609. --*/
  610. {
  611. va_list pArg;
  612. if ( m_bInTrace ) {
  613. if ( m_bTraceTimestamp )
  614. swprintf( m_pwszOutBuf,
  615. L"[%010u,",
  616. GetTickCount() );
  617. else
  618. swprintf( m_pwszOutBuf,
  619. L"[-," );
  620. swprintf( m_pwszOutBuf + wcslen( m_pwszOutBuf ),
  621. L"0x%06x:0x%04x:0x%08x] %s {%s}: ",
  622. m_dwCurrentProcessId,
  623. GetCurrentThreadId(),
  624. m_dwContextId,
  625. m_bTraceEnter ? L"ENTER" : L"EXIT ",
  626. m_pwszFunctionName );
  627. OutputString();
  628. //
  629. // read the variable length parameter list into a formatted string
  630. //
  631. va_start( pArg, pwszFormatStr );
  632. _vsnwprintf( m_pwszOutBuf, BS_DBG_OUT_BUF_SIZE-1, pwszFormatStr, pArg );
  633. va_end( pArg );
  634. OutputString();
  635. //
  636. // Finish up with a carriage return.
  637. //
  638. wcscpy( m_pwszOutBuf, L"\r\n" );
  639. OutputString();
  640. }
  641. return S_OK;
  642. }
  643. HRESULT
  644. CBsDbgTrace::OutputString()
  645. /*++
  646. Routine Description:
  647. Prints the trace message out to the trace file and/or debugger.
  648. Arguments:
  649. Assumes m_pwszOutBuf has the string to be printed.
  650. Return Value:
  651. HRESULT
  652. --*/
  653. {
  654. //
  655. // Make sure we didn't go off the end. Can't use BS_ASSERT(), it
  656. // will cause an deadlock.
  657. //
  658. _ASSERTE( wcslen( m_pwszOutBuf ) < BS_DBG_OUT_BUF_SIZE );
  659. //
  660. // Print to the debug stream for debug builds
  661. //
  662. if ( m_bTraceToDebugger )
  663. OutputDebugString( m_pwszOutBuf );
  664. //
  665. // If file tracing is enabled, dump to file
  666. //
  667. if ( m_hTraceFile != INVALID_HANDLE_VALUE ) {
  668. DWORD dwBytesWritten;
  669. ::WriteFile( m_hTraceFile,
  670. m_pwszOutBuf,
  671. (DWORD)(wcslen( m_pwszOutBuf ) * sizeof(WCHAR)),
  672. &dwBytesWritten,
  673. NULL );
  674. }
  675. return S_OK;
  676. }
  677. VOID CBsDbgTrace::SetContextNum(
  678. IN DWORD dwContextNum
  679. )
  680. /*++
  681. Routine Description:
  682. Use to be used to set the context number of the operation. Now it is only
  683. used to determine if a DLL is loading using the trace class.
  684. Arguments:
  685. LTS_CONTEXT_DELAYED_DLL - DLL is using the class object.
  686. --*/
  687. {
  688. if (dwContextNum == LTS_CONTEXT_DELAYED_DLL && !m_bInitialized )
  689. {
  690. Initialize();
  691. }
  692. }
  693. /*++
  694. Routine Description:
  695. Puts the UNICODE UCS-2 BOM (Byte Order Mark) at the beginning of the file
  696. to let applications know that 1. this is a UCS-2 UNICODE file and 2. that
  697. the byte ordering is little-endian.
  698. Assumes the file is empty.
  699. Arguments:
  700. hFile - Handle to the file
  701. Return Value:
  702. <Enter return values here>
  703. --*/
  704. static VOID MakeFileUnicode(
  705. IN HANDLE hFile
  706. )
  707. {
  708. BS_ASSERT( hFile != INVALID_HANDLE_VALUE );
  709. BYTE byteBOM[2] = { 0xFF, 0xFE };
  710. DWORD dwBytesWritten;
  711. ::WriteFile( hFile,
  712. byteBOM,
  713. sizeof byteBOM,
  714. &dwBytesWritten,
  715. NULL );
  716. }
  717. void __cdecl CVssFunctionTracer::TranslateError
  718. (
  719. IN CVssDebugInfo dbgInfo, // Caller debugging info
  720. IN HRESULT hr,
  721. IN LPCWSTR wszRoutine
  722. )
  723. /*++
  724. Routine Description:
  725. Translates an error into a well defined error code. May log to
  726. the event log if the error is unexpected
  727. --*/
  728. {
  729. if (hr == E_OUTOFMEMORY ||
  730. hr == HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY) ||
  731. hr == HRESULT_FROM_WIN32(ERROR_NO_MORE_SEARCH_HANDLES) ||
  732. hr == HRESULT_FROM_WIN32(ERROR_NO_LOG_SPACE) ||
  733. hr == HRESULT_FROM_WIN32(ERROR_DISK_FULL) ||
  734. hr == HRESULT_FROM_WIN32(ERROR_NO_MORE_USER_HANDLES))
  735. Throw(dbgInfo, E_OUTOFMEMORY, L"Out of memory detected in function %s", wszRoutine);
  736. else
  737. {
  738. LogError(VSS_ERROR_UNEXPECTED_CALLING_ROUTINE, dbgInfo << wszRoutine << hr);
  739. Throw(dbgInfo, E_UNEXPECTED, L"Unexpected error in routine %s. hr = 0x%08lx", wszRoutine, hr);
  740. }
  741. }
  742. void __cdecl CVssFunctionTracer::TranslateGenericError
  743. (
  744. IN CVssDebugInfo dbgInfo, // Caller debugging info
  745. IN HRESULT hr,
  746. IN LPCWSTR wszErrorTextFormat,
  747. IN ...
  748. )
  749. /*++
  750. Routine Description:
  751. Translates an error into a well defined error code. May log to
  752. the event log if the error is unexpected
  753. Throws:
  754. E_UNEXPECTED
  755. - on unrecognized error codes
  756. --*/
  757. {
  758. WCHAR wszOutputBuffer[nVssMsgBufferSize + 1];
  759. va_list marker;
  760. va_start( marker, wszErrorTextFormat );
  761. _vsnwprintf( wszOutputBuffer, nVssMsgBufferSize, wszErrorTextFormat, marker );
  762. va_end( marker );
  763. if (hr == E_OUTOFMEMORY ||
  764. hr == HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY) ||
  765. hr == HRESULT_FROM_WIN32(ERROR_NO_MORE_SEARCH_HANDLES) ||
  766. hr == HRESULT_FROM_WIN32(ERROR_NO_LOG_SPACE) ||
  767. hr == HRESULT_FROM_WIN32(ERROR_DISK_FULL) ||
  768. hr == HRESULT_FROM_WIN32(ERROR_NO_MORE_USER_HANDLES))
  769. Throw(dbgInfo, E_OUTOFMEMORY, L"Out of memory detected. %s", wszOutputBuffer);
  770. else
  771. {
  772. LogError(VSS_ERROR_UNEXPECTED_ERRORCODE, dbgInfo << wszOutputBuffer << hr);
  773. Throw(dbgInfo, E_UNEXPECTED, L"Unexpected error: %s [hr = 0x%08lx]", wszOutputBuffer, hr);
  774. }
  775. }
  776. void __cdecl CVssFunctionTracer::TranslateProviderError
  777. (
  778. IN CVssDebugInfo dbgInfo, // Caller debugging info
  779. IN GUID ProviderID,
  780. IN LPCWSTR wszErrorTextFormat,
  781. IN ...
  782. )
  783. /*++
  784. Routine Description:
  785. Translates an error into a well defined error code. May log to
  786. the event log if the error is unexpected
  787. The error is coming from a provider call
  788. Throws:
  789. E_OUTOFMEMORY
  790. VSS_E_UNEXPECTED_PROVIDER_ERROR
  791. - Unexpected provider error. The error code is logged into the event log.
  792. VSS_E_PROVIDER_VETO
  793. - Expected provider error. The provider already did the logging.
  794. --*/
  795. {
  796. WCHAR wszOutputBuffer[nVssMsgBufferSize + 1];
  797. va_list marker;
  798. va_start( marker, wszErrorTextFormat );
  799. _vsnwprintf( wszOutputBuffer, nVssMsgBufferSize, wszErrorTextFormat, marker );
  800. va_end( marker );
  801. if (hr == E_OUTOFMEMORY)
  802. Throw(dbgInfo, E_OUTOFMEMORY, L"Out of memory detected. %s. Provider ID = " WSTR_GUID_FMT,
  803. wszOutputBuffer, GUID_PRINTF_ARG(ProviderID));
  804. else if (hr == E_INVALIDARG) {
  805. LogError(VSS_ERROR_CALLING_PROVIDER_ROUTINE_INVALIDARG, dbgInfo << ProviderID << wszOutputBuffer );
  806. Throw(dbgInfo, E_INVALIDARG, L"Invalid argument detected. %s. Provider ID = " WSTR_GUID_FMT,
  807. wszOutputBuffer, GUID_PRINTF_ARG(ProviderID));
  808. }
  809. else if (hr == VSS_E_PROVIDER_VETO)
  810. Throw(dbgInfo, VSS_E_PROVIDER_VETO, L"Provider veto detected. %s. Provider ID = " WSTR_GUID_FMT,
  811. wszOutputBuffer, GUID_PRINTF_ARG(ProviderID));
  812. else
  813. {
  814. LogError(VSS_ERROR_CALLING_PROVIDER_ROUTINE, dbgInfo << ProviderID << wszOutputBuffer << hr );
  815. Throw(dbgInfo, VSS_E_UNEXPECTED_PROVIDER_ERROR,
  816. L"Unexpected error calling a provider routine: %s [hr = 0x%08lx] Provider ID = " WSTR_GUID_FMT,
  817. wszOutputBuffer, hr, GUID_PRINTF_ARG(ProviderID));
  818. }
  819. }
  820. void __cdecl CVssFunctionTracer::TranslateInternalProviderError
  821. (
  822. IN CVssDebugInfo dbgInfo, // Caller debugging info
  823. IN HRESULT hrToBeTreated,
  824. IN HRESULT hrToBeThrown,
  825. IN LPCWSTR wszErrorTextFormat,
  826. IN ...
  827. )
  828. /*++
  829. Routine Description:
  830. Translates an error into a well defined error code. May log to
  831. the event log if the error is unexpected
  832. The error is coming from a provider call
  833. Throws:
  834. E_OUTOFMEMORY
  835. hrToBeThrown
  836. --*/
  837. {
  838. WCHAR wszOutputBuffer[nVssMsgBufferSize + 1];
  839. va_list marker;
  840. va_start( marker, wszErrorTextFormat );
  841. _vsnwprintf( wszOutputBuffer, nVssMsgBufferSize, wszErrorTextFormat, marker );
  842. va_end( marker );
  843. hr = hrToBeTreated;
  844. if (hr == E_OUTOFMEMORY ||
  845. hr == HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY) ||
  846. hr == HRESULT_FROM_WIN32(ERROR_NO_MORE_SEARCH_HANDLES) ||
  847. hr == HRESULT_FROM_WIN32(ERROR_NO_LOG_SPACE) ||
  848. hr == HRESULT_FROM_WIN32(ERROR_DISK_FULL) ||
  849. hr == HRESULT_FROM_WIN32(ERROR_NO_MORE_USER_HANDLES))
  850. Throw(dbgInfo, E_OUTOFMEMORY, L"Out of memory detected. %s.", wszOutputBuffer);
  851. else if ( hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) ||
  852. hr == HRESULT_FROM_WIN32(ERROR_DEVICE_NOT_CONNECTED) ||
  853. hr == HRESULT_FROM_WIN32(ERROR_NOT_READY))
  854. {
  855. LogError(VSS_ERROR_DEVICE_NOT_CONNECTED, dbgInfo << wszOutputBuffer );
  856. Throw(dbgInfo, VSS_E_OBJECT_NOT_FOUND,
  857. L"Invalid device when calling a provider routine: %s", wszOutputBuffer);
  858. }
  859. else
  860. {
  861. LogError(VSS_ERROR_UNEXPECTED_ERRORCODE, dbgInfo << wszOutputBuffer << hr );
  862. Throw(dbgInfo, hrToBeThrown,
  863. L"Unexpected error calling a provider routine: %s [hr = 0x%08lx] ", wszOutputBuffer, hr );
  864. }
  865. }
  866. void __cdecl CVssFunctionTracer::TranslateWriterReturnCode
  867. (
  868. IN CVssDebugInfo dbgInfo, // Caller debugging info
  869. IN LPCWSTR wszErrorTextFormat,
  870. IN ...
  871. )
  872. /*++
  873. Routine Description:
  874. Translates an error into a well defined error code. May log to
  875. the event log if the error is unexpected
  876. The error is coming from a writer call (CoCreteInstance of the writer COM+ event class
  877. or sending an event.
  878. Throws:
  879. E_OUTOFMEMORY
  880. VSS_E_UNEXPECTED_WRITER_ERROR
  881. - Unexpected writer error. The error code is logged into the event log.
  882. --*/
  883. {
  884. if (HrSucceeded()) {
  885. BS_ASSERT(hr == S_OK || hr == EVENT_S_NOSUBSCRIBERS || hr == EVENT_S_SOME_SUBSCRIBERS_FAILED);
  886. hr = S_OK;
  887. return;
  888. }
  889. WCHAR wszOutputBuffer[nVssMsgBufferSize + 1];
  890. va_list marker;
  891. va_start( marker, wszErrorTextFormat );
  892. _vsnwprintf( wszOutputBuffer, nVssMsgBufferSize, wszErrorTextFormat, marker );
  893. va_end( marker );
  894. if (hr == EVENT_E_ALL_SUBSCRIBERS_FAILED) {
  895. Warning( VSSDBG_COORD, L"%s event failed at one writer. hr = 0x%08lx", wszOutputBuffer, hr);
  896. // ignore the error;
  897. return;
  898. }
  899. else if (hr == E_OUTOFMEMORY)
  900. Throw(dbgInfo, E_OUTOFMEMORY, L"Out of memory detected. %s.", wszOutputBuffer);
  901. else
  902. {
  903. LogError(VSS_ERROR_UNEXPECTED_WRITER_ERROR, dbgInfo << wszOutputBuffer << hr );
  904. Throw(dbgInfo, VSS_E_UNEXPECTED_WRITER_ERROR,
  905. L"Unexpected error calling a provider routine: %s [hr = 0x%08lx] ", wszOutputBuffer, hr);
  906. }
  907. }
  908. void __cdecl CVssFunctionTracer::LogGenericWarning
  909. (
  910. IN CVssDebugInfo dbgInfo, // Caller debugging info
  911. IN LPCWSTR wszErrorTextFormat,
  912. IN ...
  913. )
  914. /*++
  915. Routine Description:
  916. Log a generic warning.
  917. --*/
  918. {
  919. WCHAR wszOutputBuffer[nVssMsgBufferSize + 1];
  920. va_list marker;
  921. va_start( marker, wszErrorTextFormat );
  922. _vsnwprintf( wszOutputBuffer, nVssMsgBufferSize, wszErrorTextFormat, marker );
  923. va_end( marker );
  924. LogError(VSS_WARNING_UNEXPECTED, dbgInfo << wszOutputBuffer << hr, EVENTLOG_WARNING_TYPE);
  925. Trace(dbgInfo, L"WARNING: %s [hr = 0x%08lx]", wszOutputBuffer, hr);
  926. }
  927. // This method must be called prior to calling a CoCreateInstance that may start VSS
  928. void CVssFunctionTracer::LogVssStartupAttempt()
  929. {
  930. // the name of the Volume Snapshot Service
  931. const LPCWSTR wszVssvcServiceName = L"VSS";
  932. SC_HANDLE shSCManager = NULL;
  933. SC_HANDLE shSCService = NULL;
  934. try
  935. {
  936. //
  937. // Check to see if VSSVC is running. If not, we are putting an entry into the trace log if enabled.
  938. //
  939. // Connect to the local service control manager
  940. shSCManager = OpenSCManager (NULL, NULL, SC_MANAGER_CONNECT);
  941. if (!shSCManager)
  942. TranslateGenericError(VSSDBG_GEN, HRESULT_FROM_WIN32(GetLastError()),
  943. L"OpenSCManager(NULL,NULL,SC_MANAGER_CONNECT)");
  944. // Get a handle to the service
  945. shSCService = OpenService (shSCManager, wszVssvcServiceName, SERVICE_QUERY_STATUS);
  946. if (!shSCService)
  947. TranslateGenericError(VSSDBG_GEN, HRESULT_FROM_WIN32(GetLastError()),
  948. L" OpenService (shSCManager, \'%s\', SERVICE_QUERY_STATUS)", wszVssvcServiceName);
  949. // Now query the service to see what state it is in at the moment.
  950. SERVICE_STATUS sSStat;
  951. if (!QueryServiceStatus (shSCService, &sSStat))
  952. TranslateGenericError(VSSDBG_GEN, HRESULT_FROM_WIN32(GetLastError()),
  953. L"QueryServiceStatus (shSCService, &sSStat)");
  954. // If the service is not running, then we will put an informational error log entry
  955. // if (sSStat.dwCurrentState != SERVICE_RUNNING)
  956. // LogError(VSS_INFO_SERVICE_STARTUP,
  957. // VSSDBG_GEN << GetCommandLineW() << (HRESULT)sSStat.dwCurrentState,
  958. // EVENTLOG_INFORMATION_TYPE);
  959. if (sSStat.dwCurrentState != SERVICE_RUNNING)
  960. Trace( VSSDBG_GEN,
  961. L"Volume Snapshots Service information: Service starting at request of process '%s'. [0x%08x]",
  962. GetCommandLineW(), sSStat.dwCurrentState );
  963. } VSS_STANDARD_CATCH ((*this));
  964. // Close handles
  965. if (NULL != shSCService) CloseServiceHandle (shSCService);
  966. if (NULL != shSCManager) CloseServiceHandle (shSCManager);
  967. }