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.

2961 lines
90 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. tracelog.c
  5. Abstract:
  6. This is the source file that implements the private routines for
  7. the performance event tracing and logging facility.
  8. The routines here work on a single event tracing session, the logging
  9. thread, and buffer synchronization within a session.
  10. Author:
  11. Jee Fung Pang (jeepang) 03-Dec-1996
  12. Revision History:
  13. --*/
  14. // TODO: In future, may need to align buffer size to larger of disk alignment
  15. // or 1024.
  16. #pragma warning(disable:4214)
  17. #pragma warning(disable:4115)
  18. #pragma warning(disable:4201)
  19. #pragma warning(disable:4127)
  20. #include "ntverp.h"
  21. #include "ntos.h"
  22. #include "wmikmp.h"
  23. #include <zwapi.h>
  24. #pragma warning(default:4214)
  25. #pragma warning(default:4115)
  26. #pragma warning(default:4201)
  27. #pragma warning(default:4127)
  28. #ifndef _WMIKM_
  29. #define _WMIKM_
  30. #endif
  31. #include "evntrace.h"
  32. //
  33. // Constants and Types used locally
  34. //
  35. #if DBG
  36. ULONG WmipTraceDebugLevel=0;
  37. // 5 All messages
  38. // 4 Messages up to event operations
  39. // 3 Messages up to buffer operations
  40. // 2 Flush operations
  41. // 1 Common operations and debugging statements
  42. // 0 Always on - use for real error
  43. #endif
  44. #define ERROR_RETRY_COUNT 100
  45. //#define BUFFER_STATE_UNUSED 0 // Buffer is empty, not used
  46. //#define BUFFER_STATE_DIRTY 1 // Buffer is being used
  47. //#define BUFFER_STATE_FULL 2 // Buffer is filled up
  48. //#define BUFFER_STATE_FLUSH 4 // Buffer ready for flush
  49. #include "tracep.h"
  50. // Non-paged global variables
  51. //
  52. ULONG WmiTraceAlignment = DEFAULT_TRACE_ALIGNMENT;
  53. ULONG WmiUsePerfClock = EVENT_TRACE_CLOCK_SYSTEMTIME; // Global clock switch
  54. LONG WmipRefCount[MAXLOGGERS];
  55. ULONG WmipGlobalSequence = 0;
  56. PWMI_LOGGER_CONTEXT WmipLoggerContext[MAXLOGGERS];
  57. PWMI_BUFFER_HEADER WmipContextSwapProcessorBuffers[MAXIMUM_PROCESSORS];
  58. #ifdef WMI_NON_BLOCKING
  59. KSPIN_LOCK WmiSlistLock;
  60. #endif //WMI_NON_BLOCKING
  61. //
  62. // Paged global variables
  63. //
  64. #ifdef ALLOC_DATA_PRAGMA
  65. #pragma data_seg("PAGEDATA")
  66. #endif
  67. ULONG WmiWriteFailureLimit = ERROR_RETRY_COUNT;
  68. ULONG WmipFileSystemReady = FALSE;
  69. WMI_TRACE_BUFFER_CALLBACK WmipGlobalBufferCallback = NULL;
  70. PVOID WmipGlobalCallbackContext = NULL;
  71. #ifdef ALLOC_DATA_PRAGMA
  72. #pragma data_seg()
  73. #endif
  74. //
  75. // Function prototypes for routines used locally
  76. //
  77. PWMI_BUFFER_HEADER
  78. WmipSwitchBuffer(
  79. IN PWMI_LOGGER_CONTEXT LoggerContext,
  80. IN PWMI_BUFFER_HEADER OldBuffer,
  81. IN ULONG Processor
  82. );
  83. NTSTATUS
  84. WmipPrepareHeader(
  85. IN PWMI_LOGGER_CONTEXT LoggerContext,
  86. IN OUT PWMI_BUFFER_HEADER Buffer
  87. );
  88. VOID
  89. FASTCALL
  90. WmipResetBufferHeader (
  91. PWMI_LOGGER_CONTEXT LoggerContext,
  92. PWMI_BUFFER_HEADER Buffer
  93. );
  94. VOID
  95. FASTCALL
  96. WmipPushDirtyBuffer (
  97. PWMI_LOGGER_CONTEXT LoggerContext,
  98. PWMI_BUFFER_HEADER Buffer
  99. );
  100. //
  101. // Logger functions
  102. //
  103. NTSTATUS
  104. WmipCreateLogFile(
  105. IN PWMI_LOGGER_CONTEXT LoggerContext,
  106. IN ULONG SwitchFile,
  107. IN ULONG Append
  108. );
  109. NTSTATUS
  110. WmipFinalizeHeader(
  111. IN HANDLE FileHandle,
  112. IN PWMI_LOGGER_CONTEXT LoggerContext
  113. );
  114. #ifdef ALLOC_PRAGMA
  115. #pragma alloc_text(PAGE, WmipLogger)
  116. #pragma alloc_text(PAGE, WmipSendNotification)
  117. #pragma alloc_text(PAGE, WmipCreateLogFile)
  118. #pragma alloc_text(PAGE, WmipFlushActiveBuffers)
  119. #pragma alloc_text(PAGE, WmipGenerateFileName)
  120. #pragma alloc_text(PAGE, WmipPrepareHeader)
  121. #pragma alloc_text(PAGE, WmiBootPhase1)
  122. #pragma alloc_text(PAGE, WmipFinalizeHeader)
  123. #pragma alloc_text(PAGEWMI, WmipFlushBuffer)
  124. #pragma alloc_text(PAGEWMI, WmipReserveTraceBuffer)
  125. #pragma alloc_text(PAGEWMI, WmipGetFreeBuffer)
  126. #pragma alloc_text(PAGEWMI, WmiReserveWithPerfHeader)
  127. #pragma alloc_text(PAGEWMI, WmiReserveWithSystemHeader)
  128. #ifdef WMI_NON_BLOCKING
  129. #pragma alloc_text(PAGEWMI, WmipAllocateFreeBuffers)
  130. #pragma alloc_text(PAGE, WmipAdjustFreeBuffers)
  131. #else
  132. #pragma alloc_text(PAGEWMI, WmipSwitchBuffer)
  133. #endif //NWMI_NON_BLOCKING
  134. #pragma alloc_text(PAGEWMI, WmipReleaseTraceBuffer)
  135. #pragma alloc_text(PAGEWMI, WmiReleaseKernelBuffer)
  136. #pragma alloc_text(PAGEWMI, WmipResetBufferHeader)
  137. #pragma alloc_text(PAGEWMI, WmipPushDirtyBuffer)
  138. #pragma alloc_text(PAGEWMI, WmipPopFreeContextSwapBuffer)
  139. #pragma alloc_text(PAGEWMI, WmipPushDirtyContextSwapBuffer)
  140. #ifdef NTPERF
  141. #pragma alloc_text(PAGEWMI, WmipSwitchPerfmemBuffer)
  142. #endif //NTPERF
  143. #endif
  144. //
  145. // Actual code starts here
  146. //
  147. #ifdef WMI_NON_BLOCKING
  148. PWMI_BUFFER_HEADER
  149. WmipGetFreeBuffer(
  150. IN PWMI_LOGGER_CONTEXT LoggerContext
  151. )
  152. //
  153. // This routine works at any IRQL
  154. //
  155. {
  156. PWMI_BUFFER_HEADER Buffer;
  157. PSINGLE_LIST_ENTRY Entry;
  158. if (LoggerContext->SwitchingInProgress == 0) {
  159. //
  160. // Not in the middle of switching.
  161. //
  162. ReTry:
  163. Entry = InterlockedPopEntrySList(&LoggerContext->FreeList);
  164. if (Entry != NULL) {
  165. Buffer = CONTAINING_RECORD (Entry,
  166. WMI_BUFFER_HEADER,
  167. SlistEntry);
  168. //
  169. // Reset the buffer
  170. //
  171. WmipResetBufferHeader( LoggerContext, Buffer );
  172. //
  173. // Maintain some Wmi logger context buffer counts
  174. //
  175. InterlockedDecrement((PLONG) &LoggerContext->BuffersAvailable);
  176. InterlockedIncrement((PLONG) &LoggerContext->BuffersInUse);
  177. TraceDebug((2, "WmipGetFreeBuffer: %2d, %p, Free: %d, InUse: %d, Dirty: %d, Total: %d\n",
  178. LoggerContext->LoggerId,
  179. Buffer,
  180. LoggerContext->BuffersAvailable,
  181. LoggerContext->BuffersInUse,
  182. LoggerContext->BuffersDirty,
  183. LoggerContext->NumberOfBuffers));
  184. return Buffer;
  185. } else {
  186. if (LoggerContext->LoggerMode & EVENT_TRACE_BUFFERING_MODE) {
  187. //
  188. // If we are in BUFFERING Mode, put all buffers from
  189. // Flushlist into FreeList.
  190. //
  191. if (InterlockedIncrement((PLONG) &LoggerContext->SwitchingInProgress) == 1) {
  192. while (Entry = InterlockedPopEntrySList(&LoggerContext->FlushList)) {
  193. Buffer = CONTAINING_RECORD (Entry,
  194. WMI_BUFFER_HEADER,
  195. SlistEntry);
  196. InterlockedPushEntrySList(&LoggerContext->FreeList,
  197. (PSINGLE_LIST_ENTRY) &Buffer->SlistEntry);
  198. Buffer->State.Flush = 0;
  199. Buffer->State.Free = 1;
  200. InterlockedIncrement((PLONG) &LoggerContext->BuffersAvailable);
  201. InterlockedDecrement((PLONG) &LoggerContext->BuffersDirty);
  202. TraceDebug((2, "WMI Buffer Reuse: %2d, %p, Free: %d, InUse: %d, Dirty: %d, Total: %d\n",
  203. LoggerContext->LoggerId,
  204. Buffer,
  205. LoggerContext->BuffersAvailable,
  206. LoggerContext->BuffersInUse,
  207. LoggerContext->BuffersDirty,
  208. LoggerContext->NumberOfBuffers));
  209. }
  210. }
  211. InterlockedDecrement((PLONG) &LoggerContext->SwitchingInProgress);
  212. goto ReTry;
  213. }
  214. return NULL;
  215. }
  216. } else {
  217. return NULL;
  218. }
  219. }
  220. ULONG
  221. WmipAllocateFreeBuffers(
  222. IN PWMI_LOGGER_CONTEXT LoggerContext,
  223. IN ULONG NumberOfBuffers
  224. )
  225. /*++
  226. Routine Description:
  227. This routine allocate addition buffers into the free buffer list.
  228. Logger can allocate more buffer to handle bursty logging behavior.
  229. This routine can be called by multiple places and counters must be
  230. manipulated using interlocked operations.
  231. Arguments:
  232. LoggerContext - Logger Context
  233. NumberOfBuffers - Number of buffers to be allocated.
  234. Return Value:
  235. The total number of buffers actually allocated. When it is fewer than the requested number:
  236. If this is called when trace is turned on, we fail to turn on trace.
  237. If this is called by walker thread to get more buffer, it is OK.
  238. Environment:
  239. Kernel mode.
  240. --*/
  241. {
  242. ULONG i;
  243. PWMI_BUFFER_HEADER Buffer;
  244. ULONG TotalBuffers;
  245. for (i=0; i<NumberOfBuffers; i++) {
  246. //
  247. // Multiple threads can ask for more buffers, make sure
  248. // we do not go over the maximum.
  249. //
  250. TotalBuffers = InterlockedIncrement(&LoggerContext->NumberOfBuffers);
  251. if (TotalBuffers <= LoggerContext->MaximumBuffers) {
  252. #ifdef NTPERF
  253. if (PERFINFO_IS_LOGGING_TO_PERFMEM()) {
  254. Buffer = (PWMI_BUFFER_HEADER)
  255. PerfInfoReserveBytesFromPerfMem(LoggerContext->BufferSize);
  256. } else {
  257. #endif //NTPERF
  258. Buffer = (PWMI_BUFFER_HEADER)
  259. ExAllocatePoolWithTag(LoggerContext->PoolType,
  260. LoggerContext->BufferSize,
  261. TRACEPOOLTAG);
  262. #ifdef NTPERF
  263. }
  264. #endif //NTPERF
  265. if (Buffer != NULL) {
  266. TraceDebug((3,
  267. "WmipAllocateFreeBuffers: Allocated buffer size %d type %d\n",
  268. LoggerContext->BufferSize, LoggerContext->PoolType));
  269. InterlockedIncrement(&LoggerContext->BuffersAvailable);
  270. //
  271. // Initialize newly created buffer
  272. //
  273. RtlZeroMemory(Buffer, sizeof(WMI_BUFFER_HEADER));
  274. Buffer->CurrentOffset = sizeof(WMI_BUFFER_HEADER);
  275. KeQuerySystemTime(&Buffer->TimeStamp);
  276. Buffer->State.Free = 1;
  277. //
  278. // Insert it into the free List
  279. //
  280. InterlockedPushEntrySList(&LoggerContext->FreeList,
  281. (PSINGLE_LIST_ENTRY) &Buffer->SlistEntry);
  282. InterlockedPushEntrySList(&LoggerContext->GlobalList,
  283. (PSINGLE_LIST_ENTRY) &Buffer->GlobalEntry);
  284. } else {
  285. //
  286. // Allocation failed, decrement the NumberOfBuffers
  287. // we increment earlier.
  288. //
  289. InterlockedDecrement(&LoggerContext->NumberOfBuffers);
  290. break;
  291. }
  292. } else {
  293. //
  294. // Maximum is reached, decrement the NumberOfBuffers
  295. // we increment earlier.
  296. //
  297. InterlockedDecrement(&LoggerContext->NumberOfBuffers);
  298. break;
  299. }
  300. }
  301. TraceDebug((2, "WmipAllocateFreeBuffers %3d (%3d): Free: %d, InUse: %d, Dirty: %d, Total: %d\n",
  302. NumberOfBuffers,
  303. i,
  304. LoggerContext->BuffersAvailable,
  305. LoggerContext->BuffersInUse,
  306. LoggerContext->BuffersDirty,
  307. LoggerContext->NumberOfBuffers));
  308. return i;
  309. }
  310. NTSTATUS
  311. WmipAdjustFreeBuffers(
  312. IN PWMI_LOGGER_CONTEXT LoggerContext
  313. )
  314. /*++
  315. Routine Description:
  316. This routine does buffer management. It checks the number of free buffers and
  317. will allocate additonal or free some based on the situation.
  318. Arguments:
  319. LoggerContext - Logger Context
  320. Return Value:
  321. Status
  322. Environment:
  323. Kernel mode.
  324. --*/
  325. {
  326. ULONG FreeBuffers;
  327. ULONG AdditionalBuffers;
  328. NTSTATUS Status = STATUS_SUCCESS;
  329. //
  330. // Check if we need to allocate more buffers
  331. //
  332. FreeBuffers = ExQueryDepthSList(&LoggerContext->FreeList);
  333. if (FreeBuffers < LoggerContext->MinimumBuffers) {
  334. AdditionalBuffers = LoggerContext->MinimumBuffers - FreeBuffers;
  335. if (AdditionalBuffers != WmipAllocateFreeBuffers(LoggerContext, AdditionalBuffers)) {
  336. Status = STATUS_NO_MEMORY;
  337. }
  338. }
  339. return Status;
  340. }
  341. //
  342. // Event trace/record and buffer related routines
  343. //
  344. #else
  345. PWMI_BUFFER_HEADER
  346. WmipGetFreeBuffer(
  347. IN PWMI_LOGGER_CONTEXT LoggerContext
  348. )
  349. //
  350. // This routine works at IRQL <= DISPATCH_LEVEL
  351. //
  352. {
  353. PWMI_BUFFER_HEADER Buffer=NULL;
  354. //
  355. // Caller is responsible for setting up spinlock if necessary
  356. //
  357. if (IsListEmpty(&LoggerContext->FreeList)) {
  358. if ((ULONG) LoggerContext->NumberOfBuffers
  359. < LoggerContext->MaximumBuffers) {
  360. //
  361. // Try and grow the buffer pool by ONE buffer first if no free buffers left
  362. //
  363. TraceDebug((3, "WmipGetFreeBuffer: Adding buffer to pool\n"));
  364. Buffer = (PWMI_BUFFER_HEADER)
  365. ExAllocatePoolWithTag(LoggerContext->PoolType,
  366. LoggerContext->BufferSize, TRACEPOOLTAG);
  367. if (Buffer != NULL) { // not able to allocate another buffer
  368. InterlockedIncrement(&LoggerContext->NumberOfBuffers);
  369. RtlZeroMemory(Buffer, sizeof(WMI_BUFFER_HEADER));
  370. //
  371. // Initialize newly created buffer
  372. //
  373. Buffer->CurrentOffset = sizeof(WMI_BUFFER_HEADER);
  374. Buffer->Flags = BUFFER_STATE_DIRTY;
  375. Buffer->LoggerContext = LoggerContext;
  376. } // if (Buffer != NULL)
  377. } // if we can grow buffer
  378. } else {
  379. PLIST_ENTRY pEntry;
  380. pEntry = RemoveHeadList(&LoggerContext->FreeList);
  381. Buffer = CONTAINING_RECORD(
  382. pEntry,
  383. WMI_BUFFER_HEADER, Entry);
  384. InterlockedDecrement(&LoggerContext->BuffersAvailable);
  385. Buffer->Flags = BUFFER_STATE_DIRTY;
  386. Buffer->SavedOffset = 0;
  387. Buffer->CurrentOffset = sizeof(WMI_BUFFER_HEADER);
  388. Buffer->ReferenceCount = 0;
  389. Buffer->Wnode.ClientContext = 0;
  390. Buffer->LoggerContext = LoggerContext;
  391. }
  392. #if DBG
  393. if (Buffer != NULL) {
  394. TraceDebug((3,
  395. "WmipGetFreeBuffer: %X %d %d %d\n", Buffer->ClientContext,
  396. Buffer->CurrentOffset, Buffer->SavedOffset,
  397. Buffer->ReferenceCount));
  398. }
  399. #endif
  400. return Buffer;
  401. }
  402. //
  403. // Event trace/record and buffer related routines
  404. //
  405. #endif //WMI_NON_BLOCKING
  406. PSYSTEM_TRACE_HEADER
  407. FASTCALL
  408. WmiReserveWithSystemHeader(
  409. IN ULONG LoggerId,
  410. IN ULONG AuxSize,
  411. IN PETHREAD Thread,
  412. OUT PWMI_BUFFER_HEADER *BufferResource
  413. )
  414. //
  415. // This routine only works with IRQL <= DISPATCH_LEVEL
  416. // It returns with LoggerContext locked, so caller must explicitly call
  417. // WmipDereferenceLogger() after call WmipReleaseTraceBuffer()
  418. //
  419. {
  420. PSYSTEM_TRACE_HEADER Header;
  421. PWMI_LOGGER_CONTEXT LoggerContext;
  422. #if DBG
  423. LONG RefCount;
  424. #endif
  425. #if DBG
  426. RefCount =
  427. #endif
  428. WmipReferenceLogger(LoggerId);
  429. TraceDebug((4, "WmiReserveWithSystemHeader: %d %d->%d\n",
  430. LoggerId, RefCount-1, RefCount));
  431. LoggerContext = WmipGetLoggerContext(LoggerId);
  432. AuxSize += sizeof(SYSTEM_TRACE_HEADER); // add header size first
  433. Header = WmipReserveTraceBuffer(
  434. LoggerContext, AuxSize, BufferResource);
  435. if (Header != NULL) {
  436. PerfTimeStamp(Header->SystemTime);
  437. //
  438. // Now copy the necessary information into the buffer
  439. //
  440. if (Thread == NULL) {
  441. Thread = PsGetCurrentThread();
  442. }
  443. Header->Marker = SYSTEM_TRACE_MARKER;
  444. Header->ThreadId = HandleToUlong(Thread->Cid.UniqueThread);
  445. Header->ProcessId = HandleToUlong(Thread->Cid.UniqueProcess);
  446. Header->KernelTime = Thread->Tcb.KernelTime;
  447. Header->UserTime = Thread->Tcb.UserTime;
  448. Header->Packet.Size = (USHORT) AuxSize;
  449. }
  450. else {
  451. #if DBG
  452. RefCount =
  453. #endif
  454. WmipDereferenceLogger(LoggerId); //Interlocked decrement
  455. TraceDebug((4, "WmiReserveWithSystemHeader: %d %d->%d\n",
  456. LoggerId, RefCount+1, RefCount));
  457. }
  458. // NOTE: Caller must still put in a proper MARKER
  459. return Header;
  460. }
  461. PPERFINFO_TRACE_HEADER
  462. FASTCALL
  463. WmiReserveWithPerfHeader(
  464. IN ULONG AuxSize,
  465. OUT PWMI_BUFFER_HEADER *BufferResource
  466. )
  467. //
  468. // This routine only works with IRQL <= DISPATCH_LEVEL
  469. // It returns with LoggerContext locked, so caller must explicitly call
  470. // WmipDereferenceLogger() after call WmipReleaseTraceBuffer()
  471. //
  472. {
  473. PPERFINFO_TRACE_HEADER Header;
  474. ULONG LoggerId = WmipKernelLogger;
  475. #if DBG
  476. LONG RefCount;
  477. #endif
  478. //
  479. // We must have this check here to see the logger is still running
  480. // before calling ReserveTraceBuffer.
  481. // The stopping thread may have cleaned up the logger context at this
  482. // point, which will cause AV.
  483. // For all other kernel events, this check is made in callouts.c.
  484. //
  485. if (WmipIsLoggerOn(LoggerId) == NULL) {
  486. return NULL;
  487. }
  488. #if DBG
  489. RefCount =
  490. #endif
  491. WmipReferenceLogger(LoggerId);
  492. TraceDebug((4, "WmiReserveWithPerfHeader: %d %d->%d\n",
  493. LoggerId, RefCount-1, RefCount));
  494. AuxSize += FIELD_OFFSET(PERFINFO_TRACE_HEADER, Data); // add header size first
  495. Header = WmipReserveTraceBuffer(
  496. WmipGetLoggerContext(LoggerId), AuxSize, BufferResource);
  497. if (Header != NULL) {
  498. PerfTimeStamp(Header->SystemTime);
  499. //
  500. // Now copy the necessary information into the buffer
  501. //
  502. Header->Marker = PERFINFO_TRACE_MARKER;
  503. Header->Packet.Size = (USHORT) AuxSize;
  504. } else {
  505. #if DBG
  506. RefCount =
  507. #endif
  508. WmipDereferenceLogger(LoggerId);
  509. TraceDebug((4, "WmiWmiReserveWithPerfHeader: %d %d->%d\n",
  510. LoggerId, RefCount+1, RefCount));
  511. }
  512. // NOTE: Caller must still put in a proper MARKER
  513. return Header;
  514. }
  515. #ifdef WMI_NON_BLOCKING
  516. PVOID
  517. FASTCALL
  518. WmipReserveTraceBuffer(
  519. IN PWMI_LOGGER_CONTEXT LoggerContext,
  520. IN ULONG RequiredSize,
  521. OUT PWMI_BUFFER_HEADER *BufferResource
  522. )
  523. //
  524. // This routine should work at any IRQL
  525. //
  526. {
  527. PVOID ReservedSpace;
  528. PWMI_BUFFER_HEADER Buffer;
  529. ULONG Offset;
  530. ULONG Processor;
  531. PSINGLE_LIST_ENTRY SingleListEntry;
  532. //
  533. // Caller needs to ensure that the RequiredSize will not exceed
  534. // BufferSize - sizeof(WMI_BUFFER_HEADER)
  535. //
  536. if (!WmipIsValidLogger(LoggerContext)) {
  537. return NULL;
  538. }
  539. if (!LoggerContext->CollectionOn) {
  540. return NULL;
  541. }
  542. *BufferResource = NULL;
  543. RequiredSize = (ULONG) ALIGN_TO_POWER2(RequiredSize, WmiTraceAlignment);
  544. TryFindSpace:
  545. //
  546. // Get processor number again here due to possible context switch
  547. //
  548. Processor = (ULONG) KeGetCurrentProcessorNumber();
  549. //
  550. // Get the processor specific buffer pool
  551. //
  552. SingleListEntry = InterlockedPopEntrySList(&LoggerContext->ProcessorBuffers[Processor]);
  553. if (SingleListEntry == NULL) {
  554. //
  555. // Nothing in per process list, try to get one from free list
  556. //
  557. Buffer = WmipGetFreeBuffer (LoggerContext);
  558. if (Buffer == NULL) {
  559. //
  560. // Nothing available
  561. //
  562. goto LostEvent;
  563. } else {
  564. //
  565. // CPU information for the buffer.
  566. //
  567. Buffer->ClientContext.ProcessorNumber = (UCHAR) Processor;
  568. }
  569. } else {
  570. //
  571. // Found a Buffer.
  572. //
  573. Buffer = CONTAINING_RECORD (SingleListEntry,
  574. WMI_BUFFER_HEADER,
  575. SlistEntry);
  576. }
  577. //
  578. // Check if there is enough space in this buffer.
  579. //
  580. Offset = Buffer->CurrentOffset + RequiredSize;
  581. if (Offset < LoggerContext->BufferSize) {
  582. //
  583. // Space found.
  584. //
  585. ReservedSpace = (PVOID) (Buffer->CurrentOffset + (char*)Buffer);
  586. Buffer->CurrentOffset = Offset;
  587. if (LoggerContext->SequencePtr) {
  588. *((PULONG) ReservedSpace) =
  589. (ULONG)InterlockedIncrement(LoggerContext->SequencePtr);
  590. }
  591. goto FoundSpace;
  592. } else {
  593. WmipPushDirtyBuffer(LoggerContext, Buffer);
  594. if (!(LoggerContext->LoggerMode & EVENT_TRACE_BUFFERING_MODE)) {
  595. if (KeGetCurrentIrql() <= DISPATCH_LEVEL) {
  596. //
  597. // Wake up the walker thread to write it out to disk.
  598. //
  599. WmipNotifyLogger(LoggerContext);
  600. } else {
  601. //
  602. // Queue the item.
  603. //
  604. InterlockedIncrement(&LoggerContext->ReleaseQueue);
  605. }
  606. }
  607. goto TryFindSpace;
  608. }
  609. LostEvent:
  610. //
  611. // Will get here it we are throwing away the event
  612. //
  613. LoggerContext->EventsLost++; // best attempt to be accurate
  614. ReservedSpace = NULL;
  615. if (LoggerContext->SequencePtr) {
  616. InterlockedIncrement(LoggerContext->SequencePtr);
  617. }
  618. FoundSpace:
  619. //
  620. // notify the logger after critical section
  621. //
  622. *BufferResource = Buffer;
  623. return ReservedSpace;
  624. }
  625. #else
  626. PVOID
  627. FASTCALL
  628. WmipReserveTraceBuffer(
  629. IN PWMI_LOGGER_CONTEXT LoggerContext,
  630. IN ULONG RequiredSize,
  631. OUT PWMI_BUFFER_HEADER *BufferResource
  632. )
  633. //
  634. // This routine should work at any IRQL
  635. //
  636. {
  637. PVOID ReservedSpace;
  638. PWMI_BUFFER_HEADER Buffer, OldBuffer;
  639. ULONG Offset;
  640. ULONG Processor;
  641. ULONG CircularBufferOnly = FALSE;
  642. //
  643. // Caller needs to ensure that the RequiredSize will not exceed
  644. // BufferSize - sizeof(WMI_BUFFER_HEADER)
  645. //
  646. if (!WmipIsValidLogger(LoggerContext)) {
  647. return NULL;
  648. }
  649. if (!LoggerContext->CollectionOn) {
  650. return NULL;
  651. }
  652. *BufferResource = NULL;
  653. RequiredSize = (ULONG) ALIGN_TO_POWER2(RequiredSize, WmiTraceAlignment);
  654. TryFindSpace:
  655. // Get processor number again here due to possible context switch
  656. Processor = (ULONG) KeGetCurrentProcessorNumber();
  657. //
  658. // Get the processor specific buffer pool
  659. //
  660. Buffer = LoggerContext->ProcessorBuffers[Processor];
  661. if (Buffer == NULL)
  662. return NULL;
  663. //
  664. // Increment refcount to buffer first to prevent it from going away
  665. //
  666. InterlockedIncrement(&Buffer->ReferenceCount);
  667. if ( (Buffer->Flags != BUFFER_STATE_FULL) &&
  668. (Buffer->Flags != BUFFER_STATE_UNUSED) ) {
  669. //
  670. // This should happen 99% of the time. Offset will have the old value
  671. //
  672. Offset = (ULONG) InterlockedExchangeAdd(
  673. (PLONG) &Buffer->CurrentOffset, RequiredSize);
  674. //
  675. // First, check to see if there is enough space. If not, it will
  676. // need to get another fresh buffer, and have the current buffer flushed
  677. //
  678. if (Offset+RequiredSize < LoggerContext->BufferSize) {
  679. //
  680. // Found the space so return it. This should happen 99% of the time
  681. //
  682. ReservedSpace = (PVOID) (Offset + (char*)Buffer);
  683. if (LoggerContext->SequencePtr) {
  684. *((PULONG) ReservedSpace) =
  685. (ULONG)InterlockedIncrement(LoggerContext->SequencePtr);
  686. }
  687. goto FoundSpace;
  688. }
  689. }
  690. else {
  691. Offset = Buffer->CurrentOffset; // Initialize local variable
  692. }
  693. if (KeGetCurrentIrql() > DISPATCH_LEVEL) {
  694. //
  695. // Throw away event if at elevated IRQL.
  696. //
  697. goto LostEvent;
  698. }
  699. if (Offset < LoggerContext->BufferSize) {
  700. Buffer->SavedOffset = Offset; // save this for FlushBuffer
  701. }
  702. //
  703. // if there is absolutely no more buffers, then return quickly
  704. //
  705. if (((ULONG)LoggerContext->NumberOfBuffers == LoggerContext->MaximumBuffers)
  706. && (LoggerContext->BuffersAvailable == 0)) {
  707. goto LostEvent;
  708. }
  709. //
  710. // Out of buffer space. Need to take the long route to find a buffer
  711. //
  712. //
  713. // Critical section starts here
  714. //
  715. Buffer->Flags = BUFFER_STATE_FULL;
  716. OldBuffer = Buffer;
  717. Buffer = WmipSwitchBuffer(
  718. LoggerContext,
  719. OldBuffer,
  720. Processor);
  721. if (Buffer == NULL) {
  722. Buffer = OldBuffer;
  723. goto LostEvent;
  724. }
  725. #if DBG
  726. if (WmipTraceDebugLevel >= 3) {
  727. DbgPrintEx(DPFLTR_WMILIB_ID,
  728. DPFLTR_INFO_LEVEL,
  729. "WmipReserveTraceBuffer: Inserted Buffer %X to FlushList\n",
  730. OldBuffer);
  731. DbgPrintEx(DPFLTR_WMILIB_ID,
  732. DPFLTR_INFO_LEVEL,
  733. "\t%X %d %d %d\n",
  734. OldBuffer->ClientContext,
  735. OldBuffer->ReferenceCount,
  736. OldBuffer->CurrentOffset,
  737. OldBuffer->SavedOffset);
  738. }
  739. #endif
  740. //
  741. // Decrement the refcount that we blindly incremented earlier
  742. // so that it can be flushed by the logger thread
  743. //
  744. if (CircularBufferOnly) {
  745. InterlockedDecrement(&OldBuffer->ReferenceCount);
  746. }
  747. else {
  748. WmipReferenceLogger(LoggerContext->LoggerId); // since release will unlock
  749. WmipReleaseTraceBuffer( OldBuffer, LoggerContext);
  750. }
  751. Buffer->ClientContext.ProcessorNumber = (UCHAR) Processor;
  752. goto TryFindSpace;
  753. //
  754. // Will get here it we are throwing away the event
  755. //
  756. LostEvent:
  757. LoggerContext->EventsLost++; // best attempt to be accurate
  758. Buffer->EventsLost++;
  759. InterlockedDecrement(&Buffer->ReferenceCount);
  760. Buffer = NULL;
  761. ReservedSpace = NULL;
  762. if (LoggerContext->SequencePtr) {
  763. InterlockedIncrement(LoggerContext->SequencePtr);
  764. }
  765. FoundSpace:
  766. //
  767. // notify the logger after critical section
  768. //
  769. *BufferResource = Buffer;
  770. return ReservedSpace;
  771. }
  772. PWMI_BUFFER_HEADER
  773. WmipSwitchBuffer(
  774. IN PWMI_LOGGER_CONTEXT LoggerContext,
  775. IN PWMI_BUFFER_HEADER OldBuffer,
  776. IN ULONG Processor
  777. )
  778. //
  779. // This routine works at IRQL <= DISPATCH_LEVEL
  780. //
  781. {
  782. PWMI_BUFFER_HEADER Buffer;
  783. KIRQL OldIrql;
  784. ULONG CircularBufferOnly = FALSE;
  785. #if DBG
  786. TraceDebug((3, "WmipSwitchBuffer: Switching buffer %X proc %d\n",
  787. OldBuffer, Processor));
  788. #endif
  789. if ( (LoggerContext->LoggerMode & EVENT_TRACE_BUFFERING_MODE) &&
  790. (LoggerContext->BufferAgeLimit.QuadPart == 0) &&
  791. (LoggerContext->LogFileHandle == NULL) ) {
  792. CircularBufferOnly = TRUE;
  793. }
  794. ExAcquireSpinLock(&LoggerContext->BufferSpinLock, &OldIrql);
  795. if (OldBuffer != LoggerContext->ProcessorBuffers[Processor]) {
  796. ExReleaseSpinLock(&LoggerContext->BufferSpinLock, OldIrql);
  797. #if DBG
  798. TraceDebug((3, "WmipSwitchBuffer: Buffer is already switched!\n"));
  799. #endif
  800. return OldBuffer; // tell caller to try the new buffer
  801. }
  802. Buffer = WmipGetFreeBuffer(LoggerContext);
  803. if (Buffer == NULL) {
  804. // Release the spinlock immediately and return
  805. ExReleaseSpinLock(&LoggerContext->BufferSpinLock, OldIrql);
  806. #if DBG
  807. TraceDebug((1, "WmipSwitchBuffer: Cannot locate free buffer\n"));
  808. #endif
  809. return NULL;
  810. }
  811. LoggerContext->ProcessorBuffers[Processor] = Buffer;
  812. if (CircularBufferOnly) {
  813. InsertTailList(&LoggerContext->FreeList, &OldBuffer->Entry);
  814. InterlockedIncrement(&LoggerContext->BuffersAvailable);
  815. LoggerContext->LastFlushedBuffer++;
  816. #if DBG
  817. TraceDebug((3, "WmipSwitchBuffer: Inserted Buf %X Entry %X to free\n",
  818. OldBuffer, OldBuffer->Entry));
  819. #endif
  820. }
  821. else {
  822. InsertTailList(&LoggerContext->FlushList, &OldBuffer->Entry);
  823. #if DBG
  824. TraceDebug((3, "WmipSwitchBuffer: Inserted Buf %X Entry %X to flush\n",
  825. OldBuffer, OldBuffer->Entry));
  826. #endif
  827. }
  828. ExReleaseSpinLock(&LoggerContext->BufferSpinLock, OldIrql);
  829. return Buffer;
  830. }
  831. #endif //WMI_NON_BLOCKING
  832. //
  833. // Actual Logger code starts here
  834. //
  835. VOID
  836. WmipLogger(
  837. IN PWMI_LOGGER_CONTEXT LoggerContext
  838. )
  839. /*++
  840. Routine Description:
  841. This function is the logger itself. It is started as a system thread.
  842. It will not return until someone has stopped data collection or it
  843. is not successful is flushing out a buffer (e.g. disk is full).
  844. Arguments:
  845. None.
  846. Return Value:
  847. The status of running the buffer manager
  848. --*/
  849. {
  850. NTSTATUS Status;
  851. ULONG ErrorCount;
  852. #ifdef WMI_NON_BLOCKING
  853. ULONG FlushCount = 0;
  854. LARGE_INTEGER OneSecond = {(ULONG)(-1 * 1000 * 1000 * 10), -1};
  855. ULONG FlushTimeOut;
  856. #else
  857. PSINGLE_LIST_ENTRY Entry;
  858. PWMI_BUFFER_HEADER Buffer;
  859. PLARGE_INTEGER FlushTimeOut;
  860. PLIST_ENTRY pEntry;
  861. ULONG NumberOfBuffers, i;
  862. #endif //WMI_NON_BLOCKING
  863. PAGED_CODE();
  864. LoggerContext->LoggerThread = PsGetCurrentThread();
  865. if ((LoggerContext->LoggerMode & EVENT_TRACE_DELAY_OPEN_FILE_MODE)
  866. || (LoggerContext->LogFileName.Length == 0)) {
  867. // If EVENT_TRACE_DELAY_OPEN_FILE_MODE is specified, WMI does not
  868. // need to create logfile now.
  869. //
  870. // If there is no LogFileName specified, WMI does not need to create
  871. // logfile either. WmipStartLogger() already checks all possible
  872. // combination of LoggerMode and LogFileName, so we don't need to
  873. // perform the same check again.
  874. //
  875. Status = STATUS_SUCCESS;
  876. } else {
  877. Status = WmipCreateLogFile(LoggerContext,
  878. FALSE,
  879. LoggerContext->LoggerMode & EVENT_TRACE_FILE_MODE_APPEND);
  880. }
  881. LoggerContext->LoggerStatus = Status;
  882. if (NT_SUCCESS(Status)) {
  883. //
  884. // This is the only place where CollectionOn will be turn on!!!
  885. //
  886. LoggerContext->CollectionOn = TRUE;
  887. KeSetEvent(&LoggerContext->LoggerEvent, 0, FALSE);
  888. } else {
  889. if (LoggerContext->LogFileHandle != NULL) {
  890. Status = ZwClose(LoggerContext->LogFileHandle);
  891. LoggerContext->LogFileHandle = NULL;
  892. }
  893. KeSetEvent(&LoggerContext->LoggerEvent, 0, FALSE);
  894. PsTerminateSystemThread(Status);
  895. return;
  896. }
  897. ErrorCount = 0;
  898. // by now, the caller has been notified that the logger is running
  899. //
  900. // Loop and wait for buffers to be filled until someone turns off CollectionOn
  901. //
  902. KeSetPriorityThread(KeGetCurrentThread(), LOW_REALTIME_PRIORITY-1);
  903. #ifdef WMI_NON_BLOCKING
  904. FlushCount = 0;
  905. #endif //WMI_NON_BLOCKING
  906. while (LoggerContext->CollectionOn) {
  907. #ifdef WMI_NON_BLOCKING
  908. if (LoggerContext->LoggerMode & EVENT_TRACE_BUFFERING_MODE) {
  909. //
  910. // Wait forever until signalled by when logging is terminated.
  911. //
  912. Status = KeWaitForSingleObject(
  913. &LoggerContext->LoggerSemaphore,
  914. Executive,
  915. KernelMode,
  916. FALSE,
  917. NULL);
  918. LoggerContext->LoggerStatus = STATUS_SUCCESS;
  919. } else {
  920. ULONG FlushAll = 0;
  921. ULONG FlushFlag;
  922. FlushTimeOut = LoggerContext->FlushTimer;
  923. //
  924. // Wake up every second to see if there are any buffers in
  925. // flush list.
  926. //
  927. Status = KeWaitForSingleObject(
  928. &LoggerContext->LoggerSemaphore,
  929. Executive,
  930. KernelMode,
  931. FALSE,
  932. &OneSecond);
  933. //
  934. // Check if number of buffers need to be adjusted.
  935. //
  936. WmipAdjustFreeBuffers(LoggerContext);
  937. #else
  938. FlushTimeOut = &LoggerContext->FlushTimer;
  939. if ( (*FlushTimeOut).QuadPart == 0) // so that it can be set anytime
  940. FlushTimeOut = NULL;
  941. Status = KeWaitForSingleObject(
  942. &LoggerContext->LoggerSemaphore,
  943. Executive,
  944. KernelMode,
  945. FALSE,
  946. FlushTimeOut);
  947. #endif //WMI_NON_BLOCKING
  948. LoggerContext->LoggerStatus = STATUS_SUCCESS;
  949. if (LoggerContext->RequestFlag & REQUEST_FLAG_NEW_FILE) {
  950. Status = STATUS_SUCCESS;
  951. if (LoggerContext->LoggerMode & EVENT_TRACE_FILE_MODE_NEWFILE) {
  952. if (LoggerContext->LogFilePattern.Buffer == NULL) {
  953. Status = STATUS_INVALID_PARAMETER;
  954. }
  955. else {
  956. Status = WmipGenerateFileName(
  957. &LoggerContext->LogFilePattern,
  958. (PLONG) &LoggerContext->FileCounter,
  959. &LoggerContext->NewLogFileName);
  960. }
  961. }
  962. if (NT_SUCCESS(Status)) {
  963. //
  964. // called to switch to a different file
  965. // switch immediately
  966. //
  967. TraceDebug((3, "WmipLogger: New File\n"));
  968. LoggerContext->LoggerStatus = WmipCreateLogFile(LoggerContext,
  969. TRUE,
  970. EVENT_TRACE_FILE_MODE_APPEND);
  971. if (NT_SUCCESS(LoggerContext->LoggerStatus)) {
  972. LoggerContext->LoggerMode &= ~EVENT_TRACE_DELAY_OPEN_FILE_MODE;
  973. }
  974. }
  975. else {
  976. LoggerContext->LoggerStatus = Status;
  977. }
  978. KeSetEvent(&LoggerContext->FlushEvent, 0, FALSE);
  979. continue;
  980. }
  981. #ifdef WMI_NON_BLOCKING
  982. if (Status == STATUS_TIMEOUT) {
  983. if (FlushTimeOut) {
  984. FlushCount++;
  985. if (FlushCount >= FlushTimeOut) {
  986. #if DBG
  987. ULONG64 Now;
  988. KeQuerySystemTime((PLARGE_INTEGER) &Now);
  989. TraceDebug((3, "WmipLogger (%2d): Timeout at %I64u\n",
  990. LoggerContext->LoggerId,
  991. Now));
  992. #endif
  993. FlushAll = 1;
  994. // reset the couter
  995. FlushCount = 0;
  996. } else {
  997. FlushAll = 0;
  998. }
  999. } else {
  1000. FlushAll = 0;
  1001. }
  1002. }
  1003. #else
  1004. FlushAll = ((FlushTimeOut != NULL) && (Status == STATUS_TIMEOUT));
  1005. #endif //WMI_NON_BLOCKING
  1006. FlushFlag = (LoggerContext->RequestFlag & REQUEST_FLAG_FLUSH_BUFFERS);
  1007. if ( FlushFlag )
  1008. FlushAll = TRUE;
  1009. #ifdef NTPERF
  1010. if (PERFINFO_IS_LOGGING_TO_PERFMEM()) {
  1011. //
  1012. // Now check if we are in delay close mode.
  1013. // Create the log file.
  1014. //
  1015. ULONG LoggerMode = LoggerContext->LoggerMode;
  1016. if ((LoggerContext->LogFileHandle == NULL) &&
  1017. (LoggerMode & EVENT_TRACE_DELAY_OPEN_FILE_MODE) &&
  1018. (WmipFileSystemReady != 0)) {
  1019. if (LoggerContext->LogFileName.Buffer != NULL) {
  1020. ULONG Append = LoggerMode & EVENT_TRACE_FILE_MODE_APPEND;
  1021. Status = WmipDelayCreate(&LoggerContext->LogFileHandle,
  1022. &LoggerContext->LogFileName,
  1023. Append);
  1024. if (NT_SUCCESS(Status)) {
  1025. //
  1026. // Now the file has been created, add the log file header
  1027. //
  1028. LoggerContext->LoggerMode &= ~EVENT_TRACE_DELAY_OPEN_FILE_MODE;
  1029. if (!Append) {
  1030. Status = WmipAddLogHeader(LoggerContext, NULL);
  1031. }
  1032. }
  1033. }
  1034. }
  1035. } else {
  1036. #endif //NTPERF
  1037. Status = WmipFlushActiveBuffers(LoggerContext, FlushAll);
  1038. //
  1039. // Should check the status, and if failed to write a log file
  1040. // header, should clean up. As the log file is bad anyway.
  1041. //
  1042. if ( FlushFlag ) {
  1043. LoggerContext->RequestFlag &= ~REQUEST_FLAG_FLUSH_BUFFERS;
  1044. //
  1045. // If this was a flush for persistent events, this request flag must
  1046. // be reset here.
  1047. //
  1048. LoggerContext->RequestFlag &= ~REQUEST_FLAG_CIRCULAR_TRANSITION;
  1049. LoggerContext->LoggerStatus = Status;
  1050. KeSetEvent(&LoggerContext->FlushEvent, 0, FALSE);
  1051. }
  1052. if (!NT_SUCCESS(Status)) {
  1053. LoggerContext->LoggerStatus = Status;
  1054. WmipStopLoggerInstance(LoggerContext);
  1055. }
  1056. #ifdef NTPERF
  1057. }
  1058. #endif //NTPERF
  1059. }
  1060. } // while loop
  1061. if (Status == STATUS_TIMEOUT) {
  1062. Status = STATUS_SUCCESS;
  1063. }
  1064. //
  1065. // if a normal collection end, flush out all the buffers before stopping
  1066. //
  1067. TraceDebug((2, "WmipLogger: Flush all buffers before stopping...\n"));
  1068. //
  1069. // First, move the per processor buffer out to FlushList
  1070. //
  1071. #ifdef WMI_NON_BLOCKING
  1072. while ((LoggerContext->NumberOfBuffers > 0) &&
  1073. (LoggerContext->NumberOfBuffers > LoggerContext->BuffersAvailable)) {
  1074. Status = KeWaitForSingleObject(
  1075. &LoggerContext->LoggerSemaphore,
  1076. Executive,
  1077. KernelMode,
  1078. FALSE,
  1079. &OneSecond);
  1080. WmipFlushActiveBuffers(LoggerContext, 1);
  1081. TraceDebug((2, "WmipLogger: Stop %d %d %d %d %d\n",
  1082. LoggerContext->LoggerId,
  1083. LoggerContext->BuffersAvailable,
  1084. LoggerContext->BuffersInUse,
  1085. LoggerContext->BuffersDirty,
  1086. LoggerContext->NumberOfBuffers));
  1087. }
  1088. #else
  1089. for (i=0; i<(ULONG)KeNumberProcessors; i++) {
  1090. Buffer = LoggerContext->ProcessorBuffers[i];
  1091. LoggerContext->ProcessorBuffers[i] = NULL;
  1092. InsertTailList(&LoggerContext->FlushList, &Buffer->Entry);
  1093. #if DBG
  1094. if (WmipTraceDebugLevel >= 3) {
  1095. DbgPrintEx(DPFLTR_WMILIB_ID,
  1096. DPFLTR_INFO_LEVEL,
  1097. "WmipLogger: Inserted %d buffer %X to FlushList\n",
  1098. i,
  1099. Buffer);
  1100. DbgPrintEx(DPFLTR_WMILIB_ID,
  1101. DPFLTR_INFO_LEVEL,
  1102. "\t%X %d %d %d\n",
  1103. Buffer->ClientContext,
  1104. Buffer->CurrentOffset,
  1105. Buffer->SavedOffset,
  1106. Buffer->ReferenceCount);
  1107. }
  1108. #endif
  1109. }
  1110. NumberOfBuffers = LoggerContext->NumberOfBuffers;
  1111. while ( NumberOfBuffers > 0 &&
  1112. (LoggerContext->BuffersAvailable < LoggerContext->NumberOfBuffers) )
  1113. {
  1114. pEntry = ExInterlockedRemoveHeadList(
  1115. &LoggerContext->FlushList,
  1116. &LoggerContext->BufferSpinLock);
  1117. if (pEntry == NULL)
  1118. break;
  1119. if (NT_SUCCESS(Status) {
  1120. Buffer = CONTAINING_RECORD(pEntry, WMI_BUFFER_HEADER, Entry);
  1121. TraceDebug((3,
  1122. "WmipLogger: Removed buffer %X from FlushList\n",Buffer));
  1123. WmipFlushBuffer( LoggerContext, Buffer);
  1124. }
  1125. ExInterlockedInsertHeadList(
  1126. &LoggerContext->FreeList,
  1127. &Buffer->Entry,
  1128. &LoggerContext->BufferSpinLock);
  1129. NumberOfBuffers--;
  1130. }
  1131. #endif //WMI_NON_BLOCKING
  1132. //
  1133. // Note that LoggerContext->LogFileObject needs to remain set
  1134. // for QueryLogger to work after close
  1135. //
  1136. if (LoggerContext->LogFileHandle != NULL) {
  1137. ZwClose(LoggerContext->LogFileHandle);
  1138. TraceDebug((1, "WmipLogger: Close logfile with status=%X\n", Status));
  1139. }
  1140. LoggerContext->LogFileHandle = NULL;
  1141. KeSetEvent(&LoggerContext->FlushEvent, 0, FALSE);
  1142. KeSetEvent(&LoggerContext->LoggerEvent, 0, FALSE);
  1143. #if DBG
  1144. if (!NT_SUCCESS(Status)) {
  1145. TraceDebug((1, "WmipLogger: Aborting %d %X\n",
  1146. LoggerContext->LoggerId, LoggerContext->LoggerStatus));
  1147. }
  1148. #endif
  1149. WmipFreeLoggerContext(LoggerContext);
  1150. #ifdef NTPERF
  1151. //
  1152. // Check if we are logging into perfmem.
  1153. //
  1154. if (PERFINFO_IS_LOGGING_TO_PERFMEM()) {
  1155. PerfInfoStopPerfMemLog();
  1156. }
  1157. #endif //NTPERF
  1158. PsTerminateSystemThread(Status);
  1159. }
  1160. NTSTATUS
  1161. WmipSendNotification(
  1162. PWMI_LOGGER_CONTEXT LoggerContext,
  1163. NTSTATUS Status,
  1164. ULONG Flag
  1165. )
  1166. {
  1167. WMI_TRACE_EVENT WmiEvent;
  1168. RtlZeroMemory(& WmiEvent, sizeof(WmiEvent));
  1169. WmiEvent.Status = Status;
  1170. KeQuerySystemTime(& WmiEvent.Wnode.TimeStamp);
  1171. WmiEvent.Wnode.BufferSize = sizeof(WmiEvent);
  1172. WmiEvent.Wnode.Guid = TraceErrorGuid;
  1173. WmiSetLoggerId(
  1174. LoggerContext->LoggerId,
  1175. (PTRACE_ENABLE_CONTEXT) & WmiEvent.Wnode.HistoricalContext);
  1176. WmiEvent.Wnode.ClientContext = 0XFFFFFFFF;
  1177. WmiEvent.TraceErrorFlag = Flag;
  1178. WmipProcessEvent(&WmiEvent.Wnode,
  1179. FALSE,
  1180. FALSE);
  1181. return STATUS_SUCCESS;
  1182. }
  1183. //
  1184. // convenience routine to flush the current buffer by the logger above
  1185. //
  1186. NTSTATUS
  1187. WmipFlushBuffer(
  1188. IN PWMI_LOGGER_CONTEXT LoggerContext,
  1189. IN PWMI_BUFFER_HEADER Buffer
  1190. )
  1191. /*++
  1192. Routine Description:
  1193. This function is responsible for flushing a filled buffer out to
  1194. disk, or to a real time consumer.
  1195. Arguments:
  1196. LoggerContext Context of the logger
  1197. Return Value:
  1198. The status of flushing the buffer
  1199. --*/
  1200. {
  1201. IO_STATUS_BLOCK IoStatus;
  1202. NTSTATUS Status;
  1203. #ifndef WMI_NON_BLOCKING
  1204. PWMI_BUFFER_HEADER OldBuffer;
  1205. KIRQL OldIrql;
  1206. #endif
  1207. ULONG BufferSize;
  1208. ULONG BufferPersistenceData = LoggerContext->RequestFlag
  1209. & ( REQUEST_FLAG_CIRCULAR_PERSIST
  1210. | REQUEST_FLAG_CIRCULAR_TRANSITION);
  1211. ASSERT(LoggerContext != NULL);
  1212. ASSERT(Buffer != NULL);
  1213. //
  1214. // Grab the buffer to be flushed
  1215. //
  1216. BufferSize = LoggerContext->BufferSize;
  1217. //
  1218. // Put end of record marker in buffer if available space
  1219. //
  1220. TraceDebug((2, "WmipFlushBuffer: %p, Flushed %X %d %d %d\n",
  1221. Buffer,
  1222. Buffer->ClientContext, Buffer->SavedOffset,
  1223. Buffer->CurrentOffset, LoggerContext->BuffersWritten));
  1224. Status = WmipPrepareHeader(LoggerContext, Buffer);
  1225. if (Status != STATUS_SUCCESS)
  1226. goto ResetTraceBuffer;
  1227. //
  1228. // Buffering mode is mutually exclusive with REAL_TIME_MODE
  1229. //
  1230. if (!(LoggerContext->LoggerMode & EVENT_TRACE_BUFFERING_MODE)) {
  1231. if (LoggerContext->LoggerMode & EVENT_TRACE_REAL_TIME_MODE) {
  1232. if (LoggerContext->UsePerfClock == EVENT_TRACE_CLOCK_PERFCOUNTER)
  1233. Buffer->Wnode.Flags |= WNODE_FLAG_USE_TIMESTAMP;
  1234. // need to see if we can send anymore
  1235. // check for queue length
  1236. if (! NT_SUCCESS(WmipProcessEvent((PWNODE_HEADER)Buffer,
  1237. FALSE,
  1238. FALSE))) {
  1239. LoggerContext->RealTimeBuffersLost++;
  1240. }
  1241. }
  1242. }
  1243. if (LoggerContext->LogFileHandle == NULL) {
  1244. goto ResetTraceBuffer;
  1245. }
  1246. if (LoggerContext->MaximumFileSize > 0) { // if quota given
  1247. ULONG64 FileSize = LoggerContext->LastFlushedBuffer * BufferSize;
  1248. ULONG64 FileLimit = LoggerContext->MaximumFileSize * BYTES_PER_MB;
  1249. if ( FileSize >= FileLimit ) {
  1250. ULONG LoggerMode = LoggerContext->LoggerMode & 0X000000FF;
  1251. //
  1252. // Files from user mode always have the APPEND flag.
  1253. // We mask it out here to simplify the testing below.
  1254. //
  1255. LoggerMode &= ~EVENT_TRACE_FILE_MODE_APPEND;
  1256. //
  1257. // PREALLOCATE flag has to go, too.
  1258. //
  1259. LoggerMode &= ~EVENT_TRACE_FILE_MODE_PREALLOCATE;
  1260. if (LoggerMode == EVENT_TRACE_FILE_MODE_SEQUENTIAL) {
  1261. // do not write to logfile anymore
  1262. Status = STATUS_LOG_FILE_FULL; // control needs to stop logging
  1263. // need to fire up a Wmi Event to control console
  1264. WmipSendNotification(LoggerContext,
  1265. Status, STATUS_SEVERITY_ERROR);
  1266. }
  1267. else if (LoggerMode == EVENT_TRACE_FILE_MODE_CIRCULAR) {
  1268. if (BufferPersistenceData > 0) {
  1269. // treat circular logfile as sequential logfile if
  1270. // logger still processes Persistence events (events
  1271. // that cannot be overwritten in circular manner).
  1272. //
  1273. Status = STATUS_LOG_FILE_FULL;
  1274. WmipSendNotification(LoggerContext,
  1275. Status, STATUS_SEVERITY_ERROR);
  1276. }
  1277. else {
  1278. // reposition file
  1279. LoggerContext->ByteOffset
  1280. = LoggerContext->FirstBufferOffset;
  1281. LoggerContext->LastFlushedBuffer = (ULONG)
  1282. (LoggerContext->FirstBufferOffset.QuadPart
  1283. / LoggerContext->BufferSize);
  1284. }
  1285. }
  1286. else if (LoggerMode & EVENT_TRACE_FILE_MODE_NEWFILE) {
  1287. HANDLE OldHandle, NewHandle;
  1288. UNICODE_STRING NewFileName, OldFileName;
  1289. ULONG BuffersWritten;
  1290. NewFileName.Buffer = NULL;
  1291. Status = WmipGenerateFileName(
  1292. &LoggerContext->LogFilePattern,
  1293. (PLONG) &LoggerContext->FileCounter,
  1294. &NewFileName);
  1295. if (NT_SUCCESS(Status)) {
  1296. //
  1297. // Now the file has been created, add the log file header
  1298. //
  1299. Status = WmipDelayCreate(&NewHandle,
  1300. &NewFileName,
  1301. FALSE);
  1302. Buffer = WmipGetFreeBuffer(LoggerContext);
  1303. if (NT_SUCCESS(Status) && (Buffer != NULL)) {
  1304. //
  1305. // Now the file has been created, add the log file header
  1306. //
  1307. BuffersWritten = LoggerContext->BuffersWritten;
  1308. LoggerContext->BuffersWritten = 1;
  1309. Status = WmipAddLogHeader(LoggerContext, Buffer);
  1310. if (NT_SUCCESS(Status)) {
  1311. LoggerContext->BuffersWritten = 0;
  1312. Status = WmipPrepareHeader(LoggerContext, Buffer);
  1313. if (Status == STATUS_SUCCESS) {
  1314. Status = ZwWriteFile(
  1315. NewHandle,
  1316. NULL, NULL, NULL,
  1317. &IoStatus,
  1318. Buffer,
  1319. BufferSize,
  1320. NULL, NULL);
  1321. // NOTE: Header contains oldfilename!
  1322. }
  1323. LoggerContext->BuffersWritten = BuffersWritten;
  1324. }
  1325. if (NT_SUCCESS(Status)) {
  1326. OldFileName = LoggerContext->LogFileName;
  1327. OldHandle = LoggerContext->LogFileHandle;
  1328. if (OldHandle) {
  1329. WmipFinalizeHeader(OldHandle, LoggerContext);
  1330. ZwClose(OldHandle);
  1331. }
  1332. LoggerContext->LogFileHandle = NewHandle;
  1333. LoggerContext->LogFileName = NewFileName;
  1334. LoggerContext->BuffersWritten = 1;
  1335. LoggerContext->LastFlushedBuffer = 1;
  1336. LoggerContext->ByteOffset.QuadPart = BufferSize;
  1337. // NOTE: Assumes LogFileName cannot be changed
  1338. // for NEWFILE mode!!!
  1339. if (OldFileName.Buffer != NULL) {
  1340. RtlFreeUnicodeString(&OldFileName);
  1341. }
  1342. WmipSendNotification(LoggerContext,
  1343. STATUS_MEDIA_CHANGED, STATUS_SEVERITY_INFORMATIONAL);
  1344. }
  1345. else {
  1346. ZwClose(NewHandle);
  1347. LoggerContext->BuffersWritten = BuffersWritten;
  1348. }
  1349. }
  1350. if (Buffer) {
  1351. InterlockedPushEntrySList(&LoggerContext->FreeList,
  1352. (PSINGLE_LIST_ENTRY) &Buffer->SlistEntry);
  1353. InterlockedIncrement((PLONG) &LoggerContext->BuffersAvailable);
  1354. InterlockedDecrement((PLONG) &LoggerContext->BuffersInUse);
  1355. }
  1356. }
  1357. if (!NT_SUCCESS(Status) && (NewFileName.Buffer != NULL)) {
  1358. ExFreePool(NewFileName.Buffer);
  1359. }
  1360. }
  1361. }
  1362. }
  1363. if (NT_SUCCESS(Status)) {
  1364. Status = ZwWriteFile(
  1365. LoggerContext->LogFileHandle,
  1366. NULL,
  1367. NULL,
  1368. NULL,
  1369. &IoStatus,
  1370. Buffer,
  1371. BufferSize,
  1372. &LoggerContext->ByteOffset,
  1373. NULL);
  1374. if (NT_SUCCESS(Status)) {
  1375. LoggerContext->ByteOffset.QuadPart += BufferSize;
  1376. if (BufferPersistenceData > 0) {
  1377. // update FirstBufferOffset so that persistence event will
  1378. // not be overwritten in circular logfile
  1379. //
  1380. LoggerContext->FirstBufferOffset.QuadPart += BufferSize;
  1381. }
  1382. }
  1383. else if (Status == STATUS_LOG_FILE_FULL ||
  1384. Status == STATUS_DISK_FULL) {
  1385. // need to fire up a Wmi Event to control console
  1386. WmipSendNotification(LoggerContext,
  1387. STATUS_LOG_FILE_FULL, STATUS_SEVERITY_ERROR);
  1388. }
  1389. else {
  1390. TraceDebug((2, "WmipFlushBuffer: Unknown WriteFile Failure with status=%X\n", Status));
  1391. }
  1392. }
  1393. ResetTraceBuffer:
  1394. if (NT_SUCCESS(Status)) {
  1395. LoggerContext->BuffersWritten++;
  1396. LoggerContext->LastFlushedBuffer++;
  1397. }
  1398. else {
  1399. #if DBG
  1400. if (Status == STATUS_NO_DATA_DETECTED) {
  1401. TraceDebug((2, "WmipFlushBuffer: Empty buffer detected\n"));
  1402. }
  1403. else if (Status == STATUS_SEVERITY_WARNING) {
  1404. TraceDebug((2, "WmipFlushBuffer: Buffer could be corrupted\n"));
  1405. }
  1406. else {
  1407. TraceDebug((2,
  1408. "WmipFlushBuffer: Unable to write buffer: status=%X\n",
  1409. Status));
  1410. }
  1411. #endif
  1412. if ((Status != STATUS_NO_DATA_DETECTED) &&
  1413. (Status != STATUS_SEVERITY_WARNING))
  1414. LoggerContext->LogBuffersLost++;
  1415. }
  1416. if (WmipGlobalBufferCallback) {
  1417. (WmipGlobalBufferCallback) (Buffer, WmipGlobalCallbackContext);
  1418. }
  1419. if (LoggerContext->BufferCallback) {
  1420. (LoggerContext->BufferCallback) (Buffer, LoggerContext->CallbackContext);
  1421. }
  1422. //
  1423. // Reset the buffer state
  1424. //
  1425. Buffer->EventsLost = 0;
  1426. Buffer->SavedOffset = 0;
  1427. #ifndef WMI_NON_BLOCKING
  1428. Buffer->ReferenceCount = 0;
  1429. #endif //WMI_NON_BLOCKING
  1430. Buffer->Flags = BUFFER_STATE_UNUSED;
  1431. //
  1432. // Try and remove an unused buffer if it has not been used for a while
  1433. //
  1434. #ifndef WMI_NON_BLOCKING
  1435. if ((LoggerContext->BufferAgeLimit.QuadPart > 0) &&
  1436. (WmipRefCount[LoggerContext->LoggerId] <= 2) &&
  1437. (LoggerContext->BuffersAvailable + (ULONG)KeNumberProcessors)
  1438. > LoggerContext->MinimumBuffers) {
  1439. PLIST_ENTRY Entry;
  1440. ExAcquireSpinLock(&LoggerContext->BufferSpinLock, &OldIrql);
  1441. Entry = LoggerContext->FreeList.Blink;
  1442. OldBuffer = (PWMI_BUFFER_HEADER)
  1443. CONTAINING_RECORD( Entry, WMI_BUFFER_HEADER, Entry);
  1444. #if DBG
  1445. TraceDebug((2, "Aging test %I64u %I64u %I64u\n",
  1446. Buffer->TimeStamp, OldBuffer->TimeStamp,
  1447. LoggerContext->BufferAgeLimit));
  1448. #endif
  1449. if (Entry != &LoggerContext->FreeList) {
  1450. if (((Buffer->TimeStamp.QuadPart - OldBuffer->TimeStamp.QuadPart)
  1451. + LoggerContext->BufferAgeLimit.QuadPart) > 0) {
  1452. if ((ULONG) LoggerContext->NumberOfBuffers
  1453. > LoggerContext->MinimumBuffers){
  1454. RemoveTailList(&LoggerContext->FreeList);
  1455. ExFreePool(OldBuffer);
  1456. ExReleaseSpinLock(&LoggerContext->BufferSpinLock, OldIrql);
  1457. InterlockedDecrement((PLONG) &LoggerContext->NumberOfBuffers);
  1458. return Status; // do not increment BuffersAvailable
  1459. }
  1460. }
  1461. }
  1462. ExReleaseSpinLock(&LoggerContext->BufferSpinLock, OldIrql);
  1463. }
  1464. InterlockedIncrement((PLONG) &LoggerContext->BuffersAvailable);
  1465. #endif //WMI_NON_BLOCKING
  1466. return Status;
  1467. }
  1468. NTSTATUS
  1469. WmipCreateLogFile(
  1470. IN PWMI_LOGGER_CONTEXT LoggerContext,
  1471. IN ULONG SwitchFile,
  1472. IN ULONG Append
  1473. )
  1474. {
  1475. NTSTATUS Status;
  1476. HANDLE newHandle = NULL;
  1477. IO_STATUS_BLOCK IoStatus;
  1478. FILE_STANDARD_INFORMATION FileSize;
  1479. LARGE_INTEGER ByteOffset;
  1480. BOOLEAN FileSwitched = FALSE;
  1481. UNICODE_STRING OldLogFileName;
  1482. PWCHAR strLogFileName = NULL;
  1483. PUCHAR pFirstBuffer = NULL;
  1484. PAGED_CODE();
  1485. RtlZeroMemory(&OldLogFileName, sizeof(UNICODE_STRING));
  1486. LoggerContext->RequestFlag &= ~REQUEST_FLAG_NEW_FILE;
  1487. pFirstBuffer = (PUCHAR) ExAllocatePoolWithTag(
  1488. PagedPool, LoggerContext->BufferSize, TRACEPOOLTAG);
  1489. if(pFirstBuffer == NULL) {
  1490. Status = STATUS_NO_MEMORY;
  1491. goto Cleanup;
  1492. }
  1493. RtlZeroMemory(pFirstBuffer, LoggerContext->BufferSize);
  1494. if (SwitchFile) {
  1495. Status = WmipCreateNtFileName(
  1496. LoggerContext->NewLogFileName.Buffer,
  1497. & strLogFileName);
  1498. }
  1499. else {
  1500. Status = WmipCreateNtFileName(
  1501. LoggerContext->LogFileName.Buffer,
  1502. & strLogFileName);
  1503. }
  1504. if (!NT_SUCCESS(Status)) {
  1505. goto Cleanup;
  1506. }
  1507. if (LoggerContext->ClientSecurityContext.ClientToken != NULL) {
  1508. Status = SeImpersonateClientEx(
  1509. &LoggerContext->ClientSecurityContext, NULL);
  1510. }
  1511. if (NT_SUCCESS(Status)) {
  1512. // first open logfile using user security context
  1513. //
  1514. Status = WmipCreateDirectoryFile(strLogFileName, FALSE, & newHandle, Append);
  1515. PsRevertToSelf();
  1516. }
  1517. if (!NT_SUCCESS(Status)) {
  1518. // if using user security context fails to open logfile,
  1519. // then try open logfile again using local system security context
  1520. //
  1521. Status = WmipCreateDirectoryFile(strLogFileName, FALSE, & newHandle, Append);
  1522. }
  1523. if (NT_SUCCESS(Status)) {
  1524. HANDLE tempHandle = LoggerContext->LogFileHandle;
  1525. PWMI_BUFFER_HEADER BufferChecksum;
  1526. PTRACE_LOGFILE_HEADER LogfileHeaderChecksum;
  1527. ULONG BuffersWritten = 0;
  1528. BufferChecksum = (PWMI_BUFFER_HEADER) LoggerContext->LoggerHeader;
  1529. LogfileHeaderChecksum = (PTRACE_LOGFILE_HEADER)
  1530. (((PUCHAR) BufferChecksum) + sizeof(WNODE_HEADER));
  1531. if (LogfileHeaderChecksum) {
  1532. BuffersWritten = LogfileHeaderChecksum->BuffersWritten;
  1533. }
  1534. ByteOffset.QuadPart = 0;
  1535. Status = ZwReadFile(
  1536. newHandle,
  1537. NULL,
  1538. NULL,
  1539. NULL,
  1540. & IoStatus,
  1541. pFirstBuffer,
  1542. LoggerContext->BufferSize,
  1543. & ByteOffset,
  1544. NULL);
  1545. if (NT_SUCCESS(Status)) {
  1546. PWMI_BUFFER_HEADER BufferFile;
  1547. PTRACE_LOGFILE_HEADER LogfileHeaderFile;
  1548. ULONG Size;
  1549. BufferFile =
  1550. (PWMI_BUFFER_HEADER) pFirstBuffer;
  1551. if (BufferFile->Wnode.BufferSize != LoggerContext->BufferSize) {
  1552. TraceDebug((1,
  1553. "WmipCreateLogFile::BufferSize check fails (%d,%d)\n",
  1554. BufferFile->Wnode.BufferSize,
  1555. LoggerContext->BufferSize));
  1556. Status = STATUS_FAIL_CHECK;
  1557. ZwClose(newHandle);
  1558. goto Cleanup;
  1559. }
  1560. if (RtlCompareMemory(BufferFile,
  1561. BufferChecksum,
  1562. sizeof(WNODE_HEADER))
  1563. != sizeof(WNODE_HEADER)) {
  1564. TraceDebug((1,"WmipCreateLogFile::WNODE_HEAD check fails\n"));
  1565. Status = STATUS_FAIL_CHECK;
  1566. ZwClose(newHandle);
  1567. goto Cleanup;
  1568. }
  1569. LogfileHeaderFile = (PTRACE_LOGFILE_HEADER)
  1570. (((PUCHAR) BufferFile) + sizeof(WMI_BUFFER_HEADER)
  1571. + sizeof(SYSTEM_TRACE_HEADER));
  1572. // We can only validate part of the header because a 32-bit
  1573. // DLL will be passing in 32-bit pointers
  1574. Size = FIELD_OFFSET(TRACE_LOGFILE_HEADER, LoggerName);
  1575. if (RtlCompareMemory(LogfileHeaderFile,
  1576. LogfileHeaderChecksum,
  1577. Size)
  1578. != Size) {
  1579. TraceDebug((1,
  1580. "WmipCreateLogFile::TRACE_LOGFILE_HEAD check fails\n"));
  1581. Status = STATUS_FAIL_CHECK;
  1582. ZwClose(newHandle);
  1583. goto Cleanup;
  1584. }
  1585. }
  1586. else {
  1587. ZwClose(newHandle);
  1588. goto Cleanup;
  1589. }
  1590. if (LoggerContext->LoggerMode & EVENT_TRACE_FILE_MODE_PREALLOCATE) {
  1591. ByteOffset.QuadPart = ((LONGLONG) LoggerContext->BufferSize) * BuffersWritten;
  1592. }
  1593. else {
  1594. Status = ZwQueryInformationFile(
  1595. newHandle,
  1596. &IoStatus,
  1597. &FileSize,
  1598. sizeof (FILE_STANDARD_INFORMATION),
  1599. FileStandardInformation
  1600. );
  1601. if (!NT_SUCCESS(Status)) {
  1602. ZwClose(newHandle);
  1603. goto Cleanup;
  1604. }
  1605. ByteOffset = FileSize.EndOfFile;
  1606. }
  1607. //
  1608. // Force to 1K alignment. In future, if disk alignment exceeds this,
  1609. // then use that
  1610. //
  1611. if ((ByteOffset.QuadPart % 1024) != 0) {
  1612. ByteOffset.QuadPart = ((ByteOffset.QuadPart / 1024) + 1) * 1024;
  1613. }
  1614. if (!(LoggerContext->LoggerMode & EVENT_TRACE_FILE_MODE_PREALLOCATE)) {
  1615. // NOTE: Should also validate BuffersWritten from logfile header with
  1616. // the end of file to make sure that no one else has written garbage
  1617. // to it
  1618. //
  1619. if (ByteOffset.QuadPart !=
  1620. ( ((LONGLONG) LoggerContext->BufferSize)
  1621. * BuffersWritten)) {
  1622. TraceDebug((1,
  1623. "WmipCreateLogFile::FileSize check fails (%I64d,%I64d)\n",
  1624. ByteOffset.QuadPart,
  1625. ( ((LONGLONG) LoggerContext->BufferSize)
  1626. * BuffersWritten)));
  1627. Status = STATUS_FAIL_CHECK;
  1628. ZwClose(newHandle);
  1629. goto Cleanup;
  1630. }
  1631. }
  1632. LoggerContext->FirstBufferOffset = ByteOffset;
  1633. LoggerContext->ByteOffset = ByteOffset;
  1634. if (LoggerContext->LoggerMode & EVENT_TRACE_FILE_MODE_PREALLOCATE) {
  1635. LoggerContext->BuffersWritten = BuffersWritten;
  1636. }
  1637. else {
  1638. LoggerContext->BuffersWritten = (ULONG) (FileSize.EndOfFile.QuadPart / LoggerContext->BufferSize);
  1639. }
  1640. LoggerContext->LastFlushedBuffer = LoggerContext->BuffersWritten;
  1641. // Update the log file handle and log file name in the LoggerContext
  1642. LoggerContext->LogFileHandle = newHandle;
  1643. if (SwitchFile) {
  1644. OldLogFileName = LoggerContext->LogFileName;
  1645. LoggerContext->LogFileName = LoggerContext->NewLogFileName;
  1646. FileSwitched = TRUE;
  1647. if ( tempHandle != NULL) {
  1648. //
  1649. // just to be safe, close old file after the switch
  1650. //
  1651. TraceDebug((1, "WmipCreateLogFile: Closing handle %X\n",
  1652. tempHandle));
  1653. ZwClose(tempHandle);
  1654. }
  1655. }
  1656. }
  1657. #if DBG
  1658. else {
  1659. TraceDebug((1,
  1660. "WmipCreateLogFile: ZwCreateFile(%ws) failed with status=%X\n",
  1661. LoggerContext->LogFileName.Buffer, Status));
  1662. }
  1663. #endif
  1664. Cleanup:
  1665. SeDeleteClientSecurity(& LoggerContext->ClientSecurityContext);
  1666. LoggerContext->ClientSecurityContext.ClientToken = NULL;
  1667. // Clean up unicode strings.
  1668. if (SwitchFile) {
  1669. if (!FileSwitched) {
  1670. RtlFreeUnicodeString(&LoggerContext->NewLogFileName);
  1671. }
  1672. else if (OldLogFileName.Buffer != NULL) {
  1673. // OldLogFileName.Buffer can still be NULL if it is the first update
  1674. // for the Kernel Logger.
  1675. RtlFreeUnicodeString(&OldLogFileName);
  1676. }
  1677. // Must do this for the next file switch.
  1678. RtlZeroMemory(&LoggerContext->NewLogFileName, sizeof(UNICODE_STRING));
  1679. }
  1680. if (strLogFileName != NULL) {
  1681. ExFreePool(strLogFileName);
  1682. }
  1683. if (pFirstBuffer != NULL) {
  1684. ExFreePool(pFirstBuffer);
  1685. }
  1686. LoggerContext->LoggerStatus = Status;
  1687. return Status;
  1688. }
  1689. #ifdef WMI_NON_BLOCKING
  1690. ULONG
  1691. FASTCALL
  1692. WmipReleaseTraceBuffer(
  1693. IN PWMI_BUFFER_HEADER BufferResource,
  1694. IN PWMI_LOGGER_CONTEXT LoggerContext
  1695. )
  1696. {
  1697. ULONG Processor;
  1698. LONG ReleaseQueue;
  1699. Processor = BufferResource->ClientContext.ProcessorNumber;
  1700. // NOTE: It is important to remember LoggerContext here, since
  1701. // BufferResource can be modified after the ReleaseSemaphore
  1702. InterlockedPushEntrySList (&LoggerContext->ProcessorBuffers[Processor],
  1703. (PSINGLE_LIST_ENTRY) &BufferResource->SlistEntry);
  1704. //
  1705. // Check if there are buffers to be flushed.
  1706. //
  1707. if (LoggerContext->ReleaseQueue) {
  1708. if (KeGetCurrentIrql() <= DISPATCH_LEVEL) {
  1709. WmipNotifyLogger(LoggerContext);
  1710. LoggerContext->ReleaseQueue = 0;
  1711. }
  1712. }
  1713. ReleaseQueue = LoggerContext->ReleaseQueue;
  1714. WmipDereferenceLogger(LoggerContext->LoggerId);
  1715. return (ReleaseQueue);
  1716. }
  1717. #else
  1718. ULONG
  1719. FASTCALL
  1720. WmipReleaseTraceBuffer(
  1721. IN PWMI_BUFFER_HEADER BufferResource,
  1722. IN PWMI_LOGGER_CONTEXT LoggerContext
  1723. )
  1724. //
  1725. // Caller must have locked the logger context via WmipReferenceLogger()
  1726. // as this routine will unlock it
  1727. //
  1728. {
  1729. ULONG BufRefCount;
  1730. #if DBG
  1731. ULONG RefCount;
  1732. #endif
  1733. if (BufferResource == NULL)
  1734. return 0;
  1735. BufRefCount = InterlockedDecrement((PLONG) &BufferResource->ReferenceCount);
  1736. // NOTE: It is important to remember LoggerContext here, since
  1737. // BufferResource can be modified after the ReleaseSemaphore
  1738. if ((BufRefCount == 0) && (BufferResource->Flags == BUFFER_STATE_FULL)) {
  1739. if (KeGetCurrentIrql() <= DISPATCH_LEVEL) {
  1740. BufferResource->Flags = BUFFER_STATE_FLUSH;
  1741. TraceDebug((2, "WmipReleaseTraceBuffer: Releasing buffer\n"));
  1742. WmipNotifyLogger(LoggerContext);
  1743. while (LoggerContext->ReleaseQueue > 0) {
  1744. WmipNotifyLogger(LoggerContext);
  1745. InterlockedDecrement((PLONG) &LoggerContext->ReleaseQueue);
  1746. }
  1747. }
  1748. else {
  1749. InterlockedIncrement((PLONG) &LoggerContext->ReleaseQueue);
  1750. TraceDebug((2, "WmipReleaseTraceBuffer: %d\n",
  1751. KeGetCurrentIrql() ));
  1752. }
  1753. }
  1754. #if DBG
  1755. RefCount =
  1756. #endif
  1757. WmipDereferenceLogger(LoggerContext->LoggerId);
  1758. return BufRefCount;
  1759. }
  1760. #endif //WMI_NON_BLOCKING
  1761. NTKERNELAPI
  1762. ULONG
  1763. FASTCALL
  1764. WmiReleaseKernelBuffer(
  1765. IN PWMI_BUFFER_HEADER BufferResource
  1766. )
  1767. {
  1768. PWMI_LOGGER_CONTEXT LoggerContext = WmipLoggerContext[WmipKernelLogger];
  1769. if (LoggerContext == (PWMI_LOGGER_CONTEXT) &WmipLoggerContext[0]) {
  1770. LoggerContext = BufferResource->LoggerContext;
  1771. }
  1772. WmipAssert(LoggerContext != NULL);
  1773. WmipAssert(LoggerContext != (PWMI_LOGGER_CONTEXT) &WmipLoggerContext[0]);
  1774. return WmipReleaseTraceBuffer(BufferResource, LoggerContext);
  1775. }
  1776. NTSTATUS
  1777. WmipFlushActiveBuffers(
  1778. IN PWMI_LOGGER_CONTEXT LoggerContext,
  1779. IN ULONG FlushAll
  1780. )
  1781. {
  1782. PWMI_BUFFER_HEADER Buffer;
  1783. ULONG i, ErrorCount;
  1784. NTSTATUS Status = STATUS_SUCCESS;
  1785. ULONG LoggerMode;
  1786. #ifdef WMI_NON_BLOCKING
  1787. PSINGLE_LIST_ENTRY Entry;
  1788. #else
  1789. ULONG FlushCount = 0;
  1790. PLIST_ENTRY pEntry;
  1791. #endif //WMI_NON_BLOCKING
  1792. #ifdef WMI_NON_BLOCKING
  1793. PAGED_CODE();
  1794. if (FlushAll) {
  1795. //
  1796. // Put all in-used buffers into flush list
  1797. //
  1798. for (i=0; i<(ULONG)KeNumberProcessors; i++) {
  1799. while (Entry = InterlockedPopEntrySList(&LoggerContext->ProcessorBuffers[i])){
  1800. Buffer = CONTAINING_RECORD(Entry,
  1801. WMI_BUFFER_HEADER,
  1802. SlistEntry);
  1803. WmipPushDirtyBuffer ( LoggerContext, Buffer );
  1804. }
  1805. #ifdef NTPERF
  1806. //
  1807. // Flush all buffer logging from user mode
  1808. //
  1809. if (PERFINFO_IS_LOGGING_TO_PERFMEM()) {
  1810. if (LoggerContext->LoggerId == WmipKernelLogger) {
  1811. PPERFINFO_TRACEBUF_HEADER pPerfBufHdr;
  1812. pPerfBufHdr = PerfBufHdr();
  1813. Buffer = pPerfBufHdr->UserModePerCpuBuffer[i];
  1814. if (Buffer) {
  1815. WmipPushDirtyBuffer ( LoggerContext, Buffer );
  1816. pPerfBufHdr->UserModePerCpuBuffer[i] = NULL;
  1817. }
  1818. }
  1819. }
  1820. #endif //NTPERF
  1821. }
  1822. }
  1823. if (ExQueryDepthSList(&LoggerContext->FlushList) == 0) {
  1824. //
  1825. // nothing to write, return;
  1826. //
  1827. return Status;
  1828. }
  1829. //
  1830. // First check if the file need to be created.
  1831. //
  1832. LoggerMode = LoggerContext->LoggerMode;
  1833. if (LoggerContext->LogFileHandle == NULL) {
  1834. if ((LoggerMode & EVENT_TRACE_DELAY_OPEN_FILE_MODE) ||
  1835. (LoggerMode & EVENT_TRACE_ADD_HEADER_MODE)) {
  1836. UNICODE_STRING FileName;
  1837. if (!WmipFileSystemReady) {
  1838. //
  1839. // FileSystem is not ready yet, so return for now.
  1840. //
  1841. return Status;
  1842. }
  1843. if (LoggerMode & EVENT_TRACE_FILE_MODE_NEWFILE) {
  1844. if (LoggerContext->LogFilePattern.Buffer == NULL) {
  1845. return STATUS_INVALID_PARAMETER;
  1846. }
  1847. Status = WmipGenerateFileName(
  1848. &LoggerContext->LogFilePattern,
  1849. (PLONG) &LoggerContext->FileCounter,
  1850. &FileName);
  1851. if (!NT_SUCCESS(Status)) {
  1852. return Status;
  1853. }
  1854. if (LoggerContext->LogFileName.Buffer != NULL) {
  1855. RtlFreeUnicodeString(&LoggerContext->LogFileName);
  1856. }
  1857. LoggerContext->LogFileName = FileName;
  1858. }
  1859. if (LoggerContext->LogFileName.Buffer != NULL) {
  1860. ULONG Append = LoggerMode & EVENT_TRACE_FILE_MODE_APPEND;
  1861. Status = WmipDelayCreate(&LoggerContext->LogFileHandle,
  1862. &LoggerContext->LogFileName,
  1863. Append);
  1864. if (NT_SUCCESS(Status)) {
  1865. //
  1866. // Now the file has been created, add the log file header
  1867. //
  1868. LoggerContext->LoggerMode &= ~EVENT_TRACE_DELAY_OPEN_FILE_MODE;
  1869. if (!Append) {
  1870. Status = WmipAddLogHeader(LoggerContext, NULL);
  1871. }
  1872. WmipSendNotification(LoggerContext,
  1873. STATUS_MEDIA_CHANGED, STATUS_SEVERITY_INFORMATIONAL);
  1874. }
  1875. else {
  1876. // DelayCreate failed.
  1877. TraceDebug((2, "WmipFlushActiveBuffers: DelayCreate Failed with status=%X\n", Status));
  1878. LoggerContext->LoggerStatus = Status;
  1879. return Status;
  1880. }
  1881. }
  1882. if ((LoggerContext->CollectionOn) &&
  1883. !(LoggerContext->LoggerMode & EVENT_TRACE_REAL_TIME_MODE)) {
  1884. return Status;
  1885. }
  1886. } else {
  1887. // If something bad happens to the file (like drive dismounted),
  1888. // LoggerContext->LogFileHandle can be NULL due to the previous call to
  1889. // this routine (WmipFlushActiveBuffers). For that case, we just need
  1890. // to continue.
  1891. if (!(LoggerContext->LoggerMode & EVENT_TRACE_REAL_TIME_MODE)) {
  1892. NTSTATUS LoggerStatus = LoggerContext->LoggerStatus;
  1893. TraceDebug((2, "WmipFlushActiveBuffers: LogFileHandle NULL\n"));
  1894. if (NT_SUCCESS(LoggerStatus)) {
  1895. return STATUS_INVALID_PARAMETER;
  1896. }
  1897. else {
  1898. return LoggerStatus;
  1899. }
  1900. }
  1901. }
  1902. }
  1903. //
  1904. // Write all buffers into disk
  1905. //
  1906. while (Entry = InterlockedPopEntrySList(&LoggerContext->FlushList)) {
  1907. Buffer = CONTAINING_RECORD(Entry,
  1908. WMI_BUFFER_HEADER,
  1909. SlistEntry);
  1910. if (!(LoggerContext->LoggerMode & EVENT_TRACE_BUFFERING_MODE)) {
  1911. //
  1912. // When there is a bursty logging, we can be stuck in this loop.
  1913. // Check if we need to allocate more buffers
  1914. //
  1915. // Only do buffer adjustment if we are not in buffering mode
  1916. //
  1917. WmipAdjustFreeBuffers(LoggerContext);
  1918. }
  1919. Status = WmipFlushBuffer(LoggerContext, Buffer);
  1920. InterlockedPushEntrySList(&LoggerContext->FreeList,
  1921. (PSINGLE_LIST_ENTRY) &Buffer->SlistEntry);
  1922. InterlockedIncrement((PLONG) &LoggerContext->BuffersAvailable);
  1923. InterlockedDecrement((PLONG) &LoggerContext->BuffersDirty);
  1924. TraceDebug((2, "WmipFlushActiveBuffers: %2d, %p, Free: %d, InUse: %d, %Dirty: %d, Total: %d\n",
  1925. LoggerContext->LoggerId,
  1926. Buffer,
  1927. LoggerContext->BuffersAvailable,
  1928. LoggerContext->BuffersInUse,
  1929. LoggerContext->BuffersDirty,
  1930. LoggerContext->NumberOfBuffers));
  1931. if ((Status == STATUS_LOG_FILE_FULL) ||
  1932. (Status == STATUS_DISK_FULL) ||
  1933. (Status == STATUS_NO_DATA_DETECTED) ||
  1934. (Status == STATUS_SEVERITY_WARNING)) {
  1935. TraceDebug((1,
  1936. "WmipFlushActiveBuffers: Buffer flushed with status=%X\n",
  1937. Status));
  1938. if ((Status == STATUS_LOG_FILE_FULL) ||
  1939. (Status == STATUS_DISK_FULL)) {
  1940. ErrorCount ++;
  1941. } else {
  1942. ErrorCount = 0; // reset to zero otherwise
  1943. }
  1944. if (ErrorCount <= WmiWriteFailureLimit) {
  1945. Status = STATUS_SUCCESS; // Let tracing continue
  1946. continue; // for now. Should raise WMI event
  1947. }
  1948. }
  1949. if (!NT_SUCCESS(Status)) {
  1950. TraceDebug((1,
  1951. "WmipLogger: Flush failed, status=%X LoggerContext=%X\n",
  1952. Status, LoggerContext));
  1953. if (LoggerContext->LogFileHandle != NULL) {
  1954. Status = ZwClose(LoggerContext->LogFileHandle);
  1955. TraceDebug((1,
  1956. "WmipLogger: Close logfile with status=%X\n", Status));
  1957. }
  1958. LoggerContext->LogFileHandle = NULL;
  1959. WmipSendNotification(LoggerContext,
  1960. Status, (Status & 0xC0000000) >> 30);
  1961. return Status;
  1962. }
  1963. Buffer->State.Flush = 0;
  1964. Buffer->State.Free = 1;
  1965. }
  1966. #else
  1967. if (FlushAll) {
  1968. for (i=0; i<(ULONG)KeNumberProcessors; i++) {
  1969. Buffer = LoggerContext->ProcessorBuffers[i];
  1970. if (Buffer == NULL)
  1971. continue;
  1972. if (Buffer->CurrentOffset == sizeof(WMI_BUFFER_HEADER)) {
  1973. Buffer->Flags = BUFFER_STATE_UNUSED;
  1974. TraceDebug((3, "Ignoring buffer %X proc %d\n", Buffer, i));
  1975. }
  1976. if (Buffer->Flags != BUFFER_STATE_UNUSED) {
  1977. Buffer->Flags = BUFFER_STATE_FULL;
  1978. TraceDebug((3, "Mark buf %X proc %d offset %d\n",
  1979. Buffer, i, Buffer->CurrentOffset));
  1980. if (Buffer->ReferenceCount == 0) {
  1981. Buffer = WmipSwitchBuffer(LoggerContext, Buffer, i);
  1982. FlushCount++;
  1983. }
  1984. }
  1985. else
  1986. Buffer->Flags = BUFFER_STATE_DIRTY;
  1987. }
  1988. }
  1989. else
  1990. FlushCount = 1;
  1991. for (i=0; i<FlushCount; i++) {
  1992. TraceDebug((3, "WmipLogger: FlushCount is %d\n", FlushCount));
  1993. LoggerContext->TransitionBuffer = LoggerContext->FlushList.Flink;
  1994. pEntry = ExInterlockedRemoveHeadList(
  1995. &LoggerContext->FlushList,
  1996. &LoggerContext->BufferSpinLock);
  1997. if (pEntry == NULL) { // should not happen normally
  1998. TraceDebug((1, "WmipLogger: pEntry is NULL!!\n"));
  1999. continue;
  2000. }
  2001. Buffer = CONTAINING_RECORD( pEntry, WMI_BUFFER_HEADER, Entry);
  2002. #if DBG
  2003. if (WmipTraceDebugLevel >= 3) {
  2004. DbgPrintEx(DPFLTR_WMILIB_ID,
  2005. DPFLTR_INFO_LEVEL,
  2006. "WmipLogger: Removed buffer %X from flushlist\n",
  2007. Buffer);
  2008. DbgPrintEx(DPFLTR_WMILIB_ID,
  2009. DPFLTR_INFO_LEVEL,
  2010. "WmipLogger: %X %d %d %d\n", Buffer->ClientContext,
  2011. Buffer->CurrentOffset,
  2012. Buffer->SavedOffset,
  2013. Buffer->ReferenceCount);
  2014. }
  2015. #endif
  2016. if (Buffer->Flags == BUFFER_STATE_UNUSED) {
  2017. Buffer->Flags = BUFFER_STATE_DIRTY;
  2018. }
  2019. Status = WmipFlushBuffer(LoggerContext, Buffer);
  2020. if (LoggerContext->BufferAgeLimit.QuadPart == 0) {
  2021. ExInterlockedInsertTailList(
  2022. &LoggerContext->FreeList,
  2023. &Buffer->Entry,
  2024. &LoggerContext->BufferSpinLock);
  2025. }
  2026. else {
  2027. ExInterlockedInsertHeadList(
  2028. &LoggerContext->FreeList,
  2029. &Buffer->Entry,
  2030. &LoggerContext->BufferSpinLock);
  2031. }
  2032. if ((Status == STATUS_LOG_FILE_FULL) ||
  2033. (Status == STATUS_DISK_FULL) ||
  2034. (Status == STATUS_NO_DATA_DETECTED) ||
  2035. (Status == STATUS_SEVERITY_WARNING)) {
  2036. TraceDebug((1,
  2037. "WmipFlushActiveBuffers: Buffer flushed with status=%X\n",
  2038. Status));
  2039. if ((Status == STATUS_LOG_FILE_FULL) ||
  2040. (Status == STATUS_DISK_FULL))
  2041. {
  2042. ErrorCount ++;
  2043. }
  2044. else ErrorCount = 0; // reset to zero otherwise
  2045. if (ErrorCount <= WmiWriteFailureLimit) {
  2046. Status = STATUS_SUCCESS; // let tracing continue
  2047. continue; // for now. Should raise WMI event
  2048. }
  2049. }
  2050. if (!NT_SUCCESS(Status)) {
  2051. TraceDebug((1,
  2052. "WmipFlushActiveBuffers: Flush failed, status=%X LoggerContext=%X\n",
  2053. Status, LoggerContext));
  2054. if (LoggerContext->LogFileHandle != NULL) {
  2055. Status = ZwClose(LoggerContext->LogFileHandle);
  2056. TraceDebug((1,
  2057. "WmipFlushActiveBuffers: Close logfile with status=%X\n",
  2058. Status));
  2059. }
  2060. LoggerContext->LogFileHandle = NULL;
  2061. WmipSendNotification(LoggerContext,
  2062. Status, (Status & 0xC0000000) >> 30);
  2063. return Status;
  2064. }
  2065. }
  2066. #endif //WMI_NON_BLOCKING
  2067. return Status;
  2068. }
  2069. NTSTATUS
  2070. WmipGenerateFileName(
  2071. IN PUNICODE_STRING FilePattern,
  2072. IN OUT PLONG FileCounter,
  2073. OUT PUNICODE_STRING FileName
  2074. )
  2075. {
  2076. LONG FileCount, Size;
  2077. PWCHAR Buffer = NULL;
  2078. PAGED_CODE();
  2079. if (FilePattern->Buffer == NULL)
  2080. return STATUS_INVALID_PARAMETER_MIX;
  2081. FileCount = InterlockedIncrement(FileCounter);
  2082. Size = FilePattern->MaximumLength + 64; // 32 digits: plenty for ULONG
  2083. Buffer = ExAllocatePoolWithTag(PagedPool,Size,TRACEPOOLTAG);
  2084. if (Buffer == NULL) {
  2085. return STATUS_NO_MEMORY;
  2086. }
  2087. swprintf(Buffer, FilePattern->Buffer, FileCount);
  2088. if (RtlEqualMemory(FilePattern->Buffer, Buffer, FilePattern->Length)) {
  2089. ExFreePool(Buffer);
  2090. return STATUS_INVALID_PARAMETER_MIX;
  2091. }
  2092. RtlInitUnicodeString(FileName, Buffer);
  2093. return STATUS_SUCCESS;
  2094. }
  2095. NTSTATUS
  2096. WmipPrepareHeader(
  2097. IN PWMI_LOGGER_CONTEXT LoggerContext,
  2098. IN OUT PWMI_BUFFER_HEADER Buffer
  2099. )
  2100. {
  2101. ULONG BufferSize;
  2102. PAGED_CODE();
  2103. BufferSize = LoggerContext->BufferSize;
  2104. if (Buffer->SavedOffset > 0) {
  2105. Buffer->Offset = Buffer->SavedOffset;
  2106. }
  2107. else {
  2108. Buffer->Offset = Buffer->CurrentOffset;
  2109. }
  2110. ASSERT(Buffer->Offset >= sizeof(WMI_BUFFER_HEADER));
  2111. if (Buffer->Offset == sizeof(WMI_BUFFER_HEADER)) { // empty buffer
  2112. return STATUS_NO_DATA_DETECTED;
  2113. }
  2114. //
  2115. // Fill the rest of buffer with 0XFF
  2116. //
  2117. if ( Buffer->Offset < BufferSize ) {
  2118. RtlFillMemory(
  2119. (char *) Buffer + Buffer->Offset,
  2120. BufferSize - Buffer->Offset,
  2121. 0XFF);
  2122. }
  2123. Buffer->Wnode.BufferSize = BufferSize;
  2124. Buffer->ClientContext.LoggerId = (USHORT) LoggerContext->LoggerId;
  2125. if (Buffer->ClientContext.LoggerId == 0)
  2126. Buffer->ClientContext.LoggerId = (USHORT) KERNEL_LOGGER_ID;
  2127. Buffer->ClientContext.Alignment = (UCHAR) WmiTraceAlignment;
  2128. Buffer->Wnode.Guid = LoggerContext->InstanceGuid;
  2129. Buffer->Wnode.Flags = WNODE_FLAG_TRACED_GUID;
  2130. Buffer->Wnode.ProviderId = LoggerContext->BuffersWritten+1;
  2131. KeQuerySystemTime(&Buffer->Wnode.TimeStamp);
  2132. return STATUS_SUCCESS;
  2133. }
  2134. NTKERNELAPI
  2135. VOID
  2136. WmiBootPhase1(
  2137. )
  2138. /*++
  2139. Routine Description:
  2140. NtInitializeRegistry to inform WMI that autochk is performed
  2141. and it is OK now to write to disk.
  2142. Arguments:
  2143. None
  2144. Return Value:
  2145. None
  2146. --*/
  2147. {
  2148. PAGED_CODE();
  2149. WmipFileSystemReady = TRUE;
  2150. }
  2151. NTSTATUS
  2152. WmipFinalizeHeader(
  2153. IN HANDLE FileHandle,
  2154. IN PWMI_LOGGER_CONTEXT LoggerContext
  2155. )
  2156. {
  2157. LARGE_INTEGER ByteOffset;
  2158. NTSTATUS Status;
  2159. PTRACE_LOGFILE_HEADER FileHeader;
  2160. IO_STATUS_BLOCK IoStatus;
  2161. CHAR Buffer[PAGE_SIZE]; // Assumes Headers less than PAGE_SIZE
  2162. PAGED_CODE();
  2163. ByteOffset.QuadPart = 0;
  2164. Status = ZwReadFile(
  2165. FileHandle,
  2166. NULL,
  2167. NULL,
  2168. NULL,
  2169. & IoStatus,
  2170. &Buffer[0],
  2171. PAGE_SIZE,
  2172. & ByteOffset,
  2173. NULL);
  2174. if (!NT_SUCCESS(Status)) {
  2175. return Status;
  2176. }
  2177. FileHeader = (PTRACE_LOGFILE_HEADER)
  2178. &Buffer[sizeof(WMI_BUFFER_HEADER) + sizeof(SYSTEM_TRACE_HEADER)];
  2179. FileHeader->BuffersWritten = LoggerContext->BuffersWritten;
  2180. KeQuerySystemTime(&FileHeader->EndTime);
  2181. FileHeader->BuffersLost = LoggerContext->LogBuffersLost;
  2182. FileHeader->EventsLost = LoggerContext->EventsLost;
  2183. Status = ZwWriteFile(
  2184. FileHandle,
  2185. NULL,
  2186. NULL,
  2187. NULL,
  2188. &IoStatus,
  2189. &Buffer[0],
  2190. PAGE_SIZE,
  2191. &ByteOffset,
  2192. NULL);
  2193. return Status;
  2194. }
  2195. #if DBG
  2196. #define DEBUG_BUFFER_LENGTH 1024
  2197. UCHAR TraceDebugBuffer[DEBUG_BUFFER_LENGTH];
  2198. VOID
  2199. TraceDebugPrint(
  2200. ULONG DebugPrintLevel,
  2201. PCCHAR DebugMessage,
  2202. ...
  2203. )
  2204. /*++
  2205. Routine Description:
  2206. Debug print for all DiskPerf
  2207. Arguments:
  2208. Debug print level between 0 and 3, with 3 being the most verbose.
  2209. Return Value:
  2210. None
  2211. --*/
  2212. {
  2213. LARGE_INTEGER Clock;
  2214. ULONG Tid;
  2215. va_list ap;
  2216. va_start(ap, DebugMessage);
  2217. if (WmipTraceDebugLevel >= DebugPrintLevel) {
  2218. _vsnprintf(TraceDebugBuffer, DEBUG_BUFFER_LENGTH, DebugMessage, ap);
  2219. Clock = KeQueryPerformanceCounter(NULL);
  2220. Tid = HandleToUlong(PsGetCurrentThreadId());
  2221. DbgPrintEx(DPFLTR_WMILIB_ID, DPFLTR_INFO_LEVEL,
  2222. "%u(%u): %s", Clock.LowPart, Tid, TraceDebugBuffer);
  2223. }
  2224. va_end(ap);
  2225. }
  2226. #endif
  2227. VOID
  2228. FASTCALL
  2229. WmipResetBufferHeader (
  2230. PWMI_LOGGER_CONTEXT LoggerContext,
  2231. PWMI_BUFFER_HEADER Buffer
  2232. )
  2233. /*++
  2234. Routine Description:
  2235. This is a function which initializes a few buffer header values
  2236. that are used by both WmipGetFreeBuffer and WmipPopFreeContextSwapBuffer
  2237. Note that this function increments a few logger context reference counts
  2238. Calling Functions:
  2239. - WmipGetFreeBuffer
  2240. - WmipPopFreeContextSwapBuffer
  2241. Arguments:
  2242. LoggerContext - Logger context from where we have acquired a free buffer
  2243. Buffer - Pointer to a buffer header that we wish to reset
  2244. Return Value:
  2245. None
  2246. --*/
  2247. {
  2248. Buffer->Flags = BUFFER_STATE_DIRTY;
  2249. Buffer->SavedOffset = 0;
  2250. Buffer->CurrentOffset = sizeof(WMI_BUFFER_HEADER);
  2251. Buffer->Wnode.ClientContext = 0;
  2252. Buffer->LoggerContext = LoggerContext;
  2253. Buffer->State.Free = 0;
  2254. Buffer->State.InUse = 1;
  2255. }
  2256. VOID
  2257. FASTCALL
  2258. WmipPushDirtyBuffer (
  2259. PWMI_LOGGER_CONTEXT LoggerContext,
  2260. PWMI_BUFFER_HEADER Buffer
  2261. )
  2262. /*++
  2263. Routine Description:
  2264. This is a function which prepares a buffer's header and places it on a
  2265. logger's flush list.
  2266. Note that this function manages a few logger context reference counts
  2267. Calling Functions:
  2268. - WmipFlushActiveBuffers
  2269. - WmipPushDirtyContextSwapBuffer
  2270. Arguments:
  2271. LoggerContext - Logger context from which we originally acquired a buffer
  2272. Buffer - Pointer to a buffer that we wish to flush
  2273. Return Value:
  2274. None
  2275. --*/
  2276. {
  2277. //
  2278. // Set the buffer flags to "flush" state and save the offset
  2279. //
  2280. Buffer->State.InUse = 0;
  2281. Buffer->State.Flush = 1;
  2282. Buffer->SavedOffset = Buffer->CurrentOffset;
  2283. //
  2284. // Push the buffer onto the flushlist. This could only
  2285. // fail if the Wmi kernel logger shut down without notifying us.
  2286. // If this happens, there is nothing we can do about it anyway.
  2287. // If Wmi is well behaved, this will never fail.
  2288. //
  2289. InterlockedPushEntrySList(
  2290. &LoggerContext->FlushList,
  2291. (PSINGLE_LIST_ENTRY) &Buffer->SlistEntry);
  2292. //
  2293. // Maintain some reference counts
  2294. //
  2295. InterlockedDecrement((PLONG) &LoggerContext->BuffersInUse);
  2296. InterlockedIncrement((PLONG) &LoggerContext->BuffersDirty);
  2297. TraceDebug((2, "Flush Dirty Buffer: %p, Free: %d, InUse: %d, %Dirty: %d, Total: %d, (Thread: %p)\n",
  2298. Buffer,
  2299. LoggerContext->BuffersAvailable,
  2300. LoggerContext->BuffersInUse,
  2301. LoggerContext->BuffersDirty,
  2302. LoggerContext->NumberOfBuffers,
  2303. PsGetCurrentThread()));
  2304. }
  2305. PWMI_BUFFER_HEADER
  2306. FASTCALL
  2307. WmipPopFreeContextSwapBuffer
  2308. ( UCHAR CurrentProcessor
  2309. )
  2310. /*++
  2311. Routine Description:
  2312. Attempts to remove a buffer from the kernel logger free buffer list.
  2313. We confirm that logging is on, that buffer switching is
  2314. not in progress and that the buffers available count is greater than
  2315. zero. If we are unable to acquire a buffer, we increment LostEvents
  2316. and return. Otherwise, we initialize the buffer and pass it back.
  2317. Assumptions:
  2318. - This routine will only be called from WmiTraceContextSwap
  2319. - Inherits all assumptions listed in WmiTraceContextSwap
  2320. Calling Functions:
  2321. - WmiTraceContextSwap
  2322. Arguments:
  2323. CurrentProcessor- The current processor number (0 to (NumProc - 1))
  2324. Return Value:
  2325. Pointer to the newly acquired buffer. NULL on failure.
  2326. --*/
  2327. {
  2328. PWMI_LOGGER_CONTEXT LoggerContext;
  2329. PWMI_BUFFER_HEADER Buffer;
  2330. LoggerContext = WmipLoggerContext[WmipKernelLogger];
  2331. //
  2332. // Could only happen if for some reason the logger has not been initialized
  2333. // before we see the global context swap flag set. This should not happen.
  2334. //
  2335. if(! WmipIsValidLogger(LoggerContext) ) {
  2336. return NULL;
  2337. }
  2338. //
  2339. // "Switching" is a Wmi state available only while BUFFERING is
  2340. // enabled that occurs when the free buffer list is empty. During switching
  2341. // all the buffers in the flushlist are simply moved back to the free list.
  2342. // Normally if we found that the free list was empty we would perform the
  2343. // switch here, and if the switch was already occuring we would spin until
  2344. // it completed. Instead of introducing an indefinite spin, as well as a
  2345. // ton of interlocked pops and pushes, we opt to simply drop the event.
  2346. //
  2347. if ( !(LoggerContext->SwitchingInProgress)
  2348. && LoggerContext->CollectionOn
  2349. && LoggerContext->BuffersAvailable > 0) {
  2350. //
  2351. // Attempt to get a free buffer from the Kernel Logger FreeList
  2352. //
  2353. Buffer = (PWMI_BUFFER_HEADER)InterlockedPopEntrySList(
  2354. &LoggerContext->FreeList);
  2355. //
  2356. // This second check is necessary because
  2357. // LoggerContext->BuffersAvailable may have changed.
  2358. //
  2359. if(Buffer != NULL) {
  2360. Buffer = CONTAINING_RECORD (Buffer, WMI_BUFFER_HEADER, SlistEntry);
  2361. //
  2362. // Reset the buffer header
  2363. //
  2364. WmipResetBufferHeader( LoggerContext, Buffer );
  2365. //
  2366. // Maintain some Wmi logger context buffer counts
  2367. //
  2368. InterlockedDecrement((PLONG) &LoggerContext->BuffersAvailable);
  2369. InterlockedIncrement((PLONG) &LoggerContext->BuffersInUse);
  2370. Buffer->ClientContext.ProcessorNumber = CurrentProcessor;
  2371. Buffer->Offset = LoggerContext->BufferSize;
  2372. ASSERT( Buffer->Offset % WMI_CTXSWAP_EVENTSIZE_ALIGNMENT == 0);
  2373. // Return our buffer
  2374. return Buffer;
  2375. }
  2376. }
  2377. LoggerContext->EventsLost++;
  2378. return NULL;
  2379. }
  2380. VOID
  2381. FASTCALL
  2382. WmipPushDirtyContextSwapBuffer (
  2383. UCHAR CurrentProcessor,
  2384. PWMI_BUFFER_HEADER Buffer
  2385. )
  2386. /*++
  2387. Routine Description:
  2388. Prepares the current buffer to be placed on the Wmi flushlist
  2389. and then pushes it onto the flushlist. Maintains some Wmi
  2390. Logger reference counts.
  2391. Assumptions:
  2392. - The value of WmipContextSwapProcessorBuffers[CurrentProcessor]
  2393. is not equal to NULL, and the LoggerContext reference count
  2394. is greater than zero.
  2395. - This routine will only be called when the KernelLogger struct
  2396. has been fully initialized.
  2397. - The Wmi kernel WMI_LOGGER_CONTEXT object, as well as all buffers
  2398. it allocates are allocated from nonpaged pool. All Wmi globals
  2399. that we access are also in nonpaged memory
  2400. - This code has been locked into paged memory when the logger started
  2401. - The logger context reference count has been "Locked" via the
  2402. InterlockedIncrement() operation in WmipReferenceLogger(WmipLoggerContext)
  2403. Calling Functions:
  2404. - WmiTraceContextSwap
  2405. - WmipStopContextSwapTrace
  2406. Arguments:
  2407. CurrentProcessor Processor we are currently running on
  2408. Buffer Buffer to be flushed
  2409. Return Value:
  2410. None
  2411. --*/
  2412. {
  2413. PWMI_LOGGER_CONTEXT LoggerContext;
  2414. //
  2415. // Grab the kernel logger context
  2416. // This should never be NULL as long as we keep the KernelLogger
  2417. // reference count above zero via "WmipReferenceLogger"
  2418. //
  2419. LoggerContext = WmipLoggerContext[WmipKernelLogger];
  2420. if( ! WmipIsValidLogger(LoggerContext) ) {
  2421. return;
  2422. }
  2423. WmipPushDirtyBuffer( LoggerContext, Buffer );
  2424. //
  2425. // Increment the ReleaseQueue count here. We can't signal the
  2426. // logger semaphore here while holding the context swap lock.
  2427. //
  2428. InterlockedIncrement(&LoggerContext->ReleaseQueue);
  2429. return;
  2430. }
  2431. #ifdef NTPERF
  2432. NTSTATUS
  2433. WmipSwitchPerfmemBuffer(
  2434. IN OUT PWMI_SWITCH_PERFMEM_BUFFER_INFORMATION SwitchBufferInfo
  2435. )
  2436. /*++
  2437. Routine Description:
  2438. This routine is used to switch buffers when
  2439. Assumptions:
  2440. -
  2441. Arguments:
  2442. The WMI_SWITCH_PERFMEM_BUFFER_INFORMATION structure which contains
  2443. - The buffer pointer the user mode code currently has.
  2444. - The hint to which CPU for this thread
  2445. Return Value:
  2446. Status
  2447. --*/
  2448. {
  2449. PWMI_BUFFER_HEADER CurrentBuffer, NewBuffer, OldBuffer;
  2450. PSINGLE_LIST_ENTRY Entry;
  2451. PWMI_LOGGER_CONTEXT LoggerContext = WmipLoggerContext[WmipKernelLogger];
  2452. PPERFINFO_TRACEBUF_HEADER pPerfBufHdr;
  2453. //
  2454. // Get a new buffer from Free List
  2455. //
  2456. //
  2457. // First lock the logger context.
  2458. //
  2459. WmipReferenceLogger(WmipKernelLogger);
  2460. if (!WmipIsValidLogger(LoggerContext)) {
  2461. NewBuffer = NULL;
  2462. } else if (!LoggerContext->CollectionOn) {
  2463. NewBuffer = NULL;
  2464. } else if (!PERFINFO_IS_LOGGING_TO_PERFMEM()) {
  2465. NewBuffer = NULL;
  2466. } else if ( SwitchBufferInfo->ProcessorId > MAXIMUM_PROCESSORS) {
  2467. NewBuffer = NULL;
  2468. } else {
  2469. //
  2470. // Allocate a buffer.
  2471. //
  2472. pPerfBufHdr = PerfBufHdr();
  2473. NewBuffer = WmipGetFreeBuffer (LoggerContext);
  2474. if (NewBuffer) {
  2475. OldBuffer = SwitchBufferInfo->Buffer;
  2476. CurrentBuffer = InterlockedCompareExchangePointer(
  2477. &pPerfBufHdr->UserModePerCpuBuffer[SwitchBufferInfo->ProcessorId],
  2478. NewBuffer,
  2479. OldBuffer);
  2480. if (OldBuffer != CurrentBuffer) {
  2481. //
  2482. // Someone has switched the buffer, use this one
  2483. // and push the new allocated buffer back to free list.
  2484. //
  2485. InterlockedPushEntrySList(&LoggerContext->FreeList,
  2486. (PSINGLE_LIST_ENTRY) &NewBuffer->SlistEntry);
  2487. InterlockedIncrement((PLONG) &LoggerContext->BuffersAvailable);
  2488. InterlockedDecrement((PLONG) &LoggerContext->BuffersInUse);
  2489. NewBuffer = CurrentBuffer;
  2490. } else if (OldBuffer != NULL) {
  2491. //
  2492. // Successfully switched the buffer, push the current buffer into
  2493. // flush list
  2494. //
  2495. WmipPushDirtyBuffer( LoggerContext, OldBuffer );
  2496. } else {
  2497. //
  2498. // Must be first time, initialize the buffer size
  2499. //
  2500. pPerfBufHdr->TraceBufferSize = LoggerContext->BufferSize;
  2501. }
  2502. }
  2503. }
  2504. SwitchBufferInfo->Buffer = NewBuffer;
  2505. //
  2506. // Dereference the logger context.
  2507. //
  2508. WmipDereferenceLogger(WmipKernelLogger);
  2509. return(STATUS_SUCCESS);
  2510. }
  2511. #endif // NTPERF