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.

863 lines
22 KiB

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. hooks.c
  5. Abstract:
  6. This module contains performance hooks.
  7. Author:
  8. Stephen Hsiao (shsiao) 01-Jan-2000
  9. Revision History:
  10. --*/
  11. #include "perfp.h"
  12. #ifdef ALLOC_PRAGMA
  13. #pragma alloc_text(PAGE, PerfInfoFlushProfileCache)
  14. #pragma alloc_text(PAGEWMI, PerfProfileInterrupt)
  15. #pragma alloc_text(PAGEWMI, PerfInfoLogBytesAndUnicodeString)
  16. #pragma alloc_text(PAGEWMI, PerfInfoLogFileName)
  17. #pragma alloc_text(PAGEWMI, PerfInfoCalcHashValue)
  18. #pragma alloc_text(PAGEWMI, PerfInfoAddToFileHash)
  19. #pragma alloc_text(PAGEWMI, PerfInfoFileNameRunDown)
  20. #pragma alloc_text(PAGEWMI, PerfInfoProcessRunDown)
  21. #pragma alloc_text(PAGEWMI, PerfInfoSysModuleRunDown)
  22. #endif //ALLOC_PRAGMA
  23. VOID
  24. PerfInfoFlushProfileCache(
  25. VOID
  26. )
  27. /*++
  28. Routine description:
  29. Flushes the profile cache to the log buffer. To make sure it get's valid data
  30. we read the 2 seperate version numbers (1 before and 1 after) to check if it's
  31. been changed. If so, we just read again. If that fails often, then we disable
  32. the cache. Once the cache is read, we clear it. This may cause samples to be
  33. lost but that's ok as this is statistical and it won't matter.
  34. Arguments:
  35. CheckVersion - If FALSE, the version is not checked. This used when the profile
  36. interrupt code flushes the cache.
  37. Return Value:
  38. None
  39. --*/
  40. {
  41. ULONG PreviousInProgress;
  42. if ((PerfProfileCache.Entries == 0) || (PerfInfoSampledProfileCaching == FALSE)) {
  43. return;
  44. }
  45. //
  46. // Signal the interrupt not to mess with the cache
  47. //
  48. PreviousInProgress = InterlockedIncrement(&PerfInfoSampledProfileFlushInProgress);
  49. if (PreviousInProgress != 1) {
  50. //
  51. // A flush is already in progress so just return.
  52. //
  53. InterlockedDecrement(&PerfInfoSampledProfileFlushInProgress);
  54. return;
  55. }
  56. //
  57. // Log the portion of the cache that has valid data.
  58. //
  59. PerfInfoLogBytes(PERFINFO_LOG_TYPE_SAMPLED_PROFILE_CACHE,
  60. &PerfProfileCache,
  61. FIELD_OFFSET(PERFINFO_SAMPLED_PROFILE_CACHE, Sample) +
  62. (PerfProfileCache.Entries *
  63. sizeof(PERFINFO_SAMPLED_PROFILE_INFORMATION))
  64. );
  65. //
  66. // Clear the cache for the next set of entries.
  67. //
  68. PerfProfileCache.Entries = 0;
  69. //
  70. // Let the interrupt fill the cache again.
  71. //
  72. InterlockedDecrement(&PerfInfoSampledProfileFlushInProgress);
  73. }
  74. VOID
  75. FASTCALL
  76. PerfProfileInterrupt(
  77. IN KPROFILE_SOURCE Source,
  78. IN PVOID InstructionPointer
  79. )
  80. /*++
  81. Routine description:
  82. Implements instruction profiling. If the source is not the one we're sampling on,
  83. we return. If caching is off, we write any samples coming from the immediately to
  84. the log. If caching is on, wrap the cache update with writes to the two versions so
  85. that the flush routine can know if it has a valid buffer.
  86. Arguments:
  87. Source - Type of profile interrupt
  88. InstructionPointer - IP at the time of the interrupt
  89. Return Value:
  90. None
  91. --*/
  92. {
  93. ULONG i;
  94. PERFINFO_SAMPLED_PROFILE_INFORMATION SampleData;
  95. #ifdef _X86_
  96. ULONG_PTR TwiddledIP;
  97. #endif // _X86_
  98. ULONG ThreadId;
  99. if (!PERFINFO_IS_GROUP_ON(PERF_PROFILE) &&
  100. (Source != PerfInfoProfileSourceActive)
  101. ) {
  102. //
  103. // We don't handle multple sources.
  104. //
  105. return;
  106. }
  107. ThreadId = HandleToUlong(PsGetCurrentThread()->Cid.UniqueThread);
  108. if (!PerfInfoSampledProfileCaching ||
  109. PerfInfoSampledProfileFlushInProgress != 0) {
  110. //
  111. // No caching. Log and return.
  112. //
  113. SampleData.ThreadId = ThreadId;
  114. SampleData.InstructionPointer = InstructionPointer;
  115. SampleData.Count = 1;
  116. PerfInfoLogBytes(PERFINFO_LOG_TYPE_SAMPLED_PROFILE,
  117. &SampleData,
  118. sizeof(PERFINFO_SAMPLED_PROFILE_INFORMATION)
  119. );
  120. return;
  121. }
  122. #ifdef _X86_
  123. //
  124. // Clear the low two bits to have more cache hits for loops. Don't waste
  125. // cycles on other architectures.
  126. //
  127. TwiddledIP = (ULONG_PTR)InstructionPointer & ~3;
  128. #endif // _X86_
  129. //
  130. // Initial walk thru Instruction Pointer Cache. Bump Count if address is in cache.
  131. //
  132. for (i = 0; i < PerfProfileCache.Entries ; i++) {
  133. if ((PerfProfileCache.Sample[i].ThreadId == ThreadId) &&
  134. #ifdef _X86_
  135. (((ULONG_PTR)PerfProfileCache.Sample[i].InstructionPointer & ~3) == TwiddledIP)
  136. #else
  137. (PerfProfileCache.Sample[i].InstructionPointer == InstructionPointer)
  138. #endif // _X86_
  139. ) {
  140. //
  141. // If we find the instruction pointer in the cache, bump the count
  142. //
  143. PerfProfileCache.Sample[i].Count++;
  144. return;
  145. }
  146. }
  147. if (PerfProfileCache.Entries < PERFINFO_SAMPLED_PROFILE_CACHE_MAX) {
  148. //
  149. // If we find an empty spot in the cache, use it for this instruction pointer
  150. //
  151. PerfProfileCache.Sample[i].ThreadId = ThreadId;
  152. PerfProfileCache.Sample[i].InstructionPointer = InstructionPointer;
  153. PerfProfileCache.Sample[i].Count = 1;
  154. PerfProfileCache.Entries++;
  155. return;
  156. }
  157. //
  158. // Flush the cache
  159. //
  160. PerfInfoLogBytes(PERFINFO_LOG_TYPE_SAMPLED_PROFILE_CACHE,
  161. &PerfProfileCache,
  162. sizeof(PERFINFO_SAMPLED_PROFILE_CACHE)
  163. );
  164. PerfProfileCache.Sample[0].ThreadId = ThreadId;
  165. PerfProfileCache.Sample[0].InstructionPointer = InstructionPointer;
  166. PerfProfileCache.Sample[0].Count = 1;
  167. PerfProfileCache.Entries = 1;
  168. return;
  169. }
  170. VOID
  171. FASTCALL
  172. PerfInfoLogDpc(
  173. IN PVOID DpcRoutine,
  174. IN ULONGLONG InitialTime
  175. )
  176. /*++
  177. Routine description:
  178. This routine logs the exit of a DPC service routine
  179. Arguments:
  180. DPCRoutine - DPC service routine
  181. InitialTime - Timestamp before service routine was called. The timestamp in
  182. the event is used as the end time.
  183. --*/
  184. {
  185. PERFINFO_DPC_INFORMATION st;
  186. st.DpcRoutine = DpcRoutine;
  187. st.InitialTime = InitialTime;
  188. PerfInfoLogBytes(
  189. PERFINFO_LOG_TYPE_DPC,
  190. (PVOID) &st,
  191. sizeof(st));
  192. }
  193. VOID
  194. FASTCALL
  195. PerfInfoLogInterrupt(
  196. IN PVOID InServiceRoutine,
  197. IN ULONG RetVal,
  198. IN ULONGLONG InitialTime
  199. )
  200. /*++
  201. Routine Description:
  202. This callout routine is called from ntoskrnl.exe (ke\intsup.asm) to log an
  203. interrupt and how long it takes to complete.
  204. Arguments:
  205. InServiceRoutine Address of routine that serviced the interrupt.
  206. RetVal Value returned from InServiceRoutine.
  207. InitialTime Timestamp before ISR was called. The timestamp in
  208. the event is used as the end time.
  209. Return Value:
  210. None
  211. --*/
  212. {
  213. PERFINFO_INTERRUPT_INFORMATION EventInfo;
  214. EventInfo.ServiceRoutine = InServiceRoutine;
  215. EventInfo.ReturnValue = RetVal;
  216. EventInfo.InitialTime = InitialTime;
  217. PerfInfoLogBytes(PERFINFO_LOG_TYPE_INTERRUPT,
  218. &EventInfo,
  219. sizeof(EventInfo));
  220. return;
  221. }
  222. NTSTATUS
  223. PerfInfoLogBytesAndUnicodeString(
  224. USHORT HookId,
  225. PVOID SourceData,
  226. ULONG SourceByteCount,
  227. PUNICODE_STRING String
  228. )
  229. /*++
  230. Routine description:
  231. This routine logs data with UniCode string at the end of the hook.
  232. Arguments:
  233. HookId - Hook Id.
  234. SourceData - Pointer to the data to be copied
  235. SourceByteCount - Number of bytes to be copied.
  236. String - The string to be logged.
  237. Return Value:
  238. Status
  239. --*/
  240. {
  241. NTSTATUS Status;
  242. PERFINFO_HOOK_HANDLE Hook;
  243. ULONG ByteCount;
  244. ULONG StringBytes;
  245. if (String == NULL) {
  246. StringBytes = 0;
  247. } else {
  248. StringBytes = String->Length;
  249. }
  250. ByteCount = (SourceByteCount + StringBytes + sizeof(WCHAR));
  251. Status = PerfInfoReserveBytes(&Hook, HookId, ByteCount);
  252. if (NT_SUCCESS(Status))
  253. {
  254. const PVOID pvTemp = PERFINFO_HOOK_HANDLE_TO_DATA(Hook, PVOID);
  255. RtlCopyMemory(pvTemp, SourceData, SourceByteCount);
  256. if (StringBytes != 0) {
  257. RtlCopyMemory(PERFINFO_APPLY_OFFSET_GIVING_TYPE(pvTemp, SourceByteCount, PVOID),
  258. String->Buffer,
  259. StringBytes
  260. );
  261. }
  262. (PERFINFO_APPLY_OFFSET_GIVING_TYPE(pvTemp, SourceByteCount, PWCHAR))[StringBytes / sizeof(WCHAR)] = UNICODE_NULL;
  263. PERF_FINISH_HOOK(Hook);
  264. Status = STATUS_SUCCESS;
  265. }
  266. return Status;
  267. }
  268. NTSTATUS
  269. PerfInfoLogFileName(
  270. PVOID FileObject,
  271. PUNICODE_STRING SourceString
  272. )
  273. /*++
  274. Routine Description:
  275. This routine logs a FileObject pointer and FileName to the log. The pointer is used
  276. as hash key to map this name to other trace events.
  277. Arguments:
  278. FileObject - Pointer to the FileName member within the FILE_OBJECT
  279. structure. The FileName may not yet be initialized,
  280. so the actual data comes from the SourceString
  281. parameter.
  282. SourceString - Optional pointer to the source string.
  283. Return Value:
  284. STATUS_SUCCESS
  285. --*/
  286. {
  287. NTSTATUS Status = STATUS_SUCCESS;
  288. PERFINFO_FILENAME_INFORMATION FileInfo;
  289. if ((FileObject != NULL) &&
  290. (SourceString != NULL) &&
  291. (SourceString->Length != 0)) {
  292. FileInfo.HashKeyFileNamePointer = FileObject;
  293. Status = PerfInfoLogBytesAndUnicodeString(PERFINFO_LOG_TYPE_FILENAME_CREATE,
  294. &FileInfo,
  295. FIELD_OFFSET(PERFINFO_FILENAME_INFORMATION, FileName),
  296. SourceString);
  297. }
  298. return Status;
  299. }
  300. ULONG
  301. PerfInfoCalcHashValue(
  302. PVOID Key,
  303. ULONG Len
  304. )
  305. /*++
  306. Routine Description:
  307. Generic hash routine.
  308. Arguments:
  309. Key - Pointer to data to calculate a hash value for.
  310. Len - Number of bytes pointed to by key.
  311. Return Value:
  312. Hash value.
  313. --*/
  314. {
  315. char *cp = Key;
  316. ULONG i, ConvKey=0;
  317. for(i = 0; i < Len; i++)
  318. {
  319. ConvKey = 37 * ConvKey + (unsigned int) *cp;
  320. cp++;
  321. }
  322. #define RNDM_CONSTANT 314159269
  323. #define RNDM_PRIME 1000000007
  324. return (abs(RNDM_CONSTANT * ConvKey) % RNDM_PRIME);
  325. }
  326. BOOLEAN
  327. PerfInfoAddToFileHash(
  328. PPERFINFO_ENTRY_TABLE HashTable,
  329. PFILE_OBJECT ObjectPointer
  330. )
  331. /*++
  332. Routine Description:
  333. This routine add a FileObject into the specified
  334. hash table if it is not already there.
  335. Arguments:
  336. HashTable - pointer to a hash table to be used.
  337. ObjectPointer - This is used as a key to identify a mapping.
  338. Return Value:
  339. TRUE - If either the FileObject was in the table or we add it.
  340. FALSE - If the table is full.
  341. --*/
  342. {
  343. ULONG HashIndex;
  344. LONG i;
  345. BOOLEAN Result = FALSE;
  346. LONG TableSize = HashTable->NumberOfEntries;
  347. PVOID *Table;
  348. Table = HashTable->Table;
  349. //
  350. // Get the hashed index into the table where the entry ideally
  351. // should be at.
  352. //
  353. HashIndex = PerfInfoCalcHashValue((PVOID)&ObjectPointer,
  354. sizeof(ObjectPointer)) % TableSize;
  355. for (i = 0; i < TableSize; i++) {
  356. if(Table[HashIndex] == NULL) {
  357. //
  358. // Found a empty slot. Reference the object and insert
  359. // it into the table.
  360. //
  361. ObReferenceObject(ObjectPointer);
  362. Table[HashIndex] = ObjectPointer;
  363. Result = TRUE;
  364. break;
  365. } else if (Table[HashIndex] == ObjectPointer) {
  366. //
  367. // Found a slot. Reference the object and insert
  368. // it into the table.
  369. //
  370. Result = TRUE;
  371. break;
  372. }
  373. //
  374. // Try next slot.
  375. //
  376. HashIndex = (HashIndex + 1) % TableSize;
  377. }
  378. return Result;
  379. }
  380. NTSTATUS
  381. PerfInfoFileNameRunDown (
  382. )
  383. /*++
  384. Routine Description:
  385. This routine walks through multiple lists to collect the names of all files.
  386. It includes:
  387. 1. Handle table: for all file handles
  388. 2. Process Vad for all file objects mapped in VAD.
  389. 3. MmUnusedSegment List
  390. 4. CcDirtySharedCacheMapList & CcCleanSharedCacheMapList
  391. Arguments:
  392. None.
  393. Return Value:
  394. BUGBUG Need proper return/ error handling
  395. --*/
  396. {
  397. PEPROCESS Process;
  398. ULONG AllocateBytes;
  399. PFILE_OBJECT *FileObjects;
  400. PFILE_OBJECT *File;
  401. PERFINFO_ENTRY_TABLE HashTable;
  402. extern POBJECT_TYPE IoFileObjectType;
  403. LONG i;
  404. //
  405. // First create a tempory hash table to build the list of
  406. // files to walk through
  407. //
  408. AllocateBytes = PAGE_SIZE + sizeof(PVOID) * IoFileObjectType->TotalNumberOfObjects;
  409. //
  410. // Run up to page boundary
  411. //
  412. AllocateBytes = PERFINFO_ROUND_UP(AllocateBytes, PAGE_SIZE);
  413. HashTable.Table = ExAllocatePoolWithTag(NonPagedPool, AllocateBytes, PERFPOOLTAG);
  414. if (HashTable.Table == NULL) {
  415. return STATUS_INSUFFICIENT_RESOURCES;
  416. } else {
  417. //
  418. // Allocation Succeeded
  419. //
  420. HashTable.NumberOfEntries = AllocateBytes / sizeof(PVOID);
  421. RtlZeroMemory(HashTable.Table, AllocateBytes);
  422. }
  423. //
  424. // Walk through the Cc SharedCacheMapList
  425. //
  426. CcPerfFileRunDown(&HashTable);
  427. //
  428. // Now, walk through each process
  429. //
  430. for (Process = PsGetNextProcess (NULL);
  431. Process != NULL;
  432. Process = PsGetNextProcess (Process)) {
  433. //
  434. // First Walk the VAD tree
  435. //
  436. FileObjects = MmPerfVadTreeWalk(Process);
  437. if (FileObjects != NULL) {
  438. File = FileObjects;
  439. while (*File != NULL) {
  440. PerfInfoAddToFileHash(&HashTable, *File);
  441. ObDereferenceObject(*File);
  442. File += 1;
  443. }
  444. ExFreePool(FileObjects);
  445. }
  446. //
  447. // Next, walk the handle Table
  448. //
  449. ObPerfHandleTableWalk (Process, &HashTable);
  450. }
  451. //
  452. // Walk through the kernel handle table;
  453. //
  454. ObPerfHandleTableWalk(NULL, &HashTable);
  455. //
  456. // Walk through the MmUnusedSegmentList;
  457. //
  458. FileObjects = MmPerfUnusedSegmentsEnumerate();
  459. if (FileObjects != NULL) {
  460. File = FileObjects;
  461. while (*File != NULL) {
  462. PerfInfoAddToFileHash(&HashTable, *File);
  463. ObDereferenceObject(*File);
  464. File += 1;
  465. }
  466. ExFreePool(FileObjects);
  467. }
  468. //
  469. // Now we have walked through all list.
  470. // Log the filenames and dereference the objects.
  471. //
  472. for (i = 0; i < HashTable.NumberOfEntries; i++) {
  473. if (HashTable.Table[i]) {
  474. PFILE_OBJECT FileObject = HashTable.Table[i];
  475. PerfInfoLogFileName(FileObject, &FileObject->FileName);
  476. ObDereferenceObject(FileObject);
  477. }
  478. }
  479. //
  480. // Free the tables reserved.
  481. //
  482. ExFreePool(HashTable.Table);
  483. return STATUS_SUCCESS;
  484. }
  485. NTSTATUS
  486. PerfInfoProcessRunDown (
  487. )
  488. /*++
  489. Routine Description:
  490. This routine does the Process and thread rundown in the kernel mode.
  491. Since this routine is called only by global logger (i.e., trace from boot),
  492. no Sid info is collected.
  493. Arguments:
  494. None.
  495. Return Value:
  496. Status
  497. --*/
  498. {
  499. NTSTATUS Status;
  500. PSYSTEM_PROCESS_INFORMATION ProcessInfo;
  501. PSYSTEM_EXTENDED_THREAD_INFORMATION ThreadInfo;
  502. PCHAR Buffer;
  503. ULONG BufferSize = 4096;
  504. ULONG ReturnLength;
  505. retry:
  506. Buffer = ExAllocatePoolWithTag(NonPagedPool, BufferSize, PERFPOOLTAG);
  507. if (!Buffer) {
  508. return STATUS_NO_MEMORY;
  509. }
  510. Status = NtQuerySystemInformation( SystemExtendedProcessInformation,
  511. Buffer,
  512. BufferSize,
  513. &ReturnLength
  514. );
  515. if (Status == STATUS_INFO_LENGTH_MISMATCH) {
  516. ExFreePool(Buffer);
  517. BufferSize += 8192;
  518. goto retry;
  519. }
  520. if (NT_SUCCESS(Status)) {
  521. ProcessInfo = (PSYSTEM_PROCESS_INFORMATION) Buffer;
  522. while (TRUE) {
  523. PWMI_PROCESS_INFORMATION WmiProcessInfo;
  524. PWMI_EXTENDED_THREAD_INFORMATION WmiThreadInfo;
  525. PERFINFO_HOOK_HANDLE Hook;
  526. ANSI_STRING ProcessName;
  527. PUCHAR AuxPtr;
  528. ULONG NameLength;
  529. ULONG ByteCount;
  530. ULONG SidLength = sizeof(ULONG);
  531. ULONG TmpSid = 0;
  532. ULONG TotalOffset = 0;
  533. ULONG i;
  534. //
  535. // Process Information
  536. //
  537. if ( ProcessInfo->ImageName.Buffer && ProcessInfo->ImageName.Length > 0 ) {
  538. NameLength = ProcessInfo->ImageName.Length / sizeof(WCHAR) + 1;
  539. }
  540. else {
  541. NameLength = 1;
  542. }
  543. ByteCount = FIELD_OFFSET(WMI_PROCESS_INFORMATION, Sid) + SidLength + NameLength;
  544. Status = PerfInfoReserveBytes(&Hook,
  545. WMI_LOG_TYPE_PROCESS_DC_START,
  546. ByteCount);
  547. if (NT_SUCCESS(Status)){
  548. WmiProcessInfo = PERFINFO_HOOK_HANDLE_TO_DATA(Hook, PWMI_PROCESS_INFORMATION);
  549. WmiProcessInfo->ProcessId = HandleToUlong(ProcessInfo->UniqueProcessId);
  550. WmiProcessInfo->ParentId = HandleToUlong(ProcessInfo->InheritedFromUniqueProcessId);
  551. WmiProcessInfo->SessionId = ProcessInfo->SessionId;
  552. WmiProcessInfo->PageDirectoryBase = ProcessInfo->PageDirectoryBase;
  553. AuxPtr = (PUCHAR) (&WmiProcessInfo->Sid);
  554. RtlCopyMemory(AuxPtr, &TmpSid, SidLength);
  555. AuxPtr += SidLength;
  556. if (NameLength > 1) {
  557. ProcessName.Buffer = AuxPtr;
  558. ProcessName.MaximumLength = (USHORT) NameLength;
  559. RtlUnicodeStringToAnsiString( &ProcessName,
  560. (PUNICODE_STRING) &ProcessInfo->ImageName,
  561. FALSE);
  562. AuxPtr += NameLength;
  563. }
  564. *AuxPtr = '\0';
  565. PERF_FINISH_HOOK(Hook);
  566. }
  567. //
  568. // Thread Information
  569. //
  570. ThreadInfo = (PSYSTEM_EXTENDED_THREAD_INFORMATION) (ProcessInfo + 1);
  571. for (i=0; i < ProcessInfo->NumberOfThreads; i++) {
  572. Status = PerfInfoReserveBytes(&Hook,
  573. WMI_LOG_TYPE_THREAD_DC_START,
  574. sizeof(WMI_EXTENDED_THREAD_INFORMATION));
  575. if (NT_SUCCESS(Status)){
  576. WmiThreadInfo = PERFINFO_HOOK_HANDLE_TO_DATA(Hook, PWMI_EXTENDED_THREAD_INFORMATION);
  577. WmiThreadInfo->ProcessId = HandleToUlong(ThreadInfo->ThreadInfo.ClientId.UniqueProcess);
  578. WmiThreadInfo->ThreadId = HandleToUlong(ThreadInfo->ThreadInfo.ClientId.UniqueThread);
  579. WmiThreadInfo->StackBase = ThreadInfo->StackBase;
  580. WmiThreadInfo->StackLimit = ThreadInfo->StackLimit;
  581. WmiThreadInfo->UserStackBase = NULL;
  582. WmiThreadInfo->UserStackLimit = NULL;
  583. WmiThreadInfo->StartAddr = ThreadInfo->ThreadInfo.StartAddress;
  584. WmiThreadInfo->Win32StartAddr = ThreadInfo->Win32StartAddress;
  585. WmiThreadInfo->WaitMode = -1;
  586. PERF_FINISH_HOOK(Hook);
  587. }
  588. ThreadInfo += 1;
  589. }
  590. if (ProcessInfo->NextEntryOffset == 0) {
  591. break;
  592. } else {
  593. TotalOffset += ProcessInfo->NextEntryOffset;
  594. ProcessInfo = (PSYSTEM_PROCESS_INFORMATION) &Buffer[TotalOffset];
  595. }
  596. }
  597. }
  598. ExFreePool(Buffer);
  599. return Status;
  600. }
  601. NTSTATUS
  602. PerfInfoSysModuleRunDown (
  603. )
  604. /*++
  605. Routine Description:
  606. This routine does the rundown for loaded drivers in the kernel mode.
  607. Arguments:
  608. None.
  609. Return Value:
  610. Status
  611. --*/
  612. {
  613. NTSTATUS Status;
  614. PRTL_PROCESS_MODULES Modules;
  615. PRTL_PROCESS_MODULE_INFORMATION ModuleInfo;
  616. PVOID Buffer;
  617. ULONG BufferSize = 4096;
  618. ULONG ReturnLength;
  619. ULONG i;
  620. USHORT HookId = WMI_LOG_TYPE_PROCESS_LOAD_IMAGE;
  621. retry:
  622. Buffer = ExAllocatePoolWithTag(NonPagedPool, BufferSize, PERFPOOLTAG);
  623. if (!Buffer) {
  624. return STATUS_NO_MEMORY;
  625. }
  626. Status = NtQuerySystemInformation( SystemModuleInformation,
  627. Buffer,
  628. BufferSize,
  629. &ReturnLength
  630. );
  631. if (Status == STATUS_INFO_LENGTH_MISMATCH) {
  632. ExFreePool(Buffer);
  633. BufferSize += 8192;
  634. goto retry;
  635. }
  636. if (NT_SUCCESS(Status)) {
  637. Modules = (PRTL_PROCESS_MODULES) Buffer;
  638. for (i = 0, ModuleInfo = & (Modules->Modules[0]);
  639. i < Modules->NumberOfModules;
  640. i ++, ModuleInfo ++) {
  641. PWMI_IMAGELOAD_INFORMATION ImageLoadInfo;
  642. UNICODE_STRING WstrModuleName;
  643. ANSI_STRING AstrModuleName;
  644. ULONG SizeModuleName;
  645. PERFINFO_HOOK_HANDLE Hook;
  646. ULONG ByteCount;
  647. RtlInitAnsiString( &AstrModuleName, ModuleInfo->FullPathName);
  648. SizeModuleName = sizeof(WCHAR) * (AstrModuleName.Length) + sizeof(WCHAR);
  649. ByteCount = FIELD_OFFSET(WMI_IMAGELOAD_INFORMATION, FileName)
  650. + SizeModuleName;
  651. Status = PerfInfoReserveBytes(&Hook, WMI_LOG_TYPE_PROCESS_LOAD_IMAGE, ByteCount);
  652. if (NT_SUCCESS(Status)){
  653. ImageLoadInfo = PERFINFO_HOOK_HANDLE_TO_DATA(Hook, PWMI_IMAGELOAD_INFORMATION);
  654. ImageLoadInfo->ImageBase = ModuleInfo->ImageBase;
  655. ImageLoadInfo->ImageSize = ModuleInfo->ImageSize;
  656. ImageLoadInfo->ProcessId = HandleToUlong(NULL);
  657. WstrModuleName.Buffer = (LPWSTR) &ImageLoadInfo->FileName[0];
  658. WstrModuleName.MaximumLength = (USHORT) SizeModuleName;
  659. RtlAnsiStringToUnicodeString(&WstrModuleName, & AstrModuleName, FALSE);
  660. PERF_FINISH_HOOK(Hook);
  661. }
  662. }
  663. }
  664. ExFreePool(Buffer);
  665. return Status;
  666. }