864 lines
22 KiB
C
864 lines
22 KiB
C
/*++
|
||
|
||
Copyright (c) 2000 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
hooks.c
|
||
|
||
Abstract:
|
||
|
||
This module contains performance hooks.
|
||
|
||
Author:
|
||
|
||
Stephen Hsiao (shsiao) 01-Jan-2000
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "perfp.h"
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(PAGE, PerfInfoFlushProfileCache)
|
||
#pragma alloc_text(PAGEWMI, PerfProfileInterrupt)
|
||
#pragma alloc_text(PAGEWMI, PerfInfoLogBytesAndUnicodeString)
|
||
#pragma alloc_text(PAGEWMI, PerfInfoLogFileName)
|
||
#pragma alloc_text(PAGEWMI, PerfInfoCalcHashValue)
|
||
#pragma alloc_text(PAGEWMI, PerfInfoAddToFileHash)
|
||
#pragma alloc_text(PAGEWMI, PerfInfoFileNameRunDown)
|
||
#pragma alloc_text(PAGEWMI, PerfInfoProcessRunDown)
|
||
#pragma alloc_text(PAGEWMI, PerfInfoSysModuleRunDown)
|
||
#endif //ALLOC_PRAGMA
|
||
|
||
|
||
VOID
|
||
PerfInfoFlushProfileCache(
|
||
VOID
|
||
)
|
||
/*++
|
||
|
||
Routine description:
|
||
|
||
Flushes the profile cache to the log buffer. To make sure it get's valid data
|
||
we read the 2 seperate version numbers (1 before and 1 after) to check if it's
|
||
been changed. If so, we just read again. If that fails often, then we disable
|
||
the cache. Once the cache is read, we clear it. This may cause samples to be
|
||
lost but that's ok as this is statistical and it won't matter.
|
||
|
||
Arguments:
|
||
CheckVersion - If FALSE, the version is not checked. This used when the profile
|
||
interrupt code flushes the cache.
|
||
|
||
Return Value:
|
||
None
|
||
|
||
--*/
|
||
{
|
||
ULONG PreviousInProgress;
|
||
|
||
if ((PerfProfileCache.Entries == 0) || (PerfInfoSampledProfileCaching == FALSE)) {
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Signal the interrupt not to mess with the cache
|
||
//
|
||
PreviousInProgress = InterlockedIncrement(&PerfInfoSampledProfileFlushInProgress);
|
||
if (PreviousInProgress != 1) {
|
||
//
|
||
// A flush is already in progress so just return.
|
||
//
|
||
InterlockedDecrement(&PerfInfoSampledProfileFlushInProgress);
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Log the portion of the cache that has valid data.
|
||
//
|
||
PerfInfoLogBytes(PERFINFO_LOG_TYPE_SAMPLED_PROFILE_CACHE,
|
||
&PerfProfileCache,
|
||
FIELD_OFFSET(PERFINFO_SAMPLED_PROFILE_CACHE, Sample) +
|
||
(PerfProfileCache.Entries *
|
||
sizeof(PERFINFO_SAMPLED_PROFILE_INFORMATION))
|
||
);
|
||
|
||
//
|
||
// Clear the cache for the next set of entries.
|
||
//
|
||
PerfProfileCache.Entries = 0;
|
||
|
||
//
|
||
// Let the interrupt fill the cache again.
|
||
//
|
||
InterlockedDecrement(&PerfInfoSampledProfileFlushInProgress);
|
||
}
|
||
|
||
|
||
VOID
|
||
FASTCALL
|
||
PerfProfileInterrupt(
|
||
IN KPROFILE_SOURCE Source,
|
||
IN PVOID InstructionPointer
|
||
)
|
||
/*++
|
||
|
||
Routine description:
|
||
|
||
Implements instruction profiling. If the source is not the one we're sampling on,
|
||
we return. If caching is off, we write any samples coming from the immediately to
|
||
the log. If caching is on, wrap the cache update with writes to the two versions so
|
||
that the flush routine can know if it has a valid buffer.
|
||
|
||
Arguments:
|
||
|
||
Source - Type of profile interrupt
|
||
|
||
InstructionPointer - IP at the time of the interrupt
|
||
|
||
Return Value:
|
||
None
|
||
|
||
--*/
|
||
{
|
||
ULONG i;
|
||
PERFINFO_SAMPLED_PROFILE_INFORMATION SampleData;
|
||
#ifdef _X86_
|
||
ULONG_PTR TwiddledIP;
|
||
#endif // _X86_
|
||
ULONG ThreadId;
|
||
|
||
if (!PERFINFO_IS_GROUP_ON(PERF_PROFILE) &&
|
||
(Source != PerfInfoProfileSourceActive)
|
||
) {
|
||
//
|
||
// We don't handle multple sources.
|
||
//
|
||
return;
|
||
}
|
||
|
||
ThreadId = HandleToUlong(PsGetCurrentThread()->Cid.UniqueThread);
|
||
|
||
if (!PerfInfoSampledProfileCaching ||
|
||
PerfInfoSampledProfileFlushInProgress != 0) {
|
||
//
|
||
// No caching. Log and return.
|
||
//
|
||
SampleData.ThreadId = ThreadId;
|
||
SampleData.InstructionPointer = InstructionPointer;
|
||
SampleData.Count = 1;
|
||
|
||
PerfInfoLogBytes(PERFINFO_LOG_TYPE_SAMPLED_PROFILE,
|
||
&SampleData,
|
||
sizeof(PERFINFO_SAMPLED_PROFILE_INFORMATION)
|
||
);
|
||
return;
|
||
}
|
||
|
||
#ifdef _X86_
|
||
//
|
||
// Clear the low two bits to have more cache hits for loops. Don't waste
|
||
// cycles on other architectures.
|
||
//
|
||
TwiddledIP = (ULONG_PTR)InstructionPointer & ~3;
|
||
#endif // _X86_
|
||
|
||
//
|
||
// Initial walk thru Instruction Pointer Cache. Bump Count if address is in cache.
|
||
//
|
||
for (i = 0; i < PerfProfileCache.Entries ; i++) {
|
||
|
||
if ((PerfProfileCache.Sample[i].ThreadId == ThreadId) &&
|
||
#ifdef _X86_
|
||
(((ULONG_PTR)PerfProfileCache.Sample[i].InstructionPointer & ~3) == TwiddledIP)
|
||
#else
|
||
(PerfProfileCache.Sample[i].InstructionPointer == InstructionPointer)
|
||
#endif // _X86_
|
||
) {
|
||
//
|
||
// If we find the instruction pointer in the cache, bump the count
|
||
//
|
||
|
||
PerfProfileCache.Sample[i].Count++;
|
||
return;
|
||
}
|
||
}
|
||
if (PerfProfileCache.Entries < PERFINFO_SAMPLED_PROFILE_CACHE_MAX) {
|
||
//
|
||
// If we find an empty spot in the cache, use it for this instruction pointer
|
||
//
|
||
|
||
PerfProfileCache.Sample[i].ThreadId = ThreadId;
|
||
PerfProfileCache.Sample[i].InstructionPointer = InstructionPointer;
|
||
PerfProfileCache.Sample[i].Count = 1;
|
||
PerfProfileCache.Entries++;
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Flush the cache
|
||
//
|
||
PerfInfoLogBytes(PERFINFO_LOG_TYPE_SAMPLED_PROFILE_CACHE,
|
||
&PerfProfileCache,
|
||
sizeof(PERFINFO_SAMPLED_PROFILE_CACHE)
|
||
);
|
||
|
||
PerfProfileCache.Sample[0].ThreadId = ThreadId;
|
||
PerfProfileCache.Sample[0].InstructionPointer = InstructionPointer;
|
||
PerfProfileCache.Sample[0].Count = 1;
|
||
PerfProfileCache.Entries = 1;
|
||
return;
|
||
}
|
||
|
||
|
||
VOID
|
||
FASTCALL
|
||
PerfInfoLogDpc(
|
||
IN PVOID DpcRoutine,
|
||
IN ULONGLONG InitialTime
|
||
)
|
||
/*++
|
||
|
||
Routine description:
|
||
|
||
This routine logs the exit of a DPC service routine
|
||
|
||
Arguments:
|
||
|
||
DPCRoutine - DPC service routine
|
||
|
||
InitialTime - Timestamp before service routine was called. The timestamp in
|
||
the event is used as the end time.
|
||
--*/
|
||
{
|
||
PERFINFO_DPC_INFORMATION st;
|
||
|
||
st.DpcRoutine = DpcRoutine;
|
||
st.InitialTime = InitialTime;
|
||
|
||
PerfInfoLogBytes(
|
||
PERFINFO_LOG_TYPE_DPC,
|
||
(PVOID) &st,
|
||
sizeof(st));
|
||
}
|
||
|
||
|
||
VOID
|
||
FASTCALL
|
||
PerfInfoLogInterrupt(
|
||
IN PVOID InServiceRoutine,
|
||
IN ULONG RetVal,
|
||
IN ULONGLONG InitialTime
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This callout routine is called from ntoskrnl.exe (ke\intsup.asm) to log an
|
||
interrupt and how long it takes to complete.
|
||
|
||
Arguments:
|
||
|
||
InServiceRoutine Address of routine that serviced the interrupt.
|
||
|
||
RetVal Value returned from InServiceRoutine.
|
||
|
||
InitialTime Timestamp before ISR was called. The timestamp in
|
||
the event is used as the end time.
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
{
|
||
PERFINFO_INTERRUPT_INFORMATION EventInfo;
|
||
|
||
EventInfo.ServiceRoutine = InServiceRoutine;
|
||
EventInfo.ReturnValue = RetVal;
|
||
EventInfo.InitialTime = InitialTime;
|
||
|
||
PerfInfoLogBytes(PERFINFO_LOG_TYPE_INTERRUPT,
|
||
&EventInfo,
|
||
sizeof(EventInfo));
|
||
|
||
return;
|
||
}
|
||
|
||
NTSTATUS
|
||
PerfInfoLogBytesAndUnicodeString(
|
||
USHORT HookId,
|
||
PVOID SourceData,
|
||
ULONG SourceByteCount,
|
||
PUNICODE_STRING String
|
||
)
|
||
/*++
|
||
|
||
Routine description:
|
||
|
||
This routine logs data with UniCode string at the end of the hook.
|
||
|
||
Arguments:
|
||
|
||
HookId - Hook Id.
|
||
|
||
SourceData - Pointer to the data to be copied
|
||
|
||
SourceByteCount - Number of bytes to be copied.
|
||
|
||
String - The string to be logged.
|
||
|
||
Return Value:
|
||
Status
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status;
|
||
PERFINFO_HOOK_HANDLE Hook;
|
||
ULONG ByteCount;
|
||
ULONG StringBytes;
|
||
|
||
if (String == NULL) {
|
||
StringBytes = 0;
|
||
} else {
|
||
StringBytes = String->Length;
|
||
}
|
||
|
||
ByteCount = (SourceByteCount + StringBytes + sizeof(WCHAR));
|
||
|
||
Status = PerfInfoReserveBytes(&Hook, HookId, ByteCount);
|
||
if (NT_SUCCESS(Status))
|
||
{
|
||
const PVOID pvTemp = PERFINFO_HOOK_HANDLE_TO_DATA(Hook, PVOID);
|
||
RtlCopyMemory(pvTemp, SourceData, SourceByteCount);
|
||
if (StringBytes != 0) {
|
||
RtlCopyMemory(PERFINFO_APPLY_OFFSET_GIVING_TYPE(pvTemp, SourceByteCount, PVOID),
|
||
String->Buffer,
|
||
StringBytes
|
||
);
|
||
}
|
||
(PERFINFO_APPLY_OFFSET_GIVING_TYPE(pvTemp, SourceByteCount, PWCHAR))[StringBytes / sizeof(WCHAR)] = UNICODE_NULL;
|
||
PERF_FINISH_HOOK(Hook);
|
||
|
||
Status = STATUS_SUCCESS;
|
||
}
|
||
return Status;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
PerfInfoLogFileName(
|
||
PVOID FileObject,
|
||
PUNICODE_STRING SourceString
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine logs a FileObject pointer and FileName to the log. The pointer is used
|
||
as hash key to map this name to other trace events.
|
||
|
||
Arguments:
|
||
|
||
FileObject - Pointer to the FileName member within the FILE_OBJECT
|
||
structure. The FileName may not yet be initialized,
|
||
so the actual data comes from the SourceString
|
||
parameter.
|
||
|
||
SourceString - Optional pointer to the source string.
|
||
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS
|
||
--*/
|
||
{
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
PERFINFO_FILENAME_INFORMATION FileInfo;
|
||
|
||
if ((FileObject != NULL) &&
|
||
(SourceString != NULL) &&
|
||
(SourceString->Length != 0)) {
|
||
FileInfo.HashKeyFileNamePointer = FileObject;
|
||
Status = PerfInfoLogBytesAndUnicodeString(PERFINFO_LOG_TYPE_FILENAME_CREATE,
|
||
&FileInfo,
|
||
FIELD_OFFSET(PERFINFO_FILENAME_INFORMATION, FileName),
|
||
SourceString);
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
ULONG
|
||
PerfInfoCalcHashValue(
|
||
PVOID Key,
|
||
ULONG Len
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Generic hash routine.
|
||
|
||
Arguments:
|
||
|
||
Key - Pointer to data to calculate a hash value for.
|
||
|
||
Len - Number of bytes pointed to by key.
|
||
|
||
Return Value:
|
||
|
||
Hash value.
|
||
|
||
--*/
|
||
|
||
{
|
||
char *cp = Key;
|
||
ULONG i, ConvKey=0;
|
||
for(i = 0; i < Len; i++)
|
||
{
|
||
ConvKey = 37 * ConvKey + (unsigned int) *cp;
|
||
cp++;
|
||
}
|
||
|
||
#define RNDM_CONSTANT 314159269
|
||
#define RNDM_PRIME 1000000007
|
||
|
||
return (abs(RNDM_CONSTANT * ConvKey) % RNDM_PRIME);
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
PerfInfoAddToFileHash(
|
||
PPERFINFO_ENTRY_TABLE HashTable,
|
||
PFILE_OBJECT ObjectPointer
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine add a FileObject into the specified
|
||
hash table if it is not already there.
|
||
|
||
Arguments:
|
||
|
||
HashTable - pointer to a hash table to be used.
|
||
|
||
ObjectPointer - This is used as a key to identify a mapping.
|
||
|
||
Return Value:
|
||
|
||
TRUE - If either the FileObject was in the table or we add it.
|
||
FALSE - If the table is full.
|
||
|
||
--*/
|
||
{
|
||
ULONG HashIndex;
|
||
LONG i;
|
||
BOOLEAN Result = FALSE;
|
||
LONG TableSize = HashTable->NumberOfEntries;
|
||
PVOID *Table;
|
||
|
||
Table = HashTable->Table;
|
||
//
|
||
// Get the hashed index into the table where the entry ideally
|
||
// should be at.
|
||
//
|
||
|
||
HashIndex = PerfInfoCalcHashValue((PVOID)&ObjectPointer,
|
||
sizeof(ObjectPointer)) % TableSize;
|
||
|
||
for (i = 0; i < TableSize; i++) {
|
||
|
||
if(Table[HashIndex] == NULL) {
|
||
//
|
||
// Found a empty slot. Reference the object and insert
|
||
// it into the table.
|
||
//
|
||
ObReferenceObject(ObjectPointer);
|
||
Table[HashIndex] = ObjectPointer;
|
||
|
||
Result = TRUE;
|
||
break;
|
||
} else if (Table[HashIndex] == ObjectPointer) {
|
||
//
|
||
// Found a slot. Reference the object and insert
|
||
// it into the table.
|
||
//
|
||
Result = TRUE;
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Try next slot.
|
||
//
|
||
HashIndex = (HashIndex + 1) % TableSize;
|
||
}
|
||
return Result;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
PerfInfoFileNameRunDown (
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine walks through multiple lists to collect the names of all files.
|
||
It includes:
|
||
1. Handle table: for all file handles
|
||
2. Process Vad for all file objects mapped in VAD.
|
||
3. MmUnusedSegment List
|
||
4. CcDirtySharedCacheMapList & CcCleanSharedCacheMapList
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
BUGBUG Need proper return/ error handling
|
||
|
||
--*/
|
||
{
|
||
PEPROCESS Process;
|
||
ULONG AllocateBytes;
|
||
PFILE_OBJECT *FileObjects;
|
||
PFILE_OBJECT *File;
|
||
PERFINFO_ENTRY_TABLE HashTable;
|
||
extern POBJECT_TYPE IoFileObjectType;
|
||
LONG i;
|
||
|
||
//
|
||
// First create a tempory hash table to build the list of
|
||
// files to walk through
|
||
//
|
||
AllocateBytes = PAGE_SIZE + sizeof(PVOID) * IoFileObjectType->TotalNumberOfObjects;
|
||
|
||
//
|
||
// Run up to page boundary
|
||
//
|
||
AllocateBytes = PERFINFO_ROUND_UP(AllocateBytes, PAGE_SIZE);
|
||
|
||
HashTable.Table = ExAllocatePoolWithTag(NonPagedPool, AllocateBytes, PERFPOOLTAG);
|
||
|
||
if (HashTable.Table == NULL) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
} else {
|
||
//
|
||
// Allocation Succeeded
|
||
//
|
||
HashTable.NumberOfEntries = AllocateBytes / sizeof(PVOID);
|
||
RtlZeroMemory(HashTable.Table, AllocateBytes);
|
||
}
|
||
|
||
//
|
||
// Walk through the Cc SharedCacheMapList
|
||
//
|
||
|
||
CcPerfFileRunDown(&HashTable);
|
||
|
||
//
|
||
// Now, walk through each process
|
||
//
|
||
for (Process = PsGetNextProcess (NULL);
|
||
Process != NULL;
|
||
Process = PsGetNextProcess (Process)) {
|
||
|
||
//
|
||
// First Walk the VAD tree
|
||
//
|
||
|
||
FileObjects = MmPerfVadTreeWalk(Process);
|
||
if (FileObjects != NULL) {
|
||
File = FileObjects;
|
||
while (*File != NULL) {
|
||
PerfInfoAddToFileHash(&HashTable, *File);
|
||
ObDereferenceObject(*File);
|
||
File += 1;
|
||
}
|
||
ExFreePool(FileObjects);
|
||
}
|
||
|
||
//
|
||
// Next, walk the handle Table
|
||
//
|
||
ObPerfHandleTableWalk (Process, &HashTable);
|
||
}
|
||
|
||
//
|
||
// Walk through the kernel handle table;
|
||
//
|
||
ObPerfHandleTableWalk(NULL, &HashTable);
|
||
|
||
//
|
||
// Walk through the MmUnusedSegmentList;
|
||
//
|
||
|
||
FileObjects = MmPerfUnusedSegmentsEnumerate();
|
||
|
||
if (FileObjects != NULL) {
|
||
File = FileObjects;
|
||
while (*File != NULL) {
|
||
PerfInfoAddToFileHash(&HashTable, *File);
|
||
ObDereferenceObject(*File);
|
||
File += 1;
|
||
}
|
||
ExFreePool(FileObjects);
|
||
}
|
||
|
||
//
|
||
// Now we have walked through all list.
|
||
// Log the filenames and dereference the objects.
|
||
//
|
||
|
||
for (i = 0; i < HashTable.NumberOfEntries; i++) {
|
||
if (HashTable.Table[i]) {
|
||
PFILE_OBJECT FileObject = HashTable.Table[i];
|
||
|
||
PerfInfoLogFileName(FileObject, &FileObject->FileName);
|
||
ObDereferenceObject(FileObject);
|
||
}
|
||
}
|
||
|
||
//
|
||
// Free the tables reserved.
|
||
//
|
||
ExFreePool(HashTable.Table);
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
PerfInfoProcessRunDown (
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine does the Process and thread rundown in the kernel mode.
|
||
Since this routine is called only by global logger (i.e., trace from boot),
|
||
no Sid info is collected.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
Status
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status;
|
||
PSYSTEM_PROCESS_INFORMATION ProcessInfo;
|
||
PSYSTEM_EXTENDED_THREAD_INFORMATION ThreadInfo;
|
||
PCHAR Buffer;
|
||
ULONG BufferSize = 4096;
|
||
ULONG ReturnLength;
|
||
|
||
retry:
|
||
Buffer = ExAllocatePoolWithTag(NonPagedPool, BufferSize, PERFPOOLTAG);
|
||
|
||
if (!Buffer) {
|
||
return STATUS_NO_MEMORY;
|
||
}
|
||
Status = NtQuerySystemInformation( SystemExtendedProcessInformation,
|
||
Buffer,
|
||
BufferSize,
|
||
&ReturnLength
|
||
);
|
||
|
||
if (Status == STATUS_INFO_LENGTH_MISMATCH) {
|
||
ExFreePool(Buffer);
|
||
BufferSize += 8192;
|
||
goto retry;
|
||
}
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
ProcessInfo = (PSYSTEM_PROCESS_INFORMATION) Buffer;
|
||
while (TRUE) {
|
||
PWMI_PROCESS_INFORMATION WmiProcessInfo;
|
||
PWMI_EXTENDED_THREAD_INFORMATION WmiThreadInfo;
|
||
PERFINFO_HOOK_HANDLE Hook;
|
||
ANSI_STRING ProcessName;
|
||
PUCHAR AuxPtr;
|
||
ULONG NameLength;
|
||
ULONG ByteCount;
|
||
ULONG SidLength = sizeof(ULONG);
|
||
ULONG TmpSid = 0;
|
||
ULONG TotalOffset = 0;
|
||
ULONG i;
|
||
|
||
//
|
||
// Process Information
|
||
//
|
||
if ( ProcessInfo->ImageName.Buffer && ProcessInfo->ImageName.Length > 0 ) {
|
||
NameLength = ProcessInfo->ImageName.Length / sizeof(WCHAR) + 1;
|
||
}
|
||
else {
|
||
NameLength = 1;
|
||
}
|
||
ByteCount = FIELD_OFFSET(WMI_PROCESS_INFORMATION, Sid) + SidLength + NameLength;
|
||
|
||
Status = PerfInfoReserveBytes(&Hook,
|
||
WMI_LOG_TYPE_PROCESS_DC_START,
|
||
ByteCount);
|
||
|
||
if (NT_SUCCESS(Status)){
|
||
WmiProcessInfo = PERFINFO_HOOK_HANDLE_TO_DATA(Hook, PWMI_PROCESS_INFORMATION);
|
||
|
||
WmiProcessInfo->ProcessId = HandleToUlong(ProcessInfo->UniqueProcessId);
|
||
WmiProcessInfo->ParentId = HandleToUlong(ProcessInfo->InheritedFromUniqueProcessId);
|
||
WmiProcessInfo->SessionId = ProcessInfo->SessionId;
|
||
WmiProcessInfo->PageDirectoryBase = ProcessInfo->PageDirectoryBase;
|
||
|
||
AuxPtr = (PUCHAR) (&WmiProcessInfo->Sid);
|
||
RtlCopyMemory(AuxPtr, &TmpSid, SidLength);
|
||
|
||
AuxPtr += SidLength;
|
||
if (NameLength > 1) {
|
||
|
||
ProcessName.Buffer = AuxPtr;
|
||
ProcessName.MaximumLength = (USHORT) NameLength;
|
||
|
||
RtlUnicodeStringToAnsiString( &ProcessName,
|
||
(PUNICODE_STRING) &ProcessInfo->ImageName,
|
||
FALSE);
|
||
AuxPtr += NameLength;
|
||
}
|
||
*AuxPtr = '\0';
|
||
|
||
PERF_FINISH_HOOK(Hook);
|
||
}
|
||
|
||
//
|
||
// Thread Information
|
||
//
|
||
ThreadInfo = (PSYSTEM_EXTENDED_THREAD_INFORMATION) (ProcessInfo + 1);
|
||
|
||
for (i=0; i < ProcessInfo->NumberOfThreads; i++) {
|
||
Status = PerfInfoReserveBytes(&Hook,
|
||
WMI_LOG_TYPE_THREAD_DC_START,
|
||
sizeof(WMI_EXTENDED_THREAD_INFORMATION));
|
||
if (NT_SUCCESS(Status)){
|
||
WmiThreadInfo = PERFINFO_HOOK_HANDLE_TO_DATA(Hook, PWMI_EXTENDED_THREAD_INFORMATION);
|
||
WmiThreadInfo->ProcessId = HandleToUlong(ThreadInfo->ThreadInfo.ClientId.UniqueProcess);
|
||
WmiThreadInfo->ThreadId = HandleToUlong(ThreadInfo->ThreadInfo.ClientId.UniqueThread);
|
||
WmiThreadInfo->StackBase = ThreadInfo->StackBase;
|
||
WmiThreadInfo->StackLimit = ThreadInfo->StackLimit;
|
||
|
||
WmiThreadInfo->UserStackBase = NULL;
|
||
WmiThreadInfo->UserStackLimit = NULL;
|
||
WmiThreadInfo->StartAddr = ThreadInfo->ThreadInfo.StartAddress;
|
||
WmiThreadInfo->Win32StartAddr = ThreadInfo->Win32StartAddress;
|
||
WmiThreadInfo->WaitMode = -1;
|
||
PERF_FINISH_HOOK(Hook);
|
||
}
|
||
|
||
ThreadInfo += 1;
|
||
}
|
||
|
||
if (ProcessInfo->NextEntryOffset == 0) {
|
||
break;
|
||
} else {
|
||
TotalOffset += ProcessInfo->NextEntryOffset;
|
||
ProcessInfo = (PSYSTEM_PROCESS_INFORMATION) &Buffer[TotalOffset];
|
||
}
|
||
}
|
||
}
|
||
|
||
ExFreePool(Buffer);
|
||
return Status;
|
||
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
PerfInfoSysModuleRunDown (
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine does the rundown for loaded drivers in the kernel mode.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
Status
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status;
|
||
PRTL_PROCESS_MODULES Modules;
|
||
PRTL_PROCESS_MODULE_INFORMATION ModuleInfo;
|
||
PVOID Buffer;
|
||
ULONG BufferSize = 4096;
|
||
ULONG ReturnLength;
|
||
ULONG i;
|
||
USHORT HookId = WMI_LOG_TYPE_PROCESS_LOAD_IMAGE;
|
||
|
||
retry:
|
||
Buffer = ExAllocatePoolWithTag(NonPagedPool, BufferSize, PERFPOOLTAG);
|
||
|
||
if (!Buffer) {
|
||
return STATUS_NO_MEMORY;
|
||
}
|
||
Status = NtQuerySystemInformation( SystemModuleInformation,
|
||
Buffer,
|
||
BufferSize,
|
||
&ReturnLength
|
||
);
|
||
|
||
if (Status == STATUS_INFO_LENGTH_MISMATCH) {
|
||
ExFreePool(Buffer);
|
||
BufferSize += 8192;
|
||
goto retry;
|
||
}
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
Modules = (PRTL_PROCESS_MODULES) Buffer;
|
||
for (i = 0, ModuleInfo = & (Modules->Modules[0]);
|
||
i < Modules->NumberOfModules;
|
||
i ++, ModuleInfo ++) {
|
||
|
||
PWMI_IMAGELOAD_INFORMATION ImageLoadInfo;
|
||
UNICODE_STRING WstrModuleName;
|
||
ANSI_STRING AstrModuleName;
|
||
ULONG SizeModuleName;
|
||
PERFINFO_HOOK_HANDLE Hook;
|
||
ULONG ByteCount;
|
||
|
||
RtlInitAnsiString( &AstrModuleName, ModuleInfo->FullPathName);
|
||
SizeModuleName = sizeof(WCHAR) * (AstrModuleName.Length) + sizeof(WCHAR);
|
||
ByteCount = FIELD_OFFSET(WMI_IMAGELOAD_INFORMATION, FileName)
|
||
+ SizeModuleName;
|
||
|
||
Status = PerfInfoReserveBytes(&Hook, WMI_LOG_TYPE_PROCESS_LOAD_IMAGE, ByteCount);
|
||
|
||
if (NT_SUCCESS(Status)){
|
||
ImageLoadInfo = PERFINFO_HOOK_HANDLE_TO_DATA(Hook, PWMI_IMAGELOAD_INFORMATION);
|
||
ImageLoadInfo->ImageBase = ModuleInfo->ImageBase;
|
||
ImageLoadInfo->ImageSize = ModuleInfo->ImageSize;
|
||
ImageLoadInfo->ProcessId = HandleToUlong(NULL);
|
||
WstrModuleName.Buffer = (LPWSTR) &ImageLoadInfo->FileName[0];
|
||
WstrModuleName.MaximumLength = (USHORT) SizeModuleName;
|
||
RtlAnsiStringToUnicodeString(&WstrModuleName, & AstrModuleName, FALSE);
|
||
PERF_FINISH_HOOK(Hook);
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
ExFreePool(Buffer);
|
||
return Status;
|
||
}
|