mirror of https://github.com/lianthony/NT4.0
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.
1923 lines
54 KiB
1923 lines
54 KiB
/*++
|
|
|
|
Copyright (c) 1995 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
kernrate.c
|
|
|
|
Abstract:
|
|
|
|
This program records the rate of various events over a selected
|
|
period of time. It uses the kernel profiling mechanism and iterates
|
|
through the available profile sources to produce an overall profile
|
|
for the various kernel components.
|
|
|
|
Usage:
|
|
|
|
kernrate
|
|
|
|
Author:
|
|
|
|
John Vert (jvert) 31-Mar-1995
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include <nt.h>
|
|
#include <ntrtl.h>
|
|
#include <nturtl.h>
|
|
#include <windows.h>
|
|
#include <imagehlp.h>
|
|
#include <assert.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <search.h>
|
|
#include <string.h>
|
|
#include <memory.h>
|
|
#include <ctype.h>
|
|
#include <..\pperf\pstat.h>
|
|
|
|
|
|
#define MAX_SYMNAME_SIZE 1024
|
|
CHAR symBuffer[sizeof(IMAGEHLP_SYMBOL)+MAX_SYMNAME_SIZE];
|
|
PIMAGEHLP_SYMBOL Symbol = (PIMAGEHLP_SYMBOL) symBuffer;
|
|
|
|
|
|
//
|
|
// Constant definitions
|
|
//
|
|
#define ZOOM_BUCKET 16
|
|
#define LOG2_ZOOM_BUCKET 4
|
|
|
|
|
|
//
|
|
// Type definitions
|
|
//
|
|
|
|
typedef struct _SOURCE {
|
|
PCHAR Name;
|
|
KPROFILE_SOURCE ProfileSource;
|
|
PCHAR ShortName;
|
|
ULONG DesiredInterval;
|
|
ULONG Interval;
|
|
} SOURCE, *PSOURCE;
|
|
|
|
typedef struct _RATE_DATA {
|
|
ULONGLONG StartTime;
|
|
ULONGLONG TotalTime;
|
|
ULONGLONG TotalCount;
|
|
ULONGLONG Rate; // Events/Second
|
|
HANDLE ProfileHandle;
|
|
ULONG CurrentCount;
|
|
PULONG ProfileBuffer;
|
|
} RATE_DATA, *PRATE_DATA;
|
|
|
|
typedef struct _MODULE {
|
|
struct _MODULE *Next;
|
|
HANDLE Process;
|
|
ULONG Base;
|
|
ULONG Length;
|
|
BOOLEAN Zoom;
|
|
CHAR Name[40];
|
|
RATE_DATA Rate[];
|
|
} MODULE, *PMODULE;
|
|
|
|
typedef struct _RATE_SUMMARY {
|
|
ULONGLONG TotalCount;
|
|
ULONG ModuleCount;
|
|
PMODULE *Modules;
|
|
} RATE_SUMMARY, *PRATE_SUMMARY;
|
|
|
|
//
|
|
// Global variables
|
|
//
|
|
HANDLE DoneEvent;
|
|
DWORD ChangeInterval = 1000;
|
|
DWORD SleepInterval = 0;
|
|
ULONG ModuleCount=0;
|
|
ULONG ZoomCount;
|
|
PMODULE ZoomList = NULL;
|
|
PMODULE CallbackCurrent;
|
|
BOOLEAN RawData = FALSE;
|
|
HANDLE SymHandle = (HANDLE)-1;
|
|
|
|
//
|
|
// The desired intervals are computed to give approximately
|
|
// one interrupt per millisecond and be a nice even power of 2
|
|
//
|
|
SOURCE StaticSources[] = {
|
|
{"Time", ProfileTime, "", 1000,0},
|
|
{"Alignment Fixup", ProfileAlignmentFixup, "", 1,0},
|
|
{"Total Issues", ProfileTotalIssues, "", 131072,0},
|
|
{"Pipeline Dry", ProfilePipelineDry, "", 131072,0},
|
|
{"Load Instructions", ProfileLoadInstructions, "", 65536,0},
|
|
{"Pipeline Frozen", ProfilePipelineFrozen, "", 131072,0},
|
|
{"Branch Instructions", ProfileBranchInstructions, "", 65536,0},
|
|
{"Total Nonissues", ProfileTotalNonissues, "", 131072,0},
|
|
{"Dcache Misses", ProfileDcacheMisses, "", 16384,0},
|
|
{"Icache Misses", ProfileIcacheMisses, "", 16384,0},
|
|
{"Cache Misses", ProfileCacheMisses, "", 16384,0},
|
|
{"Branch Mispredictions", ProfileBranchMispredictions, "", 16384,0},
|
|
{"Store Instructions", ProfileStoreInstructions, "", 65536,0},
|
|
{"Floating Point Instr", ProfileFpInstructions, "", 65536,0},
|
|
{"Integer Instructions", ProfileIntegerInstructions, "", 65536,0},
|
|
{"Dual Issues", Profile2Issue, "", 65536,0},
|
|
{"Triple Issues", Profile3Issue, "", 16384,0},
|
|
{"Quad Issues", Profile4Issue, "", 16384,0},
|
|
{"Special Instructions", ProfileSpecialInstructions, "", 16384,0},
|
|
{"Cycles", ProfileTotalCycles, "", 655360,0},
|
|
{"Icache Issues", ProfileIcacheIssues, "", 65536,0},
|
|
{"Dcache Accesses", ProfileDcacheAccesses, "", 65536,0},
|
|
{"MB Stall Cycles", ProfileMemoryBarrierCycles, "", 32767,0},
|
|
{"Load Linked Instructions", ProfileLoadLinkedIssues, "", 16384,0},
|
|
{NULL, ProfileMaximum, "", 0, 0}
|
|
};
|
|
|
|
PSOURCE Source = StaticSources;
|
|
ULONG SourceMaximum = 0;
|
|
|
|
|
|
//
|
|
// Function prototypes local to this module
|
|
//
|
|
PMODULE
|
|
GetKernelModuleInformation(
|
|
VOID
|
|
);
|
|
|
|
PMODULE
|
|
GetProcessModuleInformation(
|
|
IN HANDLE ProcessHandle
|
|
);
|
|
|
|
VOID
|
|
CreateProfiles(
|
|
IN PMODULE Root
|
|
);
|
|
|
|
PMODULE
|
|
CreateNewModule(
|
|
IN HANDLE ProcessHandle,
|
|
IN PCHAR ModuleName,
|
|
IN ULONG ImageBase,
|
|
IN ULONG ImageSize
|
|
);
|
|
|
|
VOID
|
|
Usage(
|
|
VOID
|
|
);
|
|
|
|
VOID
|
|
GetConfiguration(
|
|
int argc,
|
|
char *argv[]
|
|
);
|
|
|
|
VOID
|
|
InitializeProfileSourceInfo (
|
|
VOID
|
|
);
|
|
|
|
ULONG
|
|
NextSource(
|
|
IN ULONG CurrentSource,
|
|
IN PMODULE ModuleList
|
|
);
|
|
|
|
VOID
|
|
StopSource(
|
|
IN ULONG ProfileSourceIndex,
|
|
IN PMODULE ModuleList
|
|
);
|
|
|
|
VOID
|
|
StartSource(
|
|
IN ULONG ProfileSource,
|
|
IN PMODULE ModuleList
|
|
);
|
|
|
|
VOID
|
|
OutputResults(
|
|
IN FILE *Out,
|
|
IN PMODULE ModuleList
|
|
);
|
|
|
|
VOID
|
|
OutputModuleList(
|
|
IN FILE *Out,
|
|
IN PMODULE ModuleList,
|
|
IN ULONG NumberModules
|
|
);
|
|
|
|
VOID
|
|
CreateZoomedModuleList(
|
|
IN PMODULE ZoomModule
|
|
);
|
|
|
|
BOOL
|
|
CreateZoomModuleCallback(
|
|
IN LPSTR szSymName,
|
|
IN ULONG Address,
|
|
IN ULONG Size,
|
|
IN PVOID Cxt
|
|
);
|
|
|
|
VOID
|
|
OutputLine(
|
|
IN FILE *Out,
|
|
IN ULONG ProfileSourceIndex,
|
|
IN PMODULE Module,
|
|
IN PRATE_SUMMARY RateSummary
|
|
);
|
|
|
|
VOID
|
|
CreateDoneEvent(
|
|
VOID
|
|
);
|
|
|
|
VOID
|
|
OutputInterestingData(
|
|
IN FILE *Out,
|
|
IN RATE_DATA Data[],
|
|
IN PCHAR Header
|
|
);
|
|
|
|
BOOL
|
|
CtrlcH(
|
|
DWORD dwCtrlType
|
|
)
|
|
{
|
|
LARGE_INTEGER DueTime;
|
|
|
|
if ( dwCtrlType == CTRL_C_EVENT ) {
|
|
if (SleepInterval == 0) {
|
|
SetEvent(DoneEvent);
|
|
} else {
|
|
DueTime.QuadPart = (ULONGLONG)-1;
|
|
NtSetTimer(DoneEvent,
|
|
&DueTime,
|
|
NULL,
|
|
NULL,
|
|
FALSE,
|
|
0,
|
|
NULL);
|
|
}
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
VOID
|
|
Usage(
|
|
VOID
|
|
)
|
|
{
|
|
fprintf(stderr, "KERNRATE [-z modulename] [-c rateinmsec] [-s seconds]\n");
|
|
fprintf(stderr, " -z modulename Zoom in on specified module\n");
|
|
fprintf(stderr, " -c n Change source after N milliseconds (default 1000)\n");
|
|
fprintf(stderr, " -s n Stop collecting data after N seconds\n");
|
|
fprintf(stderr, " -r Raw data from zoomed modules\n");
|
|
fprintf(stderr, " -p processid monitor process instead of kernel\n");
|
|
exit(1);
|
|
}
|
|
|
|
VOID
|
|
CreateDoneEvent(
|
|
VOID
|
|
)
|
|
{
|
|
LARGE_INTEGER DueTime;
|
|
NTSTATUS Status;
|
|
DWORD Error;
|
|
|
|
if (SleepInterval == 0) {
|
|
//
|
|
// Create event that will indicate the test is complete.
|
|
//
|
|
DoneEvent = CreateEvent(NULL,
|
|
TRUE,
|
|
FALSE,
|
|
NULL);
|
|
if (DoneEvent == NULL) {
|
|
Error = GetLastError();
|
|
fprintf(stderr, "CreateEvent failed %d\n",Error);
|
|
exit(Error);
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// Create timer that will indicate the test is complete
|
|
//
|
|
Status = NtCreateTimer(&DoneEvent,
|
|
MAXIMUM_ALLOWED,
|
|
NULL,
|
|
NotificationTimer);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
fprintf(stderr, "NtCreateTimer failed %08lx\n",Status);
|
|
exit(Status);
|
|
}
|
|
|
|
DueTime.QuadPart = (ULONGLONG)SleepInterval * -10000;
|
|
Status = NtSetTimer(DoneEvent,
|
|
&DueTime,
|
|
NULL,
|
|
NULL,
|
|
FALSE,
|
|
0,
|
|
NULL);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
fprintf(stderr, "NtSetTimer failed %08lx\n",Status);
|
|
exit(Status);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
int
|
|
_CRTAPI1
|
|
main (
|
|
int argc,
|
|
char *argv[]
|
|
)
|
|
{
|
|
DWORD Error;
|
|
PMODULE ModuleList;
|
|
ULONG ActiveSource=(ULONG)-1;
|
|
BOOLEAN Enabled;
|
|
CHAR SymPath[256];
|
|
SYSTEM_BASIC_INFORMATION BasicInfo;
|
|
SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION SystemInfoBegin[32];
|
|
SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION SystemInfoEnd[32];
|
|
NTSTATUS Status;
|
|
TIME_FIELDS Time;
|
|
LARGE_INTEGER Elapsed,Idle,Kernel,User;
|
|
LARGE_INTEGER TotalElapsed, TotalIdle, TotalKernel, TotalUser;
|
|
int i;
|
|
PMODULE ZoomModule;
|
|
|
|
//
|
|
// Initialize profile source information
|
|
//
|
|
|
|
InitializeProfileSourceInfo();
|
|
|
|
//
|
|
// Initialize SourceMaxiumum
|
|
//
|
|
|
|
for (SourceMaximum = 0; Source[SourceMaximum].Name; SourceMaximum++) ;
|
|
|
|
//
|
|
// Get initial parameters
|
|
//
|
|
GetConfiguration(argc, argv);
|
|
|
|
//
|
|
// Initialize imagehlp
|
|
//
|
|
Symbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL);
|
|
Symbol->MaxNameLength = MAX_SYMNAME_SIZE;
|
|
SymInitialize( SymHandle, NULL, FALSE );
|
|
GetEnvironmentVariable("windir",SymPath, sizeof(SymPath));
|
|
SymSetSearchPath(SymHandle,SymPath);
|
|
|
|
//
|
|
// Get information on kernel modules
|
|
//
|
|
if (SymHandle == (HANDLE)-1) {
|
|
ModuleList = GetKernelModuleInformation();
|
|
} else {
|
|
ModuleList = GetProcessModuleInformation(SymHandle);
|
|
}
|
|
|
|
//
|
|
// Any remaining entries on the ZoomList are liable to be errors.
|
|
//
|
|
ZoomModule = ZoomList;
|
|
while (ZoomModule != NULL) {
|
|
fprintf(stderr, "Zoomed module %s not found\n",ZoomModule->Name);
|
|
ZoomModule = ZoomModule->Next;
|
|
}
|
|
ZoomList = NULL;
|
|
|
|
//
|
|
// Bypass any relevant security
|
|
//
|
|
RtlAdjustPrivilege(SE_SYSTEM_PROFILE_PRIVILEGE,
|
|
TRUE,
|
|
FALSE,
|
|
&Enabled);
|
|
|
|
//
|
|
// Create necessary profiles
|
|
//
|
|
CreateProfiles(ModuleList);
|
|
|
|
//
|
|
// Set priority up to realtime to minimize timing glitches.
|
|
//
|
|
SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS);
|
|
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);
|
|
|
|
//
|
|
// Wait for test to complete.
|
|
//
|
|
SetConsoleCtrlHandler(CtrlcH,TRUE);
|
|
CreateDoneEvent();
|
|
|
|
if (SleepInterval == 0) {
|
|
fprintf(stderr,"Waiting for ctrl-c\n");
|
|
} else {
|
|
fprintf(stderr, "Waiting for %d seconds\n", SleepInterval/1000);
|
|
}
|
|
Status = NtQuerySystemInformation(SystemBasicInformation,
|
|
&BasicInfo,
|
|
sizeof(BasicInfo),
|
|
NULL);
|
|
if (!NT_SUCCESS(Status)) {
|
|
fprintf(stderr, "Failed to query basic information %08lx\n",Status);
|
|
exit(Status);
|
|
}
|
|
|
|
Status = NtQuerySystemInformation(SystemProcessorPerformanceInformation,
|
|
(PVOID)&SystemInfoBegin,
|
|
sizeof(SystemInfoBegin),
|
|
NULL);
|
|
if (!NT_SUCCESS(Status)) {
|
|
fprintf(stderr, "Failed to query starting processor performance information %08lx\n",Status);
|
|
exit(Status);
|
|
}
|
|
do {
|
|
ActiveSource = NextSource(ActiveSource, ModuleList);
|
|
Error = WaitForSingleObject(DoneEvent, ChangeInterval);
|
|
} while ( Error == WAIT_TIMEOUT );
|
|
|
|
StopSource(ActiveSource, ModuleList);
|
|
|
|
NtQuerySystemInformation(SystemProcessorPerformanceInformation,
|
|
(PVOID)&SystemInfoEnd,
|
|
sizeof(SystemInfoEnd),
|
|
NULL);
|
|
if (!NT_SUCCESS(Status)) {
|
|
fprintf(stderr, "Failed to query ending processor performance information %08lx\n",Status);
|
|
exit(Status);
|
|
}
|
|
//
|
|
// Reduce priority
|
|
//
|
|
SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS);
|
|
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL);
|
|
|
|
SetConsoleCtrlHandler(CtrlcH,FALSE);
|
|
|
|
//
|
|
// Restore privilege
|
|
//
|
|
RtlAdjustPrivilege(SE_SYSTEM_PROFILE_PRIVILEGE,
|
|
Enabled,
|
|
FALSE,
|
|
&Enabled);
|
|
|
|
//
|
|
// Output time information
|
|
//
|
|
|
|
TotalElapsed.QuadPart = 0;
|
|
TotalIdle.QuadPart = 0;
|
|
TotalKernel.QuadPart = 0;
|
|
TotalUser.QuadPart = 0;
|
|
for (i=0; i<BasicInfo.NumberOfProcessors; i++) {
|
|
Kernel.QuadPart = SystemInfoEnd[i].KernelTime.QuadPart - SystemInfoBegin[i].KernelTime.QuadPart;
|
|
User.QuadPart = SystemInfoEnd[i].UserTime.QuadPart - SystemInfoBegin[i].UserTime.QuadPart;
|
|
Idle.QuadPart = SystemInfoEnd[i].IdleTime.QuadPart - SystemInfoBegin[i].IdleTime.QuadPart;
|
|
Elapsed.QuadPart = Kernel.QuadPart + User.QuadPart + Idle.QuadPart;
|
|
|
|
TotalKernel.QuadPart += Kernel.QuadPart;
|
|
TotalUser.QuadPart += User.QuadPart;
|
|
TotalIdle.QuadPart += Idle.QuadPart;
|
|
TotalElapsed.QuadPart += Elapsed.QuadPart;
|
|
printf("P%d ",i);
|
|
RtlTimeToTimeFields(&Kernel, &Time);
|
|
printf(" K %ld:%02ld:%02ld.%03ld (%d%%)",
|
|
Time.Hour,
|
|
Time.Minute,
|
|
Time.Second,
|
|
Time.Milliseconds,
|
|
100*Kernel.QuadPart / Elapsed.QuadPart);
|
|
|
|
RtlTimeToTimeFields(&User, &Time);
|
|
printf(" U %ld:%02ld:%02ld.%03ld (%d%%)",
|
|
Time.Hour,
|
|
Time.Minute,
|
|
Time.Second,
|
|
Time.Milliseconds,
|
|
100*User.QuadPart / Elapsed.QuadPart);
|
|
|
|
RtlTimeToTimeFields(&Idle, &Time);
|
|
printf(" I %ld:%02ld:%02ld.%03ld (%d%%)\n",
|
|
Time.Hour,
|
|
Time.Minute,
|
|
Time.Second,
|
|
Time.Milliseconds,
|
|
100*Idle.QuadPart / Elapsed.QuadPart);
|
|
}
|
|
|
|
if (BasicInfo.NumberOfProcessors > 1) {
|
|
printf("TOTAL");
|
|
RtlTimeToTimeFields(&TotalKernel, &Time);
|
|
printf(" K %ld:%02ld:%02ld.%03ld (%d%%)",
|
|
Time.Hour,
|
|
Time.Minute,
|
|
Time.Second,
|
|
Time.Milliseconds,
|
|
100*Kernel.QuadPart / Elapsed.QuadPart);
|
|
|
|
RtlTimeToTimeFields(&TotalUser, &Time);
|
|
printf(" U %ld:%02ld:%02ld.%03ld (%d%%)",
|
|
Time.Hour,
|
|
Time.Minute,
|
|
Time.Second,
|
|
Time.Milliseconds,
|
|
100*User.QuadPart / Elapsed.QuadPart);
|
|
|
|
RtlTimeToTimeFields(&TotalIdle, &Time);
|
|
printf(" I %ld:%02ld:%02ld.%03ld (%d%%)\n",
|
|
Time.Hour,
|
|
Time.Minute,
|
|
Time.Second,
|
|
Time.Milliseconds,
|
|
100*Idle.QuadPart / Elapsed.QuadPart);
|
|
|
|
}
|
|
|
|
//
|
|
// Output results
|
|
//
|
|
OutputResults(stdout, ModuleList);
|
|
|
|
return(0);
|
|
}
|
|
|
|
PMODULE
|
|
GetProcessModuleInformation(
|
|
IN HANDLE ProcessHandle
|
|
)
|
|
{
|
|
PROCESS_BASIC_INFORMATION BasicInfo;
|
|
PLIST_ENTRY LdrHead;
|
|
PEB_LDR_DATA Ldr;
|
|
PPEB_LDR_DATA LdrAddress;
|
|
LDR_DATA_TABLE_ENTRY LdrEntry;
|
|
PLDR_DATA_TABLE_ENTRY LdrEntryAddress;
|
|
PLIST_ENTRY LdrNext;
|
|
UNICODE_STRING Pathname;
|
|
WCHAR PathnameBuffer[500];
|
|
PEB Peb;
|
|
NTSTATUS Status;
|
|
BOOLEAN Success;
|
|
PMODULE NewModule;
|
|
PMODULE Root=NULL;
|
|
CHAR ModuleName[100];
|
|
ANSI_STRING AnsiString;
|
|
|
|
//
|
|
// Get Peb address.
|
|
//
|
|
|
|
Status = NtQueryInformationProcess(ProcessHandle,
|
|
ProcessBasicInformation,
|
|
&BasicInfo,
|
|
sizeof(BasicInfo),
|
|
NULL
|
|
);
|
|
if (!NT_SUCCESS(Status)) {
|
|
fprintf(stderr, "NtQueryInformationProcess failed status %08lx\n",Status);
|
|
return NULL;
|
|
}
|
|
if (BasicInfo.PebBaseAddress == NULL) {
|
|
fprintf(stderr, "GetProcessModuleInformation: process has no Peb.\n");
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Read Peb to get Ldr.
|
|
//
|
|
|
|
Success = ReadProcessMemory(ProcessHandle,
|
|
BasicInfo.PebBaseAddress,
|
|
&Peb,
|
|
sizeof(Peb),
|
|
NULL);
|
|
if (!Success) {
|
|
fprintf(stderr, "ReadProcessMemory to get the PEB failed, error %d\n", GetLastError());
|
|
return(NULL);
|
|
}
|
|
|
|
LdrAddress = Peb.Ldr;
|
|
if (LdrAddress == NULL) {
|
|
fprintf(stderr, "Process's LdrAddress is NULL\n");
|
|
return(NULL);
|
|
}
|
|
|
|
//
|
|
// Read Ldr to get Ldr entries.
|
|
//
|
|
|
|
Success = ReadProcessMemory(ProcessHandle,
|
|
LdrAddress,
|
|
&Ldr,
|
|
sizeof(Ldr),
|
|
NULL);
|
|
if (!Success) {
|
|
fprintf(stderr, "ReadProcessMemory to get Ldr entries failed, errror %d\n", GetLastError());
|
|
return(NULL);
|
|
}
|
|
|
|
//
|
|
// Read Ldr table entries to get image information.
|
|
//
|
|
|
|
if (Ldr.InLoadOrderModuleList.Flink == NULL) {
|
|
fprintf(stderr, "Ldr.InLoadOrderModuleList == NULL\n");
|
|
return(NULL);
|
|
}
|
|
LdrHead = &LdrAddress->InLoadOrderModuleList;
|
|
Success = ReadProcessMemory(ProcessHandle,
|
|
&LdrHead->Flink,
|
|
&LdrNext,
|
|
sizeof(LdrNext),
|
|
NULL);
|
|
if (!Success) {
|
|
fprintf(stderr, "ReadProcessMemory to get Ldr head failed, errror %d\n", GetLastError());
|
|
return(NULL);
|
|
}
|
|
|
|
//
|
|
// Loop through InLoadOrderModuleList.
|
|
//
|
|
|
|
while (LdrNext != LdrHead) {
|
|
LdrEntryAddress = CONTAINING_RECORD(LdrNext,
|
|
LDR_DATA_TABLE_ENTRY,
|
|
InLoadOrderLinks);
|
|
Success = ReadProcessMemory(ProcessHandle,
|
|
LdrEntryAddress,
|
|
&LdrEntry,
|
|
sizeof(LdrEntry),
|
|
NULL);
|
|
if (!Success) {
|
|
fprintf(stderr, "ReadProcessMemory to get LdrEntry failed, errror %d\n", GetLastError());
|
|
return(NULL);
|
|
}
|
|
|
|
//
|
|
// Get copy of image name.
|
|
//
|
|
|
|
Pathname = LdrEntry.BaseDllName;
|
|
Pathname.Buffer = &PathnameBuffer[0];
|
|
Success = ReadProcessMemory(ProcessHandle,
|
|
LdrEntry.BaseDllName.Buffer,
|
|
Pathname.Buffer,
|
|
Pathname.MaximumLength,
|
|
NULL);
|
|
if (!Success) {
|
|
fprintf(stderr, "ReadProcessMemory to get image name failed, errror %d\n", GetLastError());
|
|
return(NULL);
|
|
}
|
|
|
|
//
|
|
// Create module
|
|
//
|
|
AnsiString.Buffer = ModuleName;
|
|
AnsiString.MaximumLength = sizeof(ModuleName);
|
|
AnsiString.Length = 0;
|
|
RtlUnicodeStringToAnsiString(&AnsiString, &Pathname, FALSE);
|
|
ModuleName[AnsiString.Length] = '\0';
|
|
|
|
NewModule = CreateNewModule(ProcessHandle,
|
|
ModuleName,
|
|
(ULONG)LdrEntry.DllBase,
|
|
LdrEntry.SizeOfImage);
|
|
|
|
ModuleCount += 1;
|
|
NewModule->Next = Root;
|
|
Root = NewModule;
|
|
|
|
LdrNext = LdrEntry.InLoadOrderLinks.Flink;
|
|
}
|
|
|
|
|
|
return(Root);
|
|
}
|
|
|
|
PMODULE
|
|
GetKernelModuleInformation(
|
|
VOID
|
|
)
|
|
{
|
|
PRTL_PROCESS_MODULES Modules;
|
|
PRTL_PROCESS_MODULE_INFORMATION Module;
|
|
NTSTATUS Status;
|
|
PUCHAR Buffer;
|
|
ULONG BufferSize = 32*1024*1024;
|
|
PMODULE Root=NULL;
|
|
PMODULE NewModule;
|
|
ULONG i;
|
|
PLIST_ENTRY ListEntry;
|
|
|
|
while (TRUE) {
|
|
Buffer = malloc(BufferSize);
|
|
if (Buffer == NULL) {
|
|
fprintf(stderr, "Module buffer allocation failed\n");
|
|
exit(0);
|
|
}
|
|
|
|
Status = NtQuerySystemInformation(SystemModuleInformation,
|
|
Buffer,
|
|
BufferSize,
|
|
&BufferSize);
|
|
if (NT_SUCCESS(Status)) {
|
|
break;
|
|
}
|
|
if (Status == STATUS_INFO_LENGTH_MISMATCH) {
|
|
free(Buffer);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
Modules = (PRTL_PROCESS_MODULES)Buffer;
|
|
ModuleCount = Modules->NumberOfModules;
|
|
for (i=0; i < ModuleCount; i++) {
|
|
Module = &Modules->Modules[i];
|
|
if ((ULONG)Module->ImageBase >= 0x80000000) {
|
|
NewModule = CreateNewModule(NULL,
|
|
Module->FullPathName+Module->OffsetToFileName,
|
|
(ULONG)Module->ImageBase,
|
|
Module->ImageSize);
|
|
NewModule->Next = Root;
|
|
Root = NewModule;
|
|
}
|
|
}
|
|
|
|
return(Root);
|
|
}
|
|
|
|
VOID
|
|
CreateProfiles(
|
|
IN PMODULE Root
|
|
)
|
|
{
|
|
PMODULE Current;
|
|
KPROFILE_SOURCE ProfileSource;
|
|
NTSTATUS Status;
|
|
PRATE_DATA Rate;
|
|
ULONG ProfileSourceIndex;
|
|
|
|
for (ProfileSourceIndex=0; ProfileSourceIndex < SourceMaximum != 0; ProfileSourceIndex++) {
|
|
ProfileSource = Source[ProfileSourceIndex].ProfileSource;
|
|
if (Source[ProfileSourceIndex].Interval != 0) {
|
|
Current = Root;
|
|
while (Current != NULL) {
|
|
Rate = &Current->Rate[ProfileSourceIndex];
|
|
Rate->StartTime = 0;
|
|
Rate->TotalTime = 0;
|
|
Rate->TotalCount = 0;
|
|
Rate->CurrentCount = 0;
|
|
if (Current->Zoom) {
|
|
Rate->ProfileBuffer = malloc((Current->Length / ZOOM_BUCKET)*sizeof(ULONG));
|
|
if (Rate->ProfileBuffer == NULL) {
|
|
fprintf(stderr,
|
|
"Zoom buffer allocation for %s failed\n",
|
|
Current->Name);
|
|
exit(1);
|
|
}
|
|
ZeroMemory(Rate->ProfileBuffer, sizeof(ULONG)*(Current->Length / ZOOM_BUCKET));
|
|
Status = NtCreateProfile(&Rate->ProfileHandle,
|
|
Current->Process,
|
|
(PVOID)Current->Base,
|
|
Current->Length,
|
|
LOG2_ZOOM_BUCKET,
|
|
Rate->ProfileBuffer,
|
|
sizeof(ULONG)*(Current->Length / ZOOM_BUCKET),
|
|
ProfileSource,
|
|
(KAFFINITY)-1);
|
|
if (!NT_SUCCESS(Status)) {
|
|
fprintf(stderr,
|
|
"NtCreateProfile on zoomed module %s, source %d failed %08lx\n",
|
|
Current->Name,
|
|
ProfileSource,
|
|
Status);
|
|
fprintf(stderr,
|
|
"Base %08lx\nLength %08lx\nBufferLength %08lx\n",
|
|
(PVOID)Current->Base,
|
|
Current->Length,
|
|
Current->Length / ZOOM_BUCKET);
|
|
|
|
exit(1);
|
|
}
|
|
} else {
|
|
Status = NtCreateProfile(&Rate->ProfileHandle,
|
|
Current->Process,
|
|
(PVOID)Current->Base,
|
|
Current->Length,
|
|
31,
|
|
&Rate->CurrentCount,
|
|
sizeof(Rate->CurrentCount),
|
|
ProfileSource,
|
|
(KAFFINITY)-1);
|
|
if (!NT_SUCCESS(Status)) {
|
|
fprintf(stderr,
|
|
"NtCreateProfile on module %s, source %d failed %08lx\n",
|
|
Current->Name,
|
|
ProfileSource,
|
|
Status);
|
|
exit(1);
|
|
}
|
|
}
|
|
Current = Current->Next;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
GetConfiguration(
|
|
int argc,
|
|
char *argv[]
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Gets configuration for this run.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None, exits on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
KPROFILE_SOURCE ProfileSource;
|
|
NTSTATUS Status;
|
|
ULONG ThisInterval;
|
|
PMODULE ZoomModule;
|
|
DWORD Pid;
|
|
int i;
|
|
ULONG ProfileSourceIndex;
|
|
|
|
for (i=1; i < argc; i++) {
|
|
if ((argv[i][0] == '-') ||
|
|
(argv[i][0] == '/')) {
|
|
switch (argv[i][1]) {
|
|
case 'z':
|
|
case 'Z':
|
|
if (++i == argc) {
|
|
fprintf(stderr,
|
|
"KERNRATE: '-z modulename' option requires modulename\n");
|
|
Usage();
|
|
}
|
|
ZoomModule = malloc(sizeof(MODULE)+sizeof(RATE_DATA)*SourceMaximum);
|
|
if (ZoomModule==NULL) {
|
|
fprintf(stderr, "Allocation of zoom module %s failed\n",argv[i]);
|
|
exit(1);
|
|
}
|
|
strncpy(ZoomModule->Name,
|
|
argv[i],
|
|
8);
|
|
ZoomModule->Name[8] = '\0';
|
|
ZoomModule->Zoom = TRUE;
|
|
ZoomModule->Next = ZoomList;
|
|
ZoomList = ZoomModule;
|
|
break;
|
|
|
|
case 'c':
|
|
case 'C':
|
|
//
|
|
// Set change interval.
|
|
//
|
|
if (++i == argc) {
|
|
fprintf(stderr,
|
|
"KERNRATE: '-c N' option requires milliseconds\n");
|
|
Usage();
|
|
}
|
|
ChangeInterval = atoi(argv[i]);
|
|
if (ChangeInterval == 0) {
|
|
fprintf(stderr,
|
|
"KERNRATE: Invalid option '-c %s'\n",
|
|
argv[i]);
|
|
Usage();
|
|
}
|
|
break;
|
|
|
|
case 's':
|
|
case 'S':
|
|
//
|
|
// Set Sleep interval
|
|
//
|
|
if (++i == argc) {
|
|
fprintf(stderr,
|
|
"KERNRATE: '-s N' option requires seconds\n");
|
|
Usage();
|
|
}
|
|
SleepInterval = atoi(argv[i]) * 1000;
|
|
if (SleepInterval == 0) {
|
|
fprintf(stderr,
|
|
"KERNRATE: Invalid option '-s %s'\n",
|
|
argv[i]);
|
|
Usage();
|
|
}
|
|
break;
|
|
|
|
case 'r':
|
|
case 'R':
|
|
//
|
|
// Turn on RAW bucket dump
|
|
//
|
|
RawData = TRUE;
|
|
break;
|
|
|
|
case 'p':
|
|
case 'P':
|
|
//
|
|
// Monitor given process instead of kernel
|
|
//
|
|
if (++i == argc) {
|
|
fprintf(stderr,
|
|
"KERNRATE: '-p processid' option requires a process id\n");
|
|
Usage();
|
|
}
|
|
Pid = atoi(argv[i]);
|
|
SymHandle = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION,
|
|
FALSE,
|
|
Pid);
|
|
if (SymHandle==NULL) {
|
|
fprintf(stderr, "KERNRATE: OpenProcess(%d) failed %d\n",Pid,GetLastError());
|
|
exit(0);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
fprintf(stderr,
|
|
"KERNRATE: Unknown option %s\n",argv[i]);
|
|
Usage();
|
|
break;
|
|
}
|
|
} else {
|
|
fprintf(stderr,
|
|
"KERNRATE: Invalid switch %s\n",argv[i]);
|
|
Usage();
|
|
}
|
|
}
|
|
|
|
//
|
|
// Determine supported sources
|
|
//
|
|
for (ProfileSourceIndex = 0; ProfileSourceIndex < SourceMaximum; ProfileSourceIndex++) {
|
|
ProfileSource = Source[ProfileSourceIndex].ProfileSource;
|
|
NtSetIntervalProfile(Source[ProfileSourceIndex].DesiredInterval, ProfileSource);
|
|
Status = NtQueryIntervalProfile(ProfileSource, &ThisInterval);
|
|
if ((NT_SUCCESS(Status)) &&
|
|
(ThisInterval > 0)) {
|
|
printf("Recording %s at %d events/hit\n",
|
|
Source[ProfileSourceIndex].Name,
|
|
ThisInterval);
|
|
Source[ProfileSourceIndex].Interval = ThisInterval;
|
|
} else {
|
|
Source[ProfileSourceIndex].Interval = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
InitializeProfileSourceInfo (
|
|
VOID
|
|
)
|
|
{
|
|
#if defined(_X86_)
|
|
UNICODE_STRING DriverName;
|
|
NTSTATUS status;
|
|
OBJECT_ATTRIBUTES ObjA;
|
|
IO_STATUS_BLOCK IOSB;
|
|
UCHAR buffer[400];
|
|
ULONG i, j, Count;
|
|
PEVENTID Event;
|
|
HANDLE DriverHandle;
|
|
|
|
//
|
|
// Open PStat driver
|
|
//
|
|
|
|
RtlInitUnicodeString(&DriverName, L"\\Device\\PStat");
|
|
InitializeObjectAttributes(
|
|
&ObjA,
|
|
&DriverName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
0,
|
|
0 );
|
|
|
|
status = NtOpenFile (
|
|
&DriverHandle, // return handle
|
|
SYNCHRONIZE | FILE_READ_DATA, // desired access
|
|
&ObjA, // Object
|
|
&IOSB, // io status block
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE, // share access
|
|
FILE_SYNCHRONOUS_IO_ALERT // open options
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
return ;
|
|
}
|
|
|
|
//
|
|
// Determine how many events the driver provides
|
|
//
|
|
|
|
Event = (PEVENTID) buffer;
|
|
Count = 0;
|
|
do {
|
|
*((PULONG) buffer) = Count;
|
|
Count += 1;
|
|
|
|
status = NtDeviceIoControlFile(
|
|
DriverHandle,
|
|
(HANDLE) NULL, // event
|
|
(PIO_APC_ROUTINE) NULL,
|
|
(PVOID) NULL,
|
|
&IOSB,
|
|
PSTAT_QUERY_EVENTS,
|
|
buffer, // input buffer
|
|
sizeof (buffer),
|
|
NULL, // output buffer
|
|
0
|
|
);
|
|
} while (NT_SUCCESS(status));
|
|
|
|
//
|
|
// Detemine how many static events there are
|
|
//
|
|
|
|
for (i = 0; Source[i].Name; i++) ;
|
|
|
|
//
|
|
// Allocate memory for static events, plus the driver
|
|
// provided events
|
|
//
|
|
|
|
Source = malloc(sizeof(SOURCE) * (Count+i));
|
|
ZeroMemory (Source, sizeof(SOURCE) * (Count+i));
|
|
|
|
//
|
|
// copy static events to new list
|
|
//
|
|
|
|
for (j=0; j < i; j++) {
|
|
Source[j] = StaticSources[j];
|
|
}
|
|
|
|
//
|
|
// Append the driver provided events to new list
|
|
//
|
|
|
|
Count -= 1;
|
|
for (i=0; i < Count; i++) {
|
|
*((PULONG) buffer) = i;
|
|
NtDeviceIoControlFile(
|
|
DriverHandle,
|
|
(HANDLE) NULL, // event
|
|
(PIO_APC_ROUTINE) NULL,
|
|
(PVOID) NULL,
|
|
&IOSB,
|
|
PSTAT_QUERY_EVENTS,
|
|
buffer, // input buffer
|
|
sizeof (buffer),
|
|
NULL, // output buffer
|
|
0
|
|
);
|
|
|
|
Source[j].Name = _strdup (Event->Buffer + Event->DescriptionOffset);
|
|
Source[j].ProfileSource = Event->ProfileSource;
|
|
Source[j].DesiredInterval = Event->SuggestedIntervalBase;
|
|
j++;
|
|
}
|
|
|
|
NtClose (DriverHandle);
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
ULONG
|
|
NextSource(
|
|
IN ULONG CurrentSource,
|
|
IN PMODULE ModuleList
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Stops the current profile source and starts the next one.
|
|
|
|
If a CurrentSource of -1 is passed in, no source will
|
|
be stopped and the first active source will be started.
|
|
|
|
Arguments:
|
|
|
|
CurrentSource - Supplies the current profile source
|
|
|
|
ModuleList - Supplies the list of modules whose soruces are to be changed
|
|
|
|
Return Value:
|
|
|
|
Returns the new current profile source
|
|
|
|
--*/
|
|
|
|
{
|
|
if (CurrentSource != (ULONG) -1) {
|
|
StopSource(CurrentSource, ModuleList);
|
|
}
|
|
|
|
//
|
|
// Iterate through the available sources to find the
|
|
// next active source to be started.
|
|
//
|
|
do {
|
|
if (CurrentSource == (ULONG) -1) {
|
|
CurrentSource = 0;
|
|
} else {
|
|
CurrentSource = CurrentSource+1;
|
|
if (CurrentSource == SourceMaximum) {
|
|
CurrentSource = 0;
|
|
}
|
|
}
|
|
} while ( Source[CurrentSource].Interval == 0);
|
|
|
|
StartSource(CurrentSource,ModuleList);
|
|
|
|
return(CurrentSource);
|
|
}
|
|
|
|
|
|
VOID
|
|
StopSource(
|
|
IN ULONG ProfileSourceIndex,
|
|
IN PMODULE ModuleList
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Stops all profile objects for a given source
|
|
|
|
Arguments:
|
|
|
|
ProfileSource - Supplies the source to be stopped.
|
|
|
|
ModuleList - Supplies the list of modules whose profiles are to be stopped
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PMODULE Current;
|
|
NTSTATUS Status;
|
|
ULONGLONG StopTime;
|
|
ULONGLONG ElapsedTime;
|
|
|
|
Current = ModuleList;
|
|
while (Current != NULL) {
|
|
Status = NtStopProfile(Current->Rate[ProfileSourceIndex].ProfileHandle);
|
|
GetSystemTimeAsFileTime((LPFILETIME)&StopTime);
|
|
if (!NT_SUCCESS(Status)) {
|
|
fprintf(stderr,
|
|
"NtStopProfile on source %s failed, %08lx\n",
|
|
Source[ProfileSourceIndex].Name,
|
|
Status);
|
|
} else {
|
|
ElapsedTime = StopTime - Current->Rate[ProfileSourceIndex].StartTime;
|
|
Current->Rate[ProfileSourceIndex].TotalTime += ElapsedTime;
|
|
Current->Rate[ProfileSourceIndex].TotalCount += Current->Rate[ProfileSourceIndex].CurrentCount;
|
|
Current->Rate[ProfileSourceIndex].CurrentCount = 0;
|
|
}
|
|
Current = Current->Next;
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
StartSource(
|
|
IN ULONG ProfileSourceIndex,
|
|
IN PMODULE ModuleList
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Starts all profile objects for a given source
|
|
|
|
Arguments:
|
|
|
|
ProfileSource - Supplies the source to be started.
|
|
|
|
ModuleList - Supplies the list of modules whose profiles are to be stopped
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PMODULE Current;
|
|
NTSTATUS Status;
|
|
ULONGLONG StopTime;
|
|
ULONGLONG ElapsedTime;
|
|
|
|
Current = ModuleList;
|
|
while (Current != NULL) {
|
|
GetSystemTimeAsFileTime((LPFILETIME)&Current->Rate[ProfileSourceIndex].StartTime);
|
|
Status = NtStartProfile(Current->Rate[ProfileSourceIndex].ProfileHandle);
|
|
if (!NT_SUCCESS(Status)) {
|
|
fprintf(stderr,
|
|
"NtStartProfile on source %s failed, %08lx\n",
|
|
Source[ProfileSourceIndex].Name,
|
|
Status);
|
|
}
|
|
Current = Current->Next;
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
OutputResults(
|
|
IN FILE *Out,
|
|
IN PMODULE ModuleList
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Outputs the collected data.
|
|
|
|
Arguments:
|
|
|
|
Out - Supplies the FILE * where the output should go.
|
|
|
|
ModuleList - Supplies the list of modules to output
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PMODULE Current;
|
|
PRATE_DATA RateData;
|
|
ULONG i, ProfileSourceIndex;
|
|
DWORD Displacement;
|
|
CHAR SymName[80];
|
|
|
|
//
|
|
// Sum up the total buffers of any zoomed modules
|
|
//
|
|
Current = ModuleList;
|
|
while (Current != NULL) {
|
|
if (Current->Zoom) {
|
|
for (ProfileSourceIndex=0; ProfileSourceIndex < SourceMaximum; ProfileSourceIndex++) {
|
|
if (Source[ProfileSourceIndex].Interval != 0) {
|
|
//
|
|
// Sum the entire profile buffer for this module/source
|
|
//
|
|
RateData = &Current->Rate[ProfileSourceIndex];
|
|
RateData->TotalCount = 0;
|
|
for (i=0; i < Current->Length/ZOOM_BUCKET; i++) {
|
|
RateData->TotalCount += RateData->ProfileBuffer[i];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Current = Current->Next;
|
|
}
|
|
|
|
//
|
|
// Output the results ordered by profile source.
|
|
//
|
|
OutputModuleList(Out, ModuleList, ModuleCount);
|
|
|
|
//
|
|
// For any zoomed modules, create and output a module list
|
|
// consisting of the functions in the module.
|
|
//
|
|
Current = ModuleList;
|
|
while (Current != NULL) {
|
|
if (Current->Zoom) {
|
|
ZoomCount = 0;
|
|
ZoomList = NULL;
|
|
CreateZoomedModuleList(Current);
|
|
if (ZoomList == NULL) {
|
|
fprintf(stderr, "No symbols found for module %s\n",Current->Name);
|
|
} else {
|
|
PMODULE Temp;
|
|
|
|
fprintf(Out, "\n----- Zoomed module %s --------\n",Current->Name);
|
|
OutputModuleList(Out, ZoomList, ZoomCount);
|
|
Temp = ZoomList;
|
|
while (Temp != NULL) {
|
|
ZoomList = ZoomList->Next;
|
|
free(Temp);
|
|
Temp = ZoomList;
|
|
}
|
|
}
|
|
|
|
}
|
|
Current = Current->Next;
|
|
}
|
|
|
|
if (RawData) {
|
|
//
|
|
// Display the raw bucket counts for all zoomed modules
|
|
//
|
|
Current = ModuleList;
|
|
while (Current != NULL) {
|
|
if (Current->Zoom) {
|
|
for (ProfileSourceIndex=0; ProfileSourceIndex < SourceMaximum; ProfileSourceIndex++) {
|
|
if (Source[ProfileSourceIndex].Interval != 0) {
|
|
fprintf(Out,
|
|
"\n---- RAW %s Profile Source %s\n",
|
|
Current->Name,
|
|
Source[ProfileSourceIndex].Name);
|
|
RateData = &Current->Rate[ProfileSourceIndex];
|
|
for (i=0; i<Current->Length/ZOOM_BUCKET; i++) {
|
|
if (RateData->ProfileBuffer[i] > 0) {
|
|
if (!SymGetSymFromAddr(SymHandle, Current->Base+i*ZOOM_BUCKET, &Displacement, Symbol )) {
|
|
fprintf(stderr,
|
|
"No symbol found for bucket at %08lx\n",
|
|
Current->Base + i*ZOOM_BUCKET);
|
|
} else {
|
|
_snprintf(SymName, 80, "%s+0x%x",Symbol->Name,Displacement);
|
|
fprintf(Out,"%-40s %10d\n", SymName,RateData->ProfileBuffer[i]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Current = Current->Next;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
BOOL
|
|
CreateZoomModuleCallback(
|
|
IN LPSTR szSymName,
|
|
IN ULONG Address,
|
|
IN ULONG Size,
|
|
IN PVOID Cxt
|
|
)
|
|
{
|
|
PMODULE Module;
|
|
PRATE_DATA RateData;
|
|
ULONG StartIndex, EndIndex;
|
|
ULONG i, ProfileSourceIndex;
|
|
BOOLEAN HasHits;
|
|
|
|
Module = malloc(sizeof(MODULE)+sizeof(RATE_DATA)*SourceMaximum);
|
|
if (Module == NULL) {
|
|
fprintf(stderr, "CreateZoomModuleCallback: failed to allocate Zoom module\n");
|
|
exit(1);
|
|
}
|
|
Module->Base = Address;
|
|
Module->Length = Size;
|
|
Module->Zoom = FALSE;
|
|
strncpy(Module->Name, szSymName, sizeof(Module->Name));
|
|
|
|
//
|
|
// Compute range in profile buffer to sum.
|
|
//
|
|
StartIndex = (Module->Base - CallbackCurrent->Base) / ZOOM_BUCKET;
|
|
EndIndex = StartIndex + (Module->Length / ZOOM_BUCKET);
|
|
|
|
HasHits = FALSE;
|
|
for (ProfileSourceIndex=0; ProfileSourceIndex < SourceMaximum; ProfileSourceIndex++) {
|
|
if (Source[ProfileSourceIndex].Interval != 0) {
|
|
RateData = &Module->Rate[ProfileSourceIndex];
|
|
RateData->StartTime = CallbackCurrent->Rate[ProfileSourceIndex].StartTime;
|
|
RateData->TotalTime = CallbackCurrent->Rate[ProfileSourceIndex].TotalTime;
|
|
RateData->TotalCount = 0;
|
|
RateData->ProfileHandle = NULL;
|
|
RateData->CurrentCount = 0;
|
|
RateData->ProfileBuffer = NULL;
|
|
|
|
for (i=StartIndex; i < EndIndex; i++) {
|
|
RateData->TotalCount += CallbackCurrent->Rate[ProfileSourceIndex].ProfileBuffer[i];
|
|
}
|
|
if (RateData->TotalCount > 0) {
|
|
HasHits = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the routine has hits add it to the list, otherwise
|
|
// ignore it.
|
|
//
|
|
if (HasHits) {
|
|
Module->Next = ZoomList;
|
|
ZoomList = Module;
|
|
++ZoomCount;
|
|
} else {
|
|
free(Module);
|
|
}
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
VOID
|
|
CreateZoomedModuleList(
|
|
IN PMODULE ZoomModule
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Creates a module list from the functions in a given module
|
|
|
|
Arguments:
|
|
|
|
ZoomModule - Supplies the module whose zoomed module list is to be created
|
|
|
|
Return Value:
|
|
|
|
Pointer to the zoomed module list
|
|
NULL on error.
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOLEAN Success;
|
|
|
|
CallbackCurrent = ZoomModule;
|
|
Success = SymEnumerateSymbols(SymHandle,
|
|
ZoomModule->Base,
|
|
CreateZoomModuleCallback, NULL );
|
|
if (!Success) {
|
|
fprintf(stderr,
|
|
"SymEnumerateSymbols failed module %s\n",
|
|
ZoomModule->Name);
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
OutputModuleList(
|
|
IN FILE *Out,
|
|
IN PMODULE ModuleList,
|
|
IN ULONG NumberModules
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Outputs the given module list
|
|
|
|
Arguments:
|
|
|
|
Out - Supplies the FILE * where the output should go.
|
|
|
|
ModuleList - Supplies the list of modules to output
|
|
|
|
NumberModules - Supplies the number of modules in the list
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
CHAR HeaderString[128];
|
|
PRATE_DATA RateData;
|
|
PRATE_SUMMARY RateSummary;
|
|
PRATE_DATA SummaryData;
|
|
BOOLEAN Header;
|
|
ULONG i, ProfileSourceIndex;
|
|
PMODULE *ModuleArray;
|
|
PMODULE Current;
|
|
SYSTEM_PERFORMANCE_INFORMATION SystemInfoBegin;
|
|
SYSTEM_PERFORMANCE_INFORMATION SystemInfoEnd;
|
|
float Ratio;
|
|
|
|
RateSummary = malloc(SourceMaximum * sizeof (RATE_SUMMARY));
|
|
SummaryData = malloc(SourceMaximum * sizeof (RATE_DATA));
|
|
|
|
ZeroMemory(SummaryData, SourceMaximum * sizeof (RATE_SUMMARY));
|
|
|
|
for (ProfileSourceIndex=0; ProfileSourceIndex < SourceMaximum; ProfileSourceIndex++) {
|
|
SummaryData[ProfileSourceIndex].Rate = 0;
|
|
if (Source[ProfileSourceIndex].Interval != 0) {
|
|
//
|
|
// Walk through the module list and compute the summary
|
|
// and collect the interesting per-module data.
|
|
//
|
|
RateSummary[ProfileSourceIndex].Modules = malloc(NumberModules * sizeof(PMODULE));
|
|
RateSummary[ProfileSourceIndex].ModuleCount = 0;
|
|
RateSummary[ProfileSourceIndex].TotalCount = 0;
|
|
ModuleArray = RateSummary[ProfileSourceIndex].Modules;
|
|
Current = ModuleList;
|
|
while (Current != NULL) {
|
|
RateData = &Current->Rate[ProfileSourceIndex];
|
|
if (RateData->TotalCount > 0) {
|
|
RateSummary[ProfileSourceIndex].TotalCount += RateData->TotalCount;
|
|
//
|
|
// Insert it in sorted position in the array.
|
|
//
|
|
ModuleArray[RateSummary[ProfileSourceIndex].ModuleCount] = Current;
|
|
RateSummary[ProfileSourceIndex].ModuleCount++;
|
|
if (RateSummary[ProfileSourceIndex].ModuleCount > NumberModules) {
|
|
DbgPrint("error, ModuleCount %d > NumberModules %d for Source %s\n",
|
|
RateSummary[ProfileSourceIndex].ModuleCount,
|
|
NumberModules,
|
|
Source[ProfileSourceIndex].Name);
|
|
DbgBreakPoint();
|
|
}
|
|
for (i=0; i<RateSummary[ProfileSourceIndex].ModuleCount; i++) {
|
|
if (RateData->TotalCount > ModuleArray[i]->Rate[ProfileSourceIndex].TotalCount) {
|
|
//
|
|
// insert here
|
|
//
|
|
MoveMemory(&ModuleArray[i+1],
|
|
&ModuleArray[i],
|
|
sizeof(PMODULE)*(RateSummary[ProfileSourceIndex].ModuleCount-i-1));
|
|
ModuleArray[i] = Current;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
Current = Current->Next;
|
|
}
|
|
|
|
if (RateSummary[ProfileSourceIndex].TotalCount > 0) {
|
|
//
|
|
// Output the result
|
|
//
|
|
fprintf(Out,
|
|
"\n%s %Ld hits, %d events per hit --------\n",
|
|
Source[ProfileSourceIndex].Name,
|
|
RateSummary[ProfileSourceIndex].TotalCount,
|
|
Source[ProfileSourceIndex].Interval);
|
|
fprintf(Out," Module Hits msec %%Total Events/Sec\n");
|
|
for (i=0; i < RateSummary[ProfileSourceIndex].ModuleCount; i++) {
|
|
Current = ModuleArray[i];
|
|
fprintf(Out, "%-32s",Current->Name);
|
|
OutputLine(Out,
|
|
ProfileSourceIndex,
|
|
Current,
|
|
&RateSummary[ProfileSourceIndex]);
|
|
SummaryData[ProfileSourceIndex].Rate += Current->Rate[ProfileSourceIndex].Rate;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Output interesting data for the summary.
|
|
//
|
|
sprintf(HeaderString, "\n-------------- INTERESTING SUMMARY DATA ----------------------\n");
|
|
OutputInterestingData(Out, SummaryData, HeaderString);
|
|
|
|
//
|
|
// Output the results ordered by module
|
|
//
|
|
Current = ModuleList;
|
|
while (Current != NULL) {
|
|
Header = FALSE;
|
|
for (ProfileSourceIndex = 0; ProfileSourceIndex < SourceMaximum; ProfileSourceIndex++) {
|
|
if ((Source[ProfileSourceIndex].Interval != 0) &&
|
|
(Current->Rate[ProfileSourceIndex].TotalCount > 0)) {
|
|
if (!Header) {
|
|
fprintf(Out,"\nMODULE %s --------\n",Current->Name);
|
|
fprintf(Out," Source Hits msec %%Total Events/Sec\n");
|
|
Header = TRUE;
|
|
}
|
|
fprintf(Out, "%-32s", Source[ProfileSourceIndex].Name);
|
|
OutputLine(Out,
|
|
ProfileSourceIndex,
|
|
Current,
|
|
&RateSummary[ProfileSourceIndex]);
|
|
}
|
|
}
|
|
//
|
|
// Output interesting data for the module.
|
|
//
|
|
sprintf(HeaderString, "\n-------------- INTERESTING MODULE DATA FOR %s----------------------\n",Current->Name);
|
|
OutputInterestingData(Out, &Current->Rate[0], HeaderString);
|
|
Current = Current->Next;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
OutputLine(
|
|
IN FILE *Out,
|
|
IN ULONG ProfileSourceIndex,
|
|
IN PMODULE Module,
|
|
IN PRATE_SUMMARY RateSummary
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Outputs a line corresponding to the particular module/source
|
|
|
|
Arguments:
|
|
|
|
Out - Supplies the file pointer to output to.
|
|
|
|
ProfileSource - Supplies the source to use
|
|
|
|
Module - Supplies the module to be output
|
|
|
|
RateSummary - Supplies the rate summary for this source
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG Msec;
|
|
ULONGLONG Events;
|
|
|
|
Msec = (ULONG)(Module->Rate[ProfileSourceIndex].TotalTime/10000);
|
|
Events = Module->Rate[ProfileSourceIndex].TotalCount * Source[ProfileSourceIndex].Interval * 1000;
|
|
|
|
fprintf(Out,
|
|
" %7Ld %6d %2d %% ",
|
|
(ULONG) Module->Rate[ProfileSourceIndex].TotalCount,
|
|
(ULONG) Msec,
|
|
(ULONG)(100*Module->Rate[ProfileSourceIndex].TotalCount/
|
|
RateSummary->TotalCount));
|
|
if (Msec > 0) {
|
|
Module->Rate[ProfileSourceIndex].Rate = (ULONGLONG)Events/Msec;
|
|
fprintf(Out,"%10Ld\n",Module->Rate[ProfileSourceIndex].Rate);
|
|
} else {
|
|
Module->Rate[ProfileSourceIndex].Rate = 0;
|
|
fprintf(Out,"---\n");
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
OutputInterestingData(
|
|
IN FILE *Out,
|
|
IN RATE_DATA Data[],
|
|
IN PCHAR Header
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Computes interesting numbers and outputs them.
|
|
|
|
Arguments:
|
|
|
|
Out - Supplies the file pointer to output to.
|
|
|
|
Data - Supplies an array of RATE_DATA. The Rate field is the only interesting part.
|
|
|
|
Header - Supplies header to be printed.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONGLONG Temp1,Temp2;
|
|
float Ratio;
|
|
BOOLEAN DidHeader = FALSE;
|
|
|
|
//
|
|
// Note that we have to do a lot of funky (float)(LONGLONG) casts in order
|
|
// to prevent the weenie x86 compiler from choking.
|
|
//
|
|
|
|
//
|
|
// Compute cycles/instruction and instruction mix data.
|
|
//
|
|
if ((Data[ProfileTotalIssues].Rate != 0) &&
|
|
(Data[ProfileTotalIssues].TotalCount > 10)) {
|
|
if (Data[ProfileTotalCycles].Rate != 0) {
|
|
Ratio = (float)(LONGLONG)(Data[ProfileTotalCycles].Rate)/
|
|
(float)(LONGLONG)(Data[ProfileTotalIssues].Rate);
|
|
if (!DidHeader) {
|
|
fprintf(Out, Header);
|
|
DidHeader = TRUE;
|
|
}
|
|
fprintf(Out, "Cycles per instruction\t\t%6.2f\n", Ratio);
|
|
}
|
|
|
|
Ratio = (float)(LONGLONG)(Data[ProfileLoadInstructions].Rate)/
|
|
(float)(LONGLONG)(Data[ProfileTotalIssues].Rate);
|
|
if (Ratio >= 0.01) {
|
|
if (!DidHeader) {
|
|
fprintf(Out, Header);
|
|
DidHeader = TRUE;
|
|
}
|
|
fprintf(Out, "Load instruction percentage\t%6.2f %%\n",Ratio*100);
|
|
}
|
|
|
|
Ratio = (float)(LONGLONG)(Data[ProfileStoreInstructions].Rate)/
|
|
(float)(LONGLONG)(Data[ProfileTotalIssues].Rate);
|
|
if (Ratio >= 0.01) {
|
|
if (!DidHeader) {
|
|
fprintf(Out, Header);
|
|
DidHeader = TRUE;
|
|
}
|
|
fprintf(Out, "Store instruction percentage\t%6.2f %%\n",Ratio*100);
|
|
}
|
|
|
|
Ratio = (float)(LONGLONG)(Data[ProfileBranchInstructions].Rate)/
|
|
(float)(LONGLONG)(Data[ProfileTotalIssues].Rate);
|
|
if (Ratio >= 0.01) {
|
|
if (!DidHeader) {
|
|
fprintf(Out, Header);
|
|
DidHeader = TRUE;
|
|
}
|
|
fprintf(Out, "Branch instruction percentage\t%6.2f %%\n",Ratio*100);
|
|
}
|
|
|
|
Ratio = (float)(LONGLONG)(Data[ProfileFpInstructions].Rate)/
|
|
(float)(LONGLONG)(Data[ProfileTotalIssues].Rate);
|
|
if (Ratio >= 0.01) {
|
|
if (!DidHeader) {
|
|
fprintf(Out, Header);
|
|
DidHeader = TRUE;
|
|
}
|
|
fprintf(Out, "FP instruction percentage\t%6.2f %%\n",Ratio*100);
|
|
}
|
|
|
|
Ratio = (float)(LONGLONG)(Data[ProfileIntegerInstructions].Rate)/
|
|
(float)(LONGLONG)(Data[ProfileTotalIssues].Rate);
|
|
if (Ratio >= 0.01) {
|
|
if (!DidHeader) {
|
|
fprintf(Out, Header);
|
|
DidHeader = TRUE;
|
|
}
|
|
fprintf(Out, "Integer instruction percentage\t%6.2f %%\n",Ratio*100);
|
|
}
|
|
|
|
//
|
|
// Compute icache hit rate
|
|
//
|
|
if (Data[ProfileIcacheMisses].Rate != 0) {
|
|
Ratio = (float)(LONGLONG)(Data[ProfileTotalIssues].Rate - Data[ProfileIcacheMisses].Rate)/
|
|
(float)(LONGLONG)(Data[ProfileTotalIssues].Rate);
|
|
if (!DidHeader) {
|
|
fprintf(Out, Header);
|
|
DidHeader = TRUE;
|
|
}
|
|
fprintf(Out, "Icache hit rate\t\t\t%6.2f %%\n", Ratio*100);
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Compute dcache hit rate
|
|
//
|
|
Temp1 = Data[ProfileLoadInstructions].Rate + Data[ProfileStoreInstructions].Rate;
|
|
if ((Data[ProfileDcacheMisses].Rate != 0) &&
|
|
(Temp1 != 0) &&
|
|
(Data[ProfileDcacheMisses].TotalCount > 10)) {
|
|
|
|
Temp2 = Temp1 - Data[ProfileDcacheMisses].Rate;
|
|
Ratio = (float)(LONGLONG)(Temp2)/(float)(LONGLONG)Temp1;
|
|
if (!DidHeader) {
|
|
fprintf(Out, Header);
|
|
DidHeader = TRUE;
|
|
}
|
|
fprintf(Out, "Dcache hit rate\t\t\t%6.2f %%\n", Ratio*100);
|
|
}
|
|
|
|
//
|
|
// Compute branch prediction hit percentage
|
|
//
|
|
if ((Data[ProfileBranchInstructions].Rate != 0) &&
|
|
(Data[ProfileBranchMispredictions].Rate != 0) &&
|
|
(Data[ProfileBranchInstructions].TotalCount > 10)) {
|
|
Ratio = (float)(LONGLONG)(Data[ProfileBranchInstructions].Rate-Data[ProfileBranchMispredictions].Rate)/
|
|
(float)(LONGLONG)(Data[ProfileBranchInstructions].Rate);
|
|
if (!DidHeader) {
|
|
fprintf(Out, Header);
|
|
DidHeader = TRUE;
|
|
}
|
|
fprintf(Out, "Branch predict hit percentage\t%6.2f %%\n", Ratio*100);
|
|
}
|
|
}
|
|
|
|
PMODULE
|
|
CreateNewModule(
|
|
IN HANDLE ProcessHandle,
|
|
IN PCHAR ModuleName,
|
|
IN ULONG ImageBase,
|
|
IN ULONG ImageSize
|
|
)
|
|
{
|
|
PMODULE NewModule;
|
|
PMODULE ZoomModule;
|
|
PMODULE *ZoomPrevious;
|
|
|
|
NewModule = malloc(sizeof(MODULE)+sizeof(RATE_DATA)*SourceMaximum);
|
|
if (NewModule == NULL) {
|
|
fprintf(stderr,"Allocation of NewModule for %s failed\n",ModuleName);
|
|
exit(1);
|
|
}
|
|
NewModule->Zoom = FALSE;
|
|
strncpy(NewModule->Name,
|
|
ModuleName,
|
|
8);
|
|
NewModule->Name[8] = '\0';
|
|
if (strchr(NewModule->Name, '.')) {
|
|
*strchr(NewModule->Name, '.') = '\0';
|
|
}
|
|
|
|
//
|
|
// See if this module is on the zoom list.
|
|
// If so we will use the MODULE that was allocated when
|
|
// the zoom list was created.
|
|
//
|
|
ZoomModule = ZoomList;
|
|
ZoomPrevious = &ZoomList;
|
|
while (ZoomModule != NULL) {
|
|
if (_stricmp(ZoomModule->Name,NewModule->Name)==0) {
|
|
|
|
//
|
|
// found a match
|
|
//
|
|
free(NewModule);
|
|
NewModule = ZoomModule;
|
|
*ZoomPrevious = ZoomModule->Next;
|
|
|
|
//
|
|
// Load symbols
|
|
//
|
|
if (SymLoadModule(ProcessHandle ? ProcessHandle : (HANDLE)-1,
|
|
NULL,
|
|
ModuleName,
|
|
NULL,
|
|
ImageBase,
|
|
ImageSize)) {
|
|
fprintf(stderr,
|
|
"Symbols loaded %08lx %s\n",
|
|
ImageBase,
|
|
ModuleName);
|
|
} else {
|
|
fprintf(stderr,
|
|
"***Could not load symbols %08lx %s\n",
|
|
ImageBase,
|
|
ModuleName);
|
|
}
|
|
|
|
break;
|
|
}
|
|
ZoomPrevious = &ZoomModule->Next;
|
|
ZoomModule = ZoomModule->Next;
|
|
}
|
|
NewModule->Process = ProcessHandle;
|
|
NewModule->Base = ImageBase;
|
|
NewModule->Length = ImageSize;
|
|
ZeroMemory(NewModule->Rate, sizeof(RATE_DATA) * SourceMaximum);
|
|
return(NewModule);
|
|
}
|