windows-nt/Source/XPSP1/NT/base/tools/kernprof/kernprof.c

1514 lines
48 KiB
C
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1990 Microsoft Corporation
Module Name:
kernprof.c
Abstract:
This module contains the implementation of a kernel profiler.
It uses dbghelp for symbols and image information and
creates profile objects for each modules it finds loaded
when it starts.
Usage:
See below
Author:
Lou Perazzoli (loup) 29-Sep-1990
Envirnoment:
Revision History:
--*/
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <dbghelp.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <memory.h>
#include <ctype.h>
#include <..\pperf\pstat.h>
#define SYM_HANDLE INVALID_HANDLE_VALUE
#define DBG_PROFILE 0
#define MAX_BYTE_PER_LINE 72
#define MAX_PROFILE_COUNT 100
#define MAX_BUCKET_SHIFT 31 // 2GBytes
#define MAX_BUCKET_SIZE 0x80000000U
typedef struct _PROFILE_BLOCK {
HANDLE Handle[MAXIMUM_PROCESSORS];
PVOID ImageBase;
PULONG CodeStart;
SIZE_T CodeLength;
PULONG Buffer[MAXIMUM_PROCESSORS];
ULONG BufferSize;
ULONG BucketSize;
LPSTR ModuleName;
ULONG ModuleHitCount[MAXIMUM_PROCESSORS];
BOOLEAN SymbolsLoaded;
} PROFILE_BLOCK;
//
// This really should go into a header file but....
//
typedef struct _PROFILE_CONTROL_BLOCK {
BOOLEAN Stop;
char FileName[MAX_PATH];
} PROFILE_CONTROL_BLOCK;
typedef PROFILE_CONTROL_BLOCK * PPROFILE_CONTROL_BLOCK;
#define PRFEVENT_START_EVENT "PrfEventStartedEvent"
#define PRFEVENT_STOP_EVENT "PrfEventStopEvent"
#define PRFEVENT_SHARED_MEMORY "PrfEventSharedMemory"
//
// End header file
//
#define MAX_SYMNAME_SIZE 1024
CHAR symBuffer[sizeof(IMAGEHLP_SYMBOL)+MAX_SYMNAME_SIZE];
PIMAGEHLP_SYMBOL ThisSymbol = (PIMAGEHLP_SYMBOL) symBuffer;
CHAR LastSymBuffer[sizeof(IMAGEHLP_SYMBOL)+MAX_SYMNAME_SIZE];
PIMAGEHLP_SYMBOL LastSymbol = (PIMAGEHLP_SYMBOL) LastSymBuffer;
VOID
InitializeProfileSourceMapping (
VOID
);
NTSTATUS
InitializeKernelProfile(
VOID
);
NTSTATUS
RunEventLoop(
VOID
);
NTSTATUS
RunStdProfile(
VOID
);
NTSTATUS
StartProfile(
VOID
);
NTSTATUS
StopProfile(
VOID
);
NTSTATUS
AnalyzeProfile(
ULONG Threshold,
PSYSTEM_CONTEXT_SWITCH_INFORMATION StartContext,
PSYSTEM_CONTEXT_SWITCH_INFORMATION StopContext
);
VOID
OutputSymbolCount(
IN ULONG CountAtSymbol,
IN ULONG TotalCount,
IN PROFILE_BLOCK *ProfileObject,
IN PIMAGEHLP_SYMBOL SymbolInfo,
IN ULONG Threshold,
IN PULONG CounterStart,
IN PULONG CounterStop,
IN ULONG Va,
IN ULONG BytesPerBucket
);
#ifdef _ALPHA_
#define PAGE_SIZE 8192
#else
#define PAGE_SIZE 4096
#endif
FILE *fpOut = NULL;
PROFILE_BLOCK ProfileObject[MAX_PROFILE_COUNT];
DWORD *UserModeBuffer[MAXIMUM_PROCESSORS];
ULONG NumberOfProfileObjects = 0;
ULONG MaxProcessors = 1;
ULONG ProfileInterval = 10000;
CHAR SymbolSearchPathBuf[4096];
LPSTR lpSymbolSearchPath = SymbolSearchPathBuf;
// display flags
BOOLEAN bDisplayAddress=FALSE;
BOOLEAN bDisplayDensity=FALSE;
BOOLEAN bDisplayCounters=FALSE;
BOOLEAN bDisplayContextSwitch=FALSE;
BOOLEAN bPerProcessor = FALSE;
BOOLEAN bWaitForInput = FALSE;
BOOLEAN bEventLoop = FALSE;
BOOLEAN bPrintPercentages = FALSE;
BOOLEAN Verbose = FALSE;
//
// Image name to perform kernel mode analysis upon.
//
#define IMAGE_NAME "\\SystemRoot\\system32\\ntoskrnl.exe"
HANDLE DoneEvent;
HANDLE DelayEvent;
KPROFILE_SOURCE ProfileSource = ProfileTime;
ULONG Seconds = (ULONG)-1;
ULONG Threshold = 100;
ULONG DelaySeconds = (ULONG)-1;
//
// define the mappings between arguments and KPROFILE_SOURCE types
//
typedef struct _PROFILE_SOURCE_MAPPING {
PCHAR ShortName;
PCHAR Description;
KPROFILE_SOURCE Source;
} PROFILE_SOURCE_MAPPING, *PPROFILE_SOURCE_MAPPING;
#if defined(_ALPHA_)
PROFILE_SOURCE_MAPPING ProfileSourceMapping[] = {
{"align", "", ProfileAlignmentFixup},
{"totalissues", "", ProfileTotalIssues},
{"pipelinedry", "", ProfilePipelineDry},
{"loadinstructions", "", ProfileLoadInstructions},
{"pipelinefrozen", "", ProfilePipelineFrozen},
{"branchinstructions", "", ProfileBranchInstructions},
{"totalnonissues", "", ProfileTotalNonissues},
{"dcachemisses", "", ProfileDcacheMisses},
{"icachemisses", "", ProfileIcacheMisses},
{"branchmispredicts", "", ProfileBranchMispredictions},
{"storeinstructions", "", ProfileStoreInstructions},
{NULL,0}
};
#elif defined(_X86_)
PPROFILE_SOURCE_MAPPING ProfileSourceMapping;
#else
PROFILE_SOURCE_MAPPING ProfileSourceMapping[] = {
{NULL,0}
};
#endif
BOOL
CtrlcH(
DWORD dwCtrlType
)
{
if ( dwCtrlType == CTRL_C_EVENT ) {
SetEvent(DoneEvent);
return TRUE;
}
return FALSE;
}
void PrintUsage (void)
{
fputs ("Kernel Profiler Usage:\n\n"
"Kernprof [-acdpnrx] [-w <wait time>] [-s Source] [-t <low threshold>] [<sample time>]\n"
" -a - display function address and length and bucket size\n"
" -c - display individual counters\n"
" -d - compute hit Density for each function\n"
//UNDOC " -e - use special event syncronization for start and stop\n"
" -f filename - output file (Default stdout)\n"
" -i <interval in 100ns> (Default 10000)\n"
" -n - print hit percentages\n"
" -p - Per-processor profile objects\n"
" -r - wait for a <RETURN> before starting collection\n"
" -s Source - use Source instead of clock as profile source\n"
" ? lists Sources\n"
" -t <low threshold> - Minimum number of counts to report.\n"
" Defaults is 100\n"
" -v - Display verbose symbol information\n"
" -w - wait for <wait time> before starting collection\n"
" -x - display context switch counters\n"
" <sample time> - Specify, in seconds, how long to collect\n"
" profile information.\n"
" Default is wait until Ctrl-C\n\n"
#if defined (_ALPHA_)
"Currently supported profile sources are 'align', 'totalissues', 'pipelinedry'\n"
" 'loadinstructions', 'pipelinefrozen', 'branchinstructions', 'totalnonissues',\n"
" 'dcachemisses', 'icachemisses', 'branchmispredicts', 'storeinstructions'\n"
#endif
, stderr);
}
__cdecl
main(
int argc,
char *argv[],
char *envp[]
)
{
int j;
NTSTATUS status;
PPROFILE_SOURCE_MAPPING ProfileMapping;
SYSTEM_INFO SystemInfo;
fpOut = stdout;
ThisSymbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL);
ThisSymbol->MaxNameLength = MAX_SYMNAME_SIZE;
LastSymbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL);
LastSymbol->MaxNameLength = MAX_SYMNAME_SIZE;
SymSetOptions( SYMOPT_UNDNAME | SYMOPT_CASE_INSENSITIVE | SYMOPT_OMAP_FIND_NEAREST );
SymInitialize( SYM_HANDLE, NULL, FALSE );
SymGetSearchPath( SYM_HANDLE, SymbolSearchPathBuf, sizeof(SymbolSearchPathBuf) );
//
// Parse the input string.
//
DoneEvent = CreateEvent(NULL,FALSE,FALSE,NULL);
if (argc > 1) {
if (((argv[1][0] == '-') || (argv[1][0] == '/')) &&
((argv[1][1] == '?' ) ||
(argv[1][1] == 'H') ||
(argv[1][1] == 'H'))
) {
PrintUsage();
return ERROR_SUCCESS;
}
for (j = 1; j < argc; j++) {
BOOLEAN NextArg;
char *p;
if (argv[j][0] == '-') {
NextArg = FALSE;
for (p = &argv[j][1] ; *p && !NextArg ; p++) {
switch (toupper(*p)) {
case 'A':
bDisplayAddress = TRUE;
break;
case 'C':
bDisplayCounters = TRUE;
break;
case 'D':
bDisplayDensity = TRUE;
break;
case 'E':
bEventLoop = TRUE;
break;
case 'F':
NextArg = TRUE;
fpOut = fopen(argv[++j], "w");
break;
case 'I':
NextArg = TRUE;
ProfileInterval = atoi(argv[++j]);
break;
case 'N':
bPrintPercentages = TRUE;
break;
case 'P':
GetSystemInfo(&SystemInfo);
MaxProcessors = SystemInfo.dwNumberOfProcessors;
bPerProcessor = TRUE;
break;
case 'R':
bWaitForInput = TRUE;
break;
case 'S':
NextArg = TRUE;
if (!ProfileSourceMapping) {
InitializeProfileSourceMapping();
}
if (!argv[j+1]) {
break;
}
if (argv[j+1][0] == '?') {
ProfileMapping = ProfileSourceMapping;
if (ProfileMapping) {
fprintf (stderr, "kernprof: profile sources\n");
while (ProfileMapping->ShortName != NULL) {
fprintf (stderr, " %-10s %s\n",
ProfileMapping->ShortName,
ProfileMapping->Description
);
++ProfileMapping;
}
} else {
fprintf (stderr, "kernprof: no alternative profile sources\n");
}
return 0;
}
ProfileMapping = ProfileSourceMapping;
if (ProfileMapping) {
while (ProfileMapping->ShortName != NULL) {
if (_stricmp(ProfileMapping->ShortName, argv[j+1])==0) {
ProfileSource = ProfileMapping->Source;
fprintf (stderr, "ProfileSource %x\n", ProfileMapping->Source);
++j;
break;
}
++ProfileMapping;
}
}
break;
case 'T':
NextArg = TRUE;
Threshold = atoi(argv[++j]);
break;
case 'V':
Verbose = TRUE;
break;
case 'W':
NextArg = TRUE;
DelaySeconds = atoi(argv[++j]);
DelayEvent = CreateEvent(NULL,FALSE,FALSE,NULL);
break;
case 'X':
bDisplayContextSwitch = TRUE;
break;
}
}
} else {
Seconds = atoi(argv[j]);
}
}
}
if (bEventLoop || (DelaySeconds != -1)) {
SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS);
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
}
status = InitializeKernelProfile ();
if (!NT_SUCCESS(status)) {
fprintf(stderr, "initialize failed status - %lx\n",status);
return(status);
}
if (bEventLoop)
RunEventLoop();
else
RunStdProfile();
return STATUS_SUCCESS;
}
NTSTATUS
RunEventLoop()
{
NTSTATUS status;
SYSTEM_CONTEXT_SWITCH_INFORMATION StartContext;
SYSTEM_CONTEXT_SWITCH_INFORMATION StopContext;
HANDLE hStartedEvent = NULL;
HANDLE hStopEvent = NULL;
HANDLE hMap = NULL;
PPROFILE_CONTROL_BLOCK pShared = NULL;
// Create the events and shared memory
hStartedEvent = CreateEvent (NULL, FALSE, FALSE, PRFEVENT_START_EVENT);
if (hStartedEvent == NULL) {
fprintf(stderr, "Failed to create started event - 0x%lx\n",
GetLastError());
return(GetLastError());
}
hStopEvent = CreateEvent (NULL, FALSE, FALSE, PRFEVENT_STOP_EVENT);
if (hStopEvent == NULL) {
fprintf(stderr, "Failed to create stop event - 0x%lx\n",
GetLastError());
return(GetLastError());
}
hMap = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE | SEC_COMMIT,
0, sizeof(PROFILE_CONTROL_BLOCK),
PRFEVENT_SHARED_MEMORY);
if (hMap == NULL) {
fprintf(stderr, "Failed to create the file mapping - 0x%lx\n",
GetLastError());
return(GetLastError());
}
pShared = (PPROFILE_CONTROL_BLOCK) MapViewOfFile(hMap, FILE_MAP_WRITE,
0,0, sizeof(PROFILE_CONTROL_BLOCK));
if (pShared == NULL) {
fprintf(stderr, "Failed to map the shared memory view - 0x%lx\n",
GetLastError());
return(GetLastError());
}
// Wait for start i.e., the stop event
WaitForSingleObject(hStopEvent, INFINITE);
while (TRUE) {
if (bDisplayContextSwitch) {
NtQuerySystemInformation(SystemContextSwitchInformation,
&StartContext,
sizeof(StartContext),
NULL);
}
status = StartProfile ();
if (!NT_SUCCESS(status)) {
fprintf(stderr, "start profile failed status - %lx\n",status);
break;
}
// Signal started
SetEvent(hStartedEvent);
// Wait for stop
WaitForSingleObject(hStopEvent, INFINITE);
status = StopProfile ();
if (!NT_SUCCESS(status)) {
fprintf(stderr, "stop profile failed status - %lx\n",status);
break;
}
if (bDisplayContextSwitch) {
status = NtQuerySystemInformation(SystemContextSwitchInformation,
&StopContext,
sizeof(StopContext),
NULL);
if (!NT_SUCCESS(status)) {
fprintf(stderr, "QuerySystemInformation for context switch information failed %08lx\n",status);
bDisplayContextSwitch = FALSE;
}
}
fpOut = fopen(pShared->FileName, "w");
status = AnalyzeProfile (Threshold, &StartContext, &StopContext);
fclose(fpOut);
if (!NT_SUCCESS(status)) {
fprintf(stderr, "analyze profile failed status - %lx\n",status);
}
if (pShared->Stop == TRUE)
break;
}
SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS);
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL);
UnmapViewOfFile((void*)pShared);
CloseHandle(hMap);
CloseHandle(hStopEvent);
CloseHandle(hStartedEvent);
return(status);
}
NTSTATUS
RunStdProfile()
{
NTSTATUS status;
SYSTEM_CONTEXT_SWITCH_INFORMATION StartContext;
SYSTEM_CONTEXT_SWITCH_INFORMATION StopContext;
SetConsoleCtrlHandler(CtrlcH,TRUE);
if (DelaySeconds != -1) {
fprintf(stderr, "starting profile after %d seconds\n",DelaySeconds);
WaitForSingleObject(DelayEvent, DelaySeconds*1000);
}
if (bDisplayContextSwitch) {
NtQuerySystemInformation(SystemContextSwitchInformation,
&StartContext,
sizeof(StartContext),
NULL);
}
status = StartProfile ();
if (!NT_SUCCESS(status)) {
fprintf(stderr, "start profile failed status - %lx\n",status);
return(status);
}
if ( Seconds == -1 ) {
fprintf(stderr, "delaying until ^C\n");
} else {
fprintf(stderr, "delaying for %ld seconds... "
"report on values with %ld hits\n",
Seconds,
Threshold
);
}
if ( Seconds ) {
if ( Seconds != -1 ) {
Seconds = Seconds * 1000;
}
if ( DoneEvent ) {
WaitForSingleObject(DoneEvent,Seconds);
}
else {
Sleep(Seconds);
}
}
else {
getchar();
}
fprintf (stderr, "end of delay\n");
status = StopProfile ();
if (!NT_SUCCESS(status)) {
fprintf(stderr, "stop profile failed status - %lx\n",status);
return(status);
}
SetConsoleCtrlHandler(CtrlcH,FALSE);
if (bDisplayContextSwitch) {
status = NtQuerySystemInformation(SystemContextSwitchInformation,
&StopContext,
sizeof(StopContext),
NULL);
if (!NT_SUCCESS(status)) {
fprintf(stderr, "QuerySystemInformation for context switch information failed %08lx\n",status);
bDisplayContextSwitch = FALSE;
}
}
if (DelaySeconds != -1) {
SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS);
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL);
}
status = AnalyzeProfile (Threshold, &StartContext, &StopContext);
if (!NT_SUCCESS(status)) {
fprintf(stderr, "analyze profile failed status - %lx\n",status);
}
return(status);
}
VOID
InitializeProfileSourceMapping (
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 ;
}
//
// Initialize possible counters
//
// determine how many events there are
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));
ProfileSourceMapping = malloc(sizeof(*ProfileSourceMapping) * Count);
Count -= 1;
for (i=0, j=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
);
if (Event->ProfileSource > ProfileTime) {
ProfileSourceMapping[j].Source = Event->ProfileSource;
ProfileSourceMapping[j].ShortName = _strdup (Event->Buffer);
ProfileSourceMapping[j].Description = _strdup (Event->Buffer + Event->DescriptionOffset);
j++;
}
}
ProfileSourceMapping[j].Source = (KPROFILE_SOURCE) 0;
ProfileSourceMapping[j].ShortName = NULL;
ProfileSourceMapping[j].Description = NULL;
NtClose (DriverHandle);
#endif
}
NTSTATUS
InitializeKernelProfile (
VOID
)
/*++
Routine Description:
This routine initializes profiling for the kernel for the
current process.
Arguments:
None.
Return Value:
Returns the status of the last NtCreateProfile.
--*/
{
ULONG i;
ULONG ModuleNumber;
SIZE_T ViewSize;
PULONG CodeStart;
ULONG CodeLength;
NTSTATUS LocalStatus;
NTSTATUS status;
HANDLE CurrentProcessHandle;
QUOTA_LIMITS QuotaLimits;
PVOID Buffer;
DWORD Cells;
ULONG BucketSize;
WCHAR StringBuf[500];
PCHAR ModuleInfoBuffer;
ULONG ModuleInfoBufferLength;
ULONG ReturnedLength;
PRTL_PROCESS_MODULES Modules;
PRTL_PROCESS_MODULE_INFORMATION Module;
UNICODE_STRING Sysdisk;
UNICODE_STRING Sysroot;
UNICODE_STRING Sysdll;
UNICODE_STRING NameString;
BOOLEAN PreviousProfilePrivState;
BOOLEAN PreviousQuotaPrivState;
CHAR ImageName[256];
HANDLE hFile;
HANDLE hMap;
PVOID MappedBase;
PIMAGE_NT_HEADERS NtHeaders;
CurrentProcessHandle = NtCurrentProcess();
//
// Locate system drivers.
//
ModuleInfoBufferLength = 0;
ModuleInfoBuffer = NULL;
while (1) {
status = NtQuerySystemInformation (SystemModuleInformation,
ModuleInfoBuffer,
ModuleInfoBufferLength,
&ReturnedLength);
if (NT_SUCCESS (status)) {
break;
}
if (ModuleInfoBuffer != NULL) {
RtlFreeHeap (RtlProcessHeap (), 0, ModuleInfoBuffer);
}
if (status == STATUS_INFO_LENGTH_MISMATCH && ReturnedLength > ModuleInfoBufferLength) {
ModuleInfoBufferLength = ReturnedLength;
ModuleInfoBuffer = RtlAllocateHeap (RtlProcessHeap(), 0, ModuleInfoBufferLength);
if (ModuleInfoBuffer == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
} else if (!NT_SUCCESS(status)) {
fprintf(stderr, "query system info failed status - %lx\n",status);
return(status);
}
}
RtlInitUnicodeString (&Sysdisk,L"\\SystemRoot\\");
RtlInitUnicodeString (&Sysroot,L"\\SystemRoot\\System32\\Drivers\\");
RtlInitUnicodeString (&Sysdll, L"\\SystemRoot\\System32\\");
NameString.Buffer = StringBuf;
NameString.Length = 0;
NameString.MaximumLength = sizeof( StringBuf );
status = RtlAdjustPrivilege(
SE_SYSTEM_PROFILE_PRIVILEGE,
TRUE, //Enable
FALSE, //not impersonating
&PreviousProfilePrivState
);
if (!NT_SUCCESS(status) || status == STATUS_NOT_ALL_ASSIGNED) {
fprintf(stderr, "Enable system profile privilege failed - status 0x%lx\n",
status);
}
status = RtlAdjustPrivilege(
SE_INCREASE_QUOTA_PRIVILEGE,
TRUE, //Enable
FALSE, //not impersonating
&PreviousQuotaPrivState
);
if (!NT_SUCCESS(status) || status == STATUS_NOT_ALL_ASSIGNED) {
fprintf(stderr, "Unable to increase quota privilege (status=0x%lx)\n",
status);
}
Modules = (PRTL_PROCESS_MODULES)ModuleInfoBuffer;
Module = &Modules->Modules[ 0 ];
for (ModuleNumber=0; ModuleNumber < Modules->NumberOfModules; ModuleNumber++,Module++) {
#if DBG_PROFILE
fprintf(stderr, "module base %p\n",Module->ImageBase);
fprintf(stderr, "module full path name: %s (%u)\n",
Module->FullPathName,
Module->OffsetToFileName);
#endif
if (SymLoadModule(
SYM_HANDLE,
NULL,
&Module->FullPathName[Module->OffsetToFileName],
NULL,
(ULONG_PTR)Module->ImageBase,
Module->ImageSize
)) {
ProfileObject[NumberOfProfileObjects].SymbolsLoaded = TRUE;
if (Verbose) {
fprintf(stderr, "Symbols loaded: %p %s\n",
Module->ImageBase,
&Module->FullPathName[Module->OffsetToFileName]
);
}
} else {
ProfileObject[NumberOfProfileObjects].SymbolsLoaded = FALSE;
if (Verbose) {
fprintf(stderr, "*** Could not load symbols: %p %s\n",
Module->ImageBase,
&Module->FullPathName[Module->OffsetToFileName]
);
}
}
hFile = FindExecutableImage(
&Module->FullPathName[Module->OffsetToFileName],
lpSymbolSearchPath,
ImageName
);
if (!hFile) {
continue;
}
hMap = CreateFileMapping(
hFile,
NULL,
PAGE_READONLY,
0,
0,
NULL
);
if (!hMap) {
CloseHandle( hFile );
continue;
}
MappedBase = MapViewOfFile(
hMap,
FILE_MAP_READ,
0,
0,
0
);
if (!MappedBase) {
CloseHandle( hMap );
CloseHandle( hFile );
continue;
}
NtHeaders = ImageNtHeader( MappedBase );
CodeLength = NtHeaders->OptionalHeader.SizeOfImage;
CodeStart = (PULONG)Module->ImageBase;
UnmapViewOfFile( MappedBase );
CloseHandle( hMap );
CloseHandle( hFile );
if (CodeLength > 1024*512) {
//
// Just create a 512K byte buffer.
//
ViewSize = 1024 * 512;
} else {
ViewSize = CodeLength + PAGE_SIZE;
}
ProfileObject[NumberOfProfileObjects].CodeStart = CodeStart;
ProfileObject[NumberOfProfileObjects].CodeLength = CodeLength;
ProfileObject[NumberOfProfileObjects].ImageBase = Module->ImageBase;
ProfileObject[NumberOfProfileObjects].ModuleName = _strdup(&Module->FullPathName[Module->OffsetToFileName]);
for (i=0; i<MaxProcessors; i++) {
Buffer = NULL;
status = NtAllocateVirtualMemory (CurrentProcessHandle,
(PVOID *)&Buffer,
0,
&ViewSize,
MEM_RESERVE | MEM_COMMIT,
PAGE_READWRITE);
if (!NT_SUCCESS(status)) {
fprintf (stderr, "alloc VM failed %lx\n",status);
RtlFreeHeap (RtlProcessHeap (), 0, ModuleInfoBuffer);
return(status);
}
//
// Calculate the bucket size for the profile.
//
Cells = (DWORD)((CodeLength / (ViewSize >> 2)) >> 2);
BucketSize = 2;
while (Cells != 0) {
Cells = Cells >> 1;
BucketSize += 1;
}
ProfileObject[NumberOfProfileObjects].Buffer[i] = Buffer;
ProfileObject[NumberOfProfileObjects].BufferSize = 1 + (CodeLength >> (BucketSize - 2));
ProfileObject[NumberOfProfileObjects].BucketSize = BucketSize;
//
// Increase the working set to lock down a bigger buffer.
//
status = NtQueryInformationProcess (CurrentProcessHandle,
ProcessQuotaLimits,
&QuotaLimits,
sizeof(QUOTA_LIMITS),
NULL );
if (!NT_SUCCESS(status)) {
fprintf (stderr, "query process info failed %lx\n",status);
RtlFreeHeap (RtlProcessHeap (), 0, ModuleInfoBuffer);
return(status);
}
QuotaLimits.MaximumWorkingSetSize += ViewSize;
QuotaLimits.MinimumWorkingSetSize += ViewSize;
status = NtSetInformationProcess (CurrentProcessHandle,
ProcessQuotaLimits,
&QuotaLimits,
sizeof(QUOTA_LIMITS));
#if DBG_PROFILE
fprintf(stderr, "code start %p len %p, bucksize %lx buffer %p bsize %08x\n",
ProfileObject[NumberOfProfileObjects].CodeStart,
ProfileObject[NumberOfProfileObjects].CodeLength,
ProfileObject[NumberOfProfileObjects].BucketSize,
ProfileObject[NumberOfProfileObjects].Buffer ,
ProfileObject[NumberOfProfileObjects].BufferSize);
#endif
if (bPerProcessor) {
status = NtCreateProfile (
&ProfileObject[NumberOfProfileObjects].Handle[i],
0,
ProfileObject[NumberOfProfileObjects].CodeStart,
ProfileObject[NumberOfProfileObjects].CodeLength,
ProfileObject[NumberOfProfileObjects].BucketSize,
ProfileObject[NumberOfProfileObjects].Buffer[i] ,
ProfileObject[NumberOfProfileObjects].BufferSize,
ProfileSource,
1 << i);
} else {
status = NtCreateProfile (
&ProfileObject[NumberOfProfileObjects].Handle[i],
0,
ProfileObject[NumberOfProfileObjects].CodeStart,
ProfileObject[NumberOfProfileObjects].CodeLength,
ProfileObject[NumberOfProfileObjects].BucketSize,
ProfileObject[NumberOfProfileObjects].Buffer[i] ,
ProfileObject[NumberOfProfileObjects].BufferSize,
ProfileSource,
(KAFFINITY)-1);
}
if (status != STATUS_SUCCESS) {
fprintf(stderr, "create kernel profile %s failed - status %lx\n",
ProfileObject[NumberOfProfileObjects].ModuleName, status);
}
}
NumberOfProfileObjects += 1;
if (NumberOfProfileObjects == MAX_PROFILE_COUNT) {
RtlFreeHeap (RtlProcessHeap (), 0, ModuleInfoBuffer);
return STATUS_SUCCESS;
}
}
if (NumberOfProfileObjects < MAX_PROFILE_COUNT) {
//
// Add in usermode object
// 0x00000000 -> SystemRangeStart
//
ULONG_PTR SystemRangeStart;
ULONG UserModeBucketCount;
status = NtQuerySystemInformation(SystemRangeStartInformation,
&SystemRangeStart,
sizeof(SystemRangeStart),
NULL);
//
// How many buckets to cover the range
//
UserModeBucketCount = (ULONG)(1 + ((SystemRangeStart - 1) / MAX_BUCKET_SIZE));
if (!NT_SUCCESS(status)) {
fprintf(stderr, "NtQuerySystemInformation failed - status %lx\n", status);
RtlFreeHeap (RtlProcessHeap (), 0, ModuleInfoBuffer);
return status;
}
ProfileObject[NumberOfProfileObjects].SymbolsLoaded = FALSE;
ProfileObject[NumberOfProfileObjects].CodeStart = 0;
ProfileObject[NumberOfProfileObjects].CodeLength = SystemRangeStart;
ProfileObject[NumberOfProfileObjects].ImageBase = 0;
ProfileObject[NumberOfProfileObjects].ModuleName = "User Mode";
ProfileObject[NumberOfProfileObjects].BufferSize = UserModeBucketCount * sizeof(DWORD);
ProfileObject[NumberOfProfileObjects].BucketSize = MAX_BUCKET_SHIFT;
for (i=0; i<MaxProcessors; i++) {
UserModeBuffer[i] = HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
ProfileObject[NumberOfProfileObjects].BufferSize);
if (UserModeBuffer[i] == NULL) {
fprintf (stderr, "HeapAlloc failed\n");
RtlFreeHeap (RtlProcessHeap (), 0, ModuleInfoBuffer);
return(STATUS_NO_MEMORY);
}
ProfileObject[NumberOfProfileObjects].Buffer[i] = UserModeBuffer[i];
ProfileObject[NumberOfProfileObjects].Handle[i] = NULL;
#if DBG_PROFILE
fprintf(stderr, "code start %p len %lx, bucksize %lx buffer %p bsize %lx\n",
ProfileObject[NumberOfProfileObjects].CodeStart,
ProfileObject[NumberOfProfileObjects].CodeLength,
ProfileObject[NumberOfProfileObjects].BucketSize,
ProfileObject[NumberOfProfileObjects].Buffer ,
ProfileObject[NumberOfProfileObjects].BufferSize);
#endif
if (bPerProcessor) {
status = NtCreateProfile (
&ProfileObject[NumberOfProfileObjects].Handle[i],
0,
ProfileObject[NumberOfProfileObjects].CodeStart,
ProfileObject[NumberOfProfileObjects].CodeLength,
ProfileObject[NumberOfProfileObjects].BucketSize,
ProfileObject[NumberOfProfileObjects].Buffer[i] ,
ProfileObject[NumberOfProfileObjects].BufferSize,
ProfileSource,
1 << i);
} else {
status = NtCreateProfile (
&ProfileObject[NumberOfProfileObjects].Handle[i],
0,
ProfileObject[NumberOfProfileObjects].CodeStart,
ProfileObject[NumberOfProfileObjects].CodeLength,
ProfileObject[NumberOfProfileObjects].BucketSize,
ProfileObject[NumberOfProfileObjects].Buffer[i] ,
ProfileObject[NumberOfProfileObjects].BufferSize,
ProfileSource,
(KAFFINITY)-1);
}
if (status != STATUS_SUCCESS) {
fprintf(stderr, "create kernel profile %s failed - status %lx\n",
ProfileObject[NumberOfProfileObjects].ModuleName, status);
}
}
NumberOfProfileObjects += 1;
}
/*
if (PreviousProfilePrivState == FALSE) {
LocalStatus = RtlAdjustPrivilege(
SE_SYSTEM_PROFILE_PRIVILEGE,
FALSE, //Disable
FALSE, //not impersonating
&PreviousProfilePrivState
);
if (!NT_SUCCESS(LocalStatus) || LocalStatus == STATUS_NOT_ALL_ASSIGNED) {
fprintf(stderr, "Disable system profile privilege failed - status 0x%lx\n",
LocalStatus);
}
}
if (PreviousQuotaPrivState == FALSE) {
LocalStatus = RtlAdjustPrivilege(
SE_SYSTEM_PROFILE_PRIVILEGE,
FALSE, //Disable
FALSE, //not impersonating
&PreviousQuotaPrivState
);
if (!NT_SUCCESS(LocalStatus) || LocalStatus == STATUS_NOT_ALL_ASSIGNED) {
fprintf(stderr, "Disable increate quota privilege failed - status 0x%lx\n",
LocalStatus);
}
}
*/
RtlFreeHeap (RtlProcessHeap (), 0, ModuleInfoBuffer);
return status;
}
NTSTATUS
StartProfile (
VOID
)
/*++
Routine Description:
This routine starts all profile objects which have been initialized.
Arguments:
None.
Return Value:
Returns the status of the last NtStartProfile.
--*/
{
ULONG Object;
ULONG Processor;
NTSTATUS status;
QUOTA_LIMITS QuotaLimits;
NtSetIntervalProfile(ProfileInterval,ProfileSource);
if (bWaitForInput) {
fprintf(stderr, "Hit return to continue.\n");
(void) getchar();
}
for (Object = 0; Object < NumberOfProfileObjects; Object++) {
for (Processor = 0;Processor < MaxProcessors; Processor++) {
status = NtStartProfile (ProfileObject[Object].Handle[Processor]);
if (!NT_SUCCESS(status)) {
fprintf(stderr, "start profile %s failed - status %lx\n",
ProfileObject[Object].ModuleName, status);
return status;
}
}
}
return status;
}
NTSTATUS
StopProfile (
VOID
)
/*++
Routine Description:
This routine stops all profile objects which have been initialized.
Arguments:
None.
Return Value:
Returns the status of the last NtStopProfile.
--*/
{
ULONG i;
ULONG Processor;
NTSTATUS status;
for (i = 0; i < NumberOfProfileObjects; i++) {
for (Processor=0; Processor < MaxProcessors; Processor++) {
status = NtStopProfile (ProfileObject[i].Handle[Processor]);
if (status != STATUS_SUCCESS) {
fprintf(stderr, "stop profile %s failed - status %lx\n",
ProfileObject[i].ModuleName,status);
return status;
}
}
}
return status;
}
NTSTATUS
AnalyzeProfile (
ULONG Threshold,
PSYSTEM_CONTEXT_SWITCH_INFORMATION StartContext,
PSYSTEM_CONTEXT_SWITCH_INFORMATION StopContext
)
/*++
Routine Description:
This routine does the analysis of all the profile buffers and
correlates hits to the appropriate symbol table.
Arguments:
None.
Return Value:
None.
--*/
{
ULONG CountAtSymbol;
ULONG_PTR Va;
int i;
PULONG Counter;
ULONG_PTR Displacement;
ULONG Processor;
ULONG TotalHits = 0;
ULONG ProcessorTotalHits[MAXIMUM_PROCESSORS] = {0};
PULONG BufferEnd;
PULONG Buffer;
PULONG pInitialCounter;
ULONG OffsetVa = 0;
ULONG BytesPerBucket;
STRING NoSymbolFound = {16,15,"No Symbol Found"};
BOOLEAN UseLastSymbol = FALSE;
for (i = 0; i < (int)NumberOfProfileObjects; i++) {
for (Processor=0;Processor < MaxProcessors;Processor++) {
NtStopProfile (ProfileObject[i].Handle[Processor]);
}
}
for (Processor = 0; Processor < MaxProcessors; Processor++) {
for (i = 0; i < (int)NumberOfProfileObjects; i++) {
//
// Sum the total number of cells written.
//
BufferEnd = ProfileObject[i].Buffer[Processor] + (
ProfileObject[i].BufferSize / sizeof(ULONG));
Buffer = ProfileObject[i].Buffer[Processor];
Counter = BufferEnd;
ProfileObject[i].ModuleHitCount[Processor] = 0;
while (Counter > Buffer) {
Counter -= 1;
ProfileObject[i].ModuleHitCount[Processor] += *Counter;
}
ProcessorTotalHits[Processor] += ProfileObject[i].ModuleHitCount[Processor];
}
if (bPerProcessor) {
fprintf(fpOut, "Processor %d: %d Total hits\n",
Processor, ProcessorTotalHits[Processor]);
}
TotalHits += ProcessorTotalHits[Processor];
}
fprintf(fpOut, "%d Total hits\n",TotalHits);
for (Processor = 0; Processor < MaxProcessors; Processor++) {
if (bPerProcessor) {
fprintf(fpOut, "\nPROCESSOR %d\n",Processor);
}
for (i = 0; i < (int)NumberOfProfileObjects; i++) {
CountAtSymbol = 0;
//
// Sum the total number of cells written.
//
BufferEnd = ProfileObject[i].Buffer[Processor] + (
ProfileObject[i].BufferSize / sizeof(ULONG));
Buffer = ProfileObject[i].Buffer[Processor];
Counter = BufferEnd;
if (ProfileObject[i].ModuleHitCount[Processor] < Threshold) {
continue;
}
fprintf(fpOut, "\n%9d ",
ProfileObject[i].ModuleHitCount[Processor]);
if (bPrintPercentages) {
fprintf(fpOut, "%5.2f ",
(ProfileObject[i].ModuleHitCount[Processor] /
(double)ProcessorTotalHits[Processor]) * 100);
}
fprintf(fpOut, "%20s --Total Hits-- %s\n",
ProfileObject[i].ModuleName,
((ProfileObject[i].SymbolsLoaded) ? "" :
"(NO SYMBOLS)")
);
if (!ProfileObject[i].SymbolsLoaded) {
RtlZeroMemory(ProfileObject[i].Buffer[Processor],
ProfileObject[i].BufferSize);
continue;
}
BytesPerBucket = (1 << ProfileObject[i].BucketSize);
pInitialCounter = Buffer;
for ( Counter = Buffer; Counter < BufferEnd; Counter += 1 ) {
if ( *Counter ) {
//
// Calculate the virtual address of the counter
//
Va = Counter - Buffer; // Calculate buckets #
Va = Va * BytesPerBucket; // convert to bytes
Va = Va + (ULONG_PTR)ProfileObject[i].CodeStart; // add in base address
if (SymGetSymFromAddr( SYM_HANDLE, Va, &Displacement, ThisSymbol )) {
if (UseLastSymbol &&
LastSymbol->Address &&
(LastSymbol->Address == ThisSymbol->Address))
{
CountAtSymbol += *Counter;
} else {
OutputSymbolCount(CountAtSymbol,
ProcessorTotalHits[Processor],
&ProfileObject[i],
LastSymbol,
Threshold,
pInitialCounter,
Counter,
OffsetVa,
BytesPerBucket);
pInitialCounter = Counter;
OffsetVa = (DWORD) Displacement; // Images aren't > 2g so this cast s/b O.K.
CountAtSymbol = *Counter;
memcpy( LastSymBuffer, symBuffer, sizeof(symBuffer) );
UseLastSymbol = TRUE;
}
} else {
OutputSymbolCount(CountAtSymbol,
ProcessorTotalHits[Processor],
&ProfileObject[i],
LastSymbol,
Threshold,
pInitialCounter,
Counter,
OffsetVa,
BytesPerBucket);
} // else !(NT_SUCCESS)
} // if (*Counter)
} // for (Counter)
OutputSymbolCount(CountAtSymbol,
ProcessorTotalHits[Processor],
&ProfileObject[i],
LastSymbol,
Threshold,
pInitialCounter,
Counter,
OffsetVa,
BytesPerBucket);
//
// Clear after buffer's been checked and displayed
//
RtlZeroMemory(ProfileObject[i].Buffer[Processor], ProfileObject[i].BufferSize);
}
}
if (bDisplayContextSwitch) {
fprintf(fpOut, "\n");
fprintf(fpOut, "Context Switch Information\n");
fprintf(fpOut, " Find any processor %6ld\n", StopContext->FindAny - StartContext->FindAny);
fprintf(fpOut, " Find last processor %6ld\n", StopContext->FindLast - StartContext->FindLast);
fprintf(fpOut, " Idle any processor %6ld\n", StopContext->IdleAny - StartContext->IdleAny);
fprintf(fpOut, " Idle current processor %6ld\n", StopContext->IdleCurrent - StartContext->IdleCurrent);
fprintf(fpOut, " Idle last processor %6ld\n", StopContext->IdleLast - StartContext->IdleLast);
fprintf(fpOut, " Preempt any processor %6ld\n", StopContext->PreemptAny - StartContext->PreemptAny);
fprintf(fpOut, " Preempt current processor %6ld\n", StopContext->PreemptCurrent - StartContext->PreemptCurrent);
fprintf(fpOut, " Preempt last processor %6ld\n", StopContext->PreemptLast - StartContext->PreemptLast);
fprintf(fpOut, " Switch to idle %6ld\n", StopContext->SwitchToIdle - StartContext->SwitchToIdle);
fprintf(fpOut, "\n");
fprintf(fpOut, " Total context switches %6ld\n", StopContext->ContextSwitches - StartContext->ContextSwitches);
}
return STATUS_SUCCESS;
}
VOID
OutputSymbolCount(
IN ULONG CountAtSymbol,
IN ULONG TotalCount,
IN PROFILE_BLOCK *ProfileObject,
IN PIMAGEHLP_SYMBOL SymbolInfo,
IN ULONG Threshold,
IN PULONG CounterStart,
IN PULONG CounterStop,
IN ULONG Va,
IN ULONG BytesPerBucket
)
{
ULONG Density;
ULONG i;
if (CountAtSymbol < Threshold) {
return;
}
fprintf(fpOut, "%9d ", CountAtSymbol);
if (bPrintPercentages) {
fprintf(fpOut, "%5.2f ", (CountAtSymbol / (double) TotalCount) * 100);
}
if (bDisplayDensity) {
//
// Compute hit density = hits * 100 / function length
//
if (!SymbolInfo || !SymbolInfo->Size) {
Density = 0;
} else {
Density = CountAtSymbol * 100 / SymbolInfo->Size;
}
fprintf(fpOut, "%5d ",Density);
}
if (SymbolInfo->MaxNameLength) {
fprintf(fpOut, "%20s %s",
ProfileObject->ModuleName,
SymbolInfo->Name);
} else {
fprintf(fpOut, "%20s 0x%x",
ProfileObject->ModuleName,
SymbolInfo->Address);
}
if (bDisplayAddress) {
fprintf(fpOut, " 0x0%p %d %d",
SymbolInfo->Address,
SymbolInfo->Size,
ProfileObject->BucketSize);
}
if (bDisplayCounters) {
for (i = 0 ; CounterStart < CounterStop; i++, Va += BytesPerBucket, ++CounterStart) {
if ((i % 16) == 0) {
fprintf (fpOut, "\n0x%08x:", Va);
}
fprintf(fpOut, " %5d", *CounterStart);
}
}
fprintf (fpOut, "\n");
}