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

9946 lines
396 KiB

/*++
Copyright (c) 1995-2002 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 or User-Process components.
Usage:
kernrate <commad line options>
Author:
John Vert (jvert) 31-Mar-1995
Revision History:
The original MS version has been extensively modified by Thierry Fevrier and Dan Almosnino.
--*/
// KERNRATE Implementation Notes:
//
// 01/10/2000 - Thierry
// The following code assumes that a kernrate compiled for a specific
// platform, executes and processes perf data only for that platform.
//
// Danalm 02/15/2002:
// - Current code supports Windows 2000 and above, won't run on lower versions
//
// KERNRATE ToDoList:
//
// Thierry 09/30/97:
// - KernRate does not clean the ImageHlp API in case of exceptions. I have
// just added a SymCleanup() call at the normal exit of this program but
// it is not sufficient. We should revisit this one day...
//
// Thierry 07/01/2000:
// - Kernrate and the Kernel Profiling objects code assume that code sections
// that we are profiling are not larger than 4GB.
//
#include "kernrate.h"
VOID
vVerbosePrint(
ULONG Level,
PCCHAR Msg,
...
)
{
if ( gVerbose & Level ) {
va_list ap;
va_start( ap, Msg );
vfprintf(stderr , Msg, ap );
va_end(ap);
}
return;
} // vVerbosePrint()
BOOL
CtrlcH(
DWORD dwCtrlType
)
{
LARGE_INTEGER DueTime;
if ( dwCtrlType == CTRL_C_EVENT ) {
if(gProfilingDone != TRUE) {
if (gSleepInterval == 0) {
SetEvent(ghDoneEvent);
} else {
DueTime.QuadPart = (ULONGLONG)-1;
NtSetTimer(ghDoneEvent,
&DueTime,
NULL,
NULL,
FALSE,
0,
NULL);
}
}
else {
//MC
//
// If someone kills kernrate by pressing Ctrl-C we need to detach from the process (if attached to it)
// Otherwise that process will hang. Of course we can't do much if someone kills kernrate externally.
//
if( ghMCLib != NULL){
pfnDetachFromProcess();
FreeLibrary(ghMCLib);
ghMCLib = NULL;
exit(0);
}
//MC
}
return TRUE;
}
return FALSE;
} // CtrlcH()
static VOID
UsageVerbose(
VOID
)
{
PVERBOSE_DEFINITION pdef = VerboseDefinition;
FPRINTF( stderr, " -v [VerboseLevels] Verbose output where VerboseLevels:\n");
while( pdef->VerboseString ) {
FPRINTF( stderr, " - %x %s\n", pdef->VerboseEnum,
pdef->VerboseString
);
pdef++;
}
FPRINTF( stderr, " - Default value: %x\n", VERBOSE_DEFAULT);
FPRINTF( stderr, " These verbose levels can be OR'ed.\n");
return;
} // UsageVerbose()
static VOID
Usage(
BOOL ExitKernrate
)
{
FPRINTF( stderr, "KERNRATE - Version: %s\n", VER_PRODUCTVERSION_STR );
FPRINTF( stderr,
"KERNRATE [-l] [-lx] [-r] [-m] [-p ProcessId] [-z ModuleName] [-j SymbolPath] [-c RateInMsec] [-s Seconds] [-i [SrcShortName] Rate]\n"
" [-n ProcessName] [-w]\n\n"
" -a Do a combined Kernel and User mode profile\n"
" -av Do a combined Kernel and User mode profile and get task list and system threads info\n"
" -b BucketSize Specify profiling bucket size (default = 16 bytes, must be a power of 2)\n"
" -c RateInMsec Change source every N milliseconds (default 1000ms). Optional. By default all sources will be profiled simultaneously\n"
" -d Generate output rounding buckets up and down\n"
" -e Exclude system-wide and process specific general information (context switches, memory usage, etc.)\n"
" -f Process the collected data at high priority (useful on busy systems if the overhead is not an issue)\n"
" -g Rate Get interesting processor-counters statistics (Rate optional in events/hit), output not guarantied\n"
" -i SrcShortName Rate Specify interrupt interval rate (in events/hit)for the source specified by its ShortName, see notes below\n"
" -j SymbolPath Prepend SymbolPath to the default imagehlp search path\n"
" -k MinHitCount Limit the output to modules that have at least MinHitCount hits\n"
" -l List the default interval rates for supported sources\n"
" -lx List the default interval rates for supported sources and then exit\n"
" -m 0xN Generate per-CPU profiles on multi-processor machines, Hex CPU affinity mask optional for profiling on selected processors\n"
" -n ProcessName Monitor process by its name (default limited to first 8 by the same name), multiple usage allowed\n"
" -nv# N ProcessName Monitor up to N processes by the same name, v will print thread info and list of all running processes (optional)\n"
" -o ProcessName {CmdLine}Create and monitor ProcessName (path OK), Command Line parameters optional and must be enclosed in curly brackets\n"
" -ov# N ProcessName { } Create N instances of ProcessName, v will print thread info and list of running processes (optional), {command line} optional\n"
" -pv ProcessId Monitor process by its ProcessId, multiple usage allowed - see notes below, v (optional) same as in '-nv'\n"
" -r Raw data from zoomed modules\n"
" -rd Raw data from zoomed modules with disassembly\n"
" -s Seconds Stop collecting data after N seconds\n"
" -t Display process list + CPU usage summary for the profiling period\n"
" -t MaxTasks As above + Change the maximum no. of processes allowed in Kernrate's list to MaxTasks (default: 256)\n"
" -u Present symbols in undecorated form\n"
" -w Wait for the user to press ENTER before starting to collect profile data\n"
" -w Seconds Wait for N seconds before starting to collect profile data (default is no wait)\n"
" -wp Wait for the user to press enter to indicate that created processes (see -0 option) are settled (idle)\n"
" -wp Seconds Wait for N seconds to allow created processes settle (go idle), default is 2 seconds, (see the -o option)\n"
" -x Get both system and user-process locks information\n"
" -x# count Get both system and user-process locks information for (optional) contention >= count [def. 1000]\n"
" -xk# count Get only system lock information for (optional) contention >= count [def. 1000]\n"
" -xu# count Get only user-process lock information for (optional) contention >= count [def. 1000]\n"
" -z module Name of module to zoom on (no extension needed by default) such as ntdll, multiple usage allowed, see notes below\n"
" -v Verbose Verbose Printout, if specified with no level the default is Imagehlp symbol information\n"
);
UsageVerbose(); // -v switches
FPRINTF( stderr,
"\nMulti-Processes are allowed (each process ID needs to be preceded by -P except for the system process)\n"
"Typical multi-process profiling command line should look like:\n"
"\nkernrate .... -a -z ntoskrnl -z ntdll -z kernel32 -p 1234 -z w3svc -z iisrtl -p 4321 -z comdlg32 -z msvcrt ...\n"
"\nThe first group of -z denotes either kernel modules and-or modules common across processes\n"
"The other -z groups are process specific and should always follow the appropriate -p xxx \n"
"\nThe -z option requires to add the extension (.dll etc.) only if two or more binaries carry the same name and differ only by the extension\n"
"\nThe '-g' option will attempt to turn on multiple sources. One source at a time profiling mode will automatically be forced\n"
"\nThe '-i' option can be followed by only a source name (system default interrupt interval rate will then be assumed)\n"
"\nA '-i' option followed by a rate amount (no profile source name) will change the interval rate for the default source (time)\n"
"\nProfiling of the default source (Time) can be disabled by setting its profile interval to zero\n"
"\nWith the '-n' option, use the common modules -Z option if you expect more than one process with the same name\n"
"\nThe '-c' option will cause elapsed time to be devided equally between the sources and the monitored processes\n"
"\nThe '-o' option supports redirection of input/output/error streams within the curly brackets. Each redirection character must be escaped with a '^' character\n"
"----------------------------------------------------------------------------------------------------------------------------\n\n"
);
if(ExitKernrate){
exit(1);
} else {
return;
}
} // Usage()
VOID
CreateDoneEvent(
VOID
)
{
LARGE_INTEGER DueTime;
NTSTATUS Status;
DWORD Error;
if (gSleepInterval == 0) {
//
// Create event that will indicate the test is complete.
//
ghDoneEvent = CreateEvent(NULL,
TRUE,
FALSE,
NULL);
if (ghDoneEvent == NULL) {
Error = GetLastError();
FPRINTF(stderr, "CreateEvent failed %d\n",Error);
exit(Error);
}
} else {
//
// Create timer that will indicate the test is complete
//
Status = NtCreateTimer(&ghDoneEvent,
MAXIMUM_ALLOWED,
NULL,
NotificationTimer);
if (!NT_SUCCESS(Status)) {
FPRINTF(stderr, "NtCreateTimer failed %08lx\n",Status);
exit(Status);
}
DueTime.QuadPart = (LONGLONG) UInt32x32To64(gSleepInterval, 10000);
DueTime.QuadPart *= -1;
Status = NtSetTimer(ghDoneEvent,
&DueTime,
NULL,
NULL,
FALSE,
0,
NULL);
if (!NT_SUCCESS(Status)) {
FPRINTF(stderr, "NtSetTimer failed %08lx\n",Status);
exit(Status);
}
}
} // CreateDoneEvent()
/* BEGIN_IMS SymbolCallbackFunction
******************************************************************************
****
**** SymbolCallbackFunction ( )
****
******************************************************************************
*
* Function Description:
*
* The user function is called by IMAGEHLP at the specified operations.
* Refer to the CBA_xxx values.
*
* Arguments:
*
* HANDLE hProcess :
*
* ULONG ActionCode :
*
* PVOID CallbackData :
*
* PVOID UserContext :
*
* Return Value:
*
* BOOL
*
* Algorithm:
*
* ToBeSpecified
*
* Globals Referenced:
*
* ToBeSpecified
*
* Exception Conditions:
*
* ToBeSpecified
*
* In/Out Conditions:
*
* ToBeSpecified
*
* Notes:
*
* ToBeSpecified
*
* ToDo List:
*
* ToBeSpecified
*
* Modification History:
*
* 9/30/97 TF Initial version
*
******************************************************************************
* END_IMS SymbolCallbackFunction */
BOOL
SymbolCallbackFunction(
HANDLE hProcess,
ULONG ActionCode,
ULONG64 CallbackData,
ULONG64 UserContext
)
{
PIMAGEHLP_DEFERRED_SYMBOL_LOAD64 idsl;
PIMAGEHLP_DUPLICATE_SYMBOL idup;
PIMAGEHLP_CBA_READ_MEMORY prm;
PMODULE *pmodule;
PMODULE module;
ULONG i;
//
// Note: The default return value for this function is FALSE.
//
assert( UserContext );
idsl = (PIMAGEHLP_DEFERRED_SYMBOL_LOAD64) CallbackData;
switch( ActionCode ) {
case CBA_DEBUG_INFO:
VerbosePrint(( VERBOSE_IMAGEHLP, "%s", (LPSTR)CallbackData ));
break;
case CBA_DEFERRED_SYMBOL_LOAD_START:
if(UserContext){
pmodule = (PMODULE *)UserContext;
module = *pmodule;
if(module != NULL)
VerbosePrint(( VERBOSE_IMAGEHLP, "CallBack: Loading symbols for %s...\n",
module->module_FileName
));
return TRUE;
}
break;
case CBA_DEFERRED_SYMBOL_LOAD_FAILURE:
if (hProcess == SYM_KERNEL_HANDLE &&
idsl->SizeOfStruct >= FIELD_OFFSET(IMAGEHLP_DEFERRED_SYMBOL_LOAD,
Reparse))
{
i = 0;
if (strncmp(idsl->FileName, "dump_", sizeof("dump_")-1) == 0)
{
i = sizeof("dump_")-1;
}
else if (strncmp(idsl->FileName, "hiber_", sizeof("hiber_")-1) == 0)
{
i = sizeof("hiber_")-1;
}
if (i)
{
if (_stricmp (idsl->FileName+i, "scsiport.sys") == 0)
{
strncpy (idsl->FileName, "diskdump.sys", MAX_PATH-1);
idsl->FileName[ MAX_PATH-1 ] = '\0';
}
else
{
strncpy(idsl->FileName, idsl->FileName+i, MAX_PATH-1);
idsl->FileName[ MAX_PATH-1 ] = '\0';
}
idsl->Reparse = TRUE;
return TRUE;
}
}
if (idsl->FileName && *idsl->FileName)
{
VerbosePrint(( VERBOSE_IMAGEHLP, "CallBack: could not load symbols for %s\n",
idsl->FileName
));
}
else
{
VerbosePrint(( VERBOSE_IMAGEHLP, "CallBack: could not load symbols [MODNAME UNKNOWN]\n"
));
}
break;
case CBA_DEFERRED_SYMBOL_LOAD_COMPLETE:
if(UserContext){
pmodule = (PMODULE *)UserContext;
module = *pmodule;
if(idsl && module)
FPRINTF(stderr, "CallBack: Finished Attempt to Load symbols for %I64x %s\n\n",
idsl->BaseOfImage,
ModuleFullName( module )
);
}
return TRUE;
case CBA_SYMBOLS_UNLOADED:
VerbosePrint(( VERBOSE_IMAGEHLP, "CallBack: Symbols Unloaded.\n" ));
break;
case CBA_DUPLICATE_SYMBOL:
idup = (PIMAGEHLP_DUPLICATE_SYMBOL) CallbackData;
if(UserContext){
pmodule = (PMODULE *)UserContext;
module = *pmodule;
if(module != NULL && module->module_FileName != NULL )
VerbosePrint(( VERBOSE_IMAGEHLP, "Callback: Attempt to load Duplicate symbol for %s\n",
module->module_FileName
));
}
if(idup != NULL)
FPRINTF( stderr, "*** WARNING: Found %ld duplicate symbols for %s\n",
idup->NumberOfDups,
(idup->SelectedSymbol != (ULONG)-1) ? idup->Symbol[idup->SelectedSymbol].Name : "unknown symbol"
);
return TRUE;
case CBA_READ_MEMORY:
prm = (PIMAGEHLP_CBA_READ_MEMORY) CallbackData;
if(prm != NULL){
return ReadProcessMemory(hProcess,
(LPCVOID)prm->addr,
prm->buf,
prm->bytes,
NULL) == S_OK;
}
break;
default:
return FALSE;
}
return FALSE;
} // SymbolCallBackFunction()
static PCHAR
GetSymOptionsValues( DWORD SymOptions )
{
static CHAR values[SYM_VALUES_BUF_SIZE];
ULONG valuesSize = SYM_VALUES_BUF_SIZE - 1;
values[0] = '\0';
if ( SymOptions & SYMOPT_CASE_INSENSITIVE ) {
(void)strncat( values, "CASE_INSENSITIVE ", valuesSize );
SymOptions &= ~SYMOPT_CASE_INSENSITIVE;
}
if ( SymOptions & SYMOPT_UNDNAME ) {
(void)strncat( values, "UNDNAME ", valuesSize-lstrlen(values) );
SymOptions &= ~SYMOPT_UNDNAME;
}
if ( SymOptions & SYMOPT_DEFERRED_LOADS ) {
(void)strncat( values, "DEFERRED_LOADS ", valuesSize-lstrlen(values) );
SymOptions &= ~SYMOPT_DEFERRED_LOADS;
}
if ( SymOptions & SYMOPT_NO_CPP ) {
(void)strncat( values, "NO_CPP ", valuesSize-lstrlen(values) );
SymOptions &= ~SYMOPT_NO_CPP;
}
if ( SymOptions & SYMOPT_LOAD_LINES ) {
(void)strncat( values, "LOAD_LINES ", valuesSize-lstrlen(values) );
SymOptions &= ~SYMOPT_LOAD_LINES;
}
if ( SymOptions & SYMOPT_OMAP_FIND_NEAREST ) {
(void)strncat( values, "OMAP_FIND_NEAREST ", valuesSize-lstrlen(values) );
SymOptions &= ~SYMOPT_OMAP_FIND_NEAREST;
}
if ( SymOptions & SYMOPT_DEBUG ) {
(void)strncat( values, "DEBUG ", valuesSize-lstrlen(values) );
SymOptions &= ~SYMOPT_DEBUG;
}
if ( SymOptions ) {
CHAR uknValues[10];
(void)_snprintf( uknValues, 10, "0x%x", SymOptions );
(void)strncat( values, uknValues, valuesSize-lstrlen(values) );
}
values[valuesSize] = '\0';
return( values );
} // GetSymOptionsValues()
void __cdecl UInt64Div (
unsigned __int64 numer,
unsigned __int64 denom,
uint64div_t *result
)
{
assert(result);
if ( denom != (unsigned __int64)0 ) {
result->quot = numer / denom;
result->rem = numer % denom;
}
else {
result->rem = result->quot = (unsigned __int64)0;
}
return;
} // UInt64Div()
void __cdecl Int64Div (
__int64 numer,
__int64 denom,
int64div_t *result
)
{
assert(result);
if ( denom != (__int64)0 ) {
result->quot = numer / denom;
result->rem = numer % denom;
if (numer < 0 && result->rem > 0) {
/* did division wrong; must fix up */
++result->quot;
result->rem -= denom;
}
}
else {
result->rem = result->quot = (__int64)0;
}
return;
} // Int64Div()
unsigned __int64 __cdecl
UInt64PerCent( unsigned __int64 Val, unsigned __int64 Denom )
{
uint64div_t v;
UInt64Div( 100*Val, Denom, &v );
while ( v.rem > UINT64_MAXDWORD ) {
v.quot++;
v.rem -= UINT64_MAXDWORD;
}
return( v.quot );
} // UInt64PerCent()
double
UInt64ToDoublePerCent( unsigned __int64 Val, unsigned __int64 Denom )
{
double retval;
retval = ( Denom > (__int64) 0 )? ((double) (__int64)Val / (double) (__int64)Denom)*(double)100 : (double) 0;
return retval;
}
//////////////////////////////////////////////////
// //
// Main //
// //
//////////////////////////////////////////////////
int
__cdecl
main (
int argc,
char *argv[]
)
{
PPROC_TO_MONITOR ProcToMonitor = NULL;
ULONG i,j;
ULONG NumTasks;
BOOLEAN Enabled;
PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION SystemInfoBegin; //For the Profile period only
PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION SystemInfoEnd; //For the Profile period only
NTSTATUS Status;
PTASK_LIST tlist;
PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION StartInfo2; //For the extra system-wide and process specific information
PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION StopInfo2; //For the extra system-wide and process specific information
//// Beginning of Program-wide Assertions Section
//
//
//
// This code does not support UNICODE strings
//
#if defined(UNICODE) || defined(_UNICODE)
#error This code does not support UNICODE strings!!!
#endif // UNICODE || _UNICODE
//
//
//// End of Program-wide Assertions Section
//
// Per user request, set priority up to realtime to accelerate initialization and symbol loading,
// minimize timing glitches during the profile and post process the data at high priority
//
if (bProcessDataHighPriority == TRUE ) {
SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS);
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);
}
InitializeKernrate( argc, argv );
if(bWaitCreatedProcToSettle == TRUE){
//
// First Check if the user asked to wait for a key press (ENTER) to wait a created processes to settle
//
if(bCreatedProcWaitForUserInput == TRUE){
FPRINTF(stderr, "\n***> Waiting for created processes to settle (go idle) Please press ENTER when ready\n");
getchar();
} else {
//
// Wait for a given number of seconds for created processes to settle
//
FPRINTF(stderr, "\nWaiting for %d seconds to let created processe(s) settle (go idle)\n", gSecondsToWaitCreatedProc);
Sleep(1000*gSecondsToWaitCreatedProc);
}
}
StartInfo2 = calloc(1, gSysBasicInfo->NumberOfProcessors*sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION));
if (StartInfo2 == NULL) {
FPRINTF(stderr, "KERNRATE: Allocation for SYSTEM_PROCESSOR_PERFORMANCE_INFO(1) failed\n");
exit(1);
}
StopInfo2 = calloc(1, gSysBasicInfo->NumberOfProcessors*sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION));
if (StopInfo2 == NULL) {
FPRINTF(stderr, "KERNRATE: Allocation for SYSTEM_PROCESSOR_PERFORMANCE_INFO(2) failed\n");
exit(1);
}
SystemInfoBegin = calloc(1, gSysBasicInfo->NumberOfProcessors*sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION));
if (SystemInfoBegin == NULL) {
FPRINTF(stderr, "KERNRATE: Allocation for SYSTEM_PROCESSOR_PERFORMANCE_INFO(3) failed\n");
exit(1);
}
SystemInfoEnd = calloc(1, gSysBasicInfo->NumberOfProcessors*sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION));
if (SystemInfoEnd == NULL) {
FPRINTF(stderr, "KERNRATE: Allocation for SYSTEM_PROCESSOR_PERFORMANCE_INFO(4) failed\n");
exit(1);
}
InitAllProcessesModulesInfo();
//
// Adjust security level to the needed level for system profile
//
Status = RtlAdjustPrivilege(SE_SYSTEM_PROFILE_PRIVILEGE, //If ever not sufficient use SE_DEBUG_PRIVILEGE,
TRUE,
FALSE,
&Enabled
);
if( !NT_SUCCESS(Status) ) {
FPRINTF(stderr,"RtlAdjustPrivilege(SE_PROFILE_PRIVILEGE) failed: %08x\n", Status);
exit(1);
}
//
// Find the number of Active Sources and store the indices of the active sources
// To be used later for better performance than going over the whole source list
//
ProcToMonitor = gProcessList;
for (i=0; i < gSourceMaximum; i++) {
if (ProcToMonitor->Source[i].Interval != 0) {
gTotalActiveSources += 1;
}
}
gulActiveSources = (PULONG)malloc( gTotalActiveSources*sizeof(ULONG) );
if (gulActiveSources==NULL) {
FPRINTF(stderr, "\nMemory allocation failed for ActiveSources in GetConfiguration\n");
exit(1);
}
ProcToMonitor = gProcessList;
j = 0;
for (i=0; i < gSourceMaximum; i++) {
if (ProcToMonitor->Source[i].Interval != 0) {
gulActiveSources[j] = i;
++j;
}
}
//
// Create necessary profiles for each process
//
ProcToMonitor = gProcessList;
for (i=0; i<gNumProcToMonitor; i++){
if(ProcToMonitor->ModuleList != NULL)
CreateProfiles(ProcToMonitor->ModuleList, ProcToMonitor);
ProcToMonitor = ProcToMonitor->Next;
}
//
// Set priority up to realtime to minimize timing glitches during the profile only
//
if (bProcessDataHighPriority == FALSE ) {
SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS);
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);
}
//
// Check if the user asked to wait for a key press (ENTER) before starting the profile
//
if(bWaitForUserInput == TRUE){
FPRINTF(stderr, "\n***> Please press ENTER to start collecting profile data\n");
getchar();
}
//
// Check if the user asked to wait for a given number of seconds before starting the profile
//
if(gSecondsToDelayProfile != 0){
FPRINTF(stderr, "\nWaiting for %d seconds before starting to collect profile data\n", gSecondsToDelayProfile);
Sleep(1000*gSecondsToDelayProfile);
}
FPRINTF(stderr, "Starting to collect profile data\n\n");
if (gSleepInterval == 0) {
FPRINTF(stderr,"***> Press ctrl-c to finish collecting profile data\n");
} else {
FPRINTF(stderr, "Will collect profile data for %d seconds\n", gSleepInterval/1000);
}
//
// Wait for test to complete. Obtain any extra system-wide info out of the profile time-span
//
Status = NtQuerySystemInformation(SystemProcessorPerformanceInformation,
(PVOID)StartInfo2,
gSysBasicInfo->NumberOfProcessors*sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION),
NULL);
if (!NT_SUCCESS(Status)) {
FPRINTF(stderr, "Failed to query starting processor performance information %08lx\n",Status);
exit(Status);
}
//
// Get or update task list information
//
if(bIncludeGeneralInfo || bDisplayTaskSummary || (gVerbose & VERBOSE_PROFILING) ) {
if( gTlistStart == NULL ){
gTlistStart = calloc(1, gMaxTasks*sizeof(TASK_LIST));
if ( gTlistStart == NULL ){
FPRINTF(stderr, "KERNRATE: Failed to allocate memory for the running processes task list(5)\n");
exit(1);
}
}
gNumTasksStart = GetTaskList( gTlistStart, gMaxTasks);
}
if(bIncludeSystemLocksInfo)
GetSystemLocksInformation(START);
if(bIncludeUserProcLocksInfo){
ProcToMonitor = gProcessList;
for (i=0; i<gNumProcToMonitor; i++){
if(ProcToMonitor->ProcessHandle != SYM_KERNEL_HANDLE) {
GetProcessLocksInformation( ProcToMonitor,
RTL_QUERY_PROCESS_LOCKS,
START
);
}
ProcToMonitor = ProcToMonitor->Next;
}
}
SetConsoleCtrlHandler(CtrlcH, TRUE);
CreateDoneEvent();
if(bIncludeGeneralInfo)
GetProfileSystemInfo(START);
Status = NtQuerySystemInformation(SystemProcessorPerformanceInformation,
(PVOID)SystemInfoBegin,
gSysBasicInfo->NumberOfProcessors*sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION),
NULL);
if (!NT_SUCCESS(Status)) {
FPRINTF(stderr, "Failed to query starting processor performance information %08lx\n",Status);
exit(Status);
}
//
//Execute the actual Profiles
//
ExecuteProfiles( bOldSampling );
gProfilingDone = TRUE; // used to synchronize the ctrl handler.
//
//Obtain end of run system-wide info
//
Status = NtQuerySystemInformation(SystemProcessorPerformanceInformation,
(PVOID)SystemInfoEnd,
gSysBasicInfo->NumberOfProcessors*sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION),
NULL);
if (!NT_SUCCESS(Status)) {
FPRINTF(stderr, "Failed to query ending processor performance information %08lx\n",Status);
exit(Status);
}
if(bIncludeGeneralInfo)
GetProfileSystemInfo(STOP);
if(bIncludeUserProcLocksInfo){
ProcToMonitor = gProcessList;
for (i=0; i<gNumProcToMonitor; i++){
if(ProcToMonitor->ProcessHandle != SYM_KERNEL_HANDLE) {
GetProcessLocksInformation( ProcToMonitor,
RTL_QUERY_PROCESS_LOCKS,
STOP
);
}
ProcToMonitor = ProcToMonitor->Next;
}
}
if(bIncludeSystemLocksInfo)
GetSystemLocksInformation(STOP);
if(bIncludeGeneralInfo || bDisplayTaskSummary || (gVerbose & VERBOSE_PROFILING) ) {
tlist = calloc(1, gMaxTasks*sizeof(TASK_LIST));
if ( tlist == NULL ){
FPRINTF(stderr, "KERNRATE: Failed to allocate memory for the running processes task list(6)\n");
exit(1);
}
NumTasks = GetTaskList( tlist, gMaxTasks);
gNumTasksStop = NumTasks;
}
Status = NtQuerySystemInformation(SystemProcessorPerformanceInformation,
(PVOID)StopInfo2,
gSysBasicInfo->NumberOfProcessors*sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION),
NULL);
if (!NT_SUCCESS(Status)) {
FPRINTF(stderr, "Failed to query starting processor performance information %08lx\n", Status);
exit(Status);
}
for (i=0; i<(ULONG)gSysBasicInfo->NumberOfProcessors; i++) {
gTotal2ElapsedTime64.QuadPart += ( (StopInfo2[i].UserTime.QuadPart - StartInfo2[i].UserTime.QuadPart) +
(StopInfo2[i].KernelTime.QuadPart - StartInfo2[i].KernelTime.QuadPart) );
}
FPRINTF(stderr, "===> Finished Collecting Data, Starting to Process Results\n");
//
// Reduce priority unless user asked to process the collected data at high priority
//
if (bProcessDataHighPriority == FALSE ) {
SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS);
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL);
}
//
// Restore privilege
//
RtlAdjustPrivilege(SE_SYSTEM_PROFILE_PRIVILEGE,
Enabled,
FALSE,
&Enabled);
//
// Output System-Wide information
//
DisplaySystemWideInformation( SystemInfoBegin,
SystemInfoEnd
);
if( SystemInfoBegin != NULL ){
free(SystemInfoBegin);
SystemInfoBegin = NULL;
}
if( SystemInfoEnd != NULL ){
free(SystemInfoEnd);
SystemInfoEnd = NULL;
}
//
// Output results
//
if ( bDisplayTaskSummary || (gVerbose & VERBOSE_PROFILING) ) {
FPRINTF(stdout, "\n--- Process List and Summary At The End of Data Collection ---\n\n");
if ( bDisplayTaskSummary ) {
DisplayRunningTasksSummary (gTlistStart,
tlist
);
} else {
FPRINTF(stdout, " Pid Process\n");
FPRINTF(stdout, " ------- -----------\n");
for (i=0; i < NumTasks; i++) {
FPRINTF(stdout, "%12I64d %32s\n",
tlist[i].ProcessId,
&tlist[i].ProcessName
);
}
}
}
if( StartInfo2 != NULL ){
free(StartInfo2);
StartInfo2 = NULL;
}
if( StopInfo2 != NULL ){
free(StopInfo2);
StopInfo2 = NULL;
}
ProcToMonitor = gProcessList;
for (i=0; i<gNumProcToMonitor; i++){
if(ProcToMonitor->ProcessHandle != SYM_KERNEL_HANDLE){
FPRINTF(stdout, "\n----------------------------------------------------------------\n\n");
FPRINTF(stdout, "Results for User Mode Process %s (PID = %I64d)\n",
ProcToMonitor->ProcessName,
ProcToMonitor->Pid
);
if(bIncludeGeneralInfo)
OutputProcessPerfInfo ( tlist, NumTasks, ProcToMonitor);
if(bIncludeUserProcLocksInfo)
GetProcessLocksInformation( ProcToMonitor,
RTL_QUERY_PROCESS_LOCKS,
OUTPUT
);
FPRINTF(stdout, "------------------------------------------------------------------\n\n");
}
else{
FPRINTF(stdout, "\n-----------------------------\n\n");
FPRINTF(stdout, "Results for Kernel Mode:\n");
FPRINTF(stdout, "-----------------------------\n\n");
if( bIncludeGeneralInfo && bSystemThreadsInfo )
OutputProcessPerfInfo( tlist, NumTasks, ProcToMonitor);
}
OutputResults(stdout, ProcToMonitor);
//MC
if(ProcToMonitor->ProcessHandle != SYM_KERNEL_HANDLE){
if( ProcToMonitor->JITHeapLocationsStart != NULL ){ //Meaning we do have JIT modules monitored in this process
//So the Managed Code Helper library is already loaded
pfnAttachToProcess((DWORD)ProcToMonitor->Pid);
ProcToMonitor->JITHeapLocationsStop = pfnGetJitRange();
pfnDetachFromProcess();
OutputJITRangeComparison(ProcToMonitor);
}
}
//MC
ProcToMonitor = ProcToMonitor->Next;
}
//
// Cleanup
//
if( tlist != NULL ){
free(tlist);
tlist = NULL;
}
if(gpProcDummy != NULL){
free(gpProcDummy);
gpProcDummy = NULL;
}
if(gSymbol != NULL){
free(gSymbol);
gSymbol = NULL;
}
if(gSysBasicInfo != NULL){
free(gSysBasicInfo);
gSysBasicInfo = NULL;
}
if(gulActiveSources!= NULL){
free(gulActiveSources);
gulActiveSources = NULL;
}
//MC
if( ghMCLib != NULL){
FreeLibrary(ghMCLib);
ghMCLib = NULL;
}
//MC
SetConsoleCtrlHandler(CtrlcH,FALSE);
FPRINTF(stdout, "================================= END OF RUN ==================================\n");
FPRINTF(stderr, "============================== NORMAL END OF RUN ==============================\n");
//
// Clean up allocated IMAGEHLP resources
//
ProcToMonitor = gProcessList;
for (i=0; i<gNumProcToMonitor; i++){
(void)SymCleanup( ProcToMonitor->ProcessHandle );
ProcToMonitor = ProcToMonitor->Next;
}
if(ghInput != NULL)
CloseHandle(ghInput);
if(ghOutput != NULL)
CloseHandle(ghOutput);
if(ghError != NULL)
CloseHandle(ghError);
//
// Normal program exit
//
return(0);
} // main()
PMODULE
GetProcessModuleInformation(
IN PPROC_TO_MONITOR ProcToMonitor
)
{
PPROCESS_BASIC_INFORMATION BasicInfo;
PLIST_ENTRY LdrHead;
PPEB_LDR_DATA Ldr = NULL;
PPEB_LDR_DATA LdrAddress;
LDR_DATA_TABLE_ENTRY LdrEntry;
PLDR_DATA_TABLE_ENTRY LdrEntryAddress;
PLIST_ENTRY LdrNext;
UNICODE_STRING Pathname;
const ULONG PathnameBufferSize = 600*sizeof(WCHAR);
PWCHAR PathnameBuffer = NULL;
UNICODE_STRING fullPathName;
PWCHAR fullPathNameBuffer = NULL;
PEB Peb;
NTSTATUS Status;
BOOL Success;
PMODULE NewModule;
PMODULE Root = NULL;
PCHAR ModuleName = NULL;
PCHAR moduleFullName = NULL;
ANSI_STRING AnsiString;
HANDLE ProcessHandle = ProcToMonitor->ProcessHandle;
//MC
int i, j;
BOOL bMCInitialized = FALSE;
//MC
//
// Get Peb address.
//
BasicInfo = malloc(sizeof(PROCESS_BASIC_INFORMATION));
if(BasicInfo == NULL){
FPRINTF(stderr, "Memory Allocation failed for ProcessBasicInformation in GetProcessModuleInformation\n");
exit(1);
}
Status = NtQueryInformationProcess(ProcessHandle,
ProcessBasicInformation,
BasicInfo,
sizeof(PROCESS_BASIC_INFORMATION),
NULL
);
if (!NT_SUCCESS(Status)) {
FPRINTF(stderr, "NtQueryInformationProcess failed status %08lx\n", Status);
ProcToMonitor->ProcessName = "???(May Be gone)";
goto CLEANUP;
}
if (BasicInfo->PebBaseAddress == NULL) {
FPRINTF(stderr, "GetProcessModuleInformation: process has no Peb.\n");
ProcToMonitor->ProcessName = "???(May Be gone)";
goto CLEANUP;
}
//
// 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());
ProcToMonitor->ProcessName = "???(May Be Gone)";
goto CLEANUP;
}
LdrAddress = Peb.Ldr;
if (LdrAddress == NULL) {
FPRINTF(stderr, "Process's LdrAddress is NULL\n");
ProcToMonitor->ProcessName = "???(May Be Gone)";
goto CLEANUP;
}
//
// Read Ldr to get Ldr entries.
//
Ldr = malloc(sizeof(PEB_LDR_DATA));
if(Ldr == NULL){
FPRINTF(stderr, "Memory Allocation failed for Ldr in GetProcessModuleInformation\n");
exit(1);
}
Success = ReadProcessMemory(ProcessHandle,
LdrAddress,
Ldr,
sizeof(PEB_LDR_DATA),
NULL);
if (!Success) {
FPRINTF(stderr, "ReadProcessMemory to get Ldr entries failed, errror %d\n", GetLastError());
ProcToMonitor->ProcessName = "???(May Be Gone)";
goto CLEANUP;
}
//
// Read Ldr table entries to get image information.
//
if (Ldr->InLoadOrderModuleList.Flink == NULL) {
FPRINTF(stderr, "Ldr.InLoadOrderModuleList == NULL\n");
ProcToMonitor->ProcessName = "???(May Be Gone)";
goto CLEANUP;
}
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());
ProcToMonitor->ProcessName = "???(May Be Gone)";
goto CLEANUP;
}
//
// Loop through InLoadOrderModuleList.
//
PathnameBuffer = (PWCHAR)malloc(PathnameBufferSize);
if(PathnameBuffer == NULL){
FPRINTF(stderr, "Memory Allocation failed for PathNameBuffer in GetProcessModuleInformation\n");
exit(1);
}
fullPathNameBuffer = (PWCHAR)malloc( _MAX_PATH*sizeof(WCHAR));
if(fullPathNameBuffer == NULL){
FPRINTF(stderr, "Memory Allocation failed for FullPathNameBuffer in GetProcessModuleInformation\n");
exit(1);
}
ModuleName = malloc(cMODULE_NAME_STRLEN*sizeof(CHAR));
if(ModuleName == NULL){
FPRINTF(stderr, "Memory Allocation failed for ModuleName in GetProcessModuleInformation\n");
exit(1);
}
moduleFullName = malloc(_MAX_PATH*sizeof(CHAR));
if(moduleFullName == NULL){
FPRINTF(stderr, "Memory Allocation failed for ModuleFullName in GetProcessModuleInformation\n");
exit(1);
}
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());
ProcToMonitor->ProcessName = "???(May Be Gone)";
goto CLEANUP;
}
//
// Get copy of image name.
//
Pathname = LdrEntry.BaseDllName;
if( Pathname.MaximumLength > PathnameBufferSize ){
free( PathnameBuffer ); //We already know it's not NULL
PathnameBuffer = (PWCHAR)malloc( Pathname.MaximumLength );
if(PathnameBuffer == NULL){
FPRINTF(stderr, "Memory Allocation failed for PathNameBuffer(2) in GetProcessModuleInformation\n");
exit(1);
}
}
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());
ProcToMonitor->ProcessName = "???(May Be Gone)";
goto CLEANUP;
}
//
// Get Copy of image full pathname
//
fullPathName = LdrEntry.FullDllName;
if( fullPathName.MaximumLength > _MAX_PATH*sizeof(WCHAR) ){
free( fullPathNameBuffer ); //We already know it's not NULL
fullPathNameBuffer = (PWCHAR)malloc( fullPathName.MaximumLength );
if(fullPathNameBuffer == NULL){
FPRINTF(stderr, "Memory Allocation failed for FullPathNameBuffer(2) in GetProcessModuleInformation\n");
exit(1);
}
}
fullPathName.Buffer = fullPathNameBuffer;
Success = ReadProcessMemory( ProcessHandle,
LdrEntry.FullDllName.Buffer,
fullPathName.Buffer,
fullPathName.MaximumLength,
NULL
);
if (!Success) {
FPRINTF(stderr, "ReadProcessMemory to get image full path name failed, errror %d\n", GetLastError());
ProcToMonitor->ProcessName = "???(May Be Gone)";
goto CLEANUP;
}
//
// Create module
//
AnsiString.Buffer = ModuleName;
AnsiString.MaximumLength = cMODULE_NAME_STRLEN*sizeof(CHAR);
AnsiString.Length = 0;
Status = RtlUnicodeStringToAnsiString(&AnsiString, &Pathname, cDONOT_ALLOCATE_DESTINATION_STRING);
if (!NT_SUCCESS(Status)) {
FPRINTF(stderr, "KERNRATE WARNING:\n");
FPRINTF(stderr, "RtlUnicodeStringToAnsiString failed in GetProcessModuleInformation, status= %08lx\n", Status);
if(Status == STATUS_BUFFER_OVERFLOW){
FPRINTF(stderr, "Source String: %S\nLength= %ld", &Pathname.Buffer, Pathname.Length);
FPRINTF(stderr, "Maximum destination string Length allowed is %d\n", cMODULE_NAME_STRLEN);
}
}
ModuleName[AnsiString.Length] = '\0';
AnsiString.Buffer = moduleFullName;
AnsiString.MaximumLength = _MAX_PATH*sizeof(CHAR);
AnsiString.Length = 0;
Status = RtlUnicodeStringToAnsiString(&AnsiString, &fullPathName, cDONOT_ALLOCATE_DESTINATION_STRING );
if (!NT_SUCCESS(Status)) {
FPRINTF(stderr, "KERNRATE WARNING:\n");
FPRINTF(stderr, "RtlUnicodeStringToAnsiString failed in GetProcessModuleInformation, status= %08lx\n", Status);
if(Status == STATUS_BUFFER_OVERFLOW){
FPRINTF(stderr, "Source String: %S\nLength= %ld", &fullPathName.Buffer, fullPathName.Length);
FPRINTF(stderr, "Maximum destination string Length allowed is %d\n", _MAX_PATH);
}
}
moduleFullName[AnsiString.Length] = '\0';
NewModule = CreateNewModule(ProcToMonitor,
ModuleName,
moduleFullName,
(ULONG64)LdrEntry.DllBase,
LdrEntry.SizeOfImage);
if( NewModule != NULL){
ProcToMonitor->ModuleCount += 1;
NewModule->Next = Root;
Root = NewModule;
LdrNext = LdrEntry.InLoadOrderLinks.Flink;
}else{
FPRINTF(stderr, "KERNRATE: Failed to create new module for %s\n", ModuleName);
}
//
//The first module in the LDR InLoadOrder module list is the Process
//
if(ProcToMonitor->ModuleCount == 1){
PCHAR Name = calloc(1, cMODULE_NAME_STRLEN*sizeof(CHAR));
if(Name != NULL){
strncpy(Name, ModuleName, cMODULE_NAME_STRLEN-1);
Name[ cMODULE_NAME_STRLEN-1 ] = '\0';
ProcToMonitor->ProcessName = _strupr(Name);
}
}
//MC
//
// Initialize Managed Code Support if Managed Code main library is present
//
if( !_stricmp(ModuleName, MANAGED_CODE_MAINLIB) ){
bMCInitialized = InitializeManagedCodeSupport( ProcToMonitor );
if( !bMCInitialized ){
FPRINTF(stderr, "\nKERNRATE: Failed to Initialize Support for Managed Code for Pid = %I64d\n", ProcToMonitor->Pid);
FPRINTF(stderr, "Use Verbose Level 4 for More Details\n");
}
}
//MC
}// while (LdrNext != LdrHead)
//MC
//
// If Managed Code helper lib is loaded and we do have JIT ranges present, let's create a module for each
//
if( bMCInitialized && bMCJitRangesExist ){
i = 0;
j = 0;
while( ProcToMonitor->JITHeapLocationsStart[i] != 0 ){
_snprintf( ModuleName, cMODULE_NAME_STRLEN*sizeof(CHAR)-1, "JIT%d", j );
VerbosePrint((VERBOSE_INTERNALS, "KERNRATE: Creating JIT module %s\n", ModuleName));
strncpy(moduleFullName, "JIT_TYPE", cMODULE_NAME_STRLEN-1);
NewModule = CreateNewModule(ProcToMonitor,
ModuleName,
moduleFullName, //"JIT_TYPE",
(ULONG64)ProcToMonitor->JITHeapLocationsStart[i] ,
(ULONG)ProcToMonitor->JITHeapLocationsStart[i+1]
);
if(NewModule != NULL){
ProcToMonitor->ModuleCount += 1;
NewModule->Next = Root;
Root = NewModule;
}else{
FPRINTF(stderr, "KERNRATE: Failed to create new JIT module for %s\n", ModuleName);
}
i += 2;
j += 1;
}
}
//MC
//
// Cleanup
//
CLEANUP:
if(BasicInfo != NULL){
free(BasicInfo);
BasicInfo = NULL;
}
if(Ldr != NULL){
free(Ldr);
Ldr = NULL;
}
if(PathnameBuffer != NULL){
free(PathnameBuffer);
PathnameBuffer = NULL;
}
if(fullPathNameBuffer != NULL){
free(fullPathNameBuffer);
fullPathNameBuffer = NULL;
}
if(ModuleName != NULL){
free(ModuleName);
ModuleName = NULL;
}
if(moduleFullName != NULL){
free(moduleFullName);
moduleFullName = NULL;
}
return(Root);
} // GetProcessModuleInformation()
PMODULE
GetKernelModuleInformation(
VOID
)
{
PRTL_PROCESS_MODULES modules;
PUCHAR buffer;
ULONG bufferSize = 1*1024*1024; //not a constant!
ULONG i;
PMODULE root = NULL;
PMODULE newModule;
NTSTATUS status;
do {
buffer = malloc(bufferSize);
if (buffer == NULL) {
FPRINTF(stderr, "Buffer Allocation failed for ModuleInformation in GetKernelModuleInformation\n");
exit(1);
}
status = NtQuerySystemInformation(SystemModuleInformation,
buffer,
bufferSize,
&bufferSize);
if (NT_SUCCESS(status)) {
break;
}
if (status == STATUS_INFO_LENGTH_MISMATCH) {
free(buffer);
buffer = NULL;
} else {
FPRINTF(stderr, "GetKernelModuleInformation failed call to get SystemModuleInformation - ");
if(status == STATUS_WORKING_SET_QUOTA)
FPRINTF(stderr, "Insufficient process working set\n");
if(status == STATUS_INSUFFICIENT_RESOURCES)
FPRINTF(stderr, "Insufficient system resources\n");
exit(1);
}
} while (buffer == NULL);
#ifdef _WIN64
#define VerboseModuleFormat "start end "
#else // !_WIN64
#define VerboseModuleFormat "start end "
#endif // !_WIN64
VerbosePrint(( VERBOSE_MODULES, "Kernel Modules ========== System HighestUserAddress = 0x%p\n"
VerboseModuleFormat
"module name [full name]\n",
(PVOID)gSysBasicInfo->MaximumUserModeAddress
));
#undef VerboseModuleFormat
modules = (PRTL_PROCESS_MODULES)buffer;
gKernelModuleCount = modules->NumberOfModules;
for (i=0; i < gKernelModuleCount; i++) {
PRTL_PROCESS_MODULE_INFORMATION Module;
Module = &modules->Modules[i];
if ((ULONG_PTR)Module->ImageBase > gSysBasicInfo->MaximumUserModeAddress) {
newModule = CreateNewModule(gpSysProc,
(PCHAR)(Module->FullPathName+Module->OffsetToFileName),
(PCHAR)Module->FullPathName,
(ULONG64)(ULONG_PTR)Module->ImageBase,
Module->ImageSize);
assert( newModule );
newModule->Next = root;
root = newModule;
}
else {
#define VerboseModuleFormat "0x%p 0x%p "
VerbosePrint(( VERBOSE_MODULES, VerboseModuleFormat " %s [%s] - Base > HighestUserAddress\n",
(PVOID)Module->ImageBase,
(PVOID)((ULONG64)Module->ImageBase + (ULONG64)Module->ImageSize),
Module->FullPathName+Module->OffsetToFileName,
Module->FullPathName
));
#undef VerboseModuleFormat
}
}
//
// Cleanup
//
if(buffer != NULL){
free(buffer);
buffer = NULL;
}
return(root);
} // GetKernelModuleInformation()
VOID
CreateProfiles(
IN PMODULE Root,
IN PPROC_TO_MONITOR ProcToMonitor
)
{
PMODULE Current;
KPROFILE_SOURCE ProfileSource;
NTSTATUS Status;
PRATE_DATA Rate;
ULONG ProfileSourceIndex, Index;
ULONG BucketsNeeded;
HANDLE hProc = NULL;
KAFFINITY AffinityMask = (KAFFINITY)-1;
LONG CpuNumber;
//
// To get the kernel profile NtCreateProfile has to be called with hProc = NULL
//
hProc = (ProcToMonitor->ProcessHandle == SYM_KERNEL_HANDLE)? NULL : ProcToMonitor->ProcessHandle;
for (Index=0; Index < gTotalActiveSources; Index++) {
ProfileSourceIndex = gulActiveSources[Index];
ProfileSource = ProcToMonitor->Source[ProfileSourceIndex].ProfileSource;
Current = Root;
while (Current != NULL) {
BucketsNeeded = BUCKETS_NEEDED(Current->Length);
Rate = &Current->Rate[ProfileSourceIndex];
Rate->TotalCount = calloc(gProfileProcessors, sizeof(ULONGLONG) );
if(Rate->TotalCount == NULL){
FPRINTF(stderr, "KERNRATE: Memory allocation failed for TotalCount in CreateProfiles\n");
exit(1);
}
Rate->CurrentCount = calloc(gProfileProcessors, sizeof(ULONG) );
if(Rate->CurrentCount == NULL){
FPRINTF(stderr, "KERNRATE: Memory allocation failed for CurrentCount in CreateProfiles\n");
exit(1);
}
Rate->ProfileHandle = calloc(gProfileProcessors, sizeof(HANDLE) );
if(Rate->ProfileHandle == NULL){
FPRINTF(stderr, "KERNRATE: Memory allocation failed for ProfileHandle in CreateProfiles\n");
exit(1);
}
Rate->ProfileBuffer = calloc(gProfileProcessors, sizeof(PULONG) );
if(Rate->ProfileBuffer == NULL){
FPRINTF(stderr, "KERNRATE: Memory allocation failed for ProfileBuffer in CreateProfiles\n");
exit(1);
}
Rate->StartTime = 0;
Rate->TotalTime = 0;
Rate->Rate = 0;
Rate->GrandTotalCount = 0;
for (CpuNumber=0; CpuNumber < gProfileProcessors; CpuNumber++) {
if ( bProfileByProcessor ){
AffinityMask = (1 << CpuNumber);
}
Rate->TotalCount[CpuNumber] = 0;
Rate->CurrentCount[CpuNumber] = 0;
if (Current->bZoom) {
Rate->ProfileBuffer[CpuNumber] = calloc(1, BucketsNeeded*sizeof(ULONG));
if (Rate->ProfileBuffer[CpuNumber] == NULL) {
FPRINTF(stderr,
"KERNRATE: Memory allocation failed for Zoom buffer, Module: %s\n",
Current->module_Name
);
exit(1);
}
if ( !gAffinityMask || (AffinityMask & gAffinityMask) ) {
Status = NtCreateProfile(&Rate->ProfileHandle[CpuNumber],
hProc,
(PVOID64)Current->Base,
Current->Length,
gLog2ZoomBucket,
Rate->ProfileBuffer[CpuNumber],
sizeof(ULONG)*BucketsNeeded,
ProfileSource,
AffinityMask
);
if (!NT_SUCCESS(Status)) {
FPRINTF(stderr,
"NtCreateProfile on zoomed module %s, source %d failed %08lx\n",
Current->module_Name,
ProfileSource,
Status
);
FPRINTF(stderr,
"Base %p\nLength %08lx\nBufferLength %08lx\n",
(PVOID64)Current->Base,
Current->Length,
BucketsNeeded
);
exit(1);
}
else if ( gVerbose & VERBOSE_PROFILING ) {
FPRINTF(stderr,
"Created zoomed profiling on module %s with source: %s\n",
Current->module_Name,
ProcToMonitor->Source[ProfileSourceIndex].ShortName
);
}
}
} else {
Status = NtCreateProfile(&Rate->ProfileHandle[CpuNumber],
hProc,
(PVOID64)Current->Base,
Current->Length,
31,
&Rate->CurrentCount[CpuNumber],
sizeof(Rate->CurrentCount[CpuNumber]),
ProfileSource,
AffinityMask
);
if (!NT_SUCCESS(Status)) {
FPRINTF(stderr,
"NtCreateProfile on module %s, source %d failed %08lx\n",
Current->module_Name,
ProfileSource,
Status
);
exit(1);
}
else if ( gVerbose & VERBOSE_PROFILING ) {
FPRINTF(stderr,
"Created profiling on module %s with source: %s\n",
Current->module_Name,
ProcToMonitor->Source[ProfileSourceIndex].ShortName
);
}
}
} // CpuNumber
Current = Current->Next;
} // Module list
} // ProfileSourceIndex
}
static void
SetModuleName( PMODULE Module, PCHAR szName )
{
assert ( Module );
assert ( szName );
(void)strncpy( Module->module_Name, szName, sizeof(Module->module_Name) - 1 );
Module->module_Name[lstrlen(Module->module_Name)] = '\0';
return;
} // SetModuleName()
VOID
GetConfiguration(
int argc,
char *argv[]
)
/*++
Routine Description:
Gets configuration for this run.
Arguments:
None
Return Value:
None, exits on failure.
--*/
{
DWORD NumTasks, m;
CHAR tmpName[USER_SYMPATH_LENGTH];
NTSTATUS Status;
PPROC_TO_MONITOR ProcToMonitor = NULL;
PMODULE ZoomModule;
PMODULE tmpModule;
LONGLONG Pid;
int i, j, k;
int tJump = 0;
HANDLE SymHandle;
ULONG MaxProcSameName = MAX_PROC_SAME_NAME;
ULONG ProfileSourceIndex;
ULONG IDataCommonRate;
ULONG SourcesSoFar = 1; //Source TIME is on by default
ULONG ulVerbose;
BOOL bZoomSpecified = FALSE;
BOOL bTlistInitialized = FALSE;
BOOL tlistVerbose = FALSE;
BOOL tlistDisplayed = FALSE;
INPUT_ERROR_TYPE ietResult;
//
// Assume system wide profile.
//
gProfileProcessors = 1;
//
// The following preliminary-check purpose is to get rid of most command line precedence rules
//
for (i=1; i < argc; i++) {
if ((argv[i][0] == '-') || (argv[i][0] == '/')) {
switch ( toupper(argv[i][1]) ) {
case 'E':
//
// User asked to exclude system-wide and process specific general information
// (context switches, memory usage, etc.)
//
ietResult = IsInputValid(argc,
i,
argv,
"",
NULL,
NULL,
0,
ORDER_ANY,
OPTIONAL_ANY
);
if(ietResult == INPUT_GOOD){
bIncludeGeneralInfo = FALSE;
} else if (ietResult == BOGUS_ENTRY){
ExitWithUnknownOptionMessage(argv[i+1]);
} else {
ExitWithUnknownOptionMessage(argv[i]);
}
break;
case 'M':
//
// Do multi-processor profile. Allow optional processor affinity mask to be able to
// profile only selected processors and reduce profiling overhead on 64 and 32 way.
//
// The following precedence rule check allows to save some memory footprint
// by allocating the zoom module based on the actual gProfileProcessors instead
// of the total NumberOfProcessors
//
ietResult = IsInputValid(argc,
i,
argv,
"#",
NULL,
tmpName, //Just used as a temp storage
USER_SYMPATH_LENGTH, //with long enough space
ORDER_ANY,
OPTIONAL_ANY
);
if(ietResult == MISSING_REQUIRED_NUMBER){ //Allow a # although the optional mask is initially treated as a string
if(i+1 < argc && argv[i+1][0] == '0' && argv[i+1][1] == 'x' || argv[i+1][1] == 'X'){
ietResult = INPUT_GOOD;
} else {
FPRINTF(stderr, "KERNRATE: '-m# 0xN' option requires a valid (0x prefixed) hex number\n");
ExitWithUnknownOptionMessage(argv[i+1]);
}
}
if(ietResult == INPUT_GOOD){
ULONG LowMask, HighMask;
int nChars, iStart=0, iProc;
PCHAR cStart = NULL;
CHAR tmpstr[8] = "";
bProfileByProcessor = TRUE;
gProfileProcessors = gSysBasicInfo->NumberOfProcessors;
nChars = lstrlen(tmpName);
cStart = tmpName;
if( 'x' == tmpName[1] || 'X' == tmpName[1] ){
cStart = &tmpName[2];
nChars -= 2;
} else if ( 'x' == tmpName[0] || 'X' == tmpName[0] ){
cStart = &tmpName[1];
nChars -= 1;
}
if( nChars > 16 || (gProfileProcessors <= 32 && nChars > 8) ){
InvalidEntryMessage(argv[i],
argv[i+1],
"Expecting a valid HEX value, maximum 8 characters for up to 32 processors\nor 16 characters for up to 64 processors",
FALSE,
TRUE
);
}
if( nChars > 8 ){
strncpy(tmpstr, cStart, nChars-8);
HighMask = strtoul(tmpstr, NULL, 16);
iStart = nChars-8+1;
}
strncpy(tmpstr, &cStart[iStart], (nChars<=8)? nChars:8 );
LowMask = strtoul(tmpstr, NULL, 16);
gAffinityMask = (nChars<=8)? (KAFFINITY)LowMask:( (((KAFFINITY)HighMask) << 32) +(KAFFINITY)LowMask ) ;
if( gAffinityMask == 0 ){
InvalidEntryMessage(argv[i],
argv[i+1],
"Expecting a valid HEX value, maximum 8 characters for up to 32 processors\nor 16 characters for up to 64 processors",
FALSE,
TRUE
);
}
FPRINTF(stdout, "\nUser defined CPU affinity mask for profiling= 0x%p\n", (PVOID)gAffinityMask);
FPRINTF(stdout, "This will profile the following processors:\n");
for( iProc=0; iProc<gProfileProcessors; iProc++){
if((1 << iProc) & gAffinityMask)
FPRINTF(stdout, "P%d, ", iProc);
}
FPRINTF(stdout,"\n");
++i;
} else if (ietResult == MISSING_STRING){
bProfileByProcessor = TRUE;
gProfileProcessors = gSysBasicInfo->NumberOfProcessors;
} else if (ietResult == BOGUS_ENTRY){
ExitWithUnknownOptionMessage(argv[i+1]);
} else {
ExitWithUnknownOptionMessage(argv[i]);
}
break;
default:
break;
}
} else { //if ((argv[i][0] == '-') || (argv[i][0] == '/'))
if( !strchr(argv[i], '{') ){
continue;
} else { //Exclude any command options in the curly brackets
while( i < argc && !strchr(argv[i], '}') ){
++i;
}
}
}
}
for (i=1; i < argc; i++) {
if ((argv[i][0] == '-') || (argv[i][0] == '/')) {
switch ( toupper(argv[i][1]) ) {
case 'T':
ietResult = IsInputValid(argc,
i,
argv,
"#",
&gMaxTasks,
NULL,
0,
ORDER_ANY,
OPTIONAL_ANY
);
if(ietResult == INPUT_GOOD){
//
// User also wants to change the maximum number of tasks in the task list from Kernrate's default number
//
if (gMaxTasks == 0) {
InvalidEntryMessage(argv[i],
argv[i+1],
"Expecting a decimal number >0",
FALSE,
TRUE
);
}
FPRINTF(stdout, "---> Kernrate task list set to accommodate %ld processes\n", gMaxTasks);
tJump = 1;;
} else if(ietResult == MISSING_PARAMETER || ietResult == MISSING_NUMBER) { //Allowed
//
//User just wants task summary without changing the default maximum number of tasks
//
} else if(ietResult == MISSING_REQUIRED_NUMBER){
ExitWithMissingEntryMessage(argv[i],
"'-t# N' option requires the maximum (decimal) number of processes in Kernrate's task list",
FALSE
);
} else if(ietResult == INVALID_NUMBER) {
InvalidEntryMessage(argv[i],
argv[i+1],
"Expecting a decimal value >0 in [ms], 0 < N < 10^9",
FALSE,
TRUE
);
} else if(ietResult == UNKNOWN_OPTION) {
ExitWithUnknownOptionMessage(argv[i]);
}
bDisplayTaskSummary = TRUE;
default:
break;
}
} else { //if ((argv[i][0] == '-') || (argv[i][0] == '/'))
if( !strchr(argv[i], '{') ){
continue;
} else { //Exclude any command options in the curly brackets
while( i < argc && !strchr(argv[i], '}') ){
++i;
}
}
}
}
for (i=1; i < argc; i++) {
if ((argv[i][0] == '-') || (argv[i][0] == '/')) {
BOOL bIncludeProcessThreadsInfo = FALSE;
switch ( toupper(argv[i][1]) ) {
case 'A':
//
// Do both Kernel and User mode profiling
//
ietResult = IsInputValid(argc,
i,
argv,
"v",
NULL,
NULL,
0,
ORDER_ANY,
OPTIONAL_ANY);
if(ietResult == INPUT_GOOD){
bCombinedProfile = TRUE;
FPRINTF(stdout, "\n---> Profiling both Kernel and User Modes\n");
//
// Check if extra options are present
//
if( strchr(argv[i], 'v') || strchr(argv[i], 'V') ){
//
// User wants system threads information
//
tlistVerbose = TRUE;
bSystemThreadsInfo = TRUE;
}
} else if (ietResult == BOGUS_ENTRY){
ExitWithUnknownOptionMessage(argv[i+1]);
} else {
ExitWithUnknownOptionMessage(argv[i]);
}
if( bIncludeGeneralInfo || tlistVerbose) {
if ( (!bTlistInitialized && tlistVerbose) || (bSystemThreadsInfo == TRUE && bIncludeThreadsInfo == FALSE) ) {
//
// If we already took a tlist but that's the first time thread info is required,
// we'll have to refresh it and take thread info as well
//
if( bSystemThreadsInfo == TRUE && bIncludeThreadsInfo == FALSE ){
bIncludeThreadsInfo = TRUE;
}
//
// get the task list for the system (this is needed to identify the System Process ID)
//
if ( !bTlistInitialized ) {
gTlistStart = calloc(1, gMaxTasks*sizeof(TASK_LIST));
if (gTlistStart == NULL) {
FPRINTF(stderr, "\nKERNRATE: Allocation of memory for the running processes task list failed(1)\n");
exit(1);
}
}
NumTasks = GetTaskList( gTlistStart, gMaxTasks );
bTlistInitialized = TRUE;
gNumTasksStart = NumTasks;
}
}
if( bTlistInitialized && tlistVerbose ){
if( tlistDisplayed == FALSE ){
FPRINTF(stdout, "\nRunning processes found before profile start:\n");
FPRINTF(stdout, " Pid Process\n");
FPRINTF(stdout, " ------- -----------\n");
for (m=0, k=0; m < NumTasks; m++) {
FPRINTF(stdout, "%12I64d %32s\n",
gTlistStart[m].ProcessId,
gTlistStart[m].ProcessName
);
}
if( tlistDisplayed == FALSE ){
FPRINTF(stdout, "\nNOTE: The list above may be missing some or all processes created by the '-o' option\n");
//
//Lets not get carried away if the user specified the verbose option more than once...
//
tlistDisplayed = TRUE;
tlistVerbose = FALSE;
}
}
}
break;
case 'B':
//
// Set Zoom Bucket Size
//
ietResult = IsInputValid(argc,
i,
argv,
"#",
&gZoomBucket,
NULL,
0,
ORDER_ANY,
OPTIONAL_NONE
);
if(ietResult == INPUT_GOOD){
if (gZoomBucket == 0) {
InvalidEntryMessage(argv[i],
argv[i+1],
"Invalid bucket size, expecting a decimal value\nBucket size must be power of 2, minimum bucket size is 4",
FALSE,
TRUE
);
}
for (gLog2ZoomBucket=1; (1UL<<gLog2ZoomBucket) < gZoomBucket; gLog2ZoomBucket++)
// Empty Loop
;
if ( ( gZoomBucket < MINIMUM_ZOOM_BUCKET_SIZE ) || ( gZoomBucket != (1UL<<gLog2ZoomBucket) ) ) {
InvalidEntryMessage(argv[i],
argv[i+1],
"Bucket size must be power of 2, minimum bucket size is 4",
FALSE,
TRUE
);
}
++i;
} else if(ietResult == MISSING_PARAMETER || ietResult == MISSING_NUMBER) {
ExitWithMissingEntryMessage(argv[i],
"'-b N' option requires bucket size (minimum 4 bytes), space separated",
FALSE
);
} else if(ietResult == MISSING_REQUIRED_NUMBER){
ExitWithMissingEntryMessage(argv[i],
"'-b# N' option requires bucket size (minimum 4 bytes), space separated",
FALSE
);
} else if(ietResult == INVALID_NUMBER) {
InvalidEntryMessage(argv[i],
argv[i+1],
"Invalid bucket size, expecting a decimal value\nBucket size must be power of 2, minimum bucket size is 4",
FALSE,
TRUE
);
} else if(ietResult == UNKNOWN_OPTION) {
ExitWithUnknownOptionMessage(argv[i]);
}
FPRINTF(stdout, "---> Profile Bucket Size Set to %u bytes\n", gZoomBucket);
break;
case 'C':
//
//Use old sampling scheme (sample one source at a time, switch between sources (and monitored processes)
//every gChangeInterval in ms). If not specified, all sources will be turned on simultaneously.
//
ietResult = IsInputValid(argc,
i,
argv,
"#",
&gChangeInterval,
NULL,
0,
ORDER_ANY,
OPTIONAL_ANY
);
if(ietResult == INPUT_GOOD){
//
// User wants to sample cyclically one source at a time and also to specify the interval
//
if (gChangeInterval == 0) {
InvalidEntryMessage(argv[i],
argv[i+1],
"Expecting a decimal value >0 in [ms], 0 < N < 10^9",
FALSE,
TRUE
);
}
++i;
} else if(ietResult == MISSING_PARAMETER || ietResult == MISSING_NUMBER) { //Allowed
//
// User wants to sample cyclically one source at a time using the default interval
//
} else if(ietResult == MISSING_REQUIRED_NUMBER){
ExitWithMissingEntryMessage(argv[i],
"'-c# N' option requires a decimal value in [ms], 0 < N < 10^9",
FALSE
);
} else if(ietResult == INVALID_NUMBER) {
InvalidEntryMessage(argv[i],
argv[i+1],
"Expecting a decimal value >0 in [ms], 0 < N < 10^9",
FALSE,
TRUE
);
} else if(ietResult == UNKNOWN_OPTION) {
ExitWithUnknownOptionMessage(argv[i]);
}
bOldSampling = TRUE;
FPRINTF(stdout, "---> Using Cyclic Sampling Scheme (Profiling One Source At A time)\n");
FPRINTF(stdout, "Change Interval between Profile Sources Set to %u[ms]\n", gChangeInterval);
break;
case 'D':
//
// Output data rounding up and down
//
ietResult = IsInputValid(argc,
i,
argv,
"",
NULL,
NULL,
0,
ORDER_ANY,
OPTIONAL_ANY
);
if(ietResult == INPUT_GOOD){
FPRINTF(stdout, "---> Will output data rounding bucket addresses up and down\n");
bRoundingVerboseOutput = TRUE;
} else if (ietResult == BOGUS_ENTRY){
ExitWithUnknownOptionMessage(argv[i+1]);
} else {
ExitWithUnknownOptionMessage(argv[i]);
}
break;
case 'E':
//
// We already dealt with this command line parameter in the first loop
//
break;
case 'F':
//
// User asked to Finish processing the collected data at high priority
//
ietResult = IsInputValid(argc,
i,
argv,
"",
NULL,
NULL,
0,
ORDER_ANY,
OPTIONAL_ANY
);
if(ietResult == INPUT_GOOD){
bProcessDataHighPriority = TRUE;
} else if (ietResult == BOGUS_ENTRY){
ExitWithUnknownOptionMessage(argv[i+1]);
} else {
ExitWithUnknownOptionMessage(argv[i]);
}
break;
case 'G':
//
// User wants interesing data statistics so we need to turn on all related sources
//
{
int IDataElements = sizeof(IData)/sizeof(KPROFILE_SOURCE);
ietResult = IsInputValid(argc,
i,
argv,
"#",
&IDataCommonRate,
NULL,
0,
ORDER_ANY,
OPTIONAL_ANY
);
if(ietResult == INPUT_GOOD){
//
// User wants interesting data statistics and also to specify the common sampling interval
//
if (IDataCommonRate == 0) {
InvalidEntryMessage(argv[i],
argv[i+1],
"Expecting decimal value >0 of events/hit, 0 < N < 10^9, space separated",
FALSE,
TRUE
);
}
++i;
} else if(ietResult == MISSING_PARAMETER || ietResult == MISSING_NUMBER) { //Allowed
//
// User wants interesting data statistics using a default common sampling interval
//
IDataCommonRate = gpProcDummy->Source[IData[0]].DesiredInterval; //Use a default interval
} else if(ietResult == MISSING_REQUIRED_NUMBER){
ExitWithMissingEntryMessage(argv[i],
"'-g# N' option requires a decimal value >0 of events/hit, 0 < N < 10^9",
FALSE
);
} else if(ietResult == INVALID_NUMBER) {
InvalidEntryMessage(argv[i],
argv[i+1],
"Expecting a decimal value >0 in [ms], 0 < N < 10^9",
FALSE,
TRUE
);
} else if(ietResult == UNKNOWN_OPTION) {
ExitWithUnknownOptionMessage(argv[i]);
}
if ( IDataElements > 1 && gpProcDummy->Source[IData[0]].DesiredInterval > 0) {
for (j=1; j < IDataElements; j++) {
if ( gpProcDummy->Source[IData[j]].DesiredInterval > 0 )
IDataCommonRate = min(IDataCommonRate, gpProcDummy->Source[IData[j]].DesiredInterval);
}
FPRINTF(stdout, "---> Will attempt to set common profiling rate for interesting data statistics to %ld Events/Hit\n",
IDataCommonRate
);
for (j=0; j < IDataElements; j++) {
gpProcDummy->Source[IData[j]].Interval = IDataCommonRate;
}
bGetInterestingData = TRUE;
bOldSampling = TRUE;
FPRINTF(stdout, "---> Using Cyclic Sampling Scheme (Profiling One Source At A time)\n");
} else {
FPRINTF(stderr, "\nKERNRATE: Interesting processor-counters statistics cannot be collected on this machine\n");
}
}
break;
case 'I':
{
// We'll consider -I option as global for all processes to be profiled
// User may have put -I on the command line before any process profile source has been initialized
// We therefore supply a valid pointer to just get and store the information for later use
ULONG rate;
BOOL found;
ietResult = IsInputValid(argc,
i,
argv,
"#",
&rate,
tmpName, //Just used as a temp storage
USER_SYMPATH_LENGTH, //with long enough space
ORDER_ANY,
OPTIONAL_ANY
);
if(ietResult == INPUT_GOOD){
//
// Standard option processing (both name and rate present)
//
i += 2; // two parameters exist (number and string)
} else if(ietResult == MISSING_PARAMETER){
ExitWithMissingEntryMessage(argv[i],
"'-i Source_Name Rate' option requires at least a source name or a rate value (or both), space separated",
TRUE
);
} else if(ietResult == MISSING_STRING) { //allowed
//
// The user can specify '-i' with a rate only.
// In this case, SOURCE_TIME is used.
//
if ( rate == 0 ) {
SourcesSoFar -= 1; //Default was 1
}
gpProcDummy->Source[SOURCE_TIME].Interval = rate;
++i; // one parameter exists (number)
break; // We are done here
} else if(ietResult == MISSING_NUMBER) { //alowed
++i; // one parameter exists (string)
} else if(ietResult == MISSING_REQUIRED_NUMBER){
ExitWithMissingEntryMessage(argv[i],
"'-i# Source_Name Rate' option requires a rate value for the source interval, 0 < N < 10^9 (Source_Name optional)",
FALSE
);
} else if(ietResult == INVALID_NUMBER) {
InvalidEntryMessage(argv[i+1],
argv[i+2],
"'-i Source_Name Rate' - Invalid source interval, expecting a number 0 < N < 10^9, space separated",
FALSE,
TRUE
);
} else if(ietResult == UNKNOWN_OPTION) {
ExitWithUnknownOptionMessage(argv[i]);
}
//
// Standard option processing:
// The source shortname string is specified. If not followed by a rate amount
// we'll assume the user wants the default rate
found = FALSE;
for ( ProfileSourceIndex = 0; ProfileSourceIndex < gSourceMaximum; ProfileSourceIndex++) {
if ( !_stricmp(gpProcDummy->Source[ProfileSourceIndex].ShortName, tmpName) ) {
if (ietResult == MISSING_NUMBER || ietResult == MISSING_REQUIRED_NUMBER) { // If no rate specified,
gpProcDummy->Source[ProfileSourceIndex].Interval = gpProcDummy->Source[ProfileSourceIndex].DesiredInterval;
} else {
gpProcDummy->Source[ProfileSourceIndex].Interval = rate;
}
if ( (ProfileSourceIndex > SOURCE_TIME) && (gpProcDummy->Source[ProfileSourceIndex].Interval > 0) ) {
SourcesSoFar += 1;
} else if ( (ProfileSourceIndex == SOURCE_TIME) && (gpProcDummy->Source[ProfileSourceIndex].Interval == 0) ) {
SourcesSoFar -= 1; //Start value was 1 by default
}
found = TRUE;
break;
}
}
if ( found == FALSE) {
InvalidEntryMessage(argv[i-1],
argv[i],
"Invalid source name, or not space separated\nRun KERNRATE with the '-lx' option to list supported sources",
FALSE,
TRUE
);
}
}
break;
case 'J':
//
// User specified symbol search path.
// It is going to be prepend to the default image help symbol search path.
//
ietResult = IsInputValid(argc,
i,
argv,
"",
NULL,
gUserSymbolPath,
USER_SYMPATH_LENGTH-1,
ORDER_ANY,
OPTIONAL_NONE
);
if(ietResult == INPUT_GOOD){
if( lstrlen(argv[i+1]) > USER_SYMPATH_LENGTH){
FPRINTF(stderr, "\n===>WARNING: Command-line specified symbol path length exceeds %d characters and will be truncated\n",
USER_SYMPATH_LENGTH-1
);
}
++i;
} else if(ietResult == MISSING_PARAMETER ||
ietResult == MISSING_STRING || ietResult == MISSING_REQUIRED_STRING) {
ExitWithMissingEntryMessage(argv[i],
"'-j SymbolPath' option requires a \"SymbolPath\", space separated",
FALSE
);
} else if(ietResult == UNKNOWN_OPTION) {
ExitWithUnknownOptionMessage(argv[i]);
}
break;
case 'K':
//
// User wants to limit the output to modules that have at least MinHitsToDisplay hits
//
ietResult = IsInputValid(argc,
i,
argv,
"#",
&gMinHitsToDisplay,
NULL,
0,
ORDER_ANY,
OPTIONAL_NONE
);
if(ietResult == INPUT_GOOD){
if ( gMinHitsToDisplay == 0 ) {
InvalidEntryMessage(argv[i],
argv[i+1],
"Invalid entry for this command line option, (expecting a decimal number > 0)",
FALSE,
TRUE
);
}
++i;
} else if(ietResult == MISSING_PARAMETER || ietResult == MISSING_NUMBER || ietResult == MISSING_REQUIRED_NUMBER) {
ExitWithMissingEntryMessage(argv[i],
"'-k N' or '-k# N' options require a number for the minimum hits to display, space separated",
FALSE
);
} else if(ietResult == INVALID_NUMBER) {
InvalidEntryMessage(argv[i],
argv[i+1],
"Invalid entry for minimum hits to display (expecting a number 0 < N < 10^9)",
FALSE,
TRUE
);
} else if(ietResult == UNKNOWN_OPTION) {
ExitWithUnknownOptionMessage(argv[i]);
}
FPRINTF(stdout, "---> Minimum Number of Hits To Display in the Output Set to %u\n", gMinHitsToDisplay);
break;
case 'L':
{
// User may have put -L on the command line before any process profile source has been initialized
// We'll therefore supply a valid pointer to just get the information
PSOURCE src;
ietResult = IsInputValid(argc,
i,
argv,
"x",
NULL,
NULL,
0,
ORDER_ANY,
OPTIONAL_ANY
);
if(ietResult == INPUT_GOOD){ //Fall through
} else if (ietResult == BOGUS_ENTRY){
ExitWithUnknownOptionMessage(argv[i+1]);
} else {
ExitWithUnknownOptionMessage(argv[i]);
}
FPRINTF(stdout, "List of profile sources supported for this platform:\n\n");
FPRINTF(stdout, "%*s - %-*s - %-10s\n\n", gDescriptionMaxLen, "Name", gTokenMaxLen, "ShortName", "Interval");
//
// Print all possible sources.
//
for ( ProfileSourceIndex = 0; ProfileSourceIndex < gSourceMaximum; ProfileSourceIndex++ ) {
ULONG OldInterval = 0;
ULONG ThisInterval = 0;
src = &gpProcDummy->Source[ProfileSourceIndex];
//
// Display the supported profile sources, only.
// We'll determine if a source is supported by trying to set its interval rate
//
Status = NtQueryIntervalProfile( src->ProfileSource, &OldInterval );
if( NT_SUCCESS(Status) ) {
NtSetIntervalProfile( src->DesiredInterval, src->ProfileSource );
Status = NtQueryIntervalProfile( src->ProfileSource, &ThisInterval );
if( NT_SUCCESS(Status) && ThisInterval > 0 ) {
if ( src->DesiredInterval ) {
FPRINTF(stdout, "%*s - %-*s - %-10ld\n",
gDescriptionMaxLen,
src->Name,
gTokenMaxLen,
src->ShortName,
src->DesiredInterval
);
}
NtSetIntervalProfile( OldInterval, src->ProfileSource );
}
}
}
FPRINTF(stdout, "\nNOTE: Only up to %u sources can be turned on simultaneously on this machine.\n",
gMaxSimultaneousSources
);
FPRINTF(stdout, " This always includes the default source (TIME).\n");
FPRINTF(stdout, " A cyclic mode of profiling will be turned on automatically if more sources are specified.\n");
#if !defined(_AMD64_)
if( gMaxSimultaneousSources > 1 )
FPRINTF(stdout, " There is no guarantee that all sources specified in the combination will work together.\n");
#endif
FPRINTF(stdout, " One can always force a cyclic mode of profiling (switching between sources) by using the\n");
FPRINTF(stdout, " '-c' command line option. This will guarantee that all specified sources will run.\n");
FPRINTF(stdout, " The run time will then be divided equally between (number of sources)*(number of processes.\n");
//
// If the user specified '-lx', we exit immediately.
//
if ( strchr(argv[i], 'x') || strchr(argv[i], 'X') ) {
exit(0);
}
FPRINTF(stdout, "\n");
}
break;
case 'M':
//
// We already dealt with this command line parameter in the first loop
// Just update the index if extra parameter was found
//
if(gAffinityMask != 0)++i;
break;
case 'N':
//
// Monitor a given process Name
//
ietResult = IsInputValid(argc,
i,
argv,
"v#",
&MaxProcSameName,
tmpName,
PROCESS_NAME_SIZE,
ORDER_NUMBER_FIRST,
OPTIONAL_NUMBER
);
//
// User wants the full list of running processes
//
if(ietResult == INPUT_GOOD || ietResult == MISSING_NUMBER){
if( strchr(argv[i], 'v') || strchr(argv[i], 'V') ){
tlistVerbose = TRUE;
bIncludeProcessThreadsInfo = TRUE;
}
}
if(ietResult == INPUT_GOOD){
//
// User wants to specify a non-default maximum number of processes with same name
//
if ( MaxProcSameName == 0 ) {
InvalidEntryMessage(argv[i],
argv[i+1],
"Expecting a decimal value >0 for the maximum number of processes with the same name",
FALSE,
TRUE
);
}
FPRINTF(stdout, "---> Maximum monitored processes with same name set to= %ld\n", MaxProcSameName);
i += 2; // two parameters exist (number and string)
} else if(ietResult == MISSING_PARAMETER ||
ietResult == MISSING_STRING || ietResult == MISSING_REQUIRED_STRING) {
ExitWithMissingEntryMessage(argv[i],
"'-n process_name' option requires at least a process name, space separated",
FALSE
);
} else if(ietResult == MISSING_REQUIRED_NUMBER){
ExitWithMissingEntryMessage(argv[i],
"'-n# number process_name' option requires a number followed by a process name, space separated",
FALSE
);
} else if(ietResult == MISSING_NUMBER) { //alowed
++i; // one parameter exists (string)
} else if(ietResult == INVALID_NUMBER) {
InvalidEntryMessage(argv[i],
argv[i+1],
"'-n# number process_name' - Expecting a decimal value >0 for the maximum number of processes with the same name",
FALSE,
TRUE
);
} else if(ietResult == UNKNOWN_OPTION) {
ExitWithUnknownOptionMessage(argv[i]);
}
//
// Let's not bother the user with minor issues
//
if(!strstr(tmpName, ".exe"))
strncat(tmpName, ".exe", EXT_SIZE);
tmpName[PROCESS_SIZE - 1] = '\0';
Pid = (LONGLONG) 0xFFFFFFFF;
//
// The '-n' option requires taking a tlist to get the PID.
// If we already took a tlist but that's the first time thread info is required,
// we'll have to refresh it and take thread info as well
//
if ( !bTlistInitialized || (bIncludeProcessThreadsInfo == TRUE && bIncludeThreadsInfo == FALSE) ) {
if( bIncludeProcessThreadsInfo == TRUE && bIncludeThreadsInfo == FALSE ){
bIncludeThreadsInfo = TRUE;
}
//
// get the task list for the system
//
if ( !bTlistInitialized ){
gTlistStart = calloc(1, gMaxTasks*sizeof(TASK_LIST));
if (gTlistStart == NULL) {
FPRINTF(stderr, "\nKERNRATE: Allocation of memory for the running processes task list failed(2)\n");
exit(1);
}
}
NumTasks = GetTaskList( gTlistStart, gMaxTasks );
bTlistInitialized = TRUE;
gNumTasksStart = NumTasks;
}
//
// There may be more than one process with the specified name
// We will limit the maximum number monitored (with same name) to MAX_PROCESS_SAME_NAME for now
//
if ( tlistDisplayed == FALSE && tlistVerbose ) {
FPRINTF(stdout, "\nRunning processes found before profile start:\n");
FPRINTF(stdout, " Pid Process\n");
FPRINTF(stdout, " ------- -----------\n");
}
for (m=0, k=0; m < NumTasks; m++) {
if ( tlistDisplayed == FALSE && tlistVerbose ) {
FPRINTF(stdout, "%12I64d %32s\n",
gTlistStart[m].ProcessId,
gTlistStart[m].ProcessName
);
}
if (_stricmp(gTlistStart[m].ProcessName, tmpName) == 0) {
Pid = gTlistStart[m].ProcessId;
FPRINTF(stdout, "\n===> Found process: %s, Pid: %I64d\n\n",
gTlistStart[m].ProcessName,
Pid
);
ProcToMonitor = InitializeProcToMonitor(Pid);
if( ProcToMonitor == NULL ){ //This process may be gone
FPRINTF(stdout, "KERNRATE: Could not initialize for specified process (PID= %12I64d)\n process may be gone or wrong PID specified\n", Pid);
continue;
}
if( bIncludeGeneralInfo ){
UpdateProcessStartInfo(ProcToMonitor,
&gTlistStart[m],
bIncludeProcessThreadsInfo
);
}
if((ULONG)(++k) >= MaxProcSameName) break;
}
}
if( tlistDisplayed == FALSE && tlistVerbose ){
FPRINTF(stdout, "\nNOTE: The list above may be missing some or all processes created by the '-o' option\n");
}
//
// Let's not print it again if there is another specified on the command line
//
if ( tlistDisplayed == FALSE && tlistVerbose ){
tlistDisplayed = TRUE;
}
tlistVerbose = FALSE;
if (Pid == (LONGLONG) 0xFFFFFFFF) {
Usage(FALSE);
FPRINTF(stderr, "\n===>KERNRATE: Requested Process '%s' Not Found.\n\n", argv[i]);
FPRINTF(stderr, "Kernrate could not find this process in the task list. Either it does not exist or \n");
FPRINTF(stderr, "Kernrate hit its limit of maximum %d processes in the task list\n", DEFAULT_MAX_TASKS);
FPRINTF(stderr, "In the latter case you may want to specify the process by PID instead of by name\n");
FPRINTF(stderr, "or use the '-t' option to specify a larger maximum number (default is 256)\n");
FPRINTF(stderr, "This may also happen if you specified a number for the maximum number of processes\n");
FPRINTF(stderr, "with the same name but forgot to add the process name\n");
exit(1);
}
break;
case 'O':
{
//
// Create and monitor a given process Name
//
ULONG MaxProcToCreate;// = 1;
ULONG n;
STARTUPINFO StartupInfo;
PROCESS_INFORMATION ProcessInformation;
PCHAR tmpCmdLine;
PLONGLONG PidArray;
ULONG nFound = 0;
BOOL fInheritHandles = FALSE;
ietResult = IsInputValid(argc,
i,
argv,
"v#",
&MaxProcToCreate,
tmpName,
USER_SYMPATH_LENGTH - EXT_SIZE - 1, // We must allow a fully qualified path here
ORDER_NUMBER_FIRST,
OPTIONAL_NUMBER
);
//
// User wants the full list of running processes
//
if(ietResult == INPUT_GOOD || ietResult == MISSING_NUMBER){
if( strchr(argv[i], 'v') || strchr(argv[i], 'V') ){
tlistVerbose = TRUE;
bIncludeProcessThreadsInfo = TRUE;
bIncludeThreadsInfo = TRUE;
}
}
if(ietResult == INPUT_GOOD){
//
// User wants to specify a non-default maximum number of processes to create
//
if ( MaxProcToCreate == 0 ) {
InvalidEntryMessage(argv[i],
argv[i+1],
"Expecting a decimal value >0 for the maximum number of processes to create",
FALSE,
TRUE
);
}
FPRINTF(stdout, "---> Maximum processes to create set to= %ld for %s\n",
MaxProcToCreate,
tmpName
);
if(i+3 < argc && argv[i+3][0] == '{' ){ // Found an opening curly bracket (allowed)
i += 3; // three parameters exist (number, ProcessName and {Process Command Line})
} else {
i += 2; // two parameters exist (number and ProcessName)
}
} else if(ietResult == MISSING_PARAMETER ||
ietResult == MISSING_STRING || ietResult == MISSING_REQUIRED_STRING) {
ExitWithMissingEntryMessage(argv[i],
"Process name missing, the '-o number process_name {Process command line}' option requires at least a process Name",
FALSE
);
} else if(ietResult == MISSING_REQUIRED_NUMBER){
ExitWithMissingEntryMessage(argv[i],
"'-o# number process_name {Process command line}' option requires a number followed by a process Name",
FALSE
);
} else if(ietResult == MISSING_NUMBER) { //alowed
MaxProcToCreate = 1;
++i; // one parameter exists (string)
} else if(ietResult == INVALID_NUMBER) {
if(!strchr(argv[i], '#') && (i+2 < argc && argv[i+2][0] == '{') ){ // Found an opening curly bracket (allowed)
MaxProcToCreate = 1;
i += 2; // two parameters exist (two strings)
} else {
if( !strchr(argv[i], '#') && (i+1 < argc && argv[i+1][0] == '{') ){
InvalidEntryMessage(argv[i],
argv[i+1],
"'-o optional_number process_name {Optional Process command line}' - wrong order of parameters (or missing parameter) detected",
FALSE,
TRUE
);
} else {
InvalidEntryMessage(argv[i],
argv[i+1],
"'-o# number process_name {Optional Process command line}' - Expecting a decimal value >0",
FALSE,
TRUE
);
}
}
} else if(ietResult == UNKNOWN_OPTION) {
ExitWithUnknownOptionMessage(argv[i]);
}
ZeroMemory( &StartupInfo, sizeof(StartupInfo) );
StartupInfo.cb = sizeof(StartupInfo);
StartupInfo.wShowWindow = SW_SHOWDEFAULT;
//
// Let's not bother the user with minor issues
//
if( !strstr(tmpName, ".exe") && !strstr(tmpName, ".EXE") )
strncat(tmpName, ".exe", EXT_SIZE);
tmpName[USER_SYMPATH_LENGTH - 1] = '\0';
if( i < argc && argv[i][0] == '{'){ // Found an opening curly bracket
PCHAR InitPos = &argv[i][0];
PCHAR curptr = InitPos;
PCHAR ClosingBracket;
while(i < argc){ //Try to find the closing curly bracket
ClosingBracket = strchr(&argv[i][0], '}');
if(ClosingBracket == NULL){
curptr += (1+strlen(argv[i]));
++i;
continue;
}else{
ClosingBracket = curptr + (ClosingBracket - &argv[i][0]);
break;
}
}
if(ClosingBracket != NULL){ //Process the command line found between the curly brackets
PCHAR tmp;
ULONG MaxCount;
ULONG nChars = (ULONG)(ClosingBracket - InitPos) -1; //Skip the brackets
tmpCmdLine = calloc(1, strlen(tmpName)+ sizeof(CHAR)+ (1+nChars)*sizeof(CHAR)); //name + space + cmdline + terminator
if(tmpCmdLine == NULL){
FPRINTF(stderr, "KERNRATE: Failed to allocate memory for created process command line\n");
exit(1);
}
strncpy(tmpCmdLine, tmpName, strlen(tmpName));
strncat(tmpCmdLine, " ", 1);
memcpy(&tmpCmdLine[strlen(tmpCmdLine)], InitPos + 1, nChars); //skip opening and end brackets
tmpCmdLine[strlen(tmpName)+ nChars + 1] = '\0';
tmp = &tmpCmdLine[0];
MaxCount = strlen(tmpName) + nChars + 1;
do{ //replace any mid-cmdline string terminators with blank space character
if(*tmp == '\0')*tmp = ' ';
++tmp;
}while(--MaxCount);
MaxCount = HandleRedirections( tmpCmdLine,
strlen(tmpName) + nChars + 1,
&ghInput,
&ghOutput,
&ghError
);
if(ghInput != NULL || ghOutput != NULL || ghError != NULL){
if ( MaxProcToCreate == 1 || (ghOutput == NULL && ghError == NULL) ){
StartupInfo.dwFlags |= STARTF_USESTDHANDLES;
StartupInfo.hStdInput = ghInput;
StartupInfo.hStdOutput = ghOutput;
StartupInfo.hStdError = ghError;
fInheritHandles = TRUE;
} else { //We won't allow several processes to write to the same output stream simultaneously...
FPRINTF(stderr, "\nKERNRATE: Redirection of output streams in the curly brackets is not allowed if more than one\n");
FPRINTF(stderr, " process is to be created using the '-o Number ProcessName {parameters}' command line option\n");
if(ghInput != NULL)
CloseHandle(ghInput);
if(ghOutput != NULL)
CloseHandle(ghOutput);
if(ghError != NULL)
CloseHandle(ghError);
exit(1);
}
}
} else {
Usage(FALSE);
FPRINTF(stderr, "KERNRATE: Unmatched curly brackets containing the command line of the process to be created with the -o option\n");
FPRINTF(stderr, " This could also be the result of not escaping each redirection character: '<' '>' with a '^' character\n");
FPRINTF(stderr, " Note that piping '|' is not supported as part of the allowed parameters within the curly brackets\n");
exit(1);
}
} else {
tmpCmdLine = calloc(1, (1+strlen(tmpName))*sizeof(CHAR)); //name + terminator
if(tmpCmdLine == NULL){
FPRINTF(stderr, "KERNRATE: Failed to allocate memory for created process command line\n");
exit(1);
}
strncpy(tmpCmdLine, tmpName, strlen(tmpName));
tmpCmdLine[strlen(tmpName)] = '\0';
}
PidArray = calloc(MaxProcToCreate, sizeof(ULONG));
if(PidArray == NULL){
FPRINTF(stderr, "KERNRATE: Failed to allocate memory for created processes PID Array\n");
exit(1);
}
for (m=0; m<MaxProcToCreate; m++) {
Pid = (LONGLONG) 0xFFFFFFFF;
if(!CreateProcess(NULL, //ProcName
tmpCmdLine, //cmd line
NULL, //Security attr.
NULL, //thread attr.
fInheritHandles, //inherit handle from debugging proc
CREATE_DEFAULT_ERROR_MODE |CREATE_SEPARATE_WOW_VDM | CREATE_UNICODE_ENVIRONMENT | CREATE_NEW_PROCESS_GROUP | CREATE_NEW_CONSOLE | NORMAL_PRIORITY_CLASS,
NULL, //environment of calling proc
NULL, //Current dir same as debugging proc
&StartupInfo, //Startup Info
&ProcessInformation) //PROCESS_INFORMATION struct
) {
FPRINTF(stderr, "KERNRATE: Failed to Create Process %s\n", tmpName);
} else {
Pid = ProcessInformation.dwProcessId;
PidArray[m] = Pid;
FPRINTF(stdout, "Created Process %s, PID= %I64d\n", tmpName, Pid);
FPRINTF(stdout, "Process Command Line = %s\n", tmpCmdLine);
ProcToMonitor = InitializeProcToMonitor(Pid);
if( ProcToMonitor == NULL ){ //This process may be gone
FPRINTF(stdout, "KERNRATE: Could not initialize for specified process (PID= %12I64d)\n process may be gone or wrong PID specified\n", Pid);
continue;
}
}
}
if ( bTlistInitialized ) { //Refresh the task list to make sure we include the new process
//(this will take care of thread info as well if required for the first time)
NumTasks = GetTaskList( gTlistStart, gMaxTasks );
gNumTasksStart = NumTasks;
} else {
//
// get the task list for the system (this will take care of thread info as well if required for the first time)
//
gTlistStart = calloc(1, gMaxTasks*sizeof(TASK_LIST));
if (gTlistStart == NULL) {
FPRINTF(stderr, "\nKERNRATE: Allocation of memory for the running processes task list failed(3)\n");
exit(1);
}
NumTasks = GetTaskList( gTlistStart, gMaxTasks );
bTlistInitialized = TRUE;
gNumTasksStart = NumTasks;
}
if ( tlistDisplayed == FALSE && tlistVerbose ) {
FPRINTF(stdout, "\nRunning processes found before profile start:\n");
FPRINTF(stdout, " Pid Process\n");
FPRINTF(stdout, " ------- -----------\n");
}
for (m=0; m < NumTasks; m++) {
if ( tlistDisplayed == FALSE && tlistVerbose ) {
FPRINTF(stdout, "%12I64d %32s\n",
gTlistStart[m].ProcessId,
gTlistStart[m].ProcessName
);
}
for (n=0; n<MaxProcToCreate; n++){
if ( gTlistStart[m].ProcessId == PidArray[n] ) {
FPRINTF(stdout, "\n===> Found process: %s, Pid: %I64d\n\n",
gTlistStart[m].ProcessName,
gTlistStart[m].ProcessId
);
nFound += 1;
ProcToMonitor = gProcessList;
while (ProcToMonitor != NULL){
if(ProcToMonitor->Pid == gTlistStart[m].ProcessId && bIncludeGeneralInfo ){
UpdateProcessStartInfo(ProcToMonitor,
&gTlistStart[m],
bIncludeProcessThreadsInfo
);
break;
}
ProcToMonitor = ProcToMonitor->Next;
}
}
}
}
if( tlistDisplayed == FALSE && tlistVerbose ){
FPRINTF(stdout, "\nNOTE: The list above may be missing some or all processes created by the '-o' option\n");
}
free(tmpCmdLine);
free(PidArray);
//
// Let's not print it again if there is another specified on the command line
//
if ( tlistDisplayed == FALSE && tlistVerbose ){
tlistDisplayed = TRUE;
}
tlistVerbose = FALSE;
if(nFound == 0){
FPRINTF(stderr, "KERNRATE: None of the processes you tried to create was found in the task list\n");
FPRINTF(stderr, " This is either because:\n");
FPRINTF(stderr, " 1. The executable was not found in the default path therefore not launched\n");
FPRINTF(stderr, " The -o option does allow you to specify a fully qualified path with the process name (use quotes)\n");
FPRINTF(stderr, " 2. The default number of entries in the task list is too small\n");
FPRINTF(stderr, " The maximum number of tasks in the task list can be increased using the -t# option\n");
FPRINTF(stderr, " 3. The processes were never created because of some other reason or are gone\n");
FPRINTF(stderr, " 4. The number or order of parameters on the command line is wrong,\n");
FPRINTF(stderr, " it should be '-o# number process_name {Process command line}' (only the process name is mandatory)\n");
exit(1);
} else if (nFound < MaxProcToCreate){
FPRINTF(stderr, "KERNRATE: Only %d of the %d processes you tried to create were found in the task list\n", nFound, MaxProcToCreate);
FPRINTF(stderr, " This is either because the default number of entries in the task list is too small\n");
FPRINTF(stderr, " or because these processes were never created or are gone\n");
FPRINTF(stderr, " The maximum number of tasks in the task list can be increased using the -t# option\n");
FPRINTF(stderr, " Kernrate will try to continue the run with the existing processes\n");
}
bWaitCreatedProcToSettle = TRUE;
}
break;
case 'P':
//
// Monitor a given process ID
//
{
LONG tmpPid;
BOOL bFound = FALSE;
ietResult = IsInputValid(argc,
i,
argv,
"v#",
&tmpPid,
NULL,
0,
ORDER_ANY,
OPTIONAL_NONE
);
if(ietResult == INPUT_GOOD){
if (tmpPid == 0){
InvalidEntryMessage(argv[i],
argv[i+1],
"Invalid Process ID specified on the command line, expecting PID > 0",
FALSE,
TRUE
);
}
//
// User wants the full list of running processes
//
if( strchr(argv[i], 'v') || strchr(argv[i], 'V') ) {
tlistVerbose = TRUE;
bIncludeProcessThreadsInfo = TRUE;
}
++i;
} else if(ietResult == MISSING_PARAMETER || ietResult == MISSING_NUMBER || ietResult == MISSING_REQUIRED_NUMBER) {
ExitWithMissingEntryMessage(argv[i],
"'-p# PID' option requires a decimal process ID (PID>0), space separated",
FALSE
);
} else if(ietResult == INVALID_NUMBER) {
InvalidEntryMessage(argv[i],
argv[i+1],
"Invalid Process ID specified on the command line, expecting a decimal number",
FALSE,
TRUE
);
} else if(ietResult == UNKNOWN_OPTION) {
ExitWithUnknownOptionMessage(argv[i]);
}
Pid = (LONGLONG)tmpPid;
ProcToMonitor = InitializeProcToMonitor(Pid);
if( ProcToMonitor == NULL ){ //This process may be gone
FPRINTF(stdout, "KERNRATE: Could not initialize for specified process (PID= %12I64d)\n process may be gone or wrong PID specified\n", Pid);
continue;
}
//
//Get the task list and Copy the initial performance data for the process
//Note: It is possible that the specified Pid is not on the tlist because
//of the DEFAULT_MAX_TASKS limit. We won't stop the run because of that but there will be no
//performance data for that process.
//
if( bIncludeGeneralInfo || tlistVerbose) {
if ( !bTlistInitialized || (bIncludeProcessThreadsInfo == TRUE && bIncludeThreadsInfo == FALSE) ) {
//
// In the second check above if we already took a tlist but that's the first time thread info is required,
// we'll have to refresh it and take thread info as well
//
if( bIncludeProcessThreadsInfo == TRUE && bIncludeThreadsInfo == FALSE ){
bIncludeThreadsInfo = TRUE;
}
//
// get the task list for the system
//
if( !bTlistInitialized ){
gTlistStart = calloc(1, gMaxTasks*sizeof(TASK_LIST));
if (gTlistStart == NULL) {
FPRINTF(stderr, "\nKERNRATE: Allocation of memory for the running processes task list failed(4)\n");
exit(1);
}
}
NumTasks = GetTaskList( gTlistStart, gMaxTasks );
bTlistInitialized = TRUE;
gNumTasksStart = NumTasks;
}
}
if( bTlistInitialized && (bIncludeGeneralInfo || tlistVerbose) ) {
if ( tlistDisplayed == FALSE && tlistVerbose ) {
FPRINTF(stdout, "\nRunning processes found before profile start:\n");
FPRINTF(stdout, " Pid Process\n");
FPRINTF(stdout, " ------- -----------\n");
}
for (m=0; m < NumTasks; m++) {
if ( tlistDisplayed == FALSE && tlistVerbose) {
FPRINTF(stdout, "%12I64d %32s\n",
gTlistStart[m].ProcessId,
gTlistStart[m].ProcessName
);
}
if ( Pid == gTlistStart[m].ProcessId ) {
FPRINTF(stdout, "\n===> Found process: %s, Pid: %I64d\n\n",
gTlistStart[m].ProcessName,
Pid
);
UpdateProcessStartInfo(ProcToMonitor,
&gTlistStart[m],
bIncludeProcessThreadsInfo
);
bFound = TRUE;
if(!tlistVerbose)
break;
}
}
if( tlistDisplayed == FALSE && tlistVerbose ){
FPRINTF(stdout, "\nNOTE: The list above may be missing some or all processes created by the '-o' option\n");
//
// Let's not print it again if there is another specified on the command line
//
tlistDisplayed = TRUE;
tlistVerbose = FALSE;
}
if(!bFound){
FPRINTF(stderr, "===>KERNRATE: Process Performance Summary for PID= %I64d will not be gathered\n", Pid);
FPRINTF(stderr, "because Kernrate could not find this process in the task list.\n");
FPRINTF(stderr, "This could be due to Kernrate's limit of maximum %d processes in the task list\n", DEFAULT_MAX_TASKS);
FPRINTF(stderr, "You may use the '-t# N' option to specify a larger maximum number (default is 256)\n");
}
}
}
break;
case 'R':
//
// Turn on RAW bucket dump
//
ietResult = IsInputValid(argc,
i,
argv,
"d",
NULL,
NULL,
0,
ORDER_ANY,
OPTIONAL_ANY
);
if(ietResult == INPUT_GOOD){
bRawData = TRUE;
//
// If the user specified '-rd', we want to output disassembly with the raw data.
//
if ( strchr(argv[i], 'd') || strchr(argv[i], 'D') ){
bRawDisasm = TRUE;
#ifndef DISASM_AVAILABLE
FPRINTF(stderr, "\n===>KERNRATE: '-rd' command line option specified but disassembly is not available at present\n");
#endif
}
} else if (ietResult == BOGUS_ENTRY){
ExitWithUnknownOptionMessage(argv[i+1]);
} else {
ExitWithUnknownOptionMessage(argv[i]);
}
break;
case 'S':
//
// Set Sleep interval
//
ietResult = IsInputValid(argc,
i,
argv,//argv[i][1],
"#",
&gSleepInterval,
NULL,
0,
ORDER_ANY,
OPTIONAL_NONE
);
if(ietResult == INPUT_GOOD){
gSleepInterval *= 1000;
if (gSleepInterval == 0) {
InvalidEntryMessage(argv[i],
argv[i+1],
"Expecting a decimal number >0 in seconds",
FALSE,
TRUE
);
}
++i;
} else if(ietResult == MISSING_PARAMETER || ietResult == MISSING_NUMBER || ietResult == MISSING_REQUIRED_NUMBER) {
ExitWithMissingEntryMessage(argv[i],
"'-s N' or '-s# N' options require a specified time >0 in seconds, space separated",
FALSE
);
} else if(ietResult == INVALID_NUMBER) {
InvalidEntryMessage(argv[i],
argv[i+1],
"Expecting a decimal number >0 in seconds",
FALSE,
TRUE
);
} else if(ietResult == UNKNOWN_OPTION) {
ExitWithUnknownOptionMessage(argv[i]);
}
break;
case 'T':
//
// We already dealt with this command line parameter in the second loop
// Just update the running index in case we found an extra optional entry
//
i += tJump;
break;
case 'U':
//
// Requests IMAGEHLP to present undecorated symbol names
//
ietResult = IsInputValid(argc,
i,
argv,
"",
NULL,
NULL,
0,
ORDER_ANY,
OPTIONAL_ANY
);
if(ietResult == INPUT_GOOD){
gSymOptions |= SYMOPT_UNDNAME;
} else if (ietResult == BOGUS_ENTRY){
ExitWithUnknownOptionMessage(argv[i+1]);
} else {
ExitWithUnknownOptionMessage(argv[i]);
}
break;
case 'V':
//
// Verbose mode.
//
gVerbose = VERBOSE_DEFAULT;
ulVerbose = VERBOSE_DEFAULT;
ietResult = IsInputValid(argc,
i,
argv,
"#",
&ulVerbose,
NULL,
0,
ORDER_ANY,
OPTIONAL_ANY
);
if(ietResult == INPUT_GOOD){
gVerbose |= ulVerbose;
if ( gVerbose > VERBOSE_MAX ){
FPRINTF(stderr,
"\n===>WARNING: Invalid Verbose level '-v %s' specified, or'ed verbose levels cannot exceed %d\n",
argv[i+1],
VERBOSE_MAX
);
FPRINTF(stderr,
"---> Verbose level is set to %d\n",
VERBOSE_MAX
);
gVerbose = VERBOSE_MAX;
}
++i;
} else if(ietResult == MISSING_PARAMETER || ietResult == MISSING_NUMBER) { // Allowed
//
// No verbose level specified, using default
//
} else if(ietResult == MISSING_REQUIRED_NUMBER){
ExitWithMissingEntryMessage(argv[i],
"'-v# N' option requires a specific verbose level",
FALSE
);
} else if(ietResult == INVALID_NUMBER) {
InvalidEntryMessage(argv[i],
argv[i+1],
"Invalid Verbose level (expecting a decimal entry)",
TRUE,
TRUE
);
} else if(ietResult == UNKNOWN_OPTION) {
ExitWithUnknownOptionMessage(argv[i]);
}
if ( gVerbose & VERBOSE_IMAGEHLP ) {
gSymOptions |= SYMOPT_DEBUG;
}
break;
case 'W':
{
LONG tmpTime;
//
// Case WP: (associated with process creation via the -O option)
// Wait for CR or a given number of seconds to allow the created processes to settle.
// This is useful in cases where the created process takes time to initialize and load modules,
// or if the user needs to interact with it before profiling.
//
// Case W:
// Wait for CR or a given number of seconds before starting the profile
// This is useful in cases where the system is very busy with the task being monitored
// One can start Kernrate ahead of the task, allow for proper initialization (symbol loading)
// and then launch the task to be monitored
//
ietResult = IsInputValid(argc,
i,
argv,
"#p",
&tmpTime,
NULL,
0,
ORDER_ANY,
OPTIONAL_ANY
);
if(ietResult == INPUT_GOOD){
//
// Wait for a given number of seconds before continuing
//
if ( strchr(argv[i], 'p') || strchr(argv[i], 'P') ){
gSecondsToWaitCreatedProc = tmpTime; //Zero allowed
} else {
gSecondsToDelayProfile = tmpTime;
}
++i;
} else if(ietResult == MISSING_PARAMETER || ietResult == MISSING_NUMBER) { //Allowed
//
// Wait for user to press a key before continuing
//
if ( strchr(argv[i], 'p') || strchr(argv[i], 'P') ){
bCreatedProcWaitForUserInput = TRUE;
} else {
bWaitForUserInput = TRUE;
}
} else if(ietResult == MISSING_REQUIRED_NUMBER){
ExitWithMissingEntryMessage(argv[i-1],
"'-wp# N' or '-w# N' options require a (decimal) number of seconds to wait",
FALSE
);
} else if(ietResult == INVALID_NUMBER) {
InvalidEntryMessage(argv[i],
argv[i+1],
"Expecting a decimal value in seconds, 0 < N < 10^9",
FALSE,
TRUE
);
} else if(ietResult == UNKNOWN_OPTION) {
ExitWithUnknownOptionMessage(argv[i]);
}
}
break;
case 'X':
//
// User asked for Locks information
//
bIncludeUserProcLocksInfo = TRUE;
bIncludeSystemLocksInfo = TRUE;
ietResult = IsInputValid(argc,
i,
argv,
"uk#",
&gLockContentionMinCount,
NULL,
0,
ORDER_ANY,
OPTIONAL_ANY
);
if( strchr(argv[i], 'k') || strchr(argv[i], 'K') )
bIncludeUserProcLocksInfo = FALSE;
if( strchr(argv[i], 'u') || strchr(argv[i], 'U') )
bIncludeSystemLocksInfo = FALSE;
if(ietResult == INPUT_GOOD){
FPRINTF(stdout, "---> Minimum lock contention for processing set to= %ld\n", gLockContentionMinCount);
++i;
} else if(ietResult == MISSING_NUMBER) { //Allowed
FPRINTF(stdout, "---> Minimum lock contention for processing set to default= %ld\n", gLockContentionMinCount);
} else if(ietResult == MISSING_REQUIRED_NUMBER || ietResult == MISSING_PARAMETER){
ExitWithMissingEntryMessage(argv[i],
"'-x# number' '-xk# number' '-xu# number' options require a number for the minimum lock-contention filtering",
FALSE
);
} else if(ietResult == INVALID_NUMBER) {
InvalidEntryMessage(argv[i],
argv[i+1],
"'-x# number', '-xk# number', '-xu# number' options expect a number 0 <= N < 10^9",
FALSE,
TRUE
);
} else if(ietResult == UNKNOWN_OPTION) {
ExitWithUnknownOptionMessage(argv[i]);
}
//
// The user didn't bother to read the usage guide and specified both k and u after the '-x'...
//
if( bIncludeUserProcLocksInfo == FALSE && bIncludeSystemLocksInfo == FALSE ){
bIncludeUserProcLocksInfo = TRUE;
bIncludeSystemLocksInfo = TRUE;
}
break;
case 'Z':
ietResult = IsInputValid(argc,
i,
argv,
"",
NULL,
tmpName, //Used as a temporary storage
cMODULE_NAME_STRLEN,
ORDER_ANY,
OPTIONAL_NONE
);
if (ietResult == INPUT_GOOD){
ZoomModule = calloc(1, MODULE_SIZE);
if (ZoomModule==NULL) {
FPRINTF(stderr, "\nAllocation of zoom module %s failed\n", argv[i]);
exit(1);
}
SetModuleName( ZoomModule, tmpName );
ZoomModule->bZoom = TRUE;
//
//For compatibility with original behaviour (when monitoring kernel only)
//We'll also use this for allowing the user to specify zoom modules common accross processes
//
if (ProcToMonitor == NULL){
ZoomModule->Next = gCommonZoomList;
gCommonZoomList = ZoomModule;
} else {
ZoomModule->Next = ProcToMonitor->ZoomList;
ProcToMonitor->ZoomList = ZoomModule;
}
bZoomSpecified = TRUE;
++i;
} else if( ietResult == MISSING_PARAMETER ||
ietResult == MISSING_STRING ||
ietResult == MISSING_REQUIRED_STRING ) {
ExitWithMissingEntryMessage(argv[i],
"'-z modulename' option requires a module name, multiple usage is allowed\nRead the usage guide or help printout for more options",
TRUE
);
} else if(ietResult == UNKNOWN_OPTION) {
ExitWithUnknownOptionMessage(argv[i]);
}
break;
case 'H':
case '?':
//
// We don't really care about bogus trailing letters or entries here
// Print Usage string and exits
//
Usage(TRUE);
break;
default:
//
// Specify the unknown option and print the Usage string. Then exists.
//
ExitWithUnknownOptionMessage(argv[i]);
}
} else { //if ((argv[i][0] == '-') || (argv[i][0] == '/'))
Usage(FALSE);
FPRINTF(stderr,
"\n===>KERNRATE: Invalid switch %s\n",
argv[i]
);
exit(1);
}
}
//
// User asked for system resource information but has not turned on kernel profile
// The resource information depends on Kernrate initializing for the system process.
// We'll turn kernel profile on and issue a message
//
if( bIncludeSystemLocksInfo == TRUE && bCombinedProfile == FALSE){
bCombinedProfile = TRUE;
FPRINTF(stderr, "\n===>KERNRATE: User requested System resource (locks) information but did not turn on kernel profiling\n");
FPRINTF(stderr, " System resource information depends on Kernrate initializing for kernel profiling\n");
FPRINTF(stderr, " Kernel profiling will therefore be started by kernrate on behalf of the user\n");
}
//
// Both i386 and IA64 processors cannot always support turning on several profile sources simultaneously.
// This is because not every combination of profile sources can be turned on together.
// AMD64 does allow turning on up to 4 profile sources concurrently (in any combination).
// We'll automatically adapt the profiling method if the user specified more than the maximum
// simultaneous sources allowed but did not specify the '-c' option on the command line.
// Default switching interval will be used in that case.
//
if( SourcesSoFar > gMaxSimultaneousSources ){
if( bOldSampling == FALSE ){
bOldSampling = TRUE;
FPRINTF( stdout, "\nNOTE:\nThe number of sources specified is greater than the maximum that can be turned on\n");
FPRINTF( stdout, "simultaneously on this machine (%u), Kernrate will therefore profile one source at a time\n",
gMaxSimultaneousSources
);
FPRINTF( stdout, "The overall run time will be devided into segments (no. processes x no. profile sources)\n");
FPRINTF( stdout, "The interval for switching between sources and processes is currently set to %dms\n",
gChangeInterval
);
FPRINTF( stdout, "This interval can be adjusted using the '-c# N' command line option, where N is in [ms]\n\n");
}
}else{
if( SourcesSoFar > 1 ){
#if defined(_IA64_)
FPRINTF( stdout, "\nNOTE: Kernrate Will attempt to turn on simultaneously the sources specified\n");
FPRINTF( stdout, "but it is not guaranteed that just any combination of sources will work together\n");
FPRINTF( stdout, "on this machine. No hits will be recorded for any source that could not be turned on\n");
#else if defined(_AMD64_)
FPRINTF( stdout, "\nNOTE: The sources specified will be turned on simultaneously\n");
#endif // _IA64_
}
}
if ( SourcesSoFar == 0 ){
FPRINTF(stderr, "\n===>KERNRATE: User apparently turned OFF the default source (TIME), but did not specify any other valid source\n");
FPRINTF(stderr, " Kernrate needs at least one valid CPU source with non-zero rate to perform a profile\n");
FPRINTF(stderr, " Use the '-i SourceName Interval' command line option to specify a source\n");
FPRINTF(stderr, " Use the '-lx' command line option to get a list of supported CPU sources on the current platform\n");
FPRINTF(stderr, " Only general information will be available as a result of this run\n");
bOldSampling = FALSE; //To prevent early exit
}
//
// Determine supported sources and set Profile Interval as necessary
//
ProcToMonitor = gProcessList;
for (i=0; i < (LONG)gNumProcToMonitor; i++){
SetProfileSourcesRates( ProcToMonitor );
ProcToMonitor = ProcToMonitor->Next;
}
return;
} /* GetConfiguration() */
INPUT_ERROR_TYPE
IsInputValid(int argc,
int OptionIndex,
PCHAR *Option,
PCHAR AllowedTrailLetters,
PLONG AssociatedNumber,
PCHAR AssociatedString,
ULONG MaxStringLength,
INPUT_ORDER Order,
INPUT_OPTIONAL Optional
)
/*++
Routine Description:
Checks the validity of a command line input entry:
1. Unallowed duplicate entries
2. Bogus trailing letters
3. Missing or bogus associated parameters
4. Type and validity of additional parameters
Arguments:
argc - The number of command line arguments (including the kernrate process name)
OptionIndex - The current index of a command line entry
Option - A pointer to the command line entry (argv)
AllowedTrailLetters - A character string of the allowed sub-option letters that can come with an entry
(For example, the -n option can also accept -nv and or -n# so the string would be "v#"
AssociatedNumber - A pointer to optional or required data (number) to be specified with the option
AssociatedString - Same as above, but for a string
MaxStringLength - Maximum allowed characters in the associated string
Order - In case of two possible associated parameters (a number and a string), which one should come first
Optional - In case of two possible associated parameters (a number and a string), which one is optional
Input/Output
- When AssociatedNumber is not NULL the value found will be filled if it exists
- When AssociatedString is not NULL the string found will be filled in if it exists
Return Value:
- Error type/condition
--*/
{
BOOL bFoundNumber = FALSE;
BOOL bFoundString = FALSE;
int index;
LONG i;
LONG OptionLength = lstrlen(Option[OptionIndex]);
LONG TrailerLength = lstrlen(AllowedTrailLetters);
const int maxIndex = sizeof(InputOption)/sizeof(InputOption[0])-1;
const int wIndex = 'W' - 'A';
//
//Check for unallowed duplicate entries
//
index = toupper(Option[OptionIndex][1]) - 'A';
if( index < 0 || index > maxIndex ){ //Sanity check (should have been detected already in GetConfiguration)
ExitWithUnknownOptionMessage(Option[OptionIndex]);
}
//
// Deal with special case ('-w' and '-wp')
//
if(index == wIndex){
if( strchr(Option[OptionIndex], 'p') || strchr(Option[OptionIndex], 'P') ){
wpCount.ActualCount += 1;
if( wpCount.ActualCount > wpCount.Allowed ){
InputOption[index].ActualCount = InputOption[index].Allowed; //cause failure
}
} else {
wCount.ActualCount += 1;
if( wCount.ActualCount > wCount.Allowed ){
InputOption[index].ActualCount = InputOption[index].Allowed; //cause failure
}
}
}
InputOption[index].ActualCount += 1;
if( (InputOption[index].Allowed > -1) && InputOption[index].ActualCount > InputOption[index].Allowed ){
FPRINTF(stderr, "KERNRATE: ERROR - Command line option -%c (or a variant) appears more times than allowed (%d)\n",
InputOption[index].InputOption,
InputOption[index].Allowed
);
if( index == wIndex ){
FPRINTF(stderr, " (One time for the '-w' option and one time for the '-wp' option)\n");
}
exit(1);
} else if( (InputOption[index].Allowed == -2) && InputOption[index].ActualCount > 1 ){
FPRINTF(stderr, "KERNRATE: WARNING - Command line option -%c (or a variant) appears more than once (non-critical error)\n",
InputOption[index].InputOption
);
}
//
//Check for bogus trailing letters
//
if( OptionLength <= 2+TrailerLength ){
for(i=2; i < OptionLength; i++)
{
if( !strchr(AllowedTrailLetters, tolower(Option[OptionIndex][i])) ){
return (UNKNOWN_OPTION);
}
}
} else {
return (UNKNOWN_OPTION);
}
//
//Check for missing (or bogus) associated parameters following the command line option
//
if (OptionIndex+1 == argc || Option[OptionIndex+1][0] == '-' || Option[OptionIndex+1][0] == '/'){
if( (AssociatedNumber != NULL) && (AssociatedString != NULL) ){
return (MISSING_PARAMETER);
}
if( AssociatedNumber != NULL ){
if( strchr(Option[OptionIndex], '#') ){
return (MISSING_REQUIRED_NUMBER);
}
return (MISSING_NUMBER);
}
if( AssociatedString != NULL ){
return (MISSING_STRING);
}
} else {
if( (AssociatedNumber == NULL) && (AssociatedString == NULL) ){
return (BOGUS_ENTRY);
}
}
//
//Check if the next parameter is a number or a string and fill in its value
//Consider the specified order in the case of two parameters
//
if( strchr(Option[OptionIndex], '#') || (NULL != AssociatedNumber) ){
if( (IsStringANumber( Option[OptionIndex+1] )) &&
((Order == ORDER_NUMBER_FIRST) || (Order == ORDER_ANY)) ) {
bFoundNumber = TRUE;
if( NULL == AssociatedString){
*AssociatedNumber = atol( Option[OptionIndex+1] );
}
} else if( NULL != AssociatedString ) { //Either not a number,
//or this number must be a string as indicated by the order
strncpy( AssociatedString, Option[OptionIndex+1], MaxStringLength);
AssociatedString[MaxStringLength-1] = '\0';
bFoundString = TRUE;
} else {
return (INVALID_NUMBER); //We get here only if we did not expect a string but got one
}
} else if ( NULL != AssociatedString ) {
strncpy( AssociatedString, Option[OptionIndex+1], MaxStringLength);
AssociatedString[MaxStringLength-1] = '\0';
bFoundString = TRUE;
}
//
//Check if there is another parameter in continuation, if two parameters are asked for
//
if( (AssociatedNumber != NULL) && (AssociatedString != NULL) ){
if ( OptionIndex+2 == argc || Option[OptionIndex+2][0] == '-' || Option[OptionIndex+2][0] == '/' ){
//
// There is no second parameter
//
if(bFoundNumber){
if( (Optional == OPTIONAL_NUMBER) ) { //First parameter a number but it should be taken as a string
strncpy( AssociatedString, Option[OptionIndex+1], MaxStringLength);
AssociatedString[MaxStringLength-1] = '\0';
if( strchr(Option[OptionIndex], '#') ){
return (MISSING_REQUIRED_NUMBER);
}
return (MISSING_NUMBER);
} else {
*AssociatedNumber = atol( Option[OptionIndex+1] ); //Already checked if it is a valid number
if( ((Optional != OPTIONAL_STRING) && (Optional != OPTIONAL_ANY)) ){
return (MISSING_REQUIRED_STRING);
} else {
return (MISSING_STRING);
}
}
} else { //Found a string as first parameter
if( strchr(Option[OptionIndex], '#') ||
((Optional != OPTIONAL_NUMBER) && (Optional != OPTIONAL_ANY)) ){
return (MISSING_REQUIRED_NUMBER);
}
return (MISSING_NUMBER);
}
} else if (bFoundString){ //Found a string as first parameter
if( IsStringANumber( Option[OptionIndex+2] ) ){
*AssociatedNumber = atol( Option[OptionIndex+2] );
} else {
return (INVALID_NUMBER);
}
} else { //Found a number as first parameter
*AssociatedNumber = atol( Option[OptionIndex+1] ); //Already checked if it is a valid number
strncpy( AssociatedString, Option[OptionIndex+2], MaxStringLength );
AssociatedString[MaxStringLength-1] = '\0';
}
}
return (INPUT_GOOD);
}// IsInputValid()
VOID
ExitWithUnknownOptionMessage(PCHAR CurrentOption)
{
Usage(FALSE);
FPRINTF(stderr,
"KERNRATE: Unknown command line option '%s' <---Check for missing space separator or bogus characters/entries\n",
CurrentOption
);
exit(1);
}
VOID
InvalidEntryMessage(
PCHAR CurrentOption,
PCHAR CurrentValue,
PCHAR Remark,
BOOL bUsage,
BOOL bExit
)
{
if(bUsage)
Usage(FALSE);
FPRINTF(stderr,
"\n===>KERNRATE: Invalid entry %s %s \n%s\n",
CurrentOption,
CurrentValue,
Remark
);
if(bExit)
exit(1);
}
VOID
ExitWithMissingEntryMessage(
PCHAR CurrentOption,
PCHAR Remark,
BOOL bUsage
)
{
if(bUsage)
Usage(FALSE);
FPRINTF(stderr,
"\n===>KERNRATE: Missing entry for command line option %s \n%s\n",
CurrentOption,
Remark
);
exit(1);
}
PPROC_TO_MONITOR
InitializeProcToMonitor(LONGLONG Pid)
/*++
Routine Description:
Opens the given process and gets a handle to it
Allocates a process structure and initializes it
Initializes the profile source information for this process
Arguments:
Pid - The process ID (PID)
Return Value:
- Pointer to the process structure
--*/
{
HANDLE SymHandle;
PPROC_TO_MONITOR ProcToMonitor;
PMODULE tmpModule, ZoomModule;
SymHandle = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, //PROCESS_ALL_ACCESS,
FALSE,
(DWORD)Pid);
if (SymHandle==NULL) {
FPRINTF(stderr,
"KERNRATE: OpenProcess Pid= (%I64d) failed - it could be just gone %d\n",
Pid,
GetLastError()
);
return (NULL);
}
ProcToMonitor = calloc(1, sizeof(PROC_TO_MONITOR));
if (ProcToMonitor==NULL) {
FPRINTF(stderr, "\n===>KERNRATE: Allocation for Process %I64d failed\n", Pid);
exit(1);
}
ProcToMonitor->ProcessHandle = SymHandle;
ProcToMonitor->Pid = Pid;
ProcToMonitor->Index = gNumProcToMonitor;
ProcToMonitor->ProcessName = ""; //Will be set in GetProcessModuleInformation
ProcToMonitor->ModuleCount = 0;
ProcToMonitor->ZoomCount = 0;
ProcToMonitor->ModuleList = NULL;
ProcToMonitor->ZoomList = NULL;
ProcToMonitor->Source = NULL;
//MC
ProcToMonitor->JITHeapLocationsStart = NULL;
ProcToMonitor->JITHeapLocationsStop = NULL;
//MC
ProcToMonitor->pProcThreadInfoStart = NULL;
gNumProcToMonitor++;
//
//copy the common module list to the current process zoom list
//
tmpModule = gCommonZoomList;
while( tmpModule != NULL ){
ZoomModule = calloc(1, MODULE_SIZE);
if (ZoomModule==NULL) {
FPRINTF(stderr, "Allocation of memory for common zoom list failed\n");
exit(1);
}
strncpy(ZoomModule->module_Name, tmpModule->module_Name, cMODULE_NAME_STRLEN-1);
ZoomModule->module_Name[cMODULE_NAME_STRLEN-1] = '\0';
ZoomModule->bZoom = TRUE;
ZoomModule->Next = ProcToMonitor->ZoomList;
ProcToMonitor->ZoomList = ZoomModule;
tmpModule = tmpModule->Next;
}
//
//Initialize Profile Source Info for this process
//
InitializeProfileSourceInfo(ProcToMonitor);
ProcToMonitor->Next = gProcessList;
gProcessList = ProcToMonitor;
return (ProcToMonitor);
}// InitializeProcToMonitor()
VOID
UpdateProcessStartInfo(
PPROC_TO_MONITOR ProcToMonitor,
PTASK_LIST TaskListEntry,
BOOL bIncludeProcessThreadsInfo
)
{
if(ProcToMonitor != NULL && TaskListEntry != NULL){
memcpy(&ProcToMonitor->ProcPerfInfoStart,
&TaskListEntry->ProcessPerfInfo,
sizeof(TaskListEntry->ProcessPerfInfo)
);
if( bIncludeProcessThreadsInfo == TRUE ){
if( &TaskListEntry->pProcessThreadInfo != NULL ){
ProcToMonitor->pProcThreadInfoStart =
malloc(ProcToMonitor->ProcPerfInfoStart.NumberOfThreads*sizeof(SYSTEM_THREAD_INFORMATION));
if( ProcToMonitor->pProcThreadInfoStart == NULL){
FPRINTF(stderr, "KERNRATE: Memory allocation failed for process thread-information, attempting to continue without it\n");
return;
}
memcpy(&ProcToMonitor->pProcThreadInfoStart,
&TaskListEntry->pProcessThreadInfo,
sizeof(TaskListEntry->pProcessThreadInfo)
);
}
}
}
}
PSOURCE
InitializeStaticSources(
VOID
)
{
PSOURCE source = StaticSources;
#if defined(_IA64_)
NTSTATUS status;
PSYSTEM_PROCESSOR_INFORMATION sysProcInfo;
sysProcInfo = malloc(sizeof(SYSTEM_PROCESSOR_INFORMATION));
if(sysProcInfo == NULL){
FPRINTF(stderr,"Memory allocation failed for SystemProcessorInformation in InitializeStaticsources\n");
exit(1);
}
status = NtQuerySystemInformation( SystemProcessorInformation,
sysProcInfo,
sizeof(SYSTEM_PROCESSOR_INFORMATION),
NULL);
if ( NT_SUCCESS(status) &&
(sysProcInfo->ProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64) ) {
ULONG n;
switch( IA64ProcessorLevel2ProcessorFamily( sysProcInfo->ProcessorLevel ) ) {
case IA64_FAMILY_MERCED:
//
// Patch the last entry as defined with convention used to initialize
// gSourceMaximum.
//
n = sizeof(mercedStaticSources)/sizeof(mercedStaticSources[0]);
mercedStaticSources[n-1].Name = NULL;
mercedStaticSources[n-1].ShortName = "";
source = mercedStaticSources;
break;
case IA64_FAMILY_MCKINLEY:
default: // Following HALIA64 scheme, default IPF PMU as McKinley PMU.
n = sizeof(mckinleyStaticSources)/sizeof(mckinleyStaticSources[0]);
mckinleyStaticSources[n-1].Name = NULL;
mckinleyStaticSources[n-1].ShortName = "";
source = mckinleyStaticSources;
break;
}
if ( sysProcInfo != NULL ) {
free(sysProcInfo);
sysProcInfo = NULL;
}
}
#endif // _IA64_
#if defined(_AMD64_)
source = Amd64StaticSource;
#endif // _AMD64_
return source;
} // InitializeStaticSources()
ULONG
InitializeProfileSourceInfo (
PPROC_TO_MONITOR ProcToMonitor
)
/*++
Function Description:
This function initializes the Profile sources.
Argument:
Pointer to Process to be monitored or NULL if only Maximum Source Count is needed.
Return Value:
Maximum Source Count Found.
Algorithm:
ToBeSpecified
In/Out Conditions:
ToBeSpecified
Globals Referenced:
ToBeSpecified
Exception Conditions:
ToBeSpecified
MP Conditions:
ToBeSpecified
Notes:
This function has been enhanced from its original version
to support and use the static profiling sources even if the
pstat driver is not present or returned no active profiling
event.
ToDo List:
ToBeSpecified
Modification History:
3/17/2000 TF Initial version
--*/
{
UNICODE_STRING DriverName;
NTSTATUS status;
OBJECT_ATTRIBUTES ObjA;
IO_STATUS_BLOCK IOSB;
const ULONG bufferSize = 400;
PUCHAR buffer;
ULONG i, j;
PEVENTID Event;
HANDLE DriverHandle;
PEVENTS_INFO eventInfo;
PSOURCE staticSource, src;
ULONG staticCount, driverCount, totalCount;
DriverHandle = INVALID_HANDLE_VALUE;
staticCount = driverCount = 0;
buffer = malloc(bufferSize*sizeof(UCHAR));
if(buffer == NULL){
FPRINTF(stderr,"Memory allocation failed for buffer in InitializeProfileSourceInfo\n");
exit(1);
}
//
// Try to 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) ) {
//
// Determine how many events the driver provides
//
if (WIN2K_OS)
{
Event = (PEVENTID) buffer;
do {
*((PULONG) buffer) = driverCount;
driverCount += 1;
status = NtDeviceIoControlFile(
DriverHandle,
(HANDLE) NULL, // event
(PIO_APC_ROUTINE) NULL,
(PVOID) NULL,
&IOSB,
PSTAT_QUERY_EVENTS,
buffer, // input buffer
bufferSize,
NULL, // output buffer
0
);
} while (NT_SUCCESS(status));
} else { // WinXP/.Net and above
eventInfo = (PEVENTS_INFO)buffer;
status = NtDeviceIoControlFile( DriverHandle,
(HANDLE) NULL, // event
(PIO_APC_ROUTINE) NULL,
(PVOID) NULL,
&IOSB,
PSTAT_QUERY_EVENTS_INFO,
buffer, // input buffer
bufferSize,
NULL, // output buffer
0
);
if(NT_SUCCESS(status)) driverCount = eventInfo->Events;
if ( driverCount ) {
if ( eventInfo->TokenMaxLength > gTokenMaxLen ) {
gTokenMaxLen = eventInfo->TokenMaxLength;
}
if ( eventInfo->DescriptionMaxLength > gDescriptionMaxLen ) {
gDescriptionMaxLen = eventInfo->DescriptionMaxLength;
}
}
}
}
//
// Determine how many static events there are and
// re-initialize the format specifiers if needed.
//
src = staticSource = InitializeStaticSources();
//
// There should be at least one static source (TIME)
//
if( staticSource == NULL ) {
FPRINTF(stderr, "KERNRATE: InitializeStaticSources returned NULL, Aborting\n");
exit(1);
}
while( src->Name != NULL ) {
if ( lstrlen( src->Name ) > (LONG)gDescriptionMaxLen ) {
gDescriptionMaxLen = lstrlen( src->Name );
}
if ( strlen( src->ShortName ) > gTokenMaxLen ) {
gTokenMaxLen = lstrlen( src->ShortName );
}
staticCount++;
src++;
}
gStaticSource = staticSource;
gStaticCount = staticCount;
totalCount = driverCount + staticCount;
//
//Calling this routine with NULL will just return the maximum source count
//
if(ProcToMonitor != NULL){
//
// Allocate memory for static events, plus the driver provided events
//
ProcToMonitor->Source = calloc(totalCount, sizeof(SOURCE));
if ( ProcToMonitor->Source == NULL ) {
FPRINTF(stderr, "KERNRATE: Events memory allocation failed\n" );
if ( IsValidHandle( DriverHandle ) ) {
NtClose (DriverHandle);
}
exit(1);
}
//
// copy static events to new list
//
for (j=0; j < staticCount; j++) {
ProcToMonitor->Source[j] = staticSource[j];
}
//
// Append the driver provided events to new list
//
if ( IsValidHandle( DriverHandle ) ) {
Event = (PEVENTID) buffer;
for (i=0; i < driverCount; i++) {
*((PULONG) buffer) = i;
status = NtDeviceIoControlFile( DriverHandle,
(HANDLE) NULL, // event
(PIO_APC_ROUTINE) NULL,
(PVOID) NULL,
&IOSB,
PSTAT_QUERY_EVENTS,
buffer, // input buffer
bufferSize,
NULL, // output buffer
0
);
//
// Source Names:
// - For the Name, we use the description
// - For the short Name, we use the token string stored
// in the first string of the buffer
//
if( NT_SUCCESS(status) ){
ProcToMonitor->Source[j].Name = _strdup ( (PCHAR)(Event->Buffer + Event->DescriptionOffset) );
ProcToMonitor->Source[j].ShortName = _strdup( (PCHAR)Event->Buffer );
ProcToMonitor->Source[j].ProfileSource = Event->ProfileSource;
ProcToMonitor->Source[j].DesiredInterval = Event->SuggestedIntervalBase;
j++;
}
} //for
} //if( IsValidHandle() )
} // if( ProcToMonitor )
if ( IsValidHandle( DriverHandle ) ){
NtClose (DriverHandle);
}
if(buffer != NULL){
free(buffer);
buffer = NULL;
}
return( totalCount );
} // InitializeProfileSourceInfo()
ULONG
NextSource(
IN ULONG CurrentSource,
IN PMODULE ModuleList,
IN PPROC_TO_MONITOR ProcToMonitor
)
/*++
Routine Description:
Stops the current profile source and starts the next one.
If a Source is already started, it will be stopped first, otherwise 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
ProcToMonitor - Pointer to the process to be monitored
Return Value:
Returns the new current profile source
--*/
{
if ( ProcToMonitor->Source[CurrentSource].bProfileStarted == TRUE) {
StopSource(CurrentSource, ModuleList, ProcToMonitor);
ProcToMonitor->Source[CurrentSource].bProfileStarted = FALSE;
}
//
// Iterate through the available sources to find the
// next active source to be started.
//
if (ModuleList->mu.bProfileStarted == FALSE) {
CurrentSource = 0;
while ( ProcToMonitor->Source[CurrentSource].Interval == 0 ){
CurrentSource = CurrentSource+1;
if (CurrentSource >= gSourceMaximum) {
FPRINTF(stderr, "\n===>KERNRATE: NextSource - No valid sources found to start profiling\n");
exit(1);
}
}
} else {
do {
CurrentSource = CurrentSource+1;
if (CurrentSource == gSourceMaximum) {
CurrentSource = 0;
}
} while ( ProcToMonitor->Source[CurrentSource].Interval == 0);
}
StartSource(CurrentSource, ModuleList, ProcToMonitor);
ProcToMonitor->Source[CurrentSource].bProfileStarted = TRUE;
return(CurrentSource);
}
VOID
StopSource(
IN ULONG ProfileSourceIndex,
IN PMODULE ModuleList,
IN PPROC_TO_MONITOR ProcToMonitor
)
/*++
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
ProcToMonitor - Pointer to the process to be monitored
Return Value:
None.
--*/
{
PMODULE Current;
NTSTATUS Status;
NTSTATUS SaveStatus;
ULONGLONG StopTime;
ULONGLONG ElapsedTime;
LONG CpuNumber;
Current = ModuleList;
while (Current != NULL) {
SaveStatus = STATUS_SUCCESS;
for (CpuNumber=0; CpuNumber < gProfileProcessors; CpuNumber++) {
if( gAffinityMask && !((1<<CpuNumber) & gAffinityMask) )continue;
Status = NtStopProfile(Current->Rate[ProfileSourceIndex].ProfileHandle[CpuNumber]);
if (!NT_SUCCESS(Status)) {
SaveStatus = Status;
FPRINTF(stderr,
"Pid= %I64d (%s) - NtStopProfile on source %s on CPU %d failed, %s %08lx",
ProcToMonitor->Pid,
ProcToMonitor->ProcessName,
ProcToMonitor->Source[ProfileSourceIndex].Name,
CpuNumber,
Current->module_Name,
Status);
if(Status == STATUS_NO_MEMORY)FPRINTF(stderr, " - Status = NO_MEMORY");
if(Status == STATUS_PROFILING_NOT_STARTED)FPRINTF(stderr, " - Status = PROFILING_NOT_STARTED");
FPRINTF(stderr,"\n");
}
}
if (NT_SUCCESS(SaveStatus)) {
GetSystemTimeAsFileTime((LPFILETIME)&StopTime);
ElapsedTime = StopTime - Current->Rate[ProfileSourceIndex].StartTime;
Current->Rate[ProfileSourceIndex].TotalTime += ElapsedTime;
for (CpuNumber=0; CpuNumber < gProfileProcessors; CpuNumber++) {
Current->Rate[ProfileSourceIndex].TotalCount[CpuNumber] +=
Current->Rate[ProfileSourceIndex].CurrentCount[CpuNumber];
Current->Rate[ProfileSourceIndex].CurrentCount[CpuNumber] = 0;
}
}
Current = Current->Next;
}
}
VOID
StartSource(
IN ULONG ProfileSourceIndex,
IN PMODULE ModuleList,
IN PPROC_TO_MONITOR ProcToMonitor
)
/*++
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
ProcToMonitor - Pointer to the process to be monitored
Return Value:
None.
--*/
{
PMODULE Current;
NTSTATUS Status;
LONG CpuNumber;
Current = ModuleList;
while (Current != NULL) {
for (CpuNumber=0; CpuNumber < gProfileProcessors; CpuNumber++) {
if( gAffinityMask && !((1<<CpuNumber) & gAffinityMask) )continue;
Status = NtStartProfile(Current->Rate[ProfileSourceIndex].ProfileHandle[CpuNumber]);
if (!NT_SUCCESS(Status)) {
FPRINTF(stderr,
"Pid= %I64d (%s) - NtStartProfile on source %s for Module %s failed on CPU %d, %08lx",
ProcToMonitor->Pid,
ProcToMonitor->ProcessName,
ProcToMonitor->Source[ProfileSourceIndex].Name,
Current->module_Name,
CpuNumber,
Status);
if(Status == STATUS_PROFILING_NOT_STOPPED)FPRINTF(stderr, " - Status = PROFILING_NOT_STOPPED");
if(Status == STATUS_NO_MEMORY)FPRINTF(stderr, " - Status = NO_MEMORY");
if(Status == STATUS_INSUFFICIENT_RESOURCES){
FPRINTF(stderr, " - Status = INSUFFICIENT_RESOURCES\n");
FPRINTF(stderr, "KERNRATE: This issue can be caused by selecting a small bucket size and/or extensive zooming\n");
FPRINTF(stderr, " You may want to try the -c command line options in this case or not use the -m option");
}
FPRINTF(stderr,"\n");
}
}
GetSystemTimeAsFileTime((LPFILETIME)&Current->Rate[ProfileSourceIndex].StartTime);
Current = Current->Next;
}
}
VOID
OutputResults(
IN FILE *Out,
IN PPROC_TO_MONITOR ProcToMonitor
)
/*++
Routine Description:
Outputs the collected data.
Arguments:
Out - Supplies the FILE * where the output should go.
ProcToMonitor - Pointer to the process to be monitored
Return Value:
None.
--*/
{
PRATE_DATA RateData;
ULONG i, ProfileSourceIndex, Index;
ULONG RoundDown;
ULONG ModuleCount;
PULONG Hits;
LONG CpuNumber;
PMODULE ModuleList = ProcToMonitor->ModuleList;
PMODULE Current;
HANDLE SymHandle = ProcToMonitor->ProcessHandle;
BOOL bAttachedToProcess = FALSE;
//
// Sum up the total buffers of any zoomed modules
//
Current = ModuleList;
while (Current != NULL) {
if (Current->bZoom) {
Current->mu.bHasHits = FALSE;
for (Index=0; Index < gTotalActiveSources; Index++) {
ProfileSourceIndex = gulActiveSources[Index];
RateData = &Current->Rate[ProfileSourceIndex];
RateData->GrandTotalCount = 0;
//
// Sum the entire profile buffer for this module/source
//
for (CpuNumber=0; CpuNumber < gProfileProcessors; CpuNumber++) {
RateData->TotalCount[CpuNumber] = 0;
Hits = RateData->ProfileBuffer[CpuNumber];
for (i=0; i < BUCKETS_NEEDED(Current->Length); i++) {
RateData->TotalCount[CpuNumber] += Hits[i];
if( Hits[i] > 0 ) Current->mu.bHasHits = TRUE;
}
RateData->GrandTotalCount += RateData->TotalCount[CpuNumber];
}
}
}
Current = Current->Next;
}
//
// Output the results ordered by profile source.
//
if(SymHandle == SYM_KERNEL_HANDLE){
ModuleCount = gKernelModuleCount;
FPRINTF(stdout, "OutputResults: KernelModuleCount = %d\n", ModuleCount);
FPRINTF(stdout, "Percentage in the following table is based on the Total Hits for the Kernel\n");
}
else{
ModuleCount = ProcToMonitor->ModuleCount;
//MC
FPRINTF(stdout, "OutputResults: ProcessModuleCount (Including Managed-Code JITs) = %d\n", ModuleCount);
FPRINTF(stdout, "Percentage in the following table is based on the Total Hits for this Process\n");
//MC
}
OutputModuleList(Out, ModuleList, ModuleCount, ProcToMonitor, NULL);
//
// For any zoomed modules, create and output a module list
// consisting of the functions in the module.
//
Current = ModuleList;
while (Current != NULL) {
if (Current->bZoom && Current->mu.bHasHits == TRUE) {
FPRINTF(stderr, "===> Processing Zoomed Module %s...\n\n", Current->module_FileName);
for (RoundDown = 0; RoundDown <= (bRoundingVerboseOutput ? 1UL:0UL); RoundDown++) {
if ( (gVerbose & VERBOSE_PROFILING) ) {
FPRINTF(stdout, "\n -------------- VERBOSE PROFILE DATA FOR ZOOMED MODULE %s ----------------\n", Current->module_FileName);
FPRINTF(stdout,
"Module Name, Parent Base Address, Module Base address, Current Bucket Index, Current Bucket Address, Total Current Bucket Hits, Per CPU Hits, Remarks\n\n"
);
}
CreateZoomedModuleList(Current, RoundDown, ProcToMonitor);
if(ProcToMonitor->ZoomList == NULL) {
FPRINTF(stdout, "No Hits or No symbols found for module %s\n", Current->module_FileName);
} else {
PMODULE Temp;
FPRINTF(Out, "\n----- Zoomed module %s (Bucket size = %d bytes, Rounding %s) --------\n",
Current->module_FileName,
gZoomBucket,
(RoundDown ? "Up" : "Down" ) );
FPRINTF(stdout, "Percentage in the following table is based on the Total Hits for this Zoom Module\n");
OutputModuleList(Out, ProcToMonitor->ZoomList, gZoomCount, ProcToMonitor, Current);
CleanZoomModuleList(ProcToMonitor); //Done with current module - prepare for next module
}
} //for rounddown/roundup loop
//
// Display the raw bucket counts for all zoomed modules
//
if (bRawData) {
OutputRawDataForZoomModule( Out, ProcToMonitor, Current );
}
//
// We are finished with processing the data for this zoom module, let's unload the associated symbol information
//
if(!SymUnloadModule64( ProcToMonitor->ProcessHandle, Current->Base))
VerbosePrint(( VERBOSE_IMAGEHLP, "Kernrate: Could Not Unload Module, Base= %p for Process %s\n",
(PVOID64)Current->Base,
ProcToMonitor->ProcessName
));
} else {
if( Current->bZoom )
FPRINTF(stdout, "No Hits were recorded for zoom module %s\n", Current->module_FileName);
} //if(Current->bZoom)
Current = Current->Next;
} //while module loop
//MC
if( bAttachedToProcess ){
pfnDetachFromProcess();
bAttachedToProcess = FALSE;
}
//MC
return;
} // OutputResults()
BOOL
CreateZoomModuleCallback(
IN LPSTR szSymName,
IN ULONG64 Address,
IN ULONG Size,
IN PVOID Cxt
)
{
ULONG Index, ProfileSourceIndex;
BOOL HasHits;
LONG CpuNumber;
PMODULE Module;
PRATE_DATA pRate;
PPROC_TO_MONITOR ProcToMonitor;
ULONG64 i, StartIndex, EndIndex, ip, disp, HighLimit;
static ULONG64 LastIndex;
static PMODULE LastParentModule;
static PPROC_TO_MONITOR LastCxt;
HighLimit = Address + Size;
Module = malloc(MODULE_SIZE);
if (Module == NULL) {
FPRINTF(stderr, "KERNRATE: CreateZoomModuleCallback failed to allocate Zoom module\n");
exit(1);
}
ProcToMonitor = (PPROC_TO_MONITOR) Cxt;
Module->Base = Address;
Module->Length = Size;
Module->bZoom = FALSE;
SetModuleName( Module, szSymName );
pRate = malloc(gSourceMaximum*(RATE_DATA_FIXED_SIZE+RATE_DATA_VAR_SIZE));
if (pRate == NULL) {
FPRINTF(stderr, "KERNRATE: CreateZoomModuleCallback failed to allocate RateData\n");
exit(1);
}
//
// Compute range in profile buffer to sum.
//
StartIndex = (ULONG64)((Module->Base - gCallbackCurrent->Base) / gZoomBucket);
EndIndex = (Module->Base + Module->Length - gCallbackCurrent->Base) / gZoomBucket;
//
// Check if we already counted the hits for this bucket for the present module
// in case we had an address gap for the present module within the bucket itself
//
if(StartIndex == LastIndex && LastParentModule != (PMODULE)NULL &&
gCallbackCurrent == LastParentModule &&
LastCxt != (PPROC_TO_MONITOR)NULL && LastCxt == ProcToMonitor ){
for (ip=gCallbackCurrent->Base+StartIndex*gZoomBucket; ip<Address; ip+=1){
if( SymGetSymFromAddr64(ProcToMonitor->ProcessHandle, ip, &disp, gSymbol ) ){
if( 0 == strcmp(gSymbol->Name, Module->module_Name ) ){
StartIndex+=1; // We have a match, don't count this bucket for this module again
if(StartIndex > EndIndex){
HasHits = FALSE;
goto LABEL; // No hits, jump to exit
}
}
}
}
}
//
// Look for hits in the current module and sum them up
//
HasHits = FALSE;
for (Index=0; Index < gTotalActiveSources; Index++) {
ProfileSourceIndex = gulActiveSources[Index];
Module->Rate[ProfileSourceIndex] = pRate[ProfileSourceIndex];
Module->Rate[ProfileSourceIndex].StartTime = gCallbackCurrent->Rate[ProfileSourceIndex].StartTime;
Module->Rate[ProfileSourceIndex].TotalTime = gCallbackCurrent->Rate[ProfileSourceIndex].TotalTime;
Module->Rate[ProfileSourceIndex].TotalCount = calloc(gProfileProcessors, sizeof(ULONGLONG) );
if( Module->Rate[ProfileSourceIndex].TotalCount == NULL ){
FPRINTF(stderr, "KERNRATE: Memory allocation failed for TotalCount in CreateZoomModuleCallback\n");
exit(1);
}
Module->Rate[ProfileSourceIndex].DoubtfulCounts = 0;
for (CpuNumber=0; CpuNumber < gProfileProcessors; CpuNumber++) {
Module->Rate[ProfileSourceIndex].TotalCount[CpuNumber] = 0;
for (i=StartIndex; i <= EndIndex; i++) {
Module->Rate[ProfileSourceIndex].TotalCount[CpuNumber] +=
gCallbackCurrent->Rate[ProfileSourceIndex].ProfileBuffer[CpuNumber][i];
}
if (Module->Rate[ProfileSourceIndex].TotalCount[CpuNumber] > 0) {
HasHits = TRUE;
}
}
}
LABEL:
//
// If the routine has hits add it to the list, otherwise
// ignore it.
//
if (HasHits) {
//
//useful verbose print for identifying shared buckets and actual bucket inhabitants, see also raw data printout
//
if ( gVerbose & VERBOSE_PROFILING ) {
ULONGLONG BucketCount, TotCount, DoubtfulCounts;
CHAR LastSymName[cMODULE_NAME_STRLEN] = "\0";
BOOL bCounted = FALSE;
PIMAGEHLP_LINE64 pLine = (PIMAGEHLP_LINE64) malloc(sizeof(IMAGEHLP_LINE64));
if ( pLine == NULL ){
FPRINTF(stderr, "KERNRATE: Memory allocation failed for pLine in CreateZoomModuleCallback\n");
exit(1);
}
pLine->SizeOfStruct = sizeof(IMAGEHLP_LINE64);
for (Index=0; Index < gTotalActiveSources; Index++) {
ProfileSourceIndex = gulActiveSources[Index];
TotCount = 0;
DoubtfulCounts = 0;
for (i=StartIndex; i <= EndIndex; i++) {
bCounted = FALSE;
BucketCount=0;
for (CpuNumber=0; CpuNumber < gProfileProcessors; CpuNumber++) {
BucketCount += gCallbackCurrent->Rate[ProfileSourceIndex].ProfileBuffer[CpuNumber][i];
}
TotCount += BucketCount;
if(BucketCount > 0){
ip = gCallbackCurrent->Base + (ULONG64)(i*gZoomBucket);
//
// Print the info for the current bucket
//
FPRINTF(stdout, "%s, 0x%I64x, 0x%I64x, %I64d, 0x%I64x, %Ld",
Module->module_Name,
gCallbackCurrent->Base,
Module->Base,
i,
ip,
BucketCount
);
for (CpuNumber=0; CpuNumber < gProfileProcessors; CpuNumber++) {
if( gAffinityMask && !((1<<CpuNumber) & gAffinityMask) )continue;
FPRINTF(stdout, ", %Ld",
gCallbackCurrent->Rate[ProfileSourceIndex].ProfileBuffer[CpuNumber][i]
);
}
OutputLineFromAddress64( ProcToMonitor->ProcessHandle, ip, pLine );
//
// Find out if anyone else is sharing this bucket
//
disp = 0;
for(; (ip<gCallbackCurrent->Base + (ULONG64)((i+1)*gZoomBucket))&&(ip < HighLimit); ip+=1) {
if ( SymGetSymFromAddr64(ProcToMonitor->ProcessHandle, ip, &disp, gSymbol ) ) {
if (0 != strcmp( Module->module_Name, gSymbol->Name ) ){
if(!bCounted){
FPRINTF(stdout, " , Actual Hits Should be Attributed to or Shared with ===> %s+0x%I64x",
gSymbol->Name,
disp
);
OutputLineFromAddress64( ProcToMonitor->ProcessHandle, ip+disp, pLine );
DoubtfulCounts += BucketCount;
strncpy(LastSymName, gSymbol->Name, cMODULE_NAME_STRLEN-1);
LastSymName[cMODULE_NAME_STRLEN-1] = '\0';
bCounted = TRUE;
} else if( 0 != strcmp(LastSymName, gSymbol->Name ) ){
FPRINTF(stdout, "\nActual Hits Should also be Attributed to or Shared with ===> %s+0x%I64x",
gSymbol->Name,
disp
);
OutputLineFromAddress64( ProcToMonitor->ProcessHandle, ip+disp, pLine );
strncpy(LastSymName, gSymbol->Name, cMODULE_NAME_STRLEN-1);
LastSymName[cMODULE_NAME_STRLEN-1] = '\0';
}
}
} else {
//
//This should be rare (no need to increase the doubtful counts)
//
FPRINTF(stdout, " , Actual Hits Should be Attributed to or Shared with ===> UNKNOWN_SYMBOL");
}
}
FPRINTF(stdout, "\n");
}
}
//
// Print the totals for the current module
//
FPRINTF(stdout, "%s, %s - Module Total Count = %I64d, Total Doubtful or Shared Counts = %I64d\n\n",
ProcToMonitor->Source[ProfileSourceIndex].Name,
Module->module_Name,
TotCount,
DoubtfulCounts
);
Module->Rate[ProfileSourceIndex].DoubtfulCounts = DoubtfulCounts;
if( pLine != NULL ){
free(pLine);
pLine = NULL;
}
}
} // if(VERBOSE_PROFILING)
Module->Next = ProcToMonitor->ZoomList;
ProcToMonitor->ZoomList = Module;
++gZoomCount;
} else {
//
// Cleanup
//
for (Index=0; Index < gTotalActiveSources; Index++) {
ProfileSourceIndex = gulActiveSources[Index];
if ( Module->Rate[ProfileSourceIndex].TotalCount != NULL){
free(Module->Rate[ProfileSourceIndex].TotalCount);
Module->Rate[ProfileSourceIndex].TotalCount = NULL;
}
}
if( pRate != NULL ){
free(pRate);
pRate = NULL;
}
if( Module != NULL ){
free(Module);
Module = NULL;
}
}
LastIndex = EndIndex;
LastParentModule = gCallbackCurrent;
LastCxt = ProcToMonitor;
//MC
//
//Imagehlp SymEnumerateSymbols64 returns SUCSESS even if no symbols are found
//We need an indication wheter we need to go into expensive Managed-Code symbol enumeration
//
bImageHlpSymbolFound = TRUE;
//MC
return(TRUE);
} //CreateZoomModuleCallback
BOOL
CreateJITZoomModuleCallback(
IN PWCHAR wszSymName,
IN LPSTR szSymName,
IN ULONG64 Address,
IN ULONG Size,
IN PVOID Cxt
)
{
ULONG ProfileSourceIndex, Index;
BOOL HasHits;
LONG CpuNumber;
PMODULE Module;
PRATE_DATA pRate;
PPROC_TO_MONITOR ProcToMonitor;
ULONG64 i, StartIndex, EndIndex, ip, HighLimit;
static ULONG64 LastIndex;
static PMODULE LastParentModule;
static PPROC_TO_MONITOR LastCxt;
HighLimit = Address + Size;
Module = malloc(MODULE_SIZE);
if (Module == NULL) {
FPRINTF(stderr, "KERNRATE: CreateZoomModuleCallback failed to allocate Zoom module\n");
exit(1);
}
ProcToMonitor = (PPROC_TO_MONITOR) Cxt;
Module->Base = Address;
Module->Length = Size;
Module->bZoom = FALSE;
SetModuleName( Module, szSymName );
pRate = malloc(gSourceMaximum*(RATE_DATA_FIXED_SIZE+RATE_DATA_VAR_SIZE));
if (pRate == NULL) {
FPRINTF(stderr, "KERNRATE: CreateZoomModuleCallback failed to allocate RateData\n");
exit(1);
}
//
// Compute range in profile buffer to sum.
//
StartIndex = (ULONG64)((Module->Base - gCallbackCurrent->Base) / gZoomBucket);
EndIndex = (Module->Base + Module->Length - gCallbackCurrent->Base) / gZoomBucket;
//
// Check if we already counted the hits for this bucket for the present module
// in case we had an address gap for the present module within the bucket itself
//
if(StartIndex == LastIndex && LastParentModule != (PMODULE)NULL &&
gCallbackCurrent == LastParentModule &&
LastCxt != (PPROC_TO_MONITOR)NULL && LastCxt == ProcToMonitor ){
for (ip=gCallbackCurrent->Base+StartIndex*gZoomBucket; ip<Address; ip+=1){
if( 0 < pfnIP2MD( (DWORD_PTR)ip, &gwszSymbol ) && (gwszSymbol != NULL) ){
if( !wcscmp( wszSymName, gwszSymbol ) ){
StartIndex += 1; // We have a match, don't count this bucket for this module again
if(StartIndex > EndIndex){
HasHits = FALSE;
goto LABEL; // No hits, jump to exit
}
}
}
}
}
//
// Look for hits in the current module
//
HasHits = FALSE;
for (Index=0; Index < gTotalActiveSources; Index++) {
ProfileSourceIndex = gulActiveSources[Index];
Module->Rate[ProfileSourceIndex] = pRate[ProfileSourceIndex];
Module->Rate[ProfileSourceIndex].StartTime = gCallbackCurrent->Rate[ProfileSourceIndex].StartTime;
Module->Rate[ProfileSourceIndex].TotalTime = gCallbackCurrent->Rate[ProfileSourceIndex].TotalTime;
Module->Rate[ProfileSourceIndex].TotalCount = calloc(gProfileProcessors, sizeof(ULONGLONG) );
if( Module->Rate[ProfileSourceIndex].TotalCount == NULL ){
FPRINTF(stderr, "KERNRATE: Memory allocation failed for TotalCount in CreateZoomModuleCallback\n");
exit(1);
}
Module->Rate[ProfileSourceIndex].DoubtfulCounts = 0;
for (CpuNumber=0; CpuNumber < gProfileProcessors; CpuNumber++) {
Module->Rate[ProfileSourceIndex].TotalCount[CpuNumber] = 0;
for (i=StartIndex; i <= EndIndex; i++) {
Module->Rate[ProfileSourceIndex].TotalCount[CpuNumber] +=
gCallbackCurrent->Rate[ProfileSourceIndex].ProfileBuffer[CpuNumber][i];
}
if (Module->Rate[ProfileSourceIndex].TotalCount[CpuNumber] > 0) {
HasHits = TRUE;
}
}
}
LABEL:
//
// If the routine has hits add it to the list, otherwise
// ignore it.
//
if (HasHits) {
//
//useful verbose print for identifying shared buckets and actual bucket inhabitants, see also raw data printout
//
if ( gVerbose & VERBOSE_PROFILING ) {
ULONGLONG BucketCount, TotCount, DoubtfulCounts;
WCHAR LastSymName[cMODULE_NAME_STRLEN];
BOOL bCounted = FALSE;
PIMAGEHLP_LINE64 pLine = malloc(sizeof(IMAGEHLP_LINE64));
if ( pLine == NULL ){
FPRINTF(stderr, "KERNRATE: Memory allocation failed for pLine in CreateZoomModuleCallback\n");
exit(1);
}
pLine->SizeOfStruct = sizeof(IMAGEHLP_LINE64);
_wcsset(&LastSymName[cMODULE_NAME_STRLEN-1], L'\0');
for (Index=0; Index < gTotalActiveSources; Index++) {
ProfileSourceIndex = gulActiveSources[Index];
TotCount = 0;
DoubtfulCounts = 0;
for (i=StartIndex; i <= EndIndex; i++) {
bCounted = FALSE;
BucketCount=0;
for (CpuNumber=0; CpuNumber < gProfileProcessors; CpuNumber++) {
BucketCount += gCallbackCurrent->Rate[ProfileSourceIndex].ProfileBuffer[CpuNumber][i];
}
TotCount += BucketCount;
if(BucketCount > 0){
ip = gCallbackCurrent->Base + (ULONG64)(i*gZoomBucket);
//
// Print the info for the current bucket
//
FPRINTF(stdout, "%s, 0x%I64x, 0x%I64x, %I64d, 0x%I64x, %Ld",
Module->module_Name,
gCallbackCurrent->Base,
Module->Base,
i,
ip,
BucketCount
);
for (CpuNumber=0; CpuNumber < gProfileProcessors; CpuNumber++) {
if( gAffinityMask && !((1<<CpuNumber) & gAffinityMask) )continue;
FPRINTF(stdout, ", %Ld",
gCallbackCurrent->Rate[ProfileSourceIndex].ProfileBuffer[CpuNumber][i]
);
}
//
// Find out if anyone else is sharing this bucket
//
for(; (ip<gCallbackCurrent->Base + (ULONG64)((i+1)*gZoomBucket)) && (ip<HighLimit); ip+=1) {
if ( (0 < pfnIP2MD( (DWORD_PTR)ip, &gwszSymbol )) && (gwszSymbol != NULL) ) {
if (0 != wcscmp( wszSymName, gwszSymbol ) ){
if(!bCounted){
FPRINTF(stdout, " , Actual Hits Should be Attributed to or Shared with ===> %S",
gwszSymbol
);
DoubtfulCounts += BucketCount;
wcsncpy(LastSymName, gwszSymbol, cMODULE_NAME_STRLEN-1);
_wcsset(&LastSymName[cMODULE_NAME_STRLEN-1], L'\0');
bCounted = TRUE;
} else if( 0 != wcscmp(LastSymName, gwszSymbol ) ){
FPRINTF(stdout, "\nActual Hits Should also be Attributed to or Shared with ===> %S",
gwszSymbol
);
wcsncpy(LastSymName, gwszSymbol, cMODULE_NAME_STRLEN-1);
_wcsset(&LastSymName[cMODULE_NAME_STRLEN-1], L'\0');
}
}
} else {
//
//This should be rare (no need to increase the doubtful counts)
//
FPRINTF(stdout, " , Actual Hits Should be Attributed to or Shared with ===> UNKNOWN_SYMBOL");
}
}
FPRINTF(stdout, "\n");
}
}
//
// Print the totals for the current module
//
FPRINTF(stdout, "%s, %s - Module Total Count = %I64d, Total Doubtful or Shared Counts = %I64d\n\n",
ProcToMonitor->Source[ProfileSourceIndex].Name,
Module->module_Name,
TotCount,
DoubtfulCounts
);
Module->Rate[ProfileSourceIndex].DoubtfulCounts = DoubtfulCounts;
if( pLine != NULL ){
free(pLine);
pLine = NULL;
}
}
} // if(VERBOSE_PROFILING)
Module->Next = ProcToMonitor->ZoomList;
ProcToMonitor->ZoomList = Module;
++gZoomCount;
} else {
//
// Cleanup
//
for (Index=0; Index < gTotalActiveSources; Index++) {
ProfileSourceIndex = gulActiveSources[Index];
if ( Module->Rate[ProfileSourceIndex].TotalCount != NULL){
free(Module->Rate[ProfileSourceIndex].TotalCount);
Module->Rate[ProfileSourceIndex].TotalCount = NULL;
}
}
if( pRate != NULL ){
free(pRate);
pRate = NULL;
}
if( Module != NULL ){
free(Module);
Module = NULL;
}
}
LastIndex = EndIndex;
LastParentModule = gCallbackCurrent;
LastCxt = ProcToMonitor;
return(TRUE);
} //CreateJITZoomModuleCallback;
/* BEGIN_IMS TkEnumerateSymbols
******************************************************************************
****
**** TkEnumerateSymbols ( )
****
******************************************************************************
*
* Function Description:
*
* Calls the specified function for every symbol in the Current module.
* The algorithm results in a round-up behavior for the output --
* for each bucket, the symbol corresponding to the first byte of the
* bucket is used.
*
* Arguments:
*
* IN HANDLE SymHandle : ImageHelp handle
*
* IN PMODULE Current : Pointer to current module structure
*
* IN PSYM_ENUMSYMBOLS_CALLBACK EnumSymbolsCallback : Routine to call for each symbol
*
* IN PVOID pProc : Pointer to Process
*
* Return Value:
*
* BOOL
*
* Algorithm:
*
* ToBeSpecified
*
* Globals Referenced:
*
* ToBeSpecified
*
* Exception Conditions:
*
* ToBeSpecified
*
* In/Out Conditions:
*
* ToBeSpecified
*
* Notes:
*
* ToBeSpecified
*
* ToDo List:
*
* ToBeSpecified
*
* Modification History:
*
* 9/5/97 TF Initial version
*
******************************************************************************
* END_IMS TkEnumerateSymbols */
BOOL
TkEnumerateSymbols(
IN HANDLE SymHandle,
IN PMODULE Current,
IN PSYM_ENUMSYMBOLS_CALLBACK64 EnumSymbolsCallback,
IN PVOID pProc
)
{
CHAR CurrentSym[cMODULE_NAME_STRLEN];
DWORD64 CurrentAddr = 0;
ULONG i;
DWORD64 Displacement;
CurrentSym[0] = '\0';
for (i=0; i<BUCKETS_NEEDED(Current->Length); i++) {
// Check if this bucket will be assigned to a different symbol...
if (SymGetSymFromAddr64(SymHandle, Current->Base+i*gZoomBucket, &Displacement, gSymbol )) {
// It will... Invoke the callback for the old one
if (CurrentSym[0] == '\0' ||
strncmp(gSymbol->Name, CurrentSym, strlen(CurrentSym))) {
if (CurrentAddr != 0) {
ULONG64 Size = (Current->Base+i*gZoomBucket) - CurrentAddr;
if ( Size == 0 ) {
FPRINTF( stderr, "XXTF Size==0 - %s = %s\n", gSymbol->Name, CurrentSym );
}
else {
if (!EnumSymbolsCallback(CurrentSym, CurrentAddr, (ULONG)Size, pProc)) {
FPRINTF(stderr, "KERNRATE: Failed CallBack in TkEnumerateSymbols Address= %x\n",
Current->Base+i*gZoomBucket
);
break;
}
}
}
// Save the new info
CurrentAddr = Current->Base+i*gZoomBucket;
strncpy(CurrentSym, gSymbol->Name, cMODULE_NAME_STRLEN-1);
CurrentSym[ cMODULE_NAME_STRLEN-1 ] = '\0';
}
}
else {
DWORD ErrorCode = GetLastError();
FPRINTF(stderr, "KERNRATE: Failed Call to SymGetSymFromAddress %x in TkEnumerateSymbols Error Code= %x\n",
Current->Base+i*gZoomBucket,
ErrorCode
);
}
}
// Cleanup for the last symbol
if (CurrentAddr != 0) {
ULONG64 Size = (Current->Base+i*gZoomBucket) - CurrentAddr;
if( (CurrentAddr + Size) < (Current->Base + Current->Length) )
(VOID) EnumSymbolsCallback(CurrentSym, CurrentAddr, (ULONG)Size, pProc);
}
return(TRUE);
} // TkEnumerateSymbols()
BOOL
JITEnumerateSymbols(
IN PMODULE Current,
IN PVOID pProc,
IN DWORD64 BaseOptional,
IN ULONG SizeOptional
)
/*++
Routine Description:
Enumerates the symbols found in a managed code module
Arguments:
Current - Pointer to the managed code module to be enumerated for symbols
pProc - Pointer to the structure of the process being monitored
BaseOptional - If not zero then the enumeration will be performed on part of the module,
starting from this address
SizeOptional - Required size if BaseOptional is non-zero
Return Value:
TRUE if symbols are found, FALSE in case no symbols are found
--*/
{
WCHAR CurrentSym[cMODULE_NAME_STRLEN];
CHAR SymName[cMODULE_NAME_STRLEN];
ANSI_STRING AnsiString;
UNICODE_STRING UnicodeString;
DWORD64 CurrentAddr = 0;
DWORD64 TopAddress;
DWORD64 Base;
ULONG Length;
BOOL bFoundSym = FALSE;
ULONG Size = 0;
ULONG InitialStep = (gZoomBucket < JIT_MAX_INITIAL_STEP)? gZoomBucket : JIT_MAX_INITIAL_STEP;
ULONG step = InitialStep;
ULONG i, j, k;
WCHAR *Symbol = (WCHAR *)malloc ( cMODULE_NAME_STRLEN * sizeof(WCHAR) );
if (Symbol == NULL){
FPRINTF(stderr, "KERNRATE: Allocation for Symbol in JITEnumerateSymbols failed\n");
exit(1);
}
CurrentSym[0] = '\0';
Base = (BaseOptional == 0)? Current->Base : BaseOptional;
CurrentAddr = Base;
Length = (SizeOptional == 0)? Current->Length : SizeOptional;
TopAddress = (SizeOptional == 0)? Current->Base + Current->Length : BaseOptional + SizeOptional;
if (CurrentAddr == 0) {
FPRINTF(stderr, "KERNRATE: Zero base address passed to JITEnumerateSymbols for module %s\n",
Current->module_Name
);
free(Symbol);
Symbol = NULL;
return (FALSE);
}
if (Current->Length == 0) {
FPRINTF(stderr, "KERNRATE: Zero module length passed to JITEnumerateSymbols for module %s\n",
Current->module_Name
);
free(Symbol);
Symbol = NULL;
return (FALSE);
}
//
// find first symbol
//
for (i=0; i < Length; i++){
j = i;
if ( (0 < pfnIP2MD( (DWORD_PTR)CurrentAddr, &Symbol )) && (Symbol != NULL) ) {
wcsncpy(CurrentSym, Symbol, cMODULE_NAME_STRLEN-1);
_wcsset(&CurrentSym[cMODULE_NAME_STRLEN-1], L'\0');
bFoundSym = TRUE;
break;
}
++CurrentAddr;
}
if( !bFoundSym ){
free(Symbol);
Symbol = NULL;
return (FALSE);
}
step = (InitialStep < Length)? InitialStep : 1;
Size = 1;
if( gVerbose & VERBOSE_INTERNALS )
FPRINTF(stdout, "\nJITEnumSymbols Verbose Detail: Symbol, Address Range, Size\n");
for (i=j+step; i < Length; ){
k = i;
if ( (0 == pfnIP2MD( (DWORD_PTR)(Base + i), &Symbol )) || (Symbol == NULL) ) {
wcsncpy(Symbol, L"NO_SYMBOL", cMODULE_NAME_STRLEN-1);
_wcsset(&Symbol[cMODULE_NAME_STRLEN-1], L'\0');
}
if( 0 == wcscmp( CurrentSym, Symbol) ) { //Same symbol, increase size and continue stepping
Size += step;
i = k + step;
if(i < Length)
continue;
} else { // not same symbol, decrease step and go back to find boudary
step >>=1;
if (step > 0){
i = k - step; // k was incremented by previous step so it's larger than present step
continue;
}
}
if( 0 != wcscmp(Symbol, L"NO_SYMBOL") && 0 != wcscmp(Symbol, L"\0") ){
RtlInitUnicodeString( &UnicodeString, CurrentSym );
AnsiString.Buffer = SymName;
AnsiString.MaximumLength = cMODULE_NAME_STRLEN*sizeof(CHAR);
AnsiString.Length = 0;
RtlUnicodeStringToAnsiString(&AnsiString, &UnicodeString, cDONOT_ALLOCATE_DESTINATION_STRING);
SymName[AnsiString.Length] = '\0';
if ( !CreateJITZoomModuleCallback(CurrentSym, SymName, CurrentAddr, Size, pProc) ) {
FPRINTF(stderr, "KERNRATE: Failed CallBack in JITEnumerateSymbols Address= %I64x for module %s\n",
CurrentAddr,
Current->module_Name
);
free(Symbol);
Symbol = NULL;
return (FALSE);
}
}
if( gVerbose & VERBOSE_INTERNALS )
FPRINTF(stdout, "%S, 0x%p - 0x%p, 0x%lx\n", CurrentSym, (PVOID)CurrentAddr, (PVOID)(CurrentAddr+Size), Size);
wcsncpy(CurrentSym, Symbol, cMODULE_NAME_STRLEN-1); //Put the next symbol into current symbol
_wcsset(&CurrentSym[cMODULE_NAME_STRLEN-1], L'\0');
CurrentAddr += Size; //Advance to next method base
Size = 1; //Reset initial size and step for the next symbol
if ( InitialStep < (TopAddress - CurrentAddr) ){
step = InitialStep;
} else {
step = 1;
}
i = k + step;
}//for i
if (Symbol != NULL) {
free(Symbol);
Symbol = NULL;
}
return TRUE;
} //JITEnumerateSymbols()
BOOL
EnumerateSymbolsByBuckets(
IN HANDLE SymHandle,
IN PMODULE Current,
IN PSYM_ENUMSYMBOLS_CALLBACK64 EnumSymbolsCallback,
IN PVOID pProc
)
/*++
Routine Description:
Finds buckets with hits in the current module and enumerates the symbols found in these buckets
Arguments:
SymHandle - ImageHelp handle to current process
Current - Pointer to the parent module to be enumerated for symbols
EnumSymbolsCallback - Pointer to a user supplied callback function
pProc - Pointer to the structure of the process being monitored
Return Value:
TRUE if any symbols are found, FALSE in case no symbols are found at all
--*/
{
DWORD64 Base;
ULONG Size;
ULONG i;
BOOL bRet = FALSE; //One time toggle switch (once set to TRUE it remains TRUE)
Base = Current->Base;
Size = 0;
for (i=0; i< BUCKETS_NEEDED(Current->Length); i++){
if ( HitsFound(pProc, i) ){
Size += gZoomBucket; //just increase the size of this segment by a bucket
continue;
} else if ( Size > 0 ){ //Reached end of segment where hits were found, enumerate the symbols within
if(SymHandle != NULL){
if ( TRUE == PrivEnumerateSymbols( SymHandle, Current, EnumSymbolsCallback, pProc, Base, Size ) )
bRet = TRUE;
} else {
if ( TRUE == JITEnumerateSymbols( Current, pProc, Base, Size ) )
bRet = TRUE;
}
Base += Size; //Done enumerating, shift the base to the end of the segment and reset the size
Size = 0;
}
Base += gZoomBucket; //No hits found, so further shift base of segment by one bucket and continue
}
return (bRet);
}
BOOL
PrivEnumerateSymbols(
IN HANDLE SymHandle,
IN PMODULE Current,
IN PSYM_ENUMSYMBOLS_CALLBACK64 EnumSymbolsCallback,
IN PVOID pProc,
IN DWORD64 BaseOptional,
IN ULONG SizeOptional
)
/*++
Routine Description:
Enumerates the symbols found in a module
Arguments:
SymHandle - ImageHelp handle to current process
Current - Pointer to the module to be enumerated for symbols
EnumSymbolsCallback - Pointer to a user supplied callback function
pProc - Pointer to the structure of the process being monitored
BaseOptional - If not zero then the enumeration will be performed on part of the module,
starting from this address
SizeOptional - Required size if BaseOptional is non-zero
Return Value:
TRUE if symbols are found, FALSE in case no symbols are found
--*/
{
CHAR CurrentSym[cMODULE_NAME_STRLEN];
CHAR SymName[cMODULE_NAME_STRLEN];
DWORD64 CurrentAddr = 0;
DWORD64 TopAddress;
DWORD64 Displacement;
DWORD64 Base;
ULONG Length;
BOOL bFoundSym = FALSE;
ULONG Size = 0;
ULONG step = INITIAL_STEP;
ULONG i, j, k;
PIMAGEHLP_SYMBOL64 Symbol = (PIMAGEHLP_SYMBOL64) malloc( sizeof(IMAGEHLP_SYMBOL64) + MAX_SYMNAME_SIZE );
if (Symbol == NULL){
FPRINTF(stderr, "KERNRATE: Allocation for Symbol in PrivEnumerateSymbols failed\n");
exit(1);
}
Symbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
Symbol->MaxNameLength = MAX_SYMNAME_SIZE;
CurrentSym[0] = '\0';
Base = (BaseOptional == 0)? Current->Base : BaseOptional;
CurrentAddr = Base;
Length = (SizeOptional == 0)? Current->Length : SizeOptional;
TopAddress = (SizeOptional == 0)? Current->Base + Current->Length : BaseOptional + SizeOptional;
if (CurrentAddr == 0) {
FPRINTF(stderr, "KERNRATE: Zero base address passed to PrivEnumerateSymbols for module %s\n",
Current->module_Name
);
free(Symbol);
Symbol = NULL;
return (FALSE);
}
if (Length == 0) {
FPRINTF(stderr, "KERNRATE: Zero module length passed to PrivEnumerateSymbols for module %s\n",
Current->module_Name
);
free(Symbol);
Symbol = NULL;
return (FALSE);
}
//
// find first symbol
//
for (i=0; i < Length; i++){
j = i;
if ( (SymGetSymFromAddr64( SymHandle, CurrentAddr, &Displacement, Symbol ))) {
if( 0 == strcmp(Symbol->Name, "\0") )
continue;
strncpy(CurrentSym, Symbol->Name, cMODULE_NAME_STRLEN-1);
CurrentSym[cMODULE_NAME_STRLEN-1] = '\0';
bFoundSym = TRUE;
break;
}
++CurrentAddr;
}
if( !bFoundSym ){
free(Symbol);
Symbol = NULL;
return (FALSE);
}
step = (INITIAL_STEP < Length)?INITIAL_STEP : 1;
Size = 1;
if( gVerbose & VERBOSE_INTERNALS )
FPRINTF(stdout, "\nPrivEnumSymbols Verbose Detail: Symbol, Address Range, Size\n");
for (i=j+step; i < Length; ){
k = i;
if ( (!SymGetSymFromAddr64( SymHandle, Base + i, &Displacement, Symbol ))) {
strncpy(Symbol->Name, "NO_SYMBOL", cMODULE_NAME_STRLEN-1);
Symbol->Name[cMODULE_NAME_STRLEN-1] = '\0';
}
if( !strcmp(CurrentSym, Symbol->Name) ) { //Same symbol, increase size and continue stepping
Size += step;
i = k + step;
if(i < Length)
continue;
} else { // not same symbol, decrease step and go back to find boudary
step >>=1;
if (step > 0){
i = k - step; // k was incremented by previous step so it's larger than present step
continue;
}
}
if( 0 != strcmp(Symbol->Name, "NO_SYMBOL") && 0 != strcmp(Symbol->Name, "\0") ){
if ( !CreateZoomModuleCallback(CurrentSym, CurrentAddr, Size, pProc) ) {
FPRINTF(stderr, "KERNRATE: Failed CallBack in PrivEnumerateSymbols Address= %I64x for module %s\n",
CurrentAddr,
Current->module_Name
);
free(Symbol);
Symbol = NULL;
return (FALSE);
}
}
if( gVerbose & VERBOSE_INTERNALS )
FPRINTF(stdout, "%s, 0x%p - 0x%p, 0x%lx\n", CurrentSym, (PVOID)CurrentAddr, (PVOID)(CurrentAddr+Size), Size);
strncpy(CurrentSym, Symbol->Name, cMODULE_NAME_STRLEN-1); //Put the next symbol into current symbol
CurrentSym[cMODULE_NAME_STRLEN-1] = '\0';
CurrentAddr += Size; //Advance to next method base
Size = 1; //Reset initial size and step for the next symbol
if ( INITIAL_STEP < (TopAddress - CurrentAddr) ){
step = INITIAL_STEP;
} else {
step = 1;
}
i = k + step;
}//for i
if (Symbol != NULL) {
free(Symbol);
Symbol = NULL;
}
return TRUE;
} //PrivEnumerateSymbols()
BOOL
HitsFound(
IN PPROC_TO_MONITOR pProc,
IN ULONG BucketIndex
)
/*++
Routine Description:
Determines if the current bucket scored any hits at all
Arguments:
pProc - Pointer to the structure of the process being monitored
BucketIndex - Index of the bucket matching the current address.
Return Value:
TRUE if hits are found, FALSE in case no hits are found
--*/
{
ULONG Index, CpuNumber;
for (Index=0; Index < gTotalActiveSources; Index++) {
for (CpuNumber=0; CpuNumber < (ULONG)gProfileProcessors; CpuNumber++) {
if ( 0 < gCallbackCurrent->Rate[gulActiveSources[Index]].ProfileBuffer[CpuNumber][BucketIndex] ){
return (TRUE);
}
}
}
return (FALSE);
}
VOID
CreateZoomedModuleList(
IN PMODULE ZoomModule,
IN ULONG RoundDown,
IN PPROC_TO_MONITOR pProc
)
/*++
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
RoundDown - Used for selecting the method of symbol enumeration
ProcToMonitor - Pointer to the process to be monitored
Return Value:
Pointer to the zoomed module list
NULL on error.
--*/
{
BOOL Success = FALSE;
HANDLE SymHandle = pProc->ProcessHandle;
gCallbackCurrent = ZoomModule;
//MC
if (( bMCHelperLoaded == TRUE ) && (!_stricmp(ZoomModule->module_FullName, "JIT_TYPE"))){
pfnAttachToProcess((DWORD)pProc->Pid);
Success = EnumerateSymbolsByBuckets( NULL,
ZoomModule,
NULL,
pProc
);
pfnDetachFromProcess();
//MC
} else {
if (RoundDown == 0) {
Success = EnumerateSymbolsByBuckets( SymHandle,
ZoomModule,
CreateZoomModuleCallback,
pProc
);
//MC
//
// If we failed the imagehlp call we have to check if this is a pre-compiled JIT module (ngen)
// We cannot count on the imagehlp return value because it will return success even if no symbols are found
//
if ( (bImageHlpSymbolFound == FALSE) && ( bMCHelperLoaded == TRUE ) ){
pfnAttachToProcess((DWORD)pProc->Pid);
Success = EnumerateSymbolsByBuckets( NULL,
ZoomModule,
NULL,
pProc
);
pfnDetachFromProcess();
}
//MC
} else {
Success = TkEnumerateSymbols( SymHandle,
ZoomModule,
CreateZoomModuleCallback,
pProc
);
}
}
if (!Success) {
DWORD ErrorCode = GetLastError();
FPRINTF(stderr,
"Symbol Enumeration failed (or no symbols found) on module %s in CreateZoomedModuleList, Error Code= %x\n",
ZoomModule->module_Name,
ErrorCode
);
}
//MC
bImageHlpSymbolFound = FALSE; //Reset for next module
//MC
return;
} // CreateZoomedModuleList()
VOID
OutputModuleList(
IN FILE *Out,
IN PMODULE ModuleList,
IN ULONG NumberModules,
IN PPROC_TO_MONITOR ProcToMonitor,
IN PMODULE Parent
)
/*++
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
ProcToMonitor - Pointer to the process to be monitored
Return Value:
None.
--*/
{
PRATE_DATA RateData;
PRATE_DATA SummaryData;
PRATE_SUMMARY RateSummary;
BOOL Header;
ULONG i, j, ProfileSourceIndex, Index;
PMODULE *ModuleArray;
PMODULE Current, tmpModule;
LONG CpuNumber;
ULONGLONG TempTotalCount, TempDoubtfulCount;
//// Beginning of Function Assertions Section:
//
//
//
// It is not really a bug but we are printing only the first 132 characters of the module name.
// This assertion will remind us this.
//
assert( sizeof(Current->module_Name) >= cMODULE_NAME_STRLEN );
//
//
//// End of Function Assertions Section
RateSummary = calloc(gSourceMaximum, sizeof (RATE_SUMMARY));
if (RateSummary == NULL) {
FPRINTF(stderr, "KERNRATE: Buffer allocation failed(1) while doing output of Module list\n");
exit(1);
}
SummaryData = calloc(gSourceMaximum, (RATE_DATA_FIXED_SIZE + RATE_DATA_VAR_SIZE));
if (SummaryData == NULL) {
FPRINTF(stderr, "KERNRATE: Buffer allocation failed(2) while doing output of Module list\n");
free(RateSummary);
RateSummary = NULL;
exit(1);
}
for (Index=0; Index < gTotalActiveSources; Index++) {
ProfileSourceIndex = gulActiveSources[Index];
SummaryData[ProfileSourceIndex].Rate = 0;
//
// Walk through the module list and compute the summary
// and collect the interesting per-module data.
//
RateSummary[ProfileSourceIndex].TotalCount = 0;
RateSummary[ProfileSourceIndex].Modules = malloc(NumberModules*sizeof(PMODULE));
if (RateSummary[ProfileSourceIndex].Modules == NULL) {
FPRINTF(stderr, "KERNRATE: Buffer allocation failed(3) while doing output of Module list\n");
exit(1);
}
RateSummary[ProfileSourceIndex].ModuleCount = 0;
ModuleArray = RateSummary[ProfileSourceIndex].Modules;
Current = ModuleList;
while (Current != NULL) {
RateData = &Current->Rate[ProfileSourceIndex];
TempTotalCount = 0;
TempDoubtfulCount = 0;
for (CpuNumber=0; CpuNumber < gProfileProcessors; CpuNumber++) {
TempTotalCount += RateData->TotalCount[CpuNumber];
}
TempDoubtfulCount = RateData->DoubtfulCounts;
RateData->GrandTotalCount = TempTotalCount;
if (TempTotalCount > 0) {
RateSummary[ProfileSourceIndex].TotalCount += TempTotalCount;
//
// Find if we already have a module by that name (optimizations may have split the module to several pieces)
// This will slow down processing, so we'll turn it off if the user decided to use the '-e' option to haste
// the output (in cases that monitored processes might go away frequently for example, we want to finish symbol
// processing before the process goes away, so any extra processing delay should be removed).
//
if(bIncludeGeneralInfo == TRUE) {
for ( i=0; i < RateSummary[ProfileSourceIndex].ModuleCount; i++){
if ( !strcmp(Current->module_Name, ModuleArray[i]->module_Name) ){ //Found a match
if (gVerbose & VERBOSE_INTERNALS)
FPRINTF(stdout, "===> Found module %s more than once, merged hit counts and re-sorted\n",
Current->module_Name
);
ModuleArray[i]->Rate[ProfileSourceIndex].GrandTotalCount += TempTotalCount; // Update the original module
for (CpuNumber=0; CpuNumber < gProfileProcessors; CpuNumber++) {
ModuleArray[i]->Rate[ProfileSourceIndex].TotalCount[CpuNumber] += RateData->TotalCount[CpuNumber];
}
ModuleArray[i]->Rate[ProfileSourceIndex].DoubtfulCounts += TempDoubtfulCount; // Update the shared counts total
//
// Re-Sort since number of hits changed
//
for (j=0; j<RateSummary[ProfileSourceIndex].ModuleCount; j++) {
if ( i > j && ModuleArray[i]->Rate[ProfileSourceIndex].GrandTotalCount > ModuleArray[j]->Rate[ProfileSourceIndex].GrandTotalCount) {
//
// insert here
//
tmpModule = ModuleArray[i]; //preserve the ptr to current module
MoveMemory(&ModuleArray[j+1], //shift the ptr array by one index to free the array element at j
&ModuleArray[j],
sizeof(PMODULE)*(i-j)
);
ModuleArray[j] = tmpModule; //set the free array element to the preserved ptr
break;
}
}
goto NEXT_1; //Go to the next module on the list (skipping current as a new module)
}
}
}
//
// No match found, so insert the new module in a 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,
ProcToMonitor->Source[ProfileSourceIndex].Name
);
DbgBreakPoint();
}
for (i=0; i<RateSummary[ProfileSourceIndex].ModuleCount; i++) {
if (TempTotalCount > ModuleArray[i]->Rate[ProfileSourceIndex].GrandTotalCount) {
//
// insert here
//
MoveMemory(&ModuleArray[i+1],
&ModuleArray[i],
sizeof(PMODULE)*(RateSummary[ProfileSourceIndex].ModuleCount-i-1)
);
ModuleArray[i] = Current;
break;
}
}
}
NEXT_1: Current = Current->Next;
}
if (RateSummary[ProfileSourceIndex].TotalCount > (ULONGLONG)0 ) {
//
// Output the result
//
PSOURCE s;
s = &ProcToMonitor->Source[ProfileSourceIndex];
if(Parent == NULL){
FPRINTF(Out, "\n%s %I64u hits, %ld events per hit --------\n",
s->Name,
RateSummary[ProfileSourceIndex].TotalCount,
s->Interval
);
} else {
FPRINTF(Out, "\n%s %I64u hits, %ld events per hit --------",
s->Name,
Parent->Rate[ProfileSourceIndex].GrandTotalCount,
s->Interval
);
if( gVerbose & VERBOSE_PROFILING ) {
FPRINTF(Out, " (%I64u total hits from summing-up the module components)\n",
RateSummary[ProfileSourceIndex].TotalCount
);
} else {
FPRINTF(Out, "\n");
}
}
if ( gVerbose & VERBOSE_PROFILING ) {
FPRINTF(Out," Module Hits Shared msec %%Total %%Certain Events/Sec\n");
} else {
FPRINTF(Out," Module Hits msec %%Total Events/Sec\n");
}
for (i=0; i < RateSummary[ProfileSourceIndex].ModuleCount; i++) {
Current = ModuleArray[i];
if ( ModuleArray[i]->Rate[ProfileSourceIndex].GrandTotalCount >= (ULONGLONG)gMinHitsToDisplay ) {
FPRINTF(Out, "%-32s", Current->module_Name); // Note only the first 132 characters are printed.
OutputLine(Out,
ProfileSourceIndex,
Current,
&RateSummary[ProfileSourceIndex],
ProcToMonitor
);
}
SummaryData[ProfileSourceIndex].Rate += Current->Rate[ProfileSourceIndex].Rate;
SummaryData[ProfileSourceIndex].GrandTotalCount += Current->Rate[ProfileSourceIndex].GrandTotalCount;
}
} else {
FPRINTF(Out, "\n%s - No Hits Recorded\n", ProcToMonitor->Source[ProfileSourceIndex].Name );
}
FPRINTF(Out, "\n");
}
//
// Output interesting data for the summary.
//
if( bGetInterestingData == TRUE ) {
FPRINTF(stdout,
"\n-------------- INTERESTING SUMMARY DATA ----------------------\n"
);
OutputInterestingData(Out, SummaryData);
}
//
// Output the results ordered by module
//
Current = ModuleList;
while (Current != NULL) {
Header = FALSE;
if ( gVerbose & VERBOSE_MODULES ) { //The printout below duplicates data already printed, let's limit the flood
for (Index=0; Index < gTotalActiveSources; Index++) {
ProfileSourceIndex = gulActiveSources[Index];
if ( Current->Rate[ProfileSourceIndex].GrandTotalCount > 0 ) {
if (!Header) {
FPRINTF(Out,"\nMODULE %s --------\n",Current->module_Name);
if ( gVerbose & VERBOSE_PROFILING ) {
FPRINTF(Out," %-*s Hits Shared msec %%Total %%Certain Events/Sec\n", gDescriptionMaxLen, "Source");
} else {
FPRINTF(Out," %-*s Hits msec %%Total Events/Sec\n", gDescriptionMaxLen, "Source");
}
Header = TRUE;
}
FPRINTF(Out, "%-*s", gDescriptionMaxLen, ProcToMonitor->Source[ProfileSourceIndex].Name);
OutputLine(Out,
ProfileSourceIndex,
Current,
&RateSummary[ProfileSourceIndex],
ProcToMonitor);
}
}
}
//
// Output interesting data for the module.
//
if( bGetInterestingData == TRUE ) {
FPRINTF(stdout,
"\n-------------- INTERESTING MODULE DATA FOR %s---------------------- \n",
Current->module_Name
);
OutputInterestingData(Out, &Current->Rate[0]);
}
Current = Current->Next;
}
return;
} // OutputModuleList()
VOID
OutputLine(
IN FILE *Out,
IN ULONG ProfileSourceIndex,
IN PMODULE Module,
IN PRATE_SUMMARY RateSummary,
IN PPROC_TO_MONITOR ProcToMonitor
)
/*++
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
ProcToMonitor - Pointer to the process to be monitored
Return Value:
None.
--*/
{
ULONG Msec;
ULONGLONG Events;
ULONGLONG TempTotalCount;
PRATE_DATA RateData;
LONG CpuNumber = 0;
RateData = &Module->Rate[ProfileSourceIndex];
TempTotalCount = RateData->GrandTotalCount;
//
//The time is in 100ns units = 0.1us = 1/10,000ms
//The events are fired every 100ns=0.1us=1/10,000ms (or 10,000,000 events per second)
//
Msec = (ULONG)(RateData->TotalTime/10000);
Events = TempTotalCount * ProcToMonitor->Source[ProfileSourceIndex].Interval * 1000; //To get Events/sec below
if ( gVerbose & VERBOSE_PROFILING ) {
FPRINTF(Out,
" %10I64u %10I64u %10ld %2d %% %2d %% ",
TempTotalCount,
RateData->DoubtfulCounts,
Msec,
(ULONG)(100*TempTotalCount/
RateSummary->TotalCount),
(ULONG)(100*(TempTotalCount - RateData->DoubtfulCounts)/
RateSummary->TotalCount)
);
} else {
FPRINTF(Out,
" %10I64u %10ld %2d %% ",
TempTotalCount,
Msec,
(ULONG)(100*TempTotalCount/
RateSummary->TotalCount)
);
}
if (Msec > 0) {
RateData->Rate = Events/Msec; //The final result is in Events/sec
FPRINTF(Out, "%10I64u\n", RateData->Rate);
} else {
RateData->Rate = 0;
FPRINTF(Out,"---\n");
}
if (bProfileByProcessor) {
for (CpuNumber=0; CpuNumber < gProfileProcessors; CpuNumber++) {
if( gAffinityMask && !((1<<CpuNumber) & gAffinityMask) )continue;
TempTotalCount = RateData->TotalCount[CpuNumber];
Events = TempTotalCount * ProcToMonitor->Source[ProfileSourceIndex].Interval * 1000;
FPRINTF(Out,
"%6d %7I64u %6ld %2d %% ",
CpuNumber,
TempTotalCount,
Msec,
(ULONG)(100*TempTotalCount/RateSummary->TotalCount));
if (Msec > 0) {
FPRINTF(Out,"%10I64d\n", Events/Msec); //The final result is in Events/sec
} else {
FPRINTF(Out,"---\n");
}
}
}
} //OutputLine()
VOID
OutputInterestingData(
IN FILE *Out,
IN RATE_DATA Data[]
)
/*++
Routine Description:
Computes interesting Processor Statistics 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;
LONGLONG Temp3;
float Ratio;
BOOL DataFound = 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].GrandTotalCount > 10)) {
if (Data[ProfileTotalCycles].Rate > 0) {
Ratio = (float)(LONGLONG)(Data[ProfileTotalCycles].Rate)/
(float)(LONGLONG)(Data[ProfileTotalIssues].Rate);
DataFound = TRUE;
FPRINTF(Out, "Cycles per instruction\t\t%6.2f\n", Ratio);
}
if (Data[ProfileLoadInstructions].Rate > 0) {
Ratio = (float)(LONGLONG)(Data[ProfileLoadInstructions].Rate)/
(float)(LONGLONG)(Data[ProfileTotalIssues].Rate);
if (Ratio >= 0.01 && Ratio <= 1.0) {
DataFound = TRUE;
FPRINTF(Out, "Load instruction percentage\t%6.2f %%\n",Ratio*100);
}
}
if (Data[ProfileStoreInstructions].Rate > 0) {
Ratio = (float)(LONGLONG)(Data[ProfileStoreInstructions].Rate)/
(float)(LONGLONG)(Data[ProfileTotalIssues].Rate);
if (Ratio >= 0.01 && Ratio <= 1.0) {
DataFound = TRUE;
FPRINTF(Out, "Store instruction percentage\t%6.2f %%\n",Ratio*100);
}
}
if (Data[ProfileBranchInstructions].Rate > 0) {
Ratio = (float)(LONGLONG)(Data[ProfileBranchInstructions].Rate)/
(float)(LONGLONG)(Data[ProfileTotalIssues].Rate);
if (Ratio >= 0.01 && Ratio <= 1.0) {
DataFound = TRUE;
FPRINTF(Out, "Branch instruction percentage\t%6.2f %%\n",Ratio*100);
}
}
if (Data[ProfileFpInstructions].Rate > 0) {
Ratio = (float)(LONGLONG)(Data[ProfileFpInstructions].Rate)/
(float)(LONGLONG)(Data[ProfileTotalIssues].Rate);
if (Ratio >= 0.01 && Ratio <= 1.0) {
DataFound = TRUE;
FPRINTF(Out, "FP instruction percentage\t%6.2f %%\n",Ratio*100);
}
}
if (Data[ProfileIntegerInstructions].Rate > 0) {
Ratio = (float)(LONGLONG)(Data[ProfileIntegerInstructions].Rate)/
(float)(LONGLONG)(Data[ProfileTotalIssues].Rate);
if (Ratio >= 0.01 && Ratio <= 1.0) {
DataFound = TRUE;
FPRINTF(Out, "Integer instruction percentage\t%6.2f %%\n",Ratio*100);
}
}
//
// Compute icache hit rate
//
if (Data[ProfileIcacheMisses].Rate > 0) {
Temp3 = (LONGLONG)(Data[ProfileTotalIssues].Rate - Data[ProfileIcacheMisses].Rate);
if(Temp3 > 0){
Ratio = (float)Temp3/
(float)(LONGLONG)(Data[ProfileTotalIssues].Rate);
if( Ratio <= 1.0 ) {
DataFound = TRUE;
FPRINTF(Out, "Icache hit rate\t\t\t%6.2f %%\n", Ratio*100);
}
}
}
}
//
// Compute dcache hit rate
//
if( Data[ProfileLoadInstructions].Rate > 0 && Data[ProfileStoreInstructions].Rate > 0 ){
Temp1 = Data[ProfileLoadInstructions].Rate + Data[ProfileStoreInstructions].Rate;
if ((Data[ProfileDcacheMisses].Rate > 0) &&
(Temp1 != 0) &&
(Data[ProfileDcacheMisses].GrandTotalCount > 10)) {
Temp2 = Temp1 - Data[ProfileDcacheMisses].Rate;
Temp3 = (LONGLONG) Temp2;
Ratio = (float)Temp3/(float)(LONGLONG)Temp1;
if( Temp3 > 0 && Ratio <= 1.0 ) {
DataFound = 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].GrandTotalCount > 10)) {
Temp3 = (LONGLONG)(Data[ProfileBranchInstructions].Rate-Data[ProfileBranchMispredictions].Rate);
if(Temp3 > 0){
Ratio = (float)Temp3 /
(float)(LONGLONG)(Data[ProfileBranchInstructions].Rate);
if( Ratio <= 1.0 ) {
DataFound = TRUE;
FPRINTF(Out, "Branch predict hit percentage\t%6.2f %%\n", Ratio*100);
}
}
}
if ( !DataFound )
FPRINTF(Out, "===> No interesting data found or hit counts too low\n");
} // OutputInterestingData()
/* BEGIN_IMS CreateNewModule
******************************************************************************
****
**** CreateNewModule ( )
****
******************************************************************************
*
* Function Description:
*
* This function allocates and initializes a module entry.
*
* Arguments:
*
* IN HANDLE ProcessHandle :
*
* IN PCHAR ModuleName :
*
* IN PCHAR ModuleFullName :
*
* IN ULONG ImageBase :
*
* IN ULONG ImageSize :
*
* Return Value:
*
* PMODULE
*
* Algorithm:
*
* ToBeSpecified
*
* Globals Referenced:
*
* ToBeSpecified
*
* Exception Conditions:
*
* ToBeSpecified
*
* In/Out Conditions:
*
* ToBeSpecified
*
* Notes:
*
* ToBeSpecified
*
* ToDo List:
*
* ToBeSpecified
*
* Modification History:
*
* 9/8/97 TF Initial version
*
******************************************************************************
* END_IMS CreateNewModule */
PMODULE
CreateNewModule(
IN PPROC_TO_MONITOR ProcToMonitor,
IN PCHAR ModuleName,
IN PCHAR ModuleFullName,
IN ULONG64 ImageBase,
IN ULONG ImageSize
)
{
PMODULE NewModule;
PMODULE ZoomModule;
HANDLE ProcessHandle = ProcToMonitor->ProcessHandle;
PCHAR lastptr = NULL, dotptr = NULL;
NewModule = calloc(1, MODULE_SIZE);
if (NewModule == NULL) {
FPRINTF(stderr, "Memory allocation of NewModule for %s failed\n", ModuleName);
exit(1);
}
NewModule->bZoom = FALSE;
SetModuleName( NewModule, ModuleName );
//
// Following WinDbg's rule: module names are filenames without their extension.
// However, currently long file names may include more than one period
// We'll try to strip only the last extension and keep the rest
//
dotptr = strchr(NewModule->module_Name, '.');
while (dotptr != NULL){
lastptr = dotptr;
dotptr = strchr(dotptr+1, '.');
}
if(lastptr != NULL)
*lastptr = '\0';
//
// See if this module is on the zoom list.
//
ZoomModule = ProcToMonitor->ZoomList;
while ( ZoomModule != NULL ) {
//
// By default the user needs to specify only the module name (no extension) for a zoom module
// The 2nd part of the following check allows the user to specify a full file name for a zoom module
// This allows kernrate to make a distinction in cases that a .exe and a .dll for example carry
// the same module name
//
if ( _stricmp(ZoomModule->module_Name, NewModule->module_Name) == 0 ||
(NULL != ModuleName && 0 == _stricmp( ModuleName, ZoomModule->module_Name )) ) {
//
// found a match
//
NewModule->hProcess = ProcessHandle;
NewModule->Base = ImageBase;
NewModule->Length = ImageSize;
NewModule->bZoom = TRUE;
NewModule->module_FileName = _strdup( ModuleName ); // File name including extension
if ( ModuleFullName ) {
NewModule->module_FullName = _strdup( ModuleFullName ); // Including the fully qualified path
}
gCurrentModule = NewModule;
//
// Load symbols
//
// Note 15/09/97 TF: do not be confused here...
// In this routine, the ModuleName variable is a filename with its
// extension: File.exe or File.dll
//
// Note 30/09/97 TF: The current kernrate version does not change
// the default IMAGEHLP behaviour in terms of symbol file loading:
// It is synchronous ( and not deferred ) with the SymLoadModule
// call. Our registered callback will be called with the standard
// symbol file operations.
// If the kernrate behaviour changes, we will have to revisit this
// assumption.
//
//MC
if(0 != _stricmp(ModuleFullName, "JIT_TYPE")){
//MC
(void)SymLoadModule64( ProcessHandle, // hProcess
NULL, // hFile [for Debugger]
ModuleName, // ImageName
NULL, // ModuleName
ImageBase, // BaseOfDll
ImageSize // SizeOfDll
);
//MC
}
//MC
gCurrentModule = (PMODULE)0;
break;
}
ZoomModule = ZoomModule->Next;
} //while
if(NewModule->bZoom == FALSE){
NewModule->hProcess = ProcessHandle;
NewModule->Base = ImageBase; // Note TF: I know for zoomed it is a redone...
NewModule->Length = ImageSize; // Note TF: I know for zoomed it is a redone...
assert( ModuleName );
if ( NewModule->module_FileName == (PCHAR)0 ) {
NewModule->module_FileName = _strdup( ModuleName );
}
if ( ModuleFullName && NewModule->module_FullName == (PCHAR)0 ) {
NewModule->module_FullName = _strdup( ModuleFullName );
}
}
#define VerboseModuleFormat "0x%p 0x%p "
VerbosePrint(( VERBOSE_MODULES, VerboseModuleFormat " %s [%s]\n",
(PVOID)NewModule->Base,
(PVOID)(NewModule->Base + (ULONG64)NewModule->Length),
NewModule->module_Name,
ModuleFullName
));
#undef VerboseModuleFormat
return(NewModule);
} // CreateNewModule()
BOOL
InitializeAsDebugger(VOID)
{
HANDLE Token;
PTOKEN_PRIVILEGES NewPrivileges;
LUID LuidPrivilege;
BOOL bRet = FALSE;
//
// Make sure we have access to adjust and to get the old token privileges
//
if (!OpenProcessToken( GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
&Token)) {
return( FALSE );
}
//
// Initialize the privilege adjustment structure
//
LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &LuidPrivilege );
NewPrivileges = (PTOKEN_PRIVILEGES)calloc(1,sizeof(TOKEN_PRIVILEGES) +
(1 - ANYSIZE_ARRAY) * sizeof(LUID_AND_ATTRIBUTES));
if (NewPrivileges == NULL) {
CloseHandle(Token);
return( FALSE );
}
NewPrivileges->PrivilegeCount = 1;
NewPrivileges->Privileges[0].Luid = LuidPrivilege;
NewPrivileges->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
//
// Enable the privilege
//
bRet = AdjustTokenPrivileges( Token,
FALSE,
NewPrivileges,
0,
(PTOKEN_PRIVILEGES)NULL,
NULL );
CloseHandle( Token );
free( NewPrivileges );
return (bRet);
} // InitializeAsDebugger()
VOID
InitSymbolPath(
HANDLE SymHandle
)
{
PCHAR tmpPath;
LONG CharsLeft;
CHAR WinDirPath[] = ";%Windir%\\System32\\Drivers;%Windir%\\System32;%Windir%";
CHAR DriversPath[] = "\\system32\\drivers;";
CHAR System32Path[] = "\\system32;";
CHAR PathSeparator[] = ";";
tmpPath = malloc(TOTAL_SYMPATH_LENGTH*sizeof(char));
if(tmpPath == NULL){
FPRINTF(stderr, "KERNRATE: Failed memory allocation for tmpPath in InitSymbolPath\n");
exit(1);
}
if ( SymSetSearchPath(SymHandle, (LPSTR)0 ) == TRUE ) {
// When SymSetSearchPath() is called with SearchPath as NULL, the following
// symbol path default is used:
// .;%_NT_SYMBOL_PATH%;%_NT_ALTERNATE_SYMBOL_PATH%;
if ( gUserSymbolPath[0] != '\0' ) {
//
// Note: We prepend the user specified path to the current search path.
//
if ( SymGetSearchPath( SymHandle, tmpPath, sizeof( tmpPath ) ) == TRUE ) {
strncpy( gSymbolPath, gUserSymbolPath, USER_SYMPATH_LENGTH-1);
strncat( gSymbolPath, PathSeparator, lstrlen(PathSeparator) );
CharsLeft = TOTAL_SYMPATH_LENGTH - lstrlen(gSymbolPath)-1;
if ( (lstrlen(gSymbolPath) + lstrlen(tmpPath) ) > TOTAL_SYMPATH_LENGTH - 1 )
FPRINTF(stderr, "===>WARNING: Overall symbol path length exceeds %d characters and will be truncated\n",
TOTAL_SYMPATH_LENGTH
);
strncat( gSymbolPath, tmpPath, CharsLeft-1 );
gSymbolPath[ TOTAL_SYMPATH_LENGTH-1 ] = '\0';
if ( SymSetSearchPath( SymHandle, gSymbolPath ) != TRUE ) {
FPRINTF( stderr, "KERNRATE: Failed to set the user specified symbol search path.\nUsing default IMAGEHLP symbol search path...\n" );
}
}
}
//
// IMAGEHLP also looks for the executabe image. Let's append %windir%\system32\drivers;%windir%\system32;%windir%
// to the end of the path. This way privates will always be searched first in the current directory.
// Also, this order will allow to find executables in their "natural" directories first, before going to dllcache etc.
//
if ( SymGetSearchPath( SymHandle, gSymbolPath, sizeof( gSymbolPath ) ) == TRUE ) {
CharsLeft = TOTAL_SYMPATH_LENGTH - lstrlen( gSymbolPath ) - 1;
strncpy( tmpPath, WinDirPath, CharsLeft);
tmpPath[CharsLeft] = '\0';
if ( (lstrlen(gSymbolPath) + lstrlen(tmpPath)) > TOTAL_SYMPATH_LENGTH - 1 )
FPRINTF(stderr, "===>WARNING: Overall symbol path length exceeds %d characters and will be truncated\n",
TOTAL_SYMPATH_LENGTH
);
strncat( gSymbolPath, tmpPath, lstrlen(tmpPath) ); //tmpPath length is CharsLeft
gSymbolPath[ TOTAL_SYMPATH_LENGTH-1 ] = '\0';
if ( SymSetSearchPath(SymHandle, gSymbolPath) != TRUE ) {
FPRINTF( stderr, "KERNRATE: Failed to set the symbol search path with %%windir%%.\nCurrent symbol search path is: %s\n", gSymbolPath );
}
}
}
else {
FPRINTF( stderr, "KERNRATE: Failed to set the IMAGEHLP default symbol search path, trying to set to %%windir%% and sub directories\n" );
//
// Set the Symbol Search Path with "%WINDIR%" -
// it was the behaviour of the original MS code...
// Let's also append system32 and system32\drivers to the path so people will stop complaining
//
if( 0 != GetEnvironmentVariable("windir", gSymbolPath, sizeof(gSymbolPath)) ){
CharsLeft = TOTAL_SYMPATH_LENGTH - 1;
if( CharsLeft >= (lstrlen(System32Path) + 3*lstrlen(gSymbolPath) + lstrlen(DriversPath) + lstrlen(PathSeparator) ) ){
strncpy(tmpPath, gSymbolPath, TOTAL_SYMPATH_LENGTH - 1);
tmpPath[ TOTAL_SYMPATH_LENGTH-1 ] = '\0';
strncat(tmpPath, DriversPath, lstrlen(DriversPath) );
strncat(tmpPath, gSymbolPath, lstrlen(gSymbolPath) );
strncat(tmpPath, System32Path, lstrlen(System32Path) );
strncat(tmpPath, gSymbolPath, lstrlen(gSymbolPath) );
strncat(tmpPath, PathSeparator, lstrlen(PathSeparator) );
strncpy(gSymbolPath, tmpPath, TOTAL_SYMPATH_LENGTH - 1 );
gSymbolPath[TOTAL_SYMPATH_LENGTH - 1] = '\0';
}
else{
FPRINTF( stderr, "KERNRATE: Overall path length for %%windir%% and sub directories exceeds %d characters\n",
TOTAL_SYMPATH_LENGTH
);
}
SymSetSearchPath(SymHandle, gSymbolPath);
} else {
FPRINTF(stderr, "KERNRATE: Failed to get environment variable for %%windir%%, failed to set alternate symbol path\n");
}
}
//
// In any case [and it is redundant to do this in some of the previous cases],
// but we want to be in sync, especially for the image and debug files checksum check.
//
if ( SymGetSearchPath(SymHandle, gSymbolPath, sizeof( gSymbolPath ) ) != TRUE ) {
FPRINTF( stderr, "KERNRATE: Failed to get IMAGEHLP symbol files search path...\n" );
//
// The content of gSymbolPath is now undefined. so clean it...
// gSymbolPath[] users have to check the content.
//
gSymbolPath[0] = '\0';
}
else if ( gVerbose & VERBOSE_IMAGEHLP ) {
FPRINTF( stderr, "KERNRATE: IMAGEHLP symbol search path is: %s\n", gSymbolPath );
}
free( tmpPath );
bSymPathInitialized = TRUE;
} // InitSymbolPath()
BOOL
InitializeKernelProfile(VOID)
{
DWORD m,k;
PPROC_TO_MONITOR ProcToMonitor = calloc(1, sizeof(PROC_TO_MONITOR));
if (ProcToMonitor==NULL) {
FPRINTF(stderr, "Allocation for the System Process failed\n");
return(FALSE);
}
gpSysProc = ProcToMonitor;
ProcToMonitor->ProcessHandle = SYM_KERNEL_HANDLE;
ProcToMonitor->Index = gNumProcToMonitor;
ProcToMonitor->Next = gProcessList; // NULL
gProcessList = ProcToMonitor;
ProcToMonitor->ZoomList = gCommonZoomList; // gCommonZoomList may contain System modules
ProcToMonitor->pProcThreadInfoStart = NULL;
for(m=0; m<gNumTasksStart; m++){
if( !_stricmp(gTlistStart[m].ProcessName, gSystemProcessName) ){
ProcToMonitor->Pid = gTlistStart[m].ProcessId;
if( bSystemThreadsInfo == TRUE ){
UpdateProcessStartInfo(ProcToMonitor,
&gTlistStart[m],
bSystemThreadsInfo
);
}
break;
}
}
InitializeProfileSourceInfo(ProcToMonitor); // Initialize ProfileSourceInfo for kernel trace
// Update profiling rate for kernel trace if necessary
SetProfileSourcesRates(ProcToMonitor);
gNumProcToMonitor++;
if (!SymInitialize( SYM_KERNEL_HANDLE, NULL, FALSE )) {
FPRINTF (stderr, "Could not initialize imagehlp for kernel - %d\n", GetLastError ());
return (FALSE);
}
if (!bSymPathInitialized) {
InitSymbolPath( SYM_KERNEL_HANDLE );
}
else {
SymSetSearchPath(ProcToMonitor->ProcessHandle, gSymbolPath);
}
if ( SymRegisterCallback64( ProcToMonitor->ProcessHandle, SymbolCallbackFunction, (ULONG64)&gCurrentModule ) != TRUE ) {
FPRINTF( stderr, "KERNRATE: Failed to register callback for IMAGEHLP Kernel handle operations...\n" );
}
return TRUE;
} //InitializeKernelProfile()
DWORD
GetTaskList(
PTASK_LIST pTask,
ULONG NumTasks
)
/*++
Routine Description:
Provides an API for getting a list of tasks running at the time of the
API call. This function uses internal NT apis and data structures. This
api is MUCH faster that the non-internal version that uses the registry.
Arguments:
pTask - Pointer to a TASK_LIST struct
NumTasks - Maximum number of tasks that the pTask array can hold
Return Value:
Number of tasks placed into the pTask array.
--*/
{
PSYSTEM_PROCESS_INFORMATION ProcessInfo;
NTSTATUS status;
ANSI_STRING pname;
PCHAR p;
PUCHAR CommonLargeBuffer;
ULONG TotalOffset;
ULONG totalTasks = 0;
ULONG CommonLargeBufferSize = 64*1024;
do {
CommonLargeBuffer = VirtualAlloc (NULL,
CommonLargeBufferSize,
MEM_COMMIT,
PAGE_READWRITE);
if (CommonLargeBuffer == NULL) {
return 0;
}
status = NtQuerySystemInformation(
SystemProcessInformation,
CommonLargeBuffer,
CommonLargeBufferSize,
NULL
);
if (status == STATUS_INFO_LENGTH_MISMATCH) {
CommonLargeBufferSize += 8192;
VirtualFree (CommonLargeBuffer, 0, MEM_RELEASE);
CommonLargeBuffer = NULL;
}
else if ( !NT_SUCCESS(status) ) {
FPRINTF(stderr, "KERNRATE: NtQuerySystemInformation failed in getTaskList, status %08lx, aborting\n", status);
exit(1);
}
} while ( CommonLargeBuffer == NULL );
ProcessInfo = (PSYSTEM_PROCESS_INFORMATION) CommonLargeBuffer;
TotalOffset = 0;
do {
pname.Buffer = NULL;
if ( ProcessInfo->ImageName.Buffer ) {
status = RtlUnicodeStringToAnsiString( &pname, (PUNICODE_STRING)&ProcessInfo->ImageName, TRUE );
if ( NT_SUCCESS(status) && pname.Buffer ) {
p = strrchr(pname.Buffer,'\\');
if ( p ) {
p++;
}
else {
p = pname.Buffer;
}
}
else {
p = "???UToAStr err"; // RtlUnicodeStringToAnsiString error.
}
}
else {
p = "System Idle Process";
}
strncpy( pTask->ProcessName, p, sizeof(pTask->ProcessName)-1 );
pTask->ProcessId = (LONGLONG)ProcessInfo->UniqueProcessId;
if(bIncludeGeneralInfo){
pTask->ProcessPerfInfo.NumberOfThreads = ProcessInfo->NumberOfThreads;
pTask->ProcessPerfInfo.UserTime = ProcessInfo->UserTime;
pTask->ProcessPerfInfo.KernelTime = ProcessInfo->KernelTime;
pTask->ProcessPerfInfo.ReadOperationCount = ProcessInfo->ReadOperationCount;
pTask->ProcessPerfInfo.WriteOperationCount = ProcessInfo->WriteOperationCount;
pTask->ProcessPerfInfo.OtherOperationCount = ProcessInfo->OtherOperationCount;
pTask->ProcessPerfInfo.ReadTransferCount = ProcessInfo->ReadTransferCount;
pTask->ProcessPerfInfo.WriteTransferCount = ProcessInfo->WriteTransferCount;
pTask->ProcessPerfInfo.OtherTransferCount = ProcessInfo->OtherTransferCount;
pTask->ProcessPerfInfo.PageFaultCount = ProcessInfo->PageFaultCount;
pTask->ProcessPerfInfo.HandleCount = ProcessInfo->HandleCount;
pTask->ProcessPerfInfo.VirtualSize = ProcessInfo->VirtualSize;
pTask->ProcessPerfInfo.WorkingSetSize = ProcessInfo->WorkingSetSize;
pTask->ProcessPerfInfo.QuotaPagedPoolUsage = ProcessInfo->QuotaPagedPoolUsage;
pTask->ProcessPerfInfo.QuotaNonPagedPoolUsage = ProcessInfo->QuotaNonPagedPoolUsage;
pTask->ProcessPerfInfo.PagefileUsage = ProcessInfo->PagefileUsage;
pTask->ProcessPerfInfo.PrivatePageCount = ProcessInfo->PrivatePageCount;
if( bIncludeThreadsInfo == TRUE ){
if(pTask->pProcessThreadInfo != NULL) //This is not the first time we,ve been called
free(pTask->pProcessThreadInfo);
pTask->pProcessThreadInfo =
(PSYSTEM_THREAD_INFORMATION)calloc(ProcessInfo->NumberOfThreads,
sizeof(SYSTEM_THREAD_INFORMATION)
);
if (pTask->pProcessThreadInfo != NULL) {
UINT nThreads = ProcessInfo->NumberOfThreads;
PSYSTEM_THREAD_INFORMATION pSysThreadInfo = (PSYSTEM_THREAD_INFORMATION)(ProcessInfo + 1);
while (nThreads--){
pTask->pProcessThreadInfo[nThreads].KernelTime = pSysThreadInfo->KernelTime;
pTask->pProcessThreadInfo[nThreads].UserTime = pSysThreadInfo->UserTime;
pTask->pProcessThreadInfo[nThreads].CreateTime = pSysThreadInfo->CreateTime;
pTask->pProcessThreadInfo[nThreads].WaitTime = pSysThreadInfo->WaitTime;
pTask->pProcessThreadInfo[nThreads].StartAddress = pSysThreadInfo->StartAddress;
pTask->pProcessThreadInfo[nThreads].ClientId.UniqueProcess =
pSysThreadInfo->ClientId.UniqueProcess;
pTask->pProcessThreadInfo[nThreads].ClientId.UniqueThread =
pSysThreadInfo->ClientId.UniqueThread;
pTask->pProcessThreadInfo[nThreads].Priority = pSysThreadInfo->Priority;
pTask->pProcessThreadInfo[nThreads].BasePriority = pSysThreadInfo->BasePriority;
pTask->pProcessThreadInfo[nThreads].ContextSwitches = pSysThreadInfo->ContextSwitches;
pTask->pProcessThreadInfo[nThreads].ThreadState = pSysThreadInfo->ThreadState;
pTask->pProcessThreadInfo[nThreads].WaitReason = pSysThreadInfo->WaitReason;
pSysThreadInfo++;
}
}
}
}
pTask++;
totalTasks++;
if ( ProcessInfo->NextEntryOffset == 0 ){
break;
}
TotalOffset += ProcessInfo->NextEntryOffset;
ProcessInfo = (PSYSTEM_PROCESS_INFORMATION)&CommonLargeBuffer[TotalOffset];
}while ( totalTasks < NumTasks );
if(CommonLargeBuffer != NULL){
VirtualFree(CommonLargeBuffer, 0, MEM_RELEASE);
}
return totalTasks;
} //getTasklist()
VOID
SetProfileSourcesRates(
PPROC_TO_MONITOR ProcToMonitor
)
/*++
Routine Description:
Attempts to set the requested (or default) sampling rates for the selected profile sources
Will try to set the system default rate if it failed to set the requested rate for a particular source
Arguments:
ProcToMonitor - Pointer to the structure of the process being monitored
Return Value:
None.
--*/
{
KPROFILE_SOURCE ProfileSource;
NTSTATUS Status;
ULONG ProfileSourceIndex;
ULONGLONG Pid;
CHAR String1[] = "\nKernel Profile (PID = %I64d): Source=";
CHAR String2[] = "\nPID = %I64d: Source=";
CHAR OutString[256] = "";
// Update profiling rate for kernel or user processes traces if necessary
for (ProfileSourceIndex = 0; ProfileSourceIndex < gSourceMaximum; ProfileSourceIndex++){
if (gpProcDummy->Source[ProfileSourceIndex].Interval != 0){
Pid = ProcToMonitor->Pid;
if( ProcToMonitor->ProcessHandle == SYM_KERNEL_HANDLE ) {
sprintf( &OutString[0], String1, Pid);
} else {
sprintf( &OutString[0], String2, Pid);
}
ProcToMonitor->Source[ProfileSourceIndex].Interval = gpProcDummy->Source[ProfileSourceIndex].Interval;
if ( ProcToMonitor->Source[ProfileSourceIndex].DesiredInterval && ProcToMonitor->Source[ProfileSourceIndex].Interval ) {
ULONG ThisInterval;
ProfileSource = ProcToMonitor->Source[ProfileSourceIndex].ProfileSource;
NtSetIntervalProfile(ProcToMonitor->Source[ProfileSourceIndex].Interval, ProfileSource);
Status = NtQueryIntervalProfile(ProfileSource, &ThisInterval);
if(gVerbose & VERBOSE_PROFILING )
FPRINTF(stdout, "Requested Rate= %ld Events/Hit, Actual Rate= %ld Events/Hit\n",
ProcToMonitor->Source[ProfileSourceIndex].Interval,
ThisInterval
);
if ((NT_SUCCESS(Status)) && RATES_MATCH(ThisInterval, gpProcDummy->Source[ProfileSourceIndex].Interval) ) {
if ( ProfileSourceIndex < gStaticCount ) {
if ( ProcToMonitor->Source[ProfileSourceIndex].Interval == gStaticSource[ProfileSourceIndex].Interval ){
if (ThisInterval == ProcToMonitor->Source[ProfileSourceIndex].Interval){
FPRINTF(stdout,
"%s %s, \nUsing Kernrate Default Rate of %ld events/hit\n",
OutString,
ProcToMonitor->Source[ProfileSourceIndex].Name,
ThisInterval
);
} else {
FPRINTF(stdout,
"%s, %s, \nTried Using Kernrate Default Rate of %ld events/hit, Actual Rate= %ld events/hit\n",
OutString,
ProcToMonitor->Source[ProfileSourceIndex].Name,
ProcToMonitor->Source[ProfileSourceIndex].Interval,
ThisInterval
);
}
} else {
FPRINTF(stdout,
"%s %s, \nUser Requested Rate= %ld events/hit, Actual Rate= %ld events/hit\n",
OutString,
ProcToMonitor->Source[ProfileSourceIndex].Name,
ProcToMonitor->Source[ProfileSourceIndex].Interval,
ThisInterval
);
}
} else {
FPRINTF(stdout,
"%s %s, \nUsing Kernrate Default or User Requested Rate of %ld events/hit\n",
OutString,
ProcToMonitor->Source[ProfileSourceIndex].Name,
ThisInterval
);
}
ProcToMonitor->Source[ProfileSourceIndex].Interval = ThisInterval;
} else {
NtSetIntervalProfile(ProcToMonitor->Source[ProfileSourceIndex].DesiredInterval, ProfileSource);
Status = NtQueryIntervalProfile(ProfileSource, &ThisInterval);
if ((NT_SUCCESS(Status)) && (ThisInterval > 0)) {
BOOL bPrint = TRUE;
//
// The StaticSources array (may) contain invalid default intervals, let's not bother the user with that
//
if ( ProfileSourceIndex < gStaticCount ) {
if ( ProcToMonitor->Source[ProfileSourceIndex].Interval == gStaticSource[ProfileSourceIndex].Interval ){
FPRINTF(stdout,
"%s %s, \nUsing Kernrate Default Rate of %ld events/hit\n",
OutString,
ProcToMonitor->Source[ProfileSourceIndex].Name,
ThisInterval
);
bPrint = FALSE;
}
}
if(bPrint == TRUE) {
FPRINTF(stdout,
"%s %s, \nCould Not Set User Requested Rate, Using System Default Rate of %ld events/hit\n",
OutString,
ProcToMonitor->Source[ProfileSourceIndex].Name,
ThisInterval
);
}
ProcToMonitor->Source[ProfileSourceIndex].Interval = ThisInterval;
} else {
ProcToMonitor->Source[ProfileSourceIndex].Interval = 0;
FPRINTF(stdout,
"%s %s, Could not Set Interval Rate, Setting to 0 (disabling this source)\n",
OutString,
ProcToMonitor->Source[ProfileSourceIndex].Name
);
}
}
} else {
ProcToMonitor->Source[ProfileSourceIndex].Interval = 0;
}
} else if( ProfileSourceIndex == SOURCE_TIME ) {
ULONG ThisInterval;
ProfileSource = ProcToMonitor->Source[ProfileSourceIndex].ProfileSource;
NtSetIntervalProfile(ProcToMonitor->Source[ProfileSourceIndex].Interval, ProfileSource);
Status = NtQueryIntervalProfile(ProfileSource, &ThisInterval);
if ((NT_SUCCESS(Status)) && (ThisInterval == 0)) {
FPRINTF(stdout,
"CPU TIME source disabled per user request\n"
);
} else {
FPRINTF(stderr,
"KERNRATE: Could not disable CPU TIME source on this platform (Kernrate will just not profile it)\n"
);
}
ProcToMonitor->Source[ProfileSourceIndex].Interval = 0;
}
} //for
} //SetProfileSourcesRates()
VOID
GetProcessLocksInformation (
PPROC_TO_MONITOR ProcToMonitor,
ULONG Flags,
ACTION_TYPE Action
)
/*++
Routine Description:
Gets and outputs the information about process locks that are under contention
The list will miss short-lived locks that were created after the start count but went away before the end count.
Arguments:
ProcToMonitor - Pointer to the structure of the process being monitored
Flags - RTL_QUERY_PROCESS_LOCKS
Action - Either START, STOP or OUTPUT
Return Value:
None.
--*/
{
NTSTATUS Status;
ULONG BufferSize = 0;
if ( !WIN2K_OS )
Flags |= RTL_QUERY_PROCESS_NONINVASIVE; //Not defined on Win2K
switch (Action) {
case START:
ProcToMonitor->pProcDebugInfoStart = RtlCreateQueryDebugBuffer( BufferSize, FALSE );
if(ProcToMonitor->pProcDebugInfoStart == NULL) {
FPRINTF(stderr, "KERNRATE: Failed to create buffer (START) in GetProcessLocksInformation\n");
FPRINTF(stderr, "KERNRATE: Process %s may have insufficient virtual memory left for collecting locks information\n",
ProcToMonitor->ProcessName
);
return;
}
Status = RtlQueryProcessDebugInformation( (HANDLE)ProcToMonitor->Pid,
Flags,
ProcToMonitor->pProcDebugInfoStart
);
if(!NT_SUCCESS(Status)) {
FPRINTF(stderr, "KERNRATE: Failed call to RtlQueryProcessDebugInformation (START) for Locks Information\n");
FPRINTF(stderr, "Process: %s, Status = %x\n", ProcToMonitor->ProcessName, Status);
if(Status == STATUS_INFO_LENGTH_MISMATCH)FPRINTF(stderr, "Status = INFO_LENGTH_MISMATCH\n");
if(Status == STATUS_NO_MEMORY)FPRINTF(stderr, "Status = NO_MEMORY\n");
return;
}
break;
case STOP:
ProcToMonitor->pProcDebugInfoStop = RtlCreateQueryDebugBuffer( BufferSize, FALSE );
if(ProcToMonitor->pProcDebugInfoStop == NULL) {
FPRINTF(stderr, "KERNRATE: Failed to create buffer (STOP) in GetProcessLocksInformation\n");
FPRINTF(stderr, "KERNRATE: Process %s may have insufficient virtual memory left for collecting locks information\n",
ProcToMonitor->ProcessName
);
return;
}
Status = RtlQueryProcessDebugInformation( (HANDLE)ProcToMonitor->Pid,
Flags,
ProcToMonitor->pProcDebugInfoStop
);
if(!NT_SUCCESS(Status)){
FPRINTF(stderr, "KERNRATE: Failed call to RtlQueryProcessDebugInformation (STOP) for Locks Information\n");
FPRINTF(stderr, "Process: %s, Status = %x\n", ProcToMonitor->ProcessName, Status);
if(Status == STATUS_INFO_LENGTH_MISMATCH)FPRINTF(stderr, "Status = INFO_LENGTH_MISMATCH\n");
if(Status == STATUS_NO_MEMORY)FPRINTF(stderr, "Status = NO_MEMORY\n");
return;
}
break;
case OUTPUT:
{
if( ProcToMonitor->pProcDebugInfoStart == NULL || ProcToMonitor->pProcDebugInfoStop == NULL) return;
OutputLocksInformation( ProcToMonitor->pProcDebugInfoStart->Locks,
ProcToMonitor->pProcDebugInfoStop->Locks,
ProcToMonitor
);
//
// Cleanup for this process - note that the target process may be gone so we need to be careful
//
try {
if ( ProcToMonitor->pProcDebugInfoStart != NULL)
RtlDestroyQueryDebugBuffer( ProcToMonitor->pProcDebugInfoStart );
if ( ProcToMonitor->pProcDebugInfoStop != NULL)
RtlDestroyQueryDebugBuffer( ProcToMonitor->pProcDebugInfoStop );
} _except ( EXCEPTION_EXECUTE_HANDLER ) {
FPRINTF(stderr, "Exception %X raised while trying to call RtlDestroyQueryDebugBuffer\n",
_exception_code()
);
FPRINTF(stderr, "This could happen if the monitored process is gone\n");
return;
}
}
break;
default:
FPRINTF(stderr, "GetProcessLocksInformation was called with an invalid Action parameter - %d\n", Action);
}
} //GetProcessLocksInformation ()
VOID
GetSystemLocksInformation (
ACTION_TYPE Action
)
/*++
Routine Description:
Gets and outputs the information about System (Kernel) locks that are under contention
The list will miss short-lived locks that were created after the start count but went away before the end count.
Arguments:
Action - Either START, STOP or OUTPUT
Return Value:
None.
--*/
{
NTSTATUS Status;
static BOOL bDisplayLockInfo;
static PRTL_PROCESS_LOCKS ProcessLockInfoStart;
static PRTL_PROCESS_LOCKS ProcessLockInfoStop;
ULONG BufferSize = sizeof(RTL_PROCESS_LOCKS);
switch (Action) {
case START:
bDisplayLockInfo = TRUE;
do {
ProcessLockInfoStart = malloc(BufferSize);
if(ProcessLockInfoStart == NULL)
{
FPRINTF(stderr, "KERNRATE: Failed to allocate Buffer for Lock Info (START) \n");
bDisplayLockInfo = FALSE;
return;
}
Status = NtQuerySystemInformation(SystemLocksInformation,
ProcessLockInfoStart,
BufferSize,
&BufferSize
);
if(Status == STATUS_SUCCESS){
break;
}
if(Status != STATUS_INFO_LENGTH_MISMATCH){
FPRINTF(stderr, "KERNRATE: Failed call to NTQuerySystemInformation for Lock Info (START) \n");
bDisplayLockInfo = FALSE;
return;
}
free( ProcessLockInfoStart );
ProcessLockInfoStart = NULL;
}while (Status == STATUS_INFO_LENGTH_MISMATCH);
break;
case STOP:
do {
ProcessLockInfoStop = malloc(BufferSize);
if(ProcessLockInfoStop == NULL)
{
FPRINTF(stderr, "KERNRATE: Failed to allocate Buffer for Lock Info (STOP) \n");
bDisplayLockInfo = FALSE;
return;
}
Status = NtQuerySystemInformation( SystemLocksInformation,
ProcessLockInfoStop,
BufferSize,
&BufferSize
);
if(Status == STATUS_SUCCESS){
break;
}
if(Status != STATUS_INFO_LENGTH_MISMATCH){
FPRINTF(stderr, "KERNRATE: Failed call to NTQuerySystemInformation for Lock Info (STOP) \n");
bDisplayLockInfo = FALSE;
return;
}
free( ProcessLockInfoStop );
ProcessLockInfoStop = NULL;
}while (Status == STATUS_INFO_LENGTH_MISMATCH);
break;
case OUTPUT:
if( bDisplayLockInfo == TRUE ){
OutputLocksInformation( ProcessLockInfoStart,
ProcessLockInfoStop,
gpSysProc
);
//
// Cleanup
//
if(ProcessLockInfoStart != NULL){
free( ProcessLockInfoStart );
ProcessLockInfoStart = NULL;
}
if(ProcessLockInfoStop != NULL){
free( ProcessLockInfoStop );
ProcessLockInfoStop = NULL;
}
}
break;
default:
FPRINTF(stderr, "KERNRATE INTERNAL ERROR: GetSystemLocksInformation was called with an invalid Action parameter - %d\n",
Action
);
}
} //GetSystemLocksInformation ()
VOID
OutputLocksInformation(
PRTL_PROCESS_LOCKS pLockInfoStart,
PRTL_PROCESS_LOCKS pLockInfoStop,
PPROC_TO_MONITOR Proc
)
/*++
Routine Description:
Outputs Lock Contention information for either System (Kernel) or User Process Locks
If a lock is new (has only final counts) or gone (has only initial counts, it will be marked accordingly
The routine will try to get the symbol name associated with the lock if one exists.
The user can control (filter) the output by changing gLockContentionMinCount
The list will miss short-lived Locks that were created after the start count but went away before the end count.
Arguments:
pLockInfoStart - Pointer to lock info struct (Initial count)
pLockInfoStop - Pointer to lock info struct (Final count)
SymHandle - Symbol Handle to the process
Return Value:
None.
--*/
{
ULONG i,j,k;
DWORD64 disp;
CHAR TypeString[32] = "UNKNOWN";
PMODULE Module;
BOOL bAnyLocksFound = FALSE;
BOOL *Index = NULL;
ULONG64 *LoadedBases = NULL;
HANDLE SymHandle;
if(Proc == NULL){
FPRINTF(stderr, "KERNRATE: NULL Process pointer passed to OutputLocksInformation\n");
FPRINTF(stderr, "Possible cause: Kernel Resource info was required with the -x or -xk command line options,\n");
FPRINTF(stderr, "but was the -a option specified on the command line as well?\n");
exit(1);
}
SymHandle = Proc->ProcessHandle;
if( pLockInfoStart != NULL ) {
if( pLockInfoStart->NumberOfLocks > 0 ) {
Index = (BOOL*)calloc(pLockInfoStart->NumberOfLocks, sizeof(BOOL));
if( Index == NULL ){
FPRINTF(stderr, "KERNRATE: Failed to allocate memory for Index data in OutputLocksInformation\n");
exit(1);
}
} else {
FPRINTF(stdout, "\nNo locks found or process %s has insufficient virtual memory for collecting lock information\n",
Proc->ProcessName
);
return;
}
}
FPRINTF(stdout,
"\nLocks Contention Info:\n\nAddress, Contention-Diff., Rate(per sec.), Thread, Type, Recursion, Waiting-Shared, Waiting-Exclusive, Symbol-Information\n"
);
// We could sort the data first for a faster search, but sorting the two arrays costs too...
if( pLockInfoStop != NULL ) {
LoadedBases = (ULONG64 *)calloc( Proc->ModuleCount, sizeof(ULONG64) );
if( LoadedBases == NULL ){
FPRINTF(stderr, "KERNRATE: Failed to allocate memory for module base data in OutputLocksInformation\n");
exit(1);
}
for (i=0; i < pLockInfoStop->NumberOfLocks; i++){
BOOL bFound = FALSE;
if( pLockInfoStop->Locks[i].ContentionCount >= gLockContentionMinCount) //Additional preliminary filter
//(if not true then contention difference not true)
if( pLockInfoStart != NULL ) {
for (j=0; j < pLockInfoStart->NumberOfLocks; j++){
if( (DWORD64)(DWORD64 *)pLockInfoStop->Locks[i].Address == (DWORD64)(DWORD64 *)pLockInfoStart->Locks[j].Address ){
LONG ContentionDiff = pLockInfoStop->Locks[i].ContentionCount -
pLockInfoStart->Locks[j].ContentionCount;
long double Rate = (long double)ContentionDiff / gldElapsedSeconds;
LONG RecursionDiff = pLockInfoStop->Locks[i].RecursionCount -
pLockInfoStart->Locks[j].RecursionCount;
LONG WaitShrdDiff = pLockInfoStop->Locks[i].NumberOfWaitingShared -
pLockInfoStart->Locks[j].NumberOfWaitingShared;
LONG WaitExclDiff = pLockInfoStop->Locks[i].NumberOfWaitingExclusive -
pLockInfoStart->Locks[j].NumberOfWaitingExclusive;
if(ContentionDiff >= (LONG)gLockContentionMinCount ){
if(pLockInfoStop->Locks[i].Type == RTL_CRITSECT_TYPE)
strncpy(TypeString, "CRITICAL_SECTION", sizeof(TypeString)-1);
if(pLockInfoStop->Locks[i].Type == RTL_RESOURCE_TYPE)
strncpy(TypeString, "RESOURCE", sizeof(TypeString)-1);
FPRINTF(stdout, "%p, %10ld, %10.0f, 0x%I64x, %s, ",
(PVOID64)pLockInfoStop->Locks[i].Address,
ContentionDiff,
Rate,
(LONGLONG)pLockInfoStop->Locks[i].OwningThread,
TypeString
);
if(pLockInfoStop->Locks[i].Type == RTL_CRITSECT_TYPE)
FPRINTF(stdout, " %10ld, N/A, N/A", RecursionDiff);
if(pLockInfoStop->Locks[i].Type == RTL_RESOURCE_TYPE)
FPRINTF(stdout, " N/A, %10ld, %10ld ", WaitShrdDiff, WaitExclDiff);
Module = FindModuleForAddress64( Proc,
(DWORD64)(DWORD64 *)pLockInfoStop->Locks[i].Address);
//
//In the folllowing it may happen that we try to load a module more than once
//we don't really care about the return status
//
if( Module != NULL ) {
if( Module->bZoom != TRUE ){
//
//Avoid trying to load what's already loaded and for later cleanup purposes
//
for( k=0; k<Proc->ModuleCount; k++ ) {
if(LoadedBases[k] == Module->Base) { //Already been loaded
break;
} else if( LoadedBases[k] == 0 ) { //End of populated list, load a new module
(void)SymLoadModule64( SymHandle, // hProcess
NULL, // hFile [for Debugger]
Module->module_Name, // ImageName
NULL, // ModuleName
Module->Base, // BaseOfDll
Module->Length // SizeOfDll
);
*LoadedBases = Module->Base;
++LoadedBases;
break;
}
}
}
if ( Module->Base )
FPRINTF(stdout, " ,base= %p", (PVOID64)Module->Base);
if ( Module->module_Name )
FPRINTF(stdout, " - %s", Module->module_Name);
}
if ( SymGetSymFromAddr64((HANDLE)SymHandle, (DWORD64)(DWORD64 *)pLockInfoStop->Locks[i].Address, &disp, gSymbol )){
FPRINTF(stdout, "!%s\n", gSymbol->Name);
} else {
FPRINTF(stdout, "\n");
}
bAnyLocksFound = TRUE;
}
Index[j] = TRUE;
bFound = TRUE;
break;
}
}//for(j)
}//if(pLockInfoStart)
//
// No matching address found for START, therefore this is a NEW lock
// Note that here the additional filter is checked anyway at the beginning
//
if( !bFound && pLockInfoStop->Locks[i].ContentionCount >= gLockContentionMinCount ) {
long double Rate = (long double)pLockInfoStop->Locks[i].ContentionCount / gldElapsedSeconds;
FPRINTF(stdout, "%p, %10Ld, %10.0f, 0x%I64x, %s, ",
(PVOID64)pLockInfoStop->Locks[i].Address,
pLockInfoStop->Locks[i].ContentionCount,
Rate,
(LONGLONG)pLockInfoStop->Locks[i].OwningThread,
TypeString
);
if( pLockInfoStop->Locks[i].Type == RTL_CRITSECT_TYPE )
FPRINTF(stdout, " %10Ld, N/A, N/A",
pLockInfoStop->Locks[i].RecursionCount);
if( pLockInfoStop->Locks[i].Type == RTL_RESOURCE_TYPE )
FPRINTF(stdout, " N/A, %10Ld, %10Ld ",
pLockInfoStop->Locks[i].NumberOfWaitingShared,
pLockInfoStop->Locks[i].NumberOfWaitingExclusive
);
Module = FindModuleForAddress64( Proc,
(DWORD64)(DWORD64 *)pLockInfoStop->Locks[i].Address);
if( Module != NULL ) {
if( Module->bZoom != TRUE ){
//
//Avoid trying to load what's already loaded and for later cleanup purposes
//
for( k=0; k<Proc->ModuleCount; k++ ) {
if(LoadedBases[k] == Module->Base) { //Already been loaded
break;
} else {
if( LoadedBases[k] == 0 ) { //End of populated list, load a new module
(void)SymLoadModule64( SymHandle, // hProcess
NULL, // hFile [for Debugger]
Module->module_Name, // ImageName
NULL, // ModuleName
Module->Base, // BaseOfDll
Module->Length // SizeOfDll
);
*LoadedBases = Module->Base;
++LoadedBases;
break;
}
}
}
}
if ( Module->Base )
FPRINTF(stdout, ", base= %p", (PVOID64)Module->Base);
if ( Module->module_Name )
FPRINTF(stdout, " - %s", Module->module_Name);
}
if ( SymGetSymFromAddr64((HANDLE)SymHandle,(DWORD64)(DWORD64 *)pLockInfoStop->Locks[i].Address, &disp, gSymbol )){
FPRINTF(stdout, "!%s, NEW - (actual rate could be higher)\n", gSymbol->Name);
} else {
FPRINTF(stdout, ", NEW - (actual rate could be higher)\n");
}
bAnyLocksFound = TRUE;
}
}// for(i)
// Cleanup
for( i=0; i<Proc->ModuleCount; i++ )
{
if(LoadedBases[i] != 0) {
if(!SymUnloadModule64( SymHandle, LoadedBases[i])) {
VerbosePrint(( VERBOSE_IMAGEHLP, "Kernrate: Could Not Unload Module, Base= %p for Process %s\n",
(PVOID64)LoadedBases[i],
Proc->ProcessName
));
continue;
}
VerbosePrint(( VERBOSE_IMAGEHLP, "Kernrate: Unloaded Module, Base= %p for Process %s\n",
(PVOID64)LoadedBases[i],
Proc->ProcessName
));
}
}
}//if(pLockInfoStop)
//
// No matching address found for STOP, therefore this lock is GONE
//
if( pLockInfoStart != NULL) {
for (j=0; j < pLockInfoStart->NumberOfLocks; j++){
if( (Index[j] == FALSE) && (pLockInfoStart->Locks[j].ContentionCount >= gLockContentionMinCount) ){
if(pLockInfoStart->Locks[j].Type == RTL_CRITSECT_TYPE)
strncpy(TypeString, "CRITICAL_SECTION", sizeof(TypeString)-1);
if(pLockInfoStart->Locks[j].Type == RTL_RESOURCE_TYPE)
strncpy(TypeString, "RESOURCE", sizeof(TypeString)-1);
FPRINTF(stdout, "%p, %10Ld, N/A, 0x%I64x, %s, ",
(PVOID64)pLockInfoStart->Locks[j].Address,
pLockInfoStart->Locks[j].ContentionCount,
(LONGLONG)pLockInfoStart->Locks[j].OwningThread,
TypeString
);
FPRINTF(stdout, " GONE\n");
bAnyLocksFound = TRUE;
}
}//for(j)
}//if(pLockInfoStart)
if ( bAnyLocksFound == FALSE)
FPRINTF(stdout, "\nNo Locks with a contention-count difference of at least %d were found\n", gLockContentionMinCount);
//
//Cleanup
//
if(Index != NULL){
free(Index);
Index = NULL;
}
if(LoadedBases != NULL){
free(LoadedBases);
LoadedBases = NULL;
}
} //OutputLocksInformation()
VOID
GetProfileSystemInfo(
ACTION_TYPE Action
)
/*++
Routine Description:
Gets and outputs System-Wide performance counts (context-switches, I/O, etc.)
for the duration of the run
Arguments:
Action - Either START, STOP or OUTPUT
Return Value:
None.
--*/
{
NTSTATUS Status;
static BOOL bDisplayPerfInfo;
static PSYSTEM_PERFORMANCE_INFORMATION SysPerfInfoStart;
static PSYSTEM_PERFORMANCE_INFORMATION SysPerfInfoStop;
switch (Action) {
case START:
bDisplayPerfInfo = TRUE;
SysPerfInfoStart = malloc(sizeof(SYSTEM_PERFORMANCE_INFORMATION));
if(SysPerfInfoStart == NULL){
FPRINTF(stderr, "Memory allocation failed for SystemPerformanceInformation in GetProfileSystemInfo\n");
exit(1);
}
Status = NtQuerySystemInformation(SystemPerformanceInformation,
SysPerfInfoStart,
sizeof(SYSTEM_PERFORMANCE_INFORMATION),
NULL);
if (!NT_SUCCESS(Status)) {
FPRINTF(stderr, "QuerySystemInformation for performance information(1) failed %08lx\n", Status);
bDisplayPerfInfo = FALSE;
}
break;
case STOP:
SysPerfInfoStop = malloc(sizeof(SYSTEM_PERFORMANCE_INFORMATION));
if(SysPerfInfoStop == NULL){
FPRINTF(stderr, "Memory allocation failed for SystemPerformanceInformation in GetProfileSystemInfo\n");
exit(1);
}
Status = NtQuerySystemInformation(SystemPerformanceInformation,
SysPerfInfoStop,
sizeof(SYSTEM_PERFORMANCE_INFORMATION),
NULL);
if (!NT_SUCCESS(Status)) {
FPRINTF(stderr, "QuerySystemInformation for performance information(2) failed %08lx\n", Status);
bDisplayPerfInfo = FALSE;
}
break;
case OUTPUT:
if( bDisplayPerfInfo == TRUE ){
FPRINTF (stdout, "\n Total Avg. Rate\n");
DisplayTotalAndRate( SysPerfInfoStart->ContextSwitches,
SysPerfInfoStop->ContextSwitches,
gldElapsedSeconds,
"Context Switches",
"sec."
);
DisplayTotalAndRate( SysPerfInfoStart->SystemCalls,
SysPerfInfoStop->SystemCalls,
gldElapsedSeconds,
"System Calls",
"sec."
);
DisplayTotalAndRate( SysPerfInfoStart->PageFaultCount,
SysPerfInfoStop->PageFaultCount,
gldElapsedSeconds,
"Page Faults",
"sec."
);
DisplayTotalAndRate( SysPerfInfoStart->IoReadOperationCount,
SysPerfInfoStop->IoReadOperationCount,
gldElapsedSeconds,
"I/O Read Operations",
"sec."
);
DisplayTotalAndRate( SysPerfInfoStart->IoWriteOperationCount,
SysPerfInfoStop->IoWriteOperationCount,
gldElapsedSeconds,
"I/O Write Operations",
"sec."
);
DisplayTotalAndRate( SysPerfInfoStart->IoOtherOperationCount,
SysPerfInfoStop->IoOtherOperationCount,
gldElapsedSeconds,
"I/O Other Operations",
"sec."
);
DisplayTotalAndRate( SysPerfInfoStart->IoReadTransferCount.QuadPart,
SysPerfInfoStop->IoReadTransferCount.QuadPart,
(long double)(SysPerfInfoStop->IoReadOperationCount - SysPerfInfoStart->IoReadOperationCount),
"I/O Read Bytes",
" I/O"
);
DisplayTotalAndRate( SysPerfInfoStart->IoWriteTransferCount.QuadPart,
SysPerfInfoStop->IoWriteTransferCount.QuadPart,
(long double)(SysPerfInfoStop->IoWriteOperationCount - SysPerfInfoStart->IoWriteOperationCount),
"I/O Write Bytes",
" I/O"
);
DisplayTotalAndRate( SysPerfInfoStart->IoOtherTransferCount.QuadPart,
SysPerfInfoStop->IoOtherTransferCount.QuadPart,
(long double)(SysPerfInfoStop->IoOtherOperationCount - SysPerfInfoStart->IoOtherOperationCount),
"I/O Other Bytes",
" I/O"
);
}
//
// Cleanup
//
if(SysPerfInfoStart != NULL){
free(SysPerfInfoStart);
SysPerfInfoStart = NULL;
}
if(SysPerfInfoStop != NULL){
free(SysPerfInfoStop);
SysPerfInfoStop = NULL;
}
break;
default:
FPRINTF(stderr, "GetProfileSystemInfo was called with an invalid Action parameter - %d\n", Action);
}
} //GetProfileSystemInfo()
VOID
DisplayTotalAndRate (
LONGLONG StartCount,
LONGLONG StopCount,
long double RateAgainst,
PCHAR CounterName,
PCHAR RateAgainstUnits
)
{
long double Rate;
LARGE_INTEGER Total;
Total.QuadPart = StopCount - StartCount;
Rate = RateAgainst > 0? (long double)Total.QuadPart/RateAgainst : 0;
FPRINTF(stdout, " %-21s, %12I64d, %.0f/%s\n",
CounterName,
Total.QuadPart,
Rate,
RateAgainstUnits
);
} //DisplayTotalAndRate()
VOID
OutputStartStopValues (
SIZE_T StartCount,
SIZE_T StopCount,
PCHAR CounterName
)
{
LARGE_INTEGER StartValue;
LARGE_INTEGER StopValue;
LARGE_INTEGER Diff;
StartValue.QuadPart = StartCount;
StopValue.QuadPart = StopCount;
Diff.QuadPart = StopValue.QuadPart - StartValue.QuadPart;
FPRINTF(stdout, " %-21s, %15I64d, %15I64d, %15I64d\n",
CounterName,
StartValue.QuadPart,
StopValue.QuadPart,
Diff.QuadPart
);
} //OutputStartStopValues()
VOID
OutputPercentValue (
LONGLONG StartCount,
LONGLONG StopCount,
LARGE_INTEGER Base,
PCHAR CounterName
)
{
long double PercentValue;
LARGE_INTEGER Diff;
Diff.QuadPart = StopCount - StartCount;
PercentValue = Base.QuadPart > 0? 100*(long double)Diff.QuadPart/(long double)Base.QuadPart : 0;
FPRINTF(stdout, " %-28s= %.2f%% of the Elapsed Time\n",
CounterName,
PercentValue
);
} //OutputPercentValue()
VOID
OutputProcessPerfInfo (
PTASK_LIST pTask,
ULONG NumTasks,
PPROC_TO_MONITOR ProcToMonitor
)
/*++
Routine Description:
Gets and outputs Process-Specific performance counts (context-switches, I/O, etc.)
for the duration of the run
Arguments:
pTask - A pointer to Kernrate's structure of running tasks
NumTasks - Number of tasks in kernrate's task list
ProcToMonitor - Pointer to the process structure
Return Value:
None.
--*/
{
DWORD m, k, nThreads;
LONGLONG Diff;
if (pTask != NULL) {
if (ProcToMonitor != NULL){
for (m=0; m < NumTasks; m++) {
if (pTask[m].ProcessId == ProcToMonitor->Pid) {
FPRINTF (stdout, "\n");
OutputPercentValue( ProcToMonitor->ProcPerfInfoStart.UserTime.QuadPart,
pTask[m].ProcessPerfInfo.UserTime.QuadPart,
gTotal2ElapsedTime64,
"User Time"
);
OutputPercentValue( ProcToMonitor->ProcPerfInfoStart.KernelTime.QuadPart,
pTask[m].ProcessPerfInfo.KernelTime.QuadPart,
gTotal2ElapsedTime64,
"Kernel Time"
);
FPRINTF (stdout, "\n Total Avg. Rate\n");
DisplayTotalAndRate( ProcToMonitor->ProcPerfInfoStart.PageFaultCount,
pTask[m].ProcessPerfInfo.PageFaultCount,
gldElapsedSeconds,
"Page Faults",
"sec."
);
DisplayTotalAndRate( ProcToMonitor->ProcPerfInfoStart.ReadOperationCount.QuadPart,
pTask[m].ProcessPerfInfo.ReadOperationCount.QuadPart,
gldElapsedSeconds,
"I/O Read Operations",
"sec."
);
DisplayTotalAndRate( ProcToMonitor->ProcPerfInfoStart.WriteOperationCount.QuadPart,
pTask[m].ProcessPerfInfo.WriteOperationCount.QuadPart,
gldElapsedSeconds,
"I/O Write Operations",
"sec."
);
DisplayTotalAndRate( ProcToMonitor->ProcPerfInfoStart.OtherOperationCount.QuadPart,
pTask[m].ProcessPerfInfo.OtherOperationCount.QuadPart,
gldElapsedSeconds,
"I/O Other Operations",
"sec."
);
Diff = pTask[m].ProcessPerfInfo.ReadOperationCount.QuadPart
- ProcToMonitor->ProcPerfInfoStart.ReadOperationCount.QuadPart;
DisplayTotalAndRate( ProcToMonitor->ProcPerfInfoStart.ReadTransferCount.QuadPart,
pTask[m].ProcessPerfInfo.ReadTransferCount.QuadPart,
(long double)Diff,
"I/O Read Bytes",
" I/O"
);
Diff = pTask[m].ProcessPerfInfo.WriteOperationCount.QuadPart
- ProcToMonitor->ProcPerfInfoStart.WriteOperationCount.QuadPart;
DisplayTotalAndRate( ProcToMonitor->ProcPerfInfoStart.WriteTransferCount.QuadPart,
pTask[m].ProcessPerfInfo.WriteTransferCount.QuadPart,
(long double)Diff,
"I/O Write Bytes",
" I/O"
);
Diff = pTask[m].ProcessPerfInfo.OtherOperationCount.QuadPart
- ProcToMonitor->ProcPerfInfoStart.OtherOperationCount.QuadPart;
DisplayTotalAndRate( ProcToMonitor->ProcPerfInfoStart.OtherTransferCount.QuadPart,
pTask[m].ProcessPerfInfo.OtherTransferCount.QuadPart,
(long double)Diff,
"I/O Other Bytes",
" I/O"
);
FPRINTF (stdout, "\n Start-Count Stop-Count Diff.\n");
OutputStartStopValues( ProcToMonitor->ProcPerfInfoStart.NumberOfThreads,
pTask[m].ProcessPerfInfo.NumberOfThreads,
"Threads"
);
OutputStartStopValues( ProcToMonitor->ProcPerfInfoStart.HandleCount,
pTask[m].ProcessPerfInfo.HandleCount,
"Handles"
);
OutputStartStopValues( ProcToMonitor->ProcPerfInfoStart.WorkingSetSize,
pTask[m].ProcessPerfInfo.WorkingSetSize,
"Working Set Bytes"
);
OutputStartStopValues( ProcToMonitor->ProcPerfInfoStart.VirtualSize,
pTask[m].ProcessPerfInfo.VirtualSize,
"Virtual Size Bytes"
);
OutputStartStopValues( ProcToMonitor->ProcPerfInfoStart.QuotaPagedPoolUsage,
pTask[m].ProcessPerfInfo.QuotaPagedPoolUsage,
"Paged Pool Bytes"
);
OutputStartStopValues( ProcToMonitor->ProcPerfInfoStart.QuotaNonPagedPoolUsage,
pTask[m].ProcessPerfInfo.QuotaNonPagedPoolUsage,
"Non Paged Pool Bytes"
);
OutputStartStopValues( ProcToMonitor->ProcPerfInfoStart.PagefileUsage,
pTask[m].ProcessPerfInfo.PagefileUsage,
"Pagefile Bytes"
);
OutputStartStopValues( ProcToMonitor->ProcPerfInfoStart.PrivatePageCount,
pTask[m].ProcessPerfInfo.PrivatePageCount,
"Private Pages Bytes"
);
if ( ProcToMonitor->pProcThreadInfoStart != NULL ){
OutputThreadInfo (pTask,
m,
ProcToMonitor
);
}
return;
}
}
FPRINTF(stdout, "\nGeneral Info Unavailable for Process %s (PID= %I64d), because the process is GONE\n",
ProcToMonitor->ProcessName,
ProcToMonitor->Pid
);
} else {
FPRINTF(stderr, "Kernrate: OuputProcessPerfInfo - ProcToMonitor is NULL\n");
}
} else {
FPRINTF(stderr, "Kernrate: OuputProcessPerfInfo - pTask is NULL\n");
}
} //OutputProcessPerfInfo()
VOID
OutputThreadInfo (
PTASK_LIST pTask,
DWORD TaskNumber,
PPROC_TO_MONITOR ProcToMonitor
)
/*++
Routine Description:
Outputs Thread-Specific counts for a given process
Arguments:
pTask - A pointer to Kernrate's structure of running tasks
TaskNumber - The task index in kernrate's task list
ProcToMonitor - Pointer to the process structure
Return Value:
None.
--*/
{
DWORD m, k, nThreads;
LONGLONG Diff;
BOOL bFound;
BOOL *Index = NULL;
m = TaskNumber;
if( ProcToMonitor->ProcPerfInfoStart.NumberOfThreads > 0 ){
Index = (BOOL*)calloc(ProcToMonitor->ProcPerfInfoStart.NumberOfThreads, sizeof(BOOL));
if( Index == NULL ){
FPRINTF(stderr, "KERNRATE: Failed to allocate memory for Index data in OutputThreadInfo\n");
exit(1);
}
}else{
FPRINTF(stderr, "KERNRATE: No Threads for process %I64d at START???\n", ProcToMonitor->Pid);
return;
}
FPRINTF(stdout, "\n------------- Thread Information ---------------\n");
FPRINTF (stdout, "\n Start-Count Stop-Count Diff.\n");
nThreads = pTask[m].ProcessPerfInfo.NumberOfThreads;
while(nThreads--){
k = ProcToMonitor->ProcPerfInfoStart.NumberOfThreads;
bFound = FALSE;
while( k-- ){
if( pTask[m].pProcessThreadInfo[nThreads].ClientId.UniqueThread
== ProcToMonitor->pProcThreadInfoStart[k].ClientId.UniqueThread){
PMODULE module = FindModuleForAddress64(ProcToMonitor, (DWORD64)ProcToMonitor->pProcThreadInfoStart[k].StartAddress);
FPRINTF(stdout, "\nPid= %I64d, Tid= %I64d, StartAddr= 0x%p",
(DWORD64)(DWORD64 *)ProcToMonitor->pProcThreadInfoStart[k].ClientId.UniqueProcess,
(DWORD64)(DWORD64 *)ProcToMonitor->pProcThreadInfoStart[k].ClientId.UniqueThread,
ProcToMonitor->pProcThreadInfoStart[k].StartAddress
);
if(module != NULL){
FPRINTF(stdout, " (%s)\n", module->module_Name);
}else{
FPRINTF(stdout, " (unknown module)\n");
}
FPRINTF(stdout, " Thread State , %15s, %15s\n",
ThreadState[ProcToMonitor->pProcThreadInfoStart[k].ThreadState],
ThreadState[pTask[m].pProcessThreadInfo[nThreads].ThreadState]
);
FPRINTF(stdout, " Wait Reason , %15s, %15s\n",
WaitReason[ProcToMonitor->pProcThreadInfoStart[k].WaitReason],
WaitReason[pTask[m].pProcessThreadInfo[nThreads].WaitReason]
);
OutputStartStopValues( ProcToMonitor->pProcThreadInfoStart[k].WaitTime,
pTask[m].pProcessThreadInfo[nThreads].WaitTime,
"Wait Time [.1 uSec]"
);
FPRINTF(stdout, " Base Priority , %15d, %15d\n",
ProcToMonitor->pProcThreadInfoStart[k].BasePriority,
pTask[m].pProcessThreadInfo[nThreads].BasePriority
);
FPRINTF(stdout, " Priority , %15d, %15d\n",
ProcToMonitor->pProcThreadInfoStart[k].Priority,
pTask[m].pProcessThreadInfo[nThreads].Priority
);
OutputStartStopValues( ProcToMonitor->pProcThreadInfoStart[k].ContextSwitches,
pTask[m].pProcessThreadInfo[nThreads].ContextSwitches,
"Context Switches"
);
Diff = pTask[m].pProcessThreadInfo[nThreads].ContextSwitches
- ProcToMonitor->pProcThreadInfoStart[k].ContextSwitches;
DisplayTotalAndRate( ProcToMonitor->pProcThreadInfoStart[k].ContextSwitches,
pTask[m].pProcessThreadInfo[nThreads].ContextSwitches,
gldElapsedSeconds,
"Context Switches",
"sec."
);
FPRINTF(stdout, "\n");
OutputPercentValue( ProcToMonitor->pProcThreadInfoStart[k].UserTime.QuadPart,
pTask[m].pProcessThreadInfo[nThreads].UserTime.QuadPart,
gTotal2ElapsedTime64,
"User Time"
);
OutputPercentValue( ProcToMonitor->pProcThreadInfoStart[k].KernelTime.QuadPart,
pTask[m].pProcessThreadInfo[nThreads].KernelTime.QuadPart,
gTotal2ElapsedTime64,
"Kernel Time"
);
Index[k] = TRUE;
bFound = TRUE;
break;
}
}
if(!bFound){ // This is a new thread
PMODULE module = FindModuleForAddress64(ProcToMonitor, (DWORD64)pTask[m].pProcessThreadInfo[nThreads].StartAddress);
FPRINTF(stdout, "\nPid= %I64d, Tid= %I64d, StartAddr= 0x%p",
(DWORD64)(DWORD64 *)pTask[m].pProcessThreadInfo[nThreads].ClientId.UniqueProcess,
(DWORD64)(DWORD64 *)pTask[m].pProcessThreadInfo[nThreads].ClientId.UniqueThread,
pTask[m].pProcessThreadInfo[nThreads].StartAddress
);
if(module != NULL){
FPRINTF(stdout, " (%s) --->(NEW)\n", module->module_Name);
}else{
FPRINTF(stdout, " (unknown module) --->(NEW)\n");
}
FPRINTF(stdout, " Thread State , %15s\n",
ThreadState[pTask[m].pProcessThreadInfo[nThreads].ThreadState]
);
FPRINTF(stdout, " Wait Reason , %15s\n",
WaitReason[pTask[m].pProcessThreadInfo[nThreads].WaitReason]
);
OutputStartStopValues( 0,
pTask[m].pProcessThreadInfo[nThreads].WaitTime,
"Wait Time [.1 uSec]"
);
FPRINTF(stdout, " Base Priority , %15d\n",
pTask[m].pProcessThreadInfo[nThreads].BasePriority
);
FPRINTF(stdout, " Priority , %15d\n",
pTask[m].pProcessThreadInfo[nThreads].Priority
);
OutputStartStopValues( 0,
pTask[m].pProcessThreadInfo[nThreads].ContextSwitches,
"Context Switches"
);
Diff = pTask[m].pProcessThreadInfo[nThreads].ContextSwitches;
DisplayTotalAndRate( 0,
pTask[m].pProcessThreadInfo[nThreads].ContextSwitches,
gldElapsedSeconds,
"Context Switches",
"sec."
);
FPRINTF(stdout, "\n");
OutputPercentValue( 0,
pTask[m].pProcessThreadInfo[nThreads].UserTime.QuadPart,
gTotal2ElapsedTime64,
"User Time"
);
OutputPercentValue( 0,
pTask[m].pProcessThreadInfo[nThreads].KernelTime.QuadPart,
gTotal2ElapsedTime64,
"Kernel Time"
);
}
}
//
// Anything beyond this is a thread that is already GONE
//
k = ProcToMonitor->ProcPerfInfoStart.NumberOfThreads;
while (k--){
if(Index[k] == FALSE){
PMODULE module = FindModuleForAddress64(ProcToMonitor, (DWORD64)ProcToMonitor->pProcThreadInfoStart[k].StartAddress);
FPRINTF(stdout, "\nPid= %I64d, Tid= %I64d, StartAddr= 0x%p",
(DWORD64)(DWORD64 *)ProcToMonitor->pProcThreadInfoStart[k].ClientId.UniqueProcess,
(DWORD64)(DWORD64 *)ProcToMonitor->pProcThreadInfoStart[k].ClientId.UniqueThread,
ProcToMonitor->pProcThreadInfoStart[k].StartAddress
);
if(module != NULL){
FPRINTF(stdout, " (%s) --->(GONE)\n", module->module_Name);
}else{
FPRINTF(stdout, " (unknown module) --->(GONE)\n");
}
}
}
if(Index != NULL){
free(Index);
Index = NULL;
}
}//OutputThreadInfo()
VOID
DisplayRunningTasksSummary (
PTASK_LIST pTaskStart,
PTASK_LIST pTaskStop
)
/*++
Routine Description:
Displays a summary of all processes running at the start and at the end of Kernrate's run
and their average CPU utilization.
Processes that existed at the start count but not at the end count will be marked as "GONE".
Processes that exist at the end count but not at the start count will be marked as "NEW".
The list will miss short-lived processes that were created after the start count but went away before the end count.
Arguments:
pTaskStart - Pointer to the task list taken at the start
pTaskStart - Pointer to the task list taken at the end
Return Value:
None.
--*/
{
ULONG i, j;
BOOL bFound;
BOOL *Index = (BOOL *)calloc(gNumTasksStart, sizeof(BOOL));
if ( Index == NULL ){
FPRINTF(stderr, "KERNRATE: Failed to allocate memory for Index in DisplayRunningTasksSummary\n");
exit(1);
}
FPRINTF(stdout, "Found %u processes at the start point, %u processes at the stop point\n",
gNumTasksStart,
gNumTasksStop
);
FPRINTF(stdout, "Percentage in the following table is based on the Elapsed Time\n");
FPRINTF(stdout, "\n ProcessID, Process Name, Kernel Time, User-Mode Time, Idle Time\n\n");
for (i=0; i < gNumTasksStop; i++) {
bFound = FALSE;
for (j=0; j < gNumTasksStart; j++) {
if ( pTaskStop[i].ProcessId == pTaskStart[j].ProcessId ) {
long double UserPercentValue;
long double KernelPercentValue;
LARGE_INTEGER Diff;
Diff.QuadPart = pTaskStop[i].ProcessPerfInfo.KernelTime.QuadPart -
pTaskStart[j].ProcessPerfInfo.KernelTime.QuadPart;
KernelPercentValue = gTotalElapsedSeconds > 0? 100*(long double)Diff.QuadPart/(long double)gTotal2ElapsedTime64.QuadPart : 0;
Diff.QuadPart = pTaskStop[i].ProcessPerfInfo.UserTime.QuadPart -
pTaskStart[j].ProcessPerfInfo.UserTime.QuadPart;
UserPercentValue = gTotalElapsedSeconds > 0? 100*(long double)Diff.QuadPart/(long double)gTotal2ElapsedTime64.QuadPart : 0;
// Additional Perf Data can be added if needed
if( pTaskStop[i].ProcessId != 0 ) {
FPRINTF(stdout, "%12I64d, %32s, %10.2f%%, %10.2f%%\n",
pTaskStop[i].ProcessId,
pTaskStop[i].ProcessName,
KernelPercentValue,
UserPercentValue
);
} else { // This is the System Idle Process
FPRINTF(stdout, "%12I64d, %32s, %10.2f%%, %10.2f%%, ~%6.2f%%\n",
pTaskStop[i].ProcessId,
pTaskStop[i].ProcessName,
KernelPercentValue,
0.00,
KernelPercentValue
);
}
bFound = TRUE;
Index[j] = TRUE;
break;
}
}
//
// No match found in the START list, therefore this is a NEW process
//
if( !bFound ) {
long double UserPercentValue;
long double KernelPercentValue;
LARGE_INTEGER Diff;
Diff.QuadPart = pTaskStop[i].ProcessPerfInfo.KernelTime.QuadPart;
KernelPercentValue = gTotalElapsedSeconds > 0? 100*(long double)Diff.QuadPart/(long double)gTotal2ElapsedTime64.QuadPart : 0;
Diff.QuadPart = pTaskStop[i].ProcessPerfInfo.UserTime.QuadPart;
UserPercentValue = gTotalElapsedSeconds > 0? 100*(long double)Diff.QuadPart/(long double)gTotal2ElapsedTime64.QuadPart : 0;
FPRINTF(stdout, "%12I64d, %32s, %10.2f%%, %10.2f%%, NEW\n",
pTaskStop[i].ProcessId,
pTaskStop[i].ProcessName,
KernelPercentValue,
UserPercentValue
);
}
}//for(i)
//
// No match found in the STOP list, therefore this process is GONE
//
for (j=0; j < gNumTasksStart; j++){
if( Index[j] == FALSE ) {
FPRINTF(stdout, "%12I64d, %32s, GONE\n",
pTaskStart[j].ProcessId,
pTaskStart[j].ProcessName
);
}
}
//
// cleanup
//
if(Index != NULL){
free(Index);
Index = NULL;
}
} //DisplayRunningTasksSummary()
PMODULE
FindModuleForAddress64(
PPROC_TO_MONITOR ProcToMonitor,
DWORD64 Address
)
{
PMODULE Module = ProcToMonitor->ModuleList;
while( Module != NULL ) {
if( ( (PVOID)Address >= (PVOID)Module->Base) && ( (PVOID)Address < (PVOID)(Module->Base + (ULONG64)Module->Length) ) ){
return (Module);
}
Module = Module->Next;
}
return (NULL);
} //FindModuleForAddress64()
VOID
OutputLineFromAddress64(
HANDLE hProc,
DWORD64 qwAddr,
PIMAGEHLP_LINE64 pLine
)
/*++
Routine Description:
Gets source-code line number and file information for a given address
and outputs the data
Arguments:
hProc - Imagehlp Handle to the process
qwAddr - Address for which we need the source code line information
pLine - Pointer to a user allocated IMAGEHLP_LINE64 struct
Return Value:
None.
Notes:
If successful, the data returned in pLine includes the source-code line number,
the full path to the source file, the displacement (not used here) and the address
where the first instruction is encountered in the line.
If unsuccessful, nothing is printed. In most cases this is due to an unmatching
symbol file or a supplied address that does not contain code instructions.
This can also happen if the pdb file does not contain source line information.
--*/
{
DWORD dwDisplacement = 0;
if ( SymGetLineFromAddr64( hProc, qwAddr, &dwDisplacement, pLine) ){
FPRINTF(stdout, " (line %ld in %s, 0x%I64x)",
pLine->LineNumber,
pLine->FileName,
pLine->Address
);
}
}//OutputLineFromAddress64
//MC
BOOL
InitializeManagedCodeSupport(
PPROC_TO_MONITOR ProcToMonitor
)
/*++
Routine Description:
If the main LKR library (mscoree.dll) is loaded, load the managed code helper (ip2md.dll) unless it's already loaded
and get pointers to its main function calls. If successful, get the JIT ranges for the current process.
If no JIT ranges are identified - return failure, but don't unload the helper library yet in case another process
needs it.
Arguments:
ProcToMonitor - Pointer to the structure of the process being monitored
Return Value:
TRUE for success, FALSE for Failure
--*/
{
BOOL bRet = FALSE;
// Managed code (CLR) currently not supported on IA64 - just return FALSE on initialization attempt
#if defined(_X86_)
int i,j;
if( bMCHelperLoaded != TRUE ){
VerbosePrint((VERBOSE_INTERNALS, "KERNRATE: Found mscoree.dll, loading ip2md.dll Managed code helper\n"));
ghMCLib = LoadLibrary( MANAGED_CODE_SYMHLPLIB );
if(ghMCLib == NULL){
VerbosePrint((VERBOSE_INTERNALS, "KERNRATE: Failed to load Managed Code helper library (ip2md.dll)\n"));
}else{
bMCHelperLoaded = TRUE;
VerbosePrint((VERBOSE_INTERNALS, "KERNRATE: Succsessfully loaded ip2md.dll Managed code helper\n"));
pfnAttachToProcess = (PFN1)GetProcAddress(ghMCLib, "AttachToProcess");
if(!pfnAttachToProcess){
VerbosePrint((VERBOSE_INTERNALS, "KERNRATE: Failed to get proc address for pfnAttachToProcess from ip2md.dll\n"));
bMCHelperLoaded = FALSE;
}
pfnDetachFromProcess = (PFN2)GetProcAddress(ghMCLib, "DetachFromProcess");
if(!pfnDetachFromProcess){
VerbosePrint((VERBOSE_INTERNALS, "KERNRATE: Failed to get proc address for pfnDetachFromProcess from ip2md.dll\n"));
bMCHelperLoaded = FALSE;
}
pfnIP2MD = (PFN3)GetProcAddress(ghMCLib, "IP2MD");
if(!pfnIP2MD){
VerbosePrint((VERBOSE_INTERNALS, "KERNRATE: Failed to get proc address for pfnIP2MD from ip2md.dll\n"));
bMCHelperLoaded = FALSE;
}
pfnGetJitRange = (PFN4)GetProcAddress(ghMCLib, "GetJitRange");
if(!pfnGetJitRange){
VerbosePrint((VERBOSE_INTERNALS, "KERNRATE: Failed to get proc address for pfnGetJitRange from ip2md.dll\n"));
bMCHelperLoaded = FALSE;
}
if( bMCHelperLoaded == FALSE && ghMCLib != NULL){
FreeLibrary(ghMCLib);
ghMCLib = NULL;
}
}
}
if( bMCHelperLoaded == TRUE ){
//
//Initialization will be considered successful at this point even if there are no JIT ranges
//We'll keep the helper library loaded because there may be some Pre-Generated modules loaded as modules in the process
//
bRet = TRUE;
VerbosePrint((VERBOSE_INTERNALS, "KERNRATE: Attaching to Process %I64d to get JIT ranges\n", ProcToMonitor->Pid));
pfnAttachToProcess( (DWORD)ProcToMonitor->Pid );
ProcToMonitor->JITHeapLocationsStart = pfnGetJitRange();
if( ProcToMonitor->JITHeapLocationsStart == NULL){
VerbosePrint((VERBOSE_INTERNALS,
"KERNRATE: No JIT heap locations returned for Pid= %I64d, detaching from process\n",
ProcToMonitor->Pid
));
} else {
if(gVerbose & VERBOSE_INTERNALS){
FPRINTF(stderr, "KERNRATE: Identified JIT ranges (Name, BaseAddr, Length:\n");
i = 0;
j = 0;
while( ProcToMonitor->JITHeapLocationsStart[i] != 0 ){
FPRINTF(stderr, "JIT%d, 0x%lx, %ld\n",
j,
ProcToMonitor->JITHeapLocationsStart[i],
ProcToMonitor->JITHeapLocationsStart[i+1]
);
i+=2;
j+=1;
}
}
bMCJitRangesExist = TRUE;
}
pfnDetachFromProcess();
}
#endif //defined(_X86_)
return (bRet);
} //InitializeManagedCodeSupport()
VOID
OutputJITRangeComparison(
PPROC_TO_MONITOR ProcToMonitor
)
/*++
Routine Description:
Compares the lists of a process JIT ranges before and after the profiling.
The list will miss short-lived JIT ranges that were created after the start count but went away before the end count.
Arguments:
ProcToMonitor - Pointer to the structure of the process being monitored
Return Value:
None.
--*/
{
ULONG i,j, jcount;
BOOL bFound;
BOOL *index;
// We already checked if ProcToMonitor->JITHeapLocationsStart != NULL before calling
jcount = 0;
if( ProcToMonitor->JITHeapLocationsStop != NULL){
while ( ProcToMonitor->JITHeapLocationsStop[jcount] ){
jcount += 2;
}
}
index = (BOOL *)calloc( jcount, sizeof(BOOL) );
if( index == NULL ){
FPRINTF(stderr, "KERNRATE: Failed to allocate memory for index array in OutputJITRangeComparison\n");
return;
}
i = 0;
FPRINTF(stdout, "\n---------------------- JIT RANGES STATUS AFTER DATA PROCESSING ------------------------\n\n");
FPRINTF(stdout, " Base-Address, Size, Status \n");
while ( ProcToMonitor->JITHeapLocationsStart[i] ){
j = 0;
bFound = FALSE;
if(ProcToMonitor->JITHeapLocationsStop != NULL){
while ( ProcToMonitor->JITHeapLocationsStop[j] ){
if( (ProcToMonitor->JITHeapLocationsStart[i] == ProcToMonitor->JITHeapLocationsStop[j]) &&
(ProcToMonitor->JITHeapLocationsStart[i+1] == ProcToMonitor->JITHeapLocationsStop[j+1]) ){
FPRINTF(stdout, "0x%lx %12ld EXISTS\n",
ProcToMonitor->JITHeapLocationsStart[i],
ProcToMonitor->JITHeapLocationsStart[i+1]
);
bFound = TRUE;
index[j] = TRUE;
break;
}else{
j += 2;
}
}
}
if(!bFound){
FPRINTF(stdout, "0x%lx %12ld GONE\n",
ProcToMonitor->JITHeapLocationsStart[i],
ProcToMonitor->JITHeapLocationsStart[i+1]
);
}
i += 2;
}
if( ProcToMonitor->JITHeapLocationsStop != NULL){
j = 0;
while ( ProcToMonitor->JITHeapLocationsStop[j] ){
if( index[j] == FALSE ){
FPRINTF(stdout, "0x%lx %12ld NEW\n",
ProcToMonitor->JITHeapLocationsStop[j],
ProcToMonitor->JITHeapLocationsStop[j+1]
);
}
j += 2;
}
}
if( index != NULL ){
free(index);
index = NULL;
}
FPRINTF(stdout, "\n-----------------------------------------------------------\n\n");
} //OutputJITRangeComparison()
//MC
VOID
OutputRawDataForZoomModule(
IN FILE *Out,
IN PPROC_TO_MONITOR ProcToMonitor,
IN PMODULE Current
)
/*++
Routine Description:
Outputs raw profile data for every bucket in a zoom module
Arguments:
Out - Supplies the file pointer to output to.
ProcToMonitor - Pointer to the struct of the current process.
Current - Pointer to the current zoom module struct.
Return Value:
None.
Notes:
The routine will also try to find other sharers of the same bucket
If the routine fails to find a symbol for an address it will try
to find a Managed Code JIT method at that address, provided that
proper Managed Code support exists
--*/
{
PRATE_DATA RateData;
LONG CpuNumber;
ULONG i, ProfileSourceIndex, Index;
//MC
ULONGLONG TempTotal, TotCount, TotUnknownSymbolCount, TotJITCount, TotPreJITCount;
//MC
CHAR LastSymbol[cMODULE_NAME_STRLEN];
HANDLE SymHandle = ProcToMonitor->ProcessHandle;
BOOL bLastSymbolExists;
BOOL bAttachedToProcess = FALSE;
BOOL bPrintLastTotal = FALSE;
BOOL bPrintJITLastTotal = FALSE;
BOOL bPrintPREJITLastTotal = FALSE;
PIMAGEHLP_LINE64 pLine = malloc(sizeof(IMAGEHLP_LINE64));
if (pLine == NULL) {
FPRINTF(stderr, "KERNRATE: Buffer allocation failed for pLine in OutputResults\n");
exit(1);
}
pLine->SizeOfStruct = sizeof(IMAGEHLP_LINE64);
for (Index=0; Index < gTotalActiveSources; Index++) {
ProfileSourceIndex = gulActiveSources[Index];
FPRINTF(Out,
"\n---- RAW %s Profile: Source= %s, Module Base= 0x%I64x\n",
Current->module_Name,
ProcToMonitor->Source[ProfileSourceIndex].Name,
Current->Base
);
FPRINTF(Out, "Function Name+Displacement[Address], Bucket Number, Total Bucket Hits");
if (bProfileByProcessor) {
FPRINTF(Out, ", Hits on Processor(s)");
for (CpuNumber=0; CpuNumber < gProfileProcessors; CpuNumber++) {
if( gAffinityMask && !((1<<CpuNumber) & gAffinityMask) )continue;
FPRINTF(Out," %2d,", CpuNumber);
}
}
FPRINTF(Out, " Source-Line, Source-File, Addr. of First Instruction\n\n");
TotCount = 0;
TotUnknownSymbolCount = 0;
//MC
TotJITCount = 0;
TotPreJITCount = 0;
//MC
bLastSymbolExists = FALSE;
RateData = &Current->Rate[ProfileSourceIndex];
for (i=0; i < BUCKETS_NEEDED(Current->Length) ; i++) {
TempTotal = 0;
for (CpuNumber=0; CpuNumber < gProfileProcessors; CpuNumber++) {
TempTotal += RateData->ProfileBuffer[CpuNumber][i];
}
if ( TempTotal > 0 ) {
// TF - FIXFIX - 07/2000.
// The current version of kernrate and kernel profiling objects code
// assume code section < 4GB.
//
ULONG64 ip = Current->Base + (ULONG64)(i * gZoomBucket);
ULONG64 disp = 0;
if ( !SymGetSymFromAddr64(SymHandle, ip, &disp, gSymbol ) ) {
//MC
//
// No Symbol, Let's check if this is a managed code address
//
int Retval = 0;
ULONG64 ipMax = ip + (ULONG64)gZoomBucket;
if( bMCHelperLoaded == TRUE) {
if( !bAttachedToProcess ){
pfnAttachToProcess((DWORD)ProcToMonitor->Pid);
bAttachedToProcess = TRUE;
}
//
// Try to find a Managed Code symbol anywhere in the bucket
//
while (Retval == 0 && ip < ipMax){
Retval = pfnIP2MD((DWORD_PTR)ip, &gwszSymbol);
ip += 1;
if( Retval > 0 && gwszSymbol == NULL ){
Retval = 0;
continue;
}
}
if( Retval > 0 && gwszSymbol != NULL ){
if( Retval == 1 ){
FPRINTF(Out, "%S[0x%I64x], %10Ld, %10Ld, JIT_TYPE",
gwszSymbol,
ip,
i,
TempTotal
);
TotJITCount += TempTotal;
}
else if(Retval == 2){
FPRINTF(Out, "%S[0x%I64x], %10Ld, %10Ld, PRE-JITTED_TYPE",
gwszSymbol,
ip,
i,
TempTotal
);
TotPreJITCount += TempTotal;
}
}
//
// Print other symbols in this bucket by incrementing
// by 2 bytes at a time. Note that we start from the ip reached above
// so the loop below may not get executed at all
//
ip += (ULONG64)2;
bPrintJITLastTotal = TRUE;
bPrintPREJITLastTotal = TRUE;
while( ip < ipMax ) {
WCHAR lastSym[cMODULE_NAME_STRLEN] = L"";
int retv = 0;
if(gwszSymbol != NULL){
wcsncpy( lastSym, gwszSymbol, cMODULE_NAME_STRLEN-1 );
_wcsset(&lastSym[ cMODULE_NAME_STRLEN-1 ], L'\0');
}
retv = pfnIP2MD( (DWORD_PTR)ip, &gwszSymbol );
if ( retv > 0 && gwszSymbol != NULL ) {
if ( wcscmp( lastSym, gwszSymbol ) ) {
if(retv == 1){
FPRINTF(Out, "JIT Module Total Count = %Ld\n\n", TotJITCount);
TotJITCount = 0;
}
else if(retv == 2){
FPRINTF(Out, "PRE-JITTED Module Total Count = %Ld\n\n", TotPreJITCount);
TotPreJITCount = 0;
}
FPRINTF( Out, " %S[0x%I64x]\n", gwszSymbol, ip );
wcsncpy( lastSym, gwszSymbol , cMODULE_NAME_STRLEN-1);
_wcsset( &lastSym[ cMODULE_NAME_STRLEN-1 ], L'\0');
bPrintJITLastTotal = FALSE;
bPrintPREJITLastTotal = FALSE;
}
}
ip += (ULONG64)2;
} // End of while( ip < ipMax )
} // if( bMCHelperLoaded )
//MC
//
// No Symbol and no managed code symbol found
//
if(!Retval){
FPRINTF(Out, "Unknown_Symbol+0x%I64x[0x%I64x], %10Ld, %10Ld",
disp,
ip,
i,
TempTotal
);
TotUnknownSymbolCount += TempTotal;
}
if (bProfileByProcessor) {
for (CpuNumber=0; CpuNumber < gProfileProcessors; CpuNumber++) {
if( gAffinityMask && !((1<<CpuNumber) & gAffinityMask) )continue;
FPRINTF( Out,", %10Ld",
RateData->ProfileBuffer[CpuNumber][i]
);
}
}
FPRINTF(Out,"\n");
}
else {
CHAR symName[cMODULE_NAME_STRLEN];
ULONG64 ipMax;
if (i > 0 && strcmp( LastSymbol, gSymbol->Name ) ) {
if ( bLastSymbolExists )
FPRINTF(Out, "Module Total Count = %Ld\n\n", TotCount);
TotCount = 0;
strncpy(LastSymbol, gSymbol->Name, cMODULE_NAME_STRLEN-1);
LastSymbol[ cMODULE_NAME_STRLEN-1 ] = '\0';
bLastSymbolExists = TRUE;
}
TotCount += TempTotal;
_snprintf( symName, cMODULE_NAME_STRLEN*sizeof(CHAR)-1, "%s+0x%I64x[0x%I64x]", gSymbol->Name, disp, ip );
symName[cMODULE_NAME_STRLEN-1] = '\0';
if ( bRawDisasm ) {
CHAR sourceCode[528];
#ifndef DISASM_AVAILABLE
// Thierry - FIXFIX - 07/2000.
// dbg is not helping... The old disassembly APIs no longer work.
// I have to re-write a full disassembly wrapper.
#define Disasm( IpAddr, Buffer, Flag ) 0
#endif // !DISASM_AVAILABLE
if ( Disasm( &ip, sourceCode, FALSE ) ) {
FPRINTF( Out,"%-40s, %10Ld, %10Ld, %s",
symName,
i,
TempTotal,
sourceCode
);
if (bProfileByProcessor) {
for (CpuNumber=0; CpuNumber < gProfileProcessors; CpuNumber++) {
if( gAffinityMask && !((1<<CpuNumber) & gAffinityMask) )continue;
FPRINTF( Out,", %10Ld",
RateData->ProfileBuffer[CpuNumber][i]
);
}
}
FPRINTF(Out,"\n");
}
else {
FPRINTF( Out,"%-40s, %10Ld, %10Ld<disasm:?????>",
symName,
i,
TempTotal
);
if (bProfileByProcessor) {
for (CpuNumber=0; CpuNumber < gProfileProcessors; CpuNumber++) {
if( gAffinityMask && !((1<<CpuNumber) & gAffinityMask) )continue;
FPRINTF( Out,", %10Ld",
RateData->ProfileBuffer[CpuNumber][i]
);
}
}
FPRINTF(Out,"\n");
}
}
else {
FPRINTF( Out,"%-40s, %10Ld, %10Ld",
symName,
i,
TempTotal
);
if (bProfileByProcessor) {
for (CpuNumber=0; CpuNumber < gProfileProcessors; CpuNumber++) {
if( gAffinityMask && !((1<<CpuNumber) & gAffinityMask) )continue;
FPRINTF( Out,", %10Ld",
RateData->ProfileBuffer[CpuNumber][i]
);
}
}
OutputLineFromAddress64( SymHandle, ip, pLine);
FPRINTF(Out,"\n");
} //if(bRawDisasm)
//
// Print other symbols in this bucket by incrementing
// by 2 bytes at a time.
//
ipMax = ip + (ULONG64)gZoomBucket;
ip += (ULONG64)2;
bPrintLastTotal = TRUE;
while( ip < ipMax ) {
CHAR lastSym[cMODULE_NAME_STRLEN];
strncpy( lastSym, gSymbol->Name, cMODULE_NAME_STRLEN-1 );
lastSym[ cMODULE_NAME_STRLEN-1 ] = '\0';
if ( SymGetSymFromAddr64(SymHandle, ip, &disp , gSymbol ) ) {
if ( strcmp( lastSym, gSymbol->Name ) ) {
FPRINTF(Out, "Module Total Count = %Ld\n\n", TotCount);
TotCount = 0;
FPRINTF( Out, " %s+0x%I64x[0x%I64x] ", gSymbol->Name, disp, ip );
OutputLineFromAddress64( SymHandle, ip, pLine);
FPRINTF(stdout, "\n");
strncpy( lastSym, gSymbol->Name , cMODULE_NAME_STRLEN-1);
lastSym[ cMODULE_NAME_STRLEN-1 ] = '\0';
bPrintLastTotal = FALSE;
}
}
ip += (ULONG64)2;
} // End of while( ip < ipMax )
} //If(SymGetSymFromAddr64)
} //if(TempTotal > 0)
} // i bucket loop
//MC
if ( bPrintLastTotal == TRUE )
FPRINTF(Out, "Module Total Count (not including JIT, PRE-JIT and Unknown) = %Ld\n\n", TotCount);
if ( TotUnknownSymbolCount > 0 )
FPRINTF(Out, "Module Total Count For Unknown Symbols = %Ld\n\n", TotUnknownSymbolCount);
if( (TotJITCount > 0) && (bPrintJITLastTotal == TRUE) )
FPRINTF(Out, "Module Total Count For JIT type = %Ld\n\n", TotJITCount);
if( (TotPreJITCount > 0) && (bPrintPREJITLastTotal == TRUE) )
FPRINTF(Out, "Module Total Count For PRE-JITTED type = %Ld\n\n", TotPreJITCount);
//MC
} // Sources loop
if( pLine != NULL ){
free(pLine);
pLine = NULL;
}
}//OutputRawDataForZoomModule()
PSYSTEM_BASIC_INFORMATION
GetSystemBasicInformation(
VOID
)
{
NTSTATUS status;
PSYSTEM_BASIC_INFORMATION SystemInformation = NULL;
SystemInformation = malloc(sizeof(SYSTEM_BASIC_INFORMATION));
if(SystemInformation == NULL){
FPRINTF(stderr, "Buffer allocation failed for SystemInformation in GetSystemBasicInformation\n");
exit(1);
}
status = NtQuerySystemInformation(SystemBasicInformation,
SystemInformation,
sizeof(SYSTEM_BASIC_INFORMATION),
NULL);
if (!NT_SUCCESS(status)) {
FPRINTF(stderr, "NtQuerySystemInformation failed status %08lx\n",status);
if ( SystemInformation != NULL ){
free( SystemInformation );
SystemInformation = NULL;
}
}
return (SystemInformation);
}
VOID
InitializeKernrate(
int argc,
char *argv[]
)
{
ULONG j;
BOOL OSCompat = FALSE;
gOSInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO) ;
GetVersionEx(&gOSInfo);
if ( gOSInfo.dwPlatformId == VER_PLATFORM_WIN32_NT ){
if ( gOSInfo.dwMajorVersion >= 5 ){
OSCompat = TRUE;
}
}
if ( OSCompat == FALSE ){
FPRINTF(stderr, "This version of Kernrate will only run on Windows 2000 or above\n");
exit(1);
}
//
// Initialize gSourceMaximum, find supported sources
// The dummy process will be used later to store user-defined event rates with the -i command line option
//
gMaxSimultaneousSources = MAX_SIMULTANEOUS_SOURCES;
gpProcDummy = calloc(1, sizeof(PROC_TO_MONITOR));
if (gpProcDummy==NULL) {
FPRINTF(stderr, "KERNRATE: Allocation for Process failed for the -I or -L options\n");
exit(1);
}
gSourceMaximum = InitializeProfileSourceInfo(gpProcDummy);
if ( gSourceMaximum == 0 || gStaticCount == 0 ) {
FPRINTF( stderr, "KERNRATE: no profile source detected for this machine...\n" );
exit(0);
}
// This will guaranty that we'll not enable any unwanted profiling
for (j=0; j < gSourceMaximum; j++) {
if( j < gStaticCount ) {
gpProcDummy->Source[j].Interval = gStaticSource[j].Interval;
if( j == SOURCE_TIME && gStaticSource[j].Interval == 0 )
gpProcDummy->Source[j].Interval = KRATE_DEFAULT_TIMESOURCE_RATE; //IA64 default is zero
} else {
gpProcDummy->Source[j].Interval = 0;
}
}
//
// Get the default IMAGEHLP global option mask
// NOTE: This global variable could be changed by GetConfigurations().
// It is required to initialize it before calling this function.
//
gSymOptions = SymGetOptions();
if ( gVerbose & VERBOSE_IMAGEHLP ) {
FPRINTF( stderr, "KERNRATE: default IMAGEHLP SymOptions: %s\n", GetSymOptionsValues( gSymOptions ) );
}
//Bump kernrate's security privileges to debugger priveleges for tough guys like csrss.exe and dllhost.exe
if (!InitializeAsDebugger()){
FPRINTF(stderr, "KERNRATE: Unable to Get Debugger Security Privileges\n");
exit(1);
}
//
// Get some basic info about the system, determine the number of processors
//
gSysBasicInfo = GetSystemBasicInformation();
if( gSysBasicInfo == NULL){
FPRINTF(stderr, "KERNRATE: Failed to get SYSTEM_BASIC_INFORMATION\n");
exit(1);
}
//
// Get initial parameters from the Command Line
//
GetConfiguration(argc, argv);
//
// Initialize dbghelp
//
// Note that gSymOptions could have been modified in GetConfiguration().
//
SymSetOptions( gSymOptions );
if ( gVerbose & VERBOSE_IMAGEHLP ) {
FPRINTF( stderr, "KERNRATE: current IMAGEHLP SymOptions: %s\n", GetSymOptionsValues( gSymOptions ) );
}
gSymbol = (PIMAGEHLP_SYMBOL64) malloc( sizeof(IMAGEHLP_SYMBOL64) + MAX_SYMNAME_SIZE );
if ( gSymbol == NULL ) {
FPRINTF(stderr, "KERNRATE: Allocation for gSymbol failed\n");
exit(1);
}
gSymbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
gSymbol->MaxNameLength = MAX_SYMNAME_SIZE;
}//InitializeKernrate()
VOID
InitAllProcessesModulesInfo(
VOID
)
{
PPROC_TO_MONITOR ProcToMonitor = NULL;
PMODULE ZoomModule;
ULONG i, j;
//
// Deal with Kernel Profile Initialization
//
if(gNumProcToMonitor == 0 || bCombinedProfile == TRUE ){
if( !InitializeKernelProfile()){
FPRINTF(stderr, "Failed to Initialize Kernel Profile\n");
exit(1);
}
}
//
// Deal with per-Process Profile Initialization
//
// Get information on kernel and/or process modules
//
ProcToMonitor = gProcessList;
for (i=0; i < gNumProcToMonitor; i++){
if(ProcToMonitor->ProcessHandle != SYM_KERNEL_HANDLE){
if ( gVerbose & VERBOSE_IMAGEHLP ) {
FPRINTF(stdout, "ProcessID= %I64d\n", ProcToMonitor->Pid);
}
SymInitialize( ProcToMonitor->ProcessHandle, NULL, FALSE );
if(!bSymPathInitialized) {
InitSymbolPath( ProcToMonitor->ProcessHandle );
}
else {
SymSetSearchPath(ProcToMonitor->ProcessHandle, gSymbolPath);
}
if ( SymRegisterCallback64( ProcToMonitor->ProcessHandle, SymbolCallbackFunction, (ULONG64)&gCurrentModule ) != TRUE ) {
FPRINTF( stderr, "KERNRATE: Failed to register callback for IMAGEHLP process-handle operations...\n" );
}
ProcToMonitor->ModuleList = GetProcessModuleInformation(ProcToMonitor);
if(ProcToMonitor->ModuleList != NULL)
ProcToMonitor->ModuleList->mu.bProfileStarted = FALSE;
for(j=0; j<gSourceMaximum; j++){
ProcToMonitor->Source[j].bProfileStarted = FALSE;
}
}
else{
ProcToMonitor->ModuleList = GetKernelModuleInformation();
if(ProcToMonitor->ModuleList != NULL)
ProcToMonitor->ModuleList->mu.bProfileStarted = FALSE;
for(j=0; j < gSourceMaximum; j++){
ProcToMonitor->Source[j].bProfileStarted=FALSE;
}
}
//
// Any remaining entries on the ZoomList are liable to be errors.
//
if(gVerbose & VERBOSE_MODULES){
ZoomModule = ProcToMonitor->ZoomList;
while (ZoomModule != NULL) {
PMODULE Module = ProcToMonitor->ModuleList;
BOOL bFound = FALSE;
while (Module != NULL) {
if( 0 == _stricmp(ZoomModule->module_Name, Module->module_Name) ||
0 == _stricmp(ZoomModule->module_Name, Module->module_FileName) ){
FPRINTF(stdout,
"===>KERNRATE: Requested zoom module %s was found in the process modules list\n",
ZoomModule->module_Name
);
bFound = TRUE;
break;
}
Module = Module->Next;
}
if(!bFound){
FPRINTF(stdout,
"===>KERNRATE: Requested zoom module %s was not found in the process modules list, ignoring\n",
ZoomModule->module_Name
);
}
ZoomModule = ZoomModule->Next;
}
}
CleanZoomModuleList(ProcToMonitor); //Done with the zoom list - cleanup for reuse in the post processing
ProcToMonitor = ProcToMonitor->Next;
} //for
}//InitAllProcessesModulesInfo
VOID
CleanZoomModuleList(
PPROC_TO_MONITOR Proc
)
{
PMODULE Temp = Proc->ZoomList;
while (Temp != NULL) {
Proc->ZoomList = Proc->ZoomList->Next;
free(Temp);
Temp = NULL;
Temp = Proc->ZoomList;
}
} // CleanZoomModuleList()
VOID
ExecuteProfiles(
BOOL bMode
)
{
ULONG i,j;
DWORD Error = ERROR_SUCCESS;
PPROC_TO_MONITOR ProcToMonitor = NULL;
ULONG ProcessActiveSource = 0;
ULONG LastActiveSource = 0;
if(bMode){
//old scheme - turn on one source at a time, switch sources every ChangeInterval ms
// Note that this will cause the total profile time to be devided equally
// between the N processes being monitored, each being profiled for
// an amount of TotalTime/N seconds...
do{
ProcToMonitor = gProcessList;
for (i=0; i<gNumProcToMonitor; i++){
if( ProcToMonitor->ModuleList != NULL ){
do{
LastActiveSource = ProcessActiveSource;
ProcessActiveSource = NextSource(ProcessActiveSource,
ProcToMonitor->ModuleList,
ProcToMonitor
);
ProcToMonitor->ModuleList->mu.bProfileStarted = TRUE;
Error = WaitForSingleObject(ghDoneEvent, gChangeInterval);
}while(ProcessActiveSource > LastActiveSource);
StopSource(ProcessActiveSource, ProcToMonitor->ModuleList, ProcToMonitor);
}
ProcToMonitor->Source[ProcessActiveSource].bProfileStarted = FALSE;
ProcToMonitor = ProcToMonitor->Next;
}
} while ( Error == WAIT_TIMEOUT );
} else { // new scheme - turn on all requested profile sources and let them run simultaneously
ProcToMonitor = gProcessList;
for (i=0; i<gNumProcToMonitor; i++){
if( ProcToMonitor->ModuleList != NULL ){
for (j=0; j < gTotalActiveSources; j++){
StartSource(gulActiveSources[j], ProcToMonitor->ModuleList, ProcToMonitor);
}
}
ProcToMonitor = ProcToMonitor->Next;
}
do{
Error = WaitForSingleObject(ghDoneEvent, gChangeInterval); //ChangeInterval does not switch any sources here
} while ( Error == WAIT_TIMEOUT );
ProcToMonitor = gProcessList;
for (i=0; i<gNumProcToMonitor; i++){
if( ProcToMonitor->ModuleList != NULL ){
for (j=0; j < gTotalActiveSources; j++){
StopSource(gulActiveSources[j], ProcToMonitor->ModuleList, ProcToMonitor);
}
}
ProcToMonitor = ProcToMonitor->Next;
}
} //if(bMode)
}//ExecuteProfiles()
VOID
DisplaySystemWideInformation(
PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION SystemInfoBegin,
PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION SystemInfoEnd
)
{
TIME_FIELDS Time;
ULONG i;
ULONG InterruptCounts, TotalInterruptCounts;
LARGE_INTEGER Elapsed, Idle, Kernel, User, Dpc, Interrupt;
LARGE_INTEGER TotalElapsed, TotalIdle, TotalKernel, TotalUser, TotalDpc, TotalInterrupt;
TotalElapsed.QuadPart = 0;
TotalIdle.QuadPart = 0;
TotalKernel.QuadPart = 0;
TotalUser.QuadPart = 0;
TotalDpc.QuadPart = 0;
TotalInterrupt.QuadPart = 0;
TotalInterruptCounts = 0;
FPRINTF(stdout, "\n------------Overall Summary:--------------\n\n");
for (i=0; i<(ULONG)gSysBasicInfo->NumberOfProcessors; i++) {
double n;
long double m;
Idle.QuadPart = SystemInfoEnd[i].IdleTime.QuadPart - SystemInfoBegin[i].IdleTime.QuadPart;
User.QuadPart = SystemInfoEnd[i].UserTime.QuadPart - SystemInfoBegin[i].UserTime.QuadPart;
Kernel.QuadPart = SystemInfoEnd[i].KernelTime.QuadPart - SystemInfoBegin[i].KernelTime.QuadPart;
Elapsed.QuadPart = Kernel.QuadPart + User.QuadPart;
Kernel.QuadPart -= Idle.QuadPart;
Dpc.QuadPart = SystemInfoEnd[i].DpcTime.QuadPart - SystemInfoBegin[i].DpcTime.QuadPart;
Interrupt.QuadPart = SystemInfoEnd[i].InterruptTime.QuadPart - SystemInfoBegin[i].InterruptTime.QuadPart;
InterruptCounts = SystemInfoEnd[i].InterruptCount - SystemInfoBegin[i].InterruptCount;
TotalKernel.QuadPart += Kernel.QuadPart;
TotalUser.QuadPart += User.QuadPart;
TotalIdle.QuadPart += Idle.QuadPart;
TotalElapsed.QuadPart += Elapsed.QuadPart;
TotalDpc.QuadPart += Dpc.QuadPart;
TotalInterrupt.QuadPart += Interrupt.QuadPart;
TotalInterruptCounts += InterruptCounts;
FPRINTF(stdout, "P%d ",i);
RtlTimeToTimeFields(&Kernel, &Time);
n = UInt64ToDoublePerCent( Kernel.QuadPart, Elapsed.QuadPart );
FPRINTF(stdout, " K %ld:%02ld:%02ld.%03ld (%4.1f%%)",
Time.Hour,
Time.Minute,
Time.Second,
Time.Milliseconds,
n );
RtlTimeToTimeFields(&User, &Time);
n = UInt64ToDoublePerCent( User.QuadPart, Elapsed.QuadPart );
FPRINTF(stdout, " U %ld:%02ld:%02ld.%03ld (%4.1f%%)",
Time.Hour,
Time.Minute,
Time.Second,
Time.Milliseconds,
n );
RtlTimeToTimeFields(&Idle, &Time);
n = UInt64ToDoublePerCent( Idle.QuadPart, Elapsed.QuadPart );
FPRINTF(stdout, " I %ld:%02ld:%02ld.%03ld (%4.1f%%)",
Time.Hour,
Time.Minute,
Time.Second,
Time.Milliseconds,
n );
RtlTimeToTimeFields(&Dpc, &Time);
n = UInt64ToDoublePerCent( Dpc.QuadPart, Elapsed.QuadPart );
FPRINTF(stdout, " DPC %ld:%02ld:%02ld.%03ld (%4.1f%%)",
Time.Hour,
Time.Minute,
Time.Second,
Time.Milliseconds,
n );
RtlTimeToTimeFields(&Interrupt, &Time);
n = UInt64ToDoublePerCent( Interrupt.QuadPart, Elapsed.QuadPart );
FPRINTF(stdout, " Interrupt %ld:%02ld:%02ld.%03ld (%4.1f%%)\n",
Time.Hour,
Time.Minute,
Time.Second,
Time.Milliseconds,
n );
m = Elapsed.QuadPart > 0 ? (long double)10000000 * (long double)InterruptCounts / (long double)Elapsed.QuadPart : 0;
FPRINTF(stdout, " Interrupts= %ld, Interrupt Rate= %.0f/sec.\n\n",
InterruptCounts,
m );
}
if (gSysBasicInfo->NumberOfProcessors > 1) {
double n;
long double m;
FPRINTF(stdout, "TOTAL");
RtlTimeToTimeFields(&TotalKernel, &Time);
n = UInt64ToDoublePerCent( TotalKernel.QuadPart, TotalElapsed.QuadPart );
FPRINTF(stdout, " K %ld:%02ld:%02ld.%03ld (%4.1f%%)",
Time.Hour,
Time.Minute,
Time.Second,
Time.Milliseconds,
n );
RtlTimeToTimeFields(&TotalUser, &Time);
n = UInt64ToDoublePerCent( TotalUser.QuadPart, TotalElapsed.QuadPart );
FPRINTF(stdout, " U %ld:%02ld:%02ld.%03ld (%4.1f%%)",
Time.Hour,
Time.Minute,
Time.Second,
Time.Milliseconds,
n );
RtlTimeToTimeFields(&TotalIdle, &Time);
n = UInt64ToDoublePerCent( TotalIdle.QuadPart, TotalElapsed.QuadPart );
gTotalIdleTime = n;
FPRINTF(stdout, " I %ld:%02ld:%02ld.%03ld (%4.1f%%)",
Time.Hour,
Time.Minute,
Time.Second,
Time.Milliseconds,
n );
RtlTimeToTimeFields(&TotalDpc, &Time);
n = UInt64ToDoublePerCent( TotalDpc.QuadPart, TotalElapsed.QuadPart );
FPRINTF(stdout, " DPC %ld:%02ld:%02ld.%03ld (%4.1f%%)",
Time.Hour,
Time.Minute,
Time.Second,
Time.Milliseconds,
n );
RtlTimeToTimeFields(&TotalInterrupt, &Time);
n = UInt64ToDoublePerCent( TotalInterrupt.QuadPart, TotalElapsed.QuadPart );
FPRINTF(stdout, " Interrupt %ld:%02ld:%02ld.%03ld (%4.1f%%)\n",
Time.Hour,
Time.Minute,
Time.Second,
Time.Milliseconds,
n );
m = Elapsed.QuadPart > 0 ? (long double)10000000 * (long double)TotalInterruptCounts / (long double)Elapsed.QuadPart : 0;
FPRINTF(stdout, " Total Interrupts= %ld, Total Interrupt Rate= %.0f/sec.\n\n",
TotalInterruptCounts,
m );
}
//
// Display system-wide counters information
//
gldElapsedSeconds = (long double)Elapsed.QuadPart / (long double)10000000;
gTotalElapsedSeconds = (ULONG)(TotalElapsed.QuadPart/10000000); //Sum on all processors
gTotalElapsedTime64.QuadPart = TotalElapsed.QuadPart;
FPRINTF(stdout, "\nTotal Profile Time = %ld msec\n", (ULONG)Elapsed.QuadPart/(ULONG)10000);
if(bIncludeGeneralInfo)
GetProfileSystemInfo(OUTPUT);
if(bIncludeSystemLocksInfo)
GetSystemLocksInformation(OUTPUT);
}//DisplaySystemWideInformation()
BOOL
IsStringANumber(
IN PCHAR String
)
/*++
Routine Description:
Checks if a string is representing a positive decimal form integer
not larger than 999,999,999 (9 digits maximum)
Arguments:
String - Supplies the character string pointer.
Return Value:
TRUE - The string represents a number under the restrictions above
FALSE - The string does not represent a number under the restrictions above
Notes:
The functions atol() and atoi() will return a number for a mixed string that starts with a number,
such as 345d (345 will be returned). Also they don't handle overflow conditions.
--*/
{
BOOL bRet = FALSE;
ULONG i = 0;
if ( String != NULL ) {
while( (i <= MAX_DIGITS_IN_INPUT_STRING) && isdigit( String[i] ) ){
if( String[i+1] == ' ' || iscntrl( String[i+1] ) ){
bRet = TRUE;
break;
}
++i;
}
}
return (bRet);
}
ULONG
HandleRedirections(
IN PCHAR cmdLine,
IN ULONG nCharsStart,
OUT HANDLE *hInput,
OUT HANDLE *hOutput,
OUT HANDLE *hError
)
{
PCHAR Input = NULL, Output = NULL, Error = NULL;
PCHAR tmp = NULL;
ULONG retLength = nCharsStart;
ULONG jump = 0;
DWORD dwMode = OPEN_EXISTING;
SECURITY_ATTRIBUTES *sa = calloc( 1, sizeof(SECURITY_ATTRIBUTES));
if( sa == NULL ){
FPRINTF(stderr, "KERNRATE: Failed to allocate memory for security attributes in HandleRedirections()\n");
exit(1);
}
sa->bInheritHandle = TRUE;
tmp = strchr(cmdLine, '|');
if (tmp != NULL ){
FPRINTF(stderr, "\nKERNRATE: Piping '|' is not supported for '-o ProcessName {command line parameters}'\n");
FPRINTF(stderr, " Redirections of the input/output/error streams are supported.\n");
exit(1);
}
tmp = strchr(cmdLine, '<');
if ( tmp != NULL ){
PCHAR Startptr = tmp;
ULONG Length;
while(tmp[jump] == ' '){
++jump;
}
Input = &tmp[jump];
Length = jump + lstrlen(Input);
*hInput = CreateFile(Input,
GENERIC_READ,
FILE_SHARE_READ,
sa,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
if (*hInput == INVALID_HANDLE_VALUE){
FPRINTF(stderr, "\nKERNRATE: Could not open user specified input file %s, attempting to continue\n", Input);
}
memcpy(Startptr, Startptr + Length, (retLength - (ULONG)(Startptr - cmdLine) - Length ) );
retLength -= Length;
cmdLine[retLength] = '\0';
}
jump = 0;
tmp = strstr(cmdLine, "2>>");
if( tmp != NULL ){
jump = 3;
dwMode = OPEN_ALWAYS;
} else {
tmp = strstr(cmdLine, "2>");
if( tmp != NULL ){
jump = 2;
dwMode = CREATE_ALWAYS;
}
}
if ( tmp != NULL ){
PCHAR Startptr = tmp;
ULONG Length;
while(tmp[jump] == ' '){
++jump;
}
Error = &tmp[jump];
Length = jump + lstrlen(Error);
*hError = CreateFile(Error,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
sa,
dwMode,
FILE_ATTRIBUTE_NORMAL,
NULL
);
if (*hError == INVALID_HANDLE_VALUE){
FPRINTF(stderr, "\nKERNRATE: Could not open or create user specified Error file %s, attempting to continue\n", Error);
}
if(dwMode == OPEN_ALWAYS){
SetFilePointer(*hError, 0, NULL, FILE_END);
}
memcpy(Startptr, Startptr + Length, (retLength - (ULONG)(Startptr - cmdLine) - Length ) );
retLength -= Length;
cmdLine[retLength] = '\0';
}
jump = 0;
tmp = strstr(cmdLine, "1>>");
if ( tmp != NULL ){
jump = 3;
dwMode = OPEN_ALWAYS;
} else {
tmp = strstr(cmdLine, "1>");
if ( tmp != NULL ){
jump = 2;
dwMode = CREATE_ALWAYS;
}
}
if ( tmp == NULL && hError == NULL ) {
tmp = strstr(cmdLine, ">>");
if (tmp != NULL){
jump = 2;
dwMode = OPEN_ALWAYS;
} else {
tmp = strchr(cmdLine, '>');
jump = 1;
dwMode = CREATE_ALWAYS;
}
}
if ( tmp != NULL ){
PCHAR Startptr = tmp;
ULONG Length;
while(tmp[jump] == ' '){
++jump;
}
Output = &tmp[jump];
Length = jump + lstrlen(Output);
*hOutput = CreateFile(Output,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
sa,
dwMode,
FILE_ATTRIBUTE_NORMAL,
NULL
);
if (*hOutput == INVALID_HANDLE_VALUE){
FPRINTF(stderr, "\nKERNRATE: Could not open or create user specified Output file %s, attempting to continue\n", Output);
}
if(dwMode == OPEN_ALWAYS){
SetFilePointer(*hOutput, 0, NULL, FILE_END);
}
memcpy(Startptr, Startptr + Length, (retLength - (ULONG)(Startptr - cmdLine) - Length ) );
retLength -= Length;
cmdLine[retLength] = '\0';
}
free(sa);
sa = NULL;
return (retLength);
}