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.

823 lines
19 KiB

  1. /*++
  2. Copyright (c) 1991-1992 Microsoft Corporation
  3. Module Name:
  4. Debug.c
  5. Abstract:
  6. This file contains routines to insulate more networking code from
  7. the actual NT debug routines.
  8. Author:
  9. John Rogers (JohnRo) 16-Apr-1991
  10. Environment:
  11. Interface is portable to any flat, 32-bit environment. (Uses Win32
  12. typedefs.) Requires ANSI C extensions: slash-slash comments, long
  13. external names. Code itself only runs under NT.
  14. Revision History:
  15. 16-Apr-1991 JohnRo
  16. Created. (Borrowed some code from LarryO's NetapipPrintf.)
  17. 19-May-1991 JohnRo
  18. Make LINT-suggested changes.
  19. 20-Aug-1991 JohnRo
  20. Another change suggested by PC-LINT.
  21. 17-Sep-1991 JohnRo
  22. Correct UNICODE use.
  23. 10-May-1992 JohnRo
  24. Correct a NetpDbgPrint bug when printing percent signs.
  25. --*/
  26. // These must be included first:
  27. #include <nt.h> // IN, LPVOID, etc.
  28. // These may be included in any order:
  29. #include <netdebug.h> // My prototypes.
  30. #include <nt.h>
  31. #include <ntrtl.h> // RtlAssert().
  32. #include <nturtl.h>
  33. #include <stdarg.h> // va_list, etc.
  34. #include <stdio.h> // vsprintf().
  35. #include <prefix.h> // PREFIX_ equates.
  36. #include <windows.h>
  37. //
  38. // Critical section used to control access to the log
  39. //
  40. RTL_CRITICAL_SECTION NetpLogCritSect;
  41. BOOL LogFileInitialized = FALSE;
  42. //
  43. // These routines are exported from netapi32.dll. We want them to still
  44. // be there in the free build, so checked binaries will run on a free
  45. // build. The following undef's are to get rid of the macros that cause
  46. // these to not be called in free builds.
  47. //
  48. #define DEBUG_DIR L"\\debug"
  49. #if !DBG
  50. #undef NetpAssertFailed
  51. #undef NetpHexDump
  52. #endif
  53. VOID
  54. NetpAssertFailed(
  55. IN LPDEBUG_STRING FailedAssertion,
  56. IN LPDEBUG_STRING FileName,
  57. IN DWORD LineNumber,
  58. IN LPDEBUG_STRING Message OPTIONAL
  59. )
  60. {
  61. #if DBG
  62. RtlAssert(
  63. FailedAssertion,
  64. FileName,
  65. (ULONG) LineNumber,
  66. (PCHAR) Message);
  67. #endif
  68. /* NOTREACHED */
  69. } // NetpAssertFailed
  70. #define MAX_PRINTF_LEN 1024 // Arbitrary.
  71. VOID
  72. NetpDbgPrint(
  73. IN LPDEBUG_STRING Format,
  74. ...
  75. )
  76. {
  77. va_list arglist;
  78. va_start(arglist, Format);
  79. vKdPrintEx((DPFLTR_NETAPI_ID, DPFLTR_INFO_LEVEL, Format, arglist));
  80. va_end(arglist);
  81. return;
  82. } // NetpDbgPrint
  83. VOID
  84. NetpHexDump(
  85. LPBYTE Buffer,
  86. DWORD BufferSize
  87. )
  88. /*++
  89. Routine Description:
  90. This function dumps the contents of the buffer to the debug screen.
  91. Arguments:
  92. Buffer - Supplies a pointer to the buffer that contains data to be dumped.
  93. BufferSize - Supplies the size of the buffer in number of bytes.
  94. Return Value:
  95. None.
  96. --*/
  97. {
  98. #define NUM_CHARS 16
  99. DWORD i, limit;
  100. TCHAR TextBuffer[NUM_CHARS + 1];
  101. //
  102. // Hex dump of the bytes
  103. //
  104. limit = ((BufferSize - 1) / NUM_CHARS + 1) * NUM_CHARS;
  105. for (i = 0; i < limit; i++) {
  106. if (i < BufferSize) {
  107. (VOID) DbgPrint("%02x ", Buffer[i]);
  108. if (Buffer[i] == TEXT('\r') ||
  109. Buffer[i] == TEXT('\n')) {
  110. TextBuffer[i % NUM_CHARS] = '.';
  111. }
  112. else if (Buffer[i] == '\0') {
  113. TextBuffer[i % NUM_CHARS] = ' ';
  114. }
  115. else {
  116. TextBuffer[i % NUM_CHARS] = (TCHAR) Buffer[i];
  117. }
  118. }
  119. else {
  120. (VOID) DbgPrint(" ");
  121. TextBuffer[i % NUM_CHARS] = ' ';
  122. }
  123. if ((i + 1) % NUM_CHARS == 0) {
  124. TextBuffer[NUM_CHARS] = 0;
  125. (VOID) DbgPrint(" %s \n", TextBuffer);
  126. }
  127. }
  128. (VOID) DbgPrint("\n");
  129. }
  130. #undef NetpBreakPoint
  131. VOID
  132. NetpBreakPoint(
  133. VOID
  134. )
  135. {
  136. #if DBG
  137. DbgBreakPoint();
  138. #endif
  139. } // NetpBreakPoint
  140. //
  141. // NOTICE
  142. // The debug log code was blatantly stolen from net\svcdlls\netlogon\server\nlp.c
  143. //
  144. //
  145. // Generalized logging support is provided below. The proper calling procedure is:
  146. //
  147. // NetpInitializeLogFile() - Call this once per process/log lifespan
  148. // NetpOpenDebugFile() - Call this to open a log file instance
  149. // NetpDebugDumpRoutine() - Call this every time you wish to
  150. // write data to the log. This can be done. Mutli-threaded safe.
  151. // NetpCloseDebugFile() - Call this to close a log instance
  152. // NetpShutdownLogFile() - Call this once per process/log lifespan
  153. //
  154. // Notes: NetpInitializeLogFile need only be called once per logging process instance,
  155. // meaning that a given logging process (such as netlogon, which does not exist as
  156. // a separate NT process, but does logging from multiple threads within a NT process).
  157. // Likewise, it would only call NetpShutdownLogFile once. This logging process can then
  158. // open and close the debug log as many times as it desires. Or, if there is only going
  159. // to be one instance of a log operating at any given moment, the Initialize and Shutdown
  160. // calls can wrap the Open and Close calls.
  161. //
  162. // The CloseDebugFile does a flush before closing the handle
  163. //
  164. VOID
  165. NetpInitializeLogFile(
  166. VOID
  167. )
  168. /*++
  169. Routine Description:
  170. Initializes the process for logging
  171. Arguments:
  172. None
  173. Return Value:
  174. None
  175. --*/
  176. {
  177. ASSERT( !LogFileInitialized );
  178. if ( !LogFileInitialized ) {
  179. try {
  180. InitializeCriticalSection( &NetpLogCritSect );
  181. LogFileInitialized = TRUE;
  182. } except( EXCEPTION_EXECUTE_HANDLER ) {
  183. NetpKdPrint(( PREFIX_NETLIB "Cannot initialize NetpLogCritSect: %lu\n",
  184. GetLastError() ));
  185. }
  186. }
  187. }
  188. VOID
  189. NetpShutdownLogFile(
  190. VOID
  191. )
  192. /*++
  193. Routine Description:
  194. The opposite of the former function
  195. Arguments:
  196. None
  197. Return Value:
  198. None
  199. --*/
  200. {
  201. if ( LogFileInitialized ) {
  202. LogFileInitialized = FALSE;
  203. DeleteCriticalSection( &NetpLogCritSect );
  204. }
  205. }
  206. HANDLE
  207. NetpOpenDebugFile(
  208. IN LPWSTR DebugLog
  209. )
  210. /*++
  211. Routine Description:
  212. Opens or re-opens the debug file
  213. This code blatantly stolen from net\svcdlls\netlogon\server\nlp.c
  214. If the file is bigger than 1 MB when it's opened, it will be moved
  215. to the *.BAK file and a new *.LOG file will be created.
  216. Arguments:
  217. DebugLog - Root name of the debug log. The given name will have a .LOG appeneded to it
  218. Return Value:
  219. None
  220. --*/
  221. {
  222. WCHAR LogFileName[MAX_PATH+1];
  223. WCHAR BakFileName[MAX_PATH+1];
  224. DWORD FileAttributes;
  225. DWORD PathLength, LogLen;
  226. DWORD WinError;
  227. HANDLE DebugLogHandle = NULL;
  228. ULONG i;
  229. //
  230. // make debug directory path first, if it is not made before.
  231. //
  232. if ( !GetWindowsDirectoryW(
  233. LogFileName,
  234. sizeof(LogFileName)/sizeof(WCHAR) ) ) {
  235. NetpKdPrint((PREFIX_NETLIB "Window Directory Path can't be retrieved, %lu.\n",
  236. GetLastError() ));
  237. return( DebugLogHandle );
  238. }
  239. //
  240. // check debug path length.
  241. //
  242. LogLen = 1 + wcslen( DebugLog ) + 4; // 1 is for the \\ and 4 is for the .LOG or .BAK
  243. PathLength = wcslen(LogFileName) * sizeof(WCHAR) +
  244. sizeof(DEBUG_DIR) + sizeof(WCHAR);
  245. if( (PathLength + ( ( LogLen + 1 ) * sizeof(WCHAR) ) > sizeof(LogFileName) ) ||
  246. (PathLength + ( ( LogLen + 1 ) * sizeof(WCHAR) ) > sizeof(BakFileName) ) ) {
  247. NetpKdPrint((PREFIX_NETLIB "Debug directory path (%ws) length is too long.\n",
  248. LogFileName));
  249. goto ErrorReturn;
  250. }
  251. wcscat(LogFileName, DEBUG_DIR);
  252. //
  253. // Check this path exists.
  254. //
  255. FileAttributes = GetFileAttributesW( LogFileName );
  256. if( FileAttributes == 0xFFFFFFFF ) {
  257. WinError = GetLastError();
  258. if( WinError == ERROR_FILE_NOT_FOUND ) {
  259. //
  260. // Create debug directory.
  261. //
  262. if( !CreateDirectoryW( LogFileName, NULL) ) {
  263. NetpKdPrint((PREFIX_NETLIB "Can't create Debug directory (%ws), %lu.\n",
  264. LogFileName, GetLastError() ));
  265. goto ErrorReturn;
  266. }
  267. }
  268. else {
  269. NetpKdPrint((PREFIX_NETLIB "Can't Get File attributes(%ws), %lu.\n",
  270. LogFileName, WinError ));
  271. goto ErrorReturn;
  272. }
  273. }
  274. else {
  275. //
  276. // if this is not a directory.
  277. //
  278. if(!(FileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
  279. NetpKdPrint((PREFIX_NETLIB "Debug directory path (%ws) exists as file.\n",
  280. LogFileName));
  281. goto ErrorReturn;
  282. }
  283. }
  284. //
  285. // Create the name of the old and new log file names
  286. //
  287. swprintf( BakFileName, L"%ws\\%ws.BAK", LogFileName, DebugLog );
  288. (VOID) wcscat( LogFileName, L"\\" );
  289. (VOID) wcscat( LogFileName, DebugLog );
  290. (VOID) wcscat( LogFileName, L".LOG" );
  291. //
  292. // We may need to create the file twice
  293. // if the file already exists and it's too big
  294. //
  295. for ( i = 0; i < 2; i++ ) {
  296. //
  297. // Open the file.
  298. //
  299. DebugLogHandle = CreateFileW( LogFileName,
  300. GENERIC_WRITE,
  301. FILE_SHARE_READ | FILE_SHARE_WRITE,
  302. NULL,
  303. OPEN_ALWAYS,
  304. FILE_ATTRIBUTE_NORMAL,
  305. NULL );
  306. if ( DebugLogHandle == INVALID_HANDLE_VALUE ) {
  307. DebugLogHandle = NULL;
  308. NetpKdPrint((PREFIX_NETLIB "cannot open %ws \n",
  309. LogFileName ));
  310. goto ErrorReturn;
  311. } else {
  312. // Position the log file at the end
  313. (VOID) SetFilePointer( DebugLogHandle,
  314. 0,
  315. NULL,
  316. FILE_END );
  317. }
  318. //
  319. // On the first iteration check whether the file is too big
  320. //
  321. if ( i == 0 ) {
  322. DWORD FileSize = GetFileSize( DebugLogHandle, NULL );
  323. if ( FileSize == 0xFFFFFFFF ) {
  324. NetpKdPrint((PREFIX_NETLIB "Cannot GetFileSize %ld\n", GetLastError() ));
  325. CloseHandle( DebugLogHandle );
  326. DebugLogHandle = NULL;
  327. goto ErrorReturn;
  328. } else if ( FileSize > 1000000 ) { // bigger than 1 MB?
  329. //
  330. // Close the file handle so we can move the file
  331. //
  332. CloseHandle( DebugLogHandle );
  333. DebugLogHandle = NULL;
  334. //
  335. // Move the file to the backup deleting the backup if it exists.
  336. // If this fails, we will reopen the same file on the next iteration.
  337. //
  338. if ( !MoveFileEx( LogFileName,
  339. BakFileName,
  340. MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH ) ) {
  341. NetpKdPrint((PREFIX_NETLIB "Cannot rename %ws to %ws (%ld)\n",
  342. LogFileName,
  343. BakFileName,
  344. GetLastError() ));
  345. NetpKdPrint((PREFIX_NETLIB "Will use the current file %ws\n", LogFileName));
  346. }
  347. } else {
  348. break; // File is not big - use it
  349. }
  350. }
  351. }
  352. return( DebugLogHandle );
  353. ErrorReturn:
  354. NetpKdPrint((PREFIX_NETLIB " Debug output will be written to debug terminal.\n"));
  355. return NULL;
  356. }
  357. VOID
  358. NetpDebugDumpRoutine(
  359. IN HANDLE LogHandle,
  360. IN PDWORD OpenLogThreadId OPTIONAL,
  361. IN LPSTR Format,
  362. va_list arglist
  363. )
  364. /*++
  365. Routine Description:
  366. Writes a formatted output string to the debug log
  367. Arguments:
  368. LogHandle -- Handle to the open log
  369. OpenLogThreadId -- The ID of the thread (obtained from
  370. GetCurrentThreadId) that explicitly opened the log.
  371. If not equal to the current thread ID, the current
  372. thread ID will be output in the log.
  373. Format -- printf style format string
  374. arglist -- List of arguments to dump
  375. Return Value:
  376. None
  377. --*/
  378. {
  379. char OutputBuffer[MAX_PRINTF_LEN+1] = {0};
  380. ULONG length;
  381. int lengthTmp;
  382. DWORD BytesWritten;
  383. SYSTEMTIME SystemTime;
  384. static BeginningOfLine = TRUE;
  385. //
  386. // If we don't have an open log file, just bail
  387. //
  388. if ( LogHandle == NULL ) {
  389. return;
  390. }
  391. EnterCriticalSection( &NetpLogCritSect );
  392. length = 0;
  393. //
  394. // Handle the beginning of a new line.
  395. //
  396. //
  397. if ( BeginningOfLine ) {
  398. //
  399. // Put the timestamp at the begining of the line.
  400. //
  401. GetLocalTime( &SystemTime );
  402. length += (ULONG) sprintf( &OutputBuffer[length],
  403. "%02u/%02u %02u:%02u:%02u ",
  404. SystemTime.wMonth,
  405. SystemTime.wDay,
  406. SystemTime.wHour,
  407. SystemTime.wMinute,
  408. SystemTime.wSecond );
  409. //
  410. // If the current thread is not the one which opened
  411. // the log, output the current thread ID
  412. //
  413. if ( OpenLogThreadId != NULL ) {
  414. DWORD CurrentThreadId = GetCurrentThreadId();
  415. if ( CurrentThreadId != *OpenLogThreadId ) {
  416. length += sprintf(&OutputBuffer[length], "[%08lx] ", CurrentThreadId);
  417. }
  418. }
  419. }
  420. //
  421. // Put the information requested by the caller onto the line
  422. //
  423. lengthTmp = _vsnprintf(&OutputBuffer[length], MAX_PRINTF_LEN - length - 1, Format, arglist);
  424. if ( lengthTmp < 0 ) {
  425. length = MAX_PRINTF_LEN - 1;
  426. // always end the line which cannot fit into the buffer
  427. OutputBuffer[length-1] = '\n';
  428. // indicate that the line is truncated by putting a rare character at the end
  429. OutputBuffer[length-2] = '#';
  430. } else {
  431. length += lengthTmp;
  432. }
  433. BeginningOfLine = (length > 0 && OutputBuffer[length-1] == '\n' );
  434. if ( BeginningOfLine ) {
  435. OutputBuffer[length-1] = '\r';
  436. OutputBuffer[length] = '\n';
  437. OutputBuffer[length+1] = '\0';
  438. length++;
  439. }
  440. ASSERT( length < sizeof( OutputBuffer ) / sizeof( CHAR ) );
  441. //
  442. // Write the debug info to the log file.
  443. //
  444. if ( LogHandle ) {
  445. if ( !WriteFile( LogHandle,
  446. OutputBuffer,
  447. length,
  448. &BytesWritten,
  449. NULL ) ) {
  450. NetpKdPrint((PREFIX_NETLIB "Log write of %s failed with %lu\n",
  451. OutputBuffer,
  452. GetLastError() ));
  453. }
  454. } else {
  455. NetpKdPrint((PREFIX_NETLIB "[LOGWRITE] %s\n", OutputBuffer));
  456. }
  457. LeaveCriticalSection( &NetpLogCritSect );
  458. }
  459. VOID
  460. NetpCloseDebugFile(
  461. IN HANDLE LogHandle
  462. )
  463. /*++
  464. Routine Description:
  465. Closes the output log
  466. Arguments:
  467. LogHandle -- Handle to the open log
  468. Return Value:
  469. None
  470. --*/
  471. {
  472. if ( LogHandle ) {
  473. if( FlushFileBuffers( LogHandle ) == FALSE ) {
  474. NetpKdPrint((PREFIX_NETLIB "Flush of debug log failed with %lu\n",
  475. GetLastError() ));
  476. }
  477. CloseHandle( LogHandle );
  478. }
  479. }
  480. //
  481. // The following functions are used by NetJoin
  482. // to facilitate the logging per tasks it performs.
  483. //
  484. // The caller of NetJoin APIs that wants NetJoin logging should initialize the logging
  485. // by calling NetpInitializeLogFile (defined above) once per the caller process lifespan.
  486. // Then NetJoin routines that are enabled for logging will log the data.
  487. //
  488. // A NetJoin routine enables logging by calling NetSetuppOpenLog to initialize the log file,
  489. // then calling NetpLogPrintHelper to perform the logging, and then calling NetSetuppCloseLog
  490. // to close the log. These functions are thread safe. The first thread to call NetSetuppOpenLog
  491. // will initialize the log, the last thread to call NetSetuppCloseLog will close the log. If the
  492. // thread doing logging is different from the one that opened the log, the logging thread ID will
  493. // be logged.
  494. //
  495. ULONG NetsetupLogRefCount=0;
  496. HANDLE hDebugLog = NULL;
  497. DWORD NetpOpenLogThreadId = 0;
  498. void
  499. NetSetuppOpenLog(
  500. VOID
  501. )
  502. /*++
  503. Routine Description:
  504. This procedure is used by a NetJoin routine to enable logging per
  505. that routine.
  506. Arguments:
  507. None
  508. Return Value:
  509. None
  510. --*/
  511. {
  512. //
  513. // If the NetJoin caller process didn't explicitly
  514. // initialize the log file, we are not to log for
  515. // that process
  516. //
  517. if ( !LogFileInitialized ) {
  518. return;
  519. }
  520. EnterCriticalSection( &NetpLogCritSect );
  521. NetsetupLogRefCount ++;
  522. if ( NetsetupLogRefCount == 1 ) {
  523. NetpOpenLogThreadId = GetCurrentThreadId();
  524. //
  525. // Now open the log and mark the start of the output
  526. //
  527. hDebugLog = NetpOpenDebugFile( L"NetSetup" );
  528. NetpLogPrintHelper( "-----------------------------------------------------------------\n" );
  529. }
  530. LeaveCriticalSection( &NetpLogCritSect );
  531. }
  532. void
  533. NetSetuppCloseLog(
  534. VOID )
  535. /*++
  536. Routine Description:
  537. This procedure is used by a NetJoin routine
  538. to indicate that it's done with the logging.
  539. Arguments:
  540. None
  541. Return Value:
  542. None
  543. --*/
  544. {
  545. //
  546. // If the NetJoin caller process didn't explicitly
  547. // initialize the log file, there is nothing for us to close
  548. //
  549. if ( !LogFileInitialized ) {
  550. return;
  551. }
  552. EnterCriticalSection( &NetpLogCritSect );
  553. //
  554. // We can walk into this routine only if
  555. // the log was previously initialized
  556. //
  557. ASSERT( NetsetupLogRefCount > 0 );
  558. NetsetupLogRefCount --;
  559. //
  560. // If we are the last thread, close the log
  561. //
  562. if ( NetsetupLogRefCount == 0 ) {
  563. NetpCloseDebugFile( hDebugLog );
  564. hDebugLog = NULL;
  565. NetpOpenLogThreadId = 0;
  566. }
  567. LeaveCriticalSection( &NetpLogCritSect );
  568. }
  569. void
  570. NetpLogPrintHelper(
  571. IN LPCSTR Format,
  572. ...)
  573. /*++
  574. Routine Description:
  575. This procedure is used by a NetJoin routine
  576. to do the logging.
  577. Arguments:
  578. None
  579. Return Value:
  580. None
  581. --*/
  582. {
  583. va_list arglist;
  584. //
  585. // If the NetJoin caller process didn't explicitly
  586. // initialize the log file, we are not to log for
  587. // that process
  588. //
  589. if ( !LogFileInitialized ) {
  590. return;
  591. }
  592. //
  593. // If the log file was opened, do the logging
  594. //
  595. EnterCriticalSection( &NetpLogCritSect );
  596. if ( NetsetupLogRefCount > 0 ) {
  597. va_start(arglist, Format);
  598. NetpDebugDumpRoutine(hDebugLog, &NetpOpenLogThreadId, (LPSTR) Format, arglist);
  599. va_end(arglist);
  600. }
  601. LeaveCriticalSection( &NetpLogCritSect );
  602. }