Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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