windows-nt/Source/XPSP1/NT/sdktools/pstat/timeit.c
2020-09-26 16:20:57 +08:00

969 lines
30 KiB
C

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <search.h>
#include <string.h>
#include <memory.h>
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <..\ztools\inc\tools.h>
#define MAX_PROCESSOR 16
SYSTEM_BASIC_INFORMATION BasicInfo;
SYSTEM_PERFORMANCE_INFORMATION SystemInfoStart;
SYSTEM_PERFORMANCE_INFORMATION SystemInfoDone;
SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION ProcessorInfoStart[MAX_PROCESSOR];
BOOLEAN IgnoreNonZeroExitCodes;
BOOLEAN ForceSort;
BOOLEAN RemoveKeys;
LPSTR KeyNameToRemove;
LONG KeyEntriesToTrim;
BOOL WINAPI
CtrlcHandler(
ULONG CtrlType
)
{
//
// Ignore control C interrupts. Let child process deal with them
// if it wants. If it doesn't then it will terminate and we will
// get control and terminate ourselves
//
return TRUE;
}
void
Usage( void )
{
fprintf( stderr, "Usage: TIMEIT [-f filename] [-a] [-c] [-i] [-d] [-s] [-t] [-k keyname | -r keyname] [commandline...]\n" );
fprintf( stderr, "where: -f specifies the name of the database file where TIMEIT\n" );
fprintf( stderr, " keeps a history of previous timings. Default is .\\timeit.dat\n" );
fprintf( stderr, " -k specifies the keyname to use for this timing run\n" );
fprintf( stderr, " -r specifies the keyname to remove from the database. If\n" );
fprintf( stderr, " keyname is followed by a comma and a number then it will\n" );
fprintf( stderr, " remove the slowest (positive number) or fastest (negative)\n" );
fprintf( stderr, " times for that keyname.\n" );
fprintf( stderr, " -a specifies that timeit should display average of all timings\n" );
fprintf( stderr, " for the specified key.\n" );
fprintf( stderr, " -i specifies to ignore non-zero return codes from program\n" );
fprintf( stderr, " -d specifies to show detail for average\n" );
fprintf( stderr, " -s specifies to suppress system wide counters\n" );
fprintf( stderr, " -t specifies to tabular output\n" );
fprintf( stderr, " -c specifies to force a resort of the data base\n" );
exit( 1 );
}
typedef struct _TIMEIT_RECORD {
UCHAR KeyName[ 24 ];
ULONG GetVersionNumber;
LARGE_INTEGER ExitTime;
ULONG ExitCode;
USHORT Flags;
USHORT NumberOfRecordsInAverage;
LARGE_INTEGER ElapsedTime;
LARGE_INTEGER ProcessTime;
ULONG SystemCalls;
ULONG ContextSwitches;
ULONG InterruptCount;
ULONG PageFaultCount;
LARGE_INTEGER IoReadTransferCount;
LARGE_INTEGER IoWriteTransferCount;
LARGE_INTEGER IoOtherTransferCount;
} TIMEIT_RECORD, *PTIMEIT_RECORD;
#define TIMEIT_RECORD_SYSTEMINFO 0x0001
#define TIMEIT_VERSION 1
typedef struct _TIMEIT_FILE {
ULONG VersionNumber;
ULONG NumberOfRecords;
TIMEIT_RECORD Records[ 1 ];
} TIMEIT_FILE, *PTIMEIT_FILE;
void
DisplayRecord(
PTIMEIT_RECORD p,
PTIMEIT_RECORD pavg
);
int
__cdecl
TimeitSortRoutine(
const void *arg1,
const void *arg2
)
{
PTIMEIT_RECORD p1 = (PTIMEIT_RECORD)arg1;
PTIMEIT_RECORD p2 = (PTIMEIT_RECORD)arg2;
int cmp;
cmp = _stricmp( p1->KeyName, p2->KeyName );
if (cmp == 0) {
if (p1->ElapsedTime.QuadPart < p2->ElapsedTime.QuadPart) {
cmp = -1;
}
else
if (p1->ElapsedTime.QuadPart > p2->ElapsedTime.QuadPart) {
cmp = 1;
}
}
return cmp;
}
void
UnmapDataBase(
PTIMEIT_FILE pf
)
{
FlushViewOfFile( pf, 0 );
UnmapViewOfFile( pf );
}
PTIMEIT_FILE
MapDataBase(
LPSTR DataBaseFileName,
BOOLEAN WriteAccess,
PTIMEIT_RECORD p,
PULONG ActualNumberOfRecords
)
{
DWORD BytesWritten;
HANDLE hf, hm;
TIMEIT_FILE Temp;
PTIMEIT_FILE pf;
ULONG i;
BOOLEAN RecordsDeleted;
DWORD FileSize;
BOOLEAN DeleteRecord, BackupFlag;
LONG KeyEntriesLeftToTrim;
hf = CreateFile( DataBaseFileName,
WriteAccess ? GENERIC_READ | GENERIC_WRITE : GENERIC_READ,
FILE_SHARE_READ,
NULL,
p != NULL ? OPEN_ALWAYS : OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
if (hf == INVALID_HANDLE_VALUE) {
return NULL;
}
if (p != NULL && GetLastError() != ERROR_ALREADY_EXISTS) {
Temp.VersionNumber = TIMEIT_VERSION;
Temp.NumberOfRecords = 0;
WriteFile( hf, &Temp, FIELD_OFFSET( TIMEIT_FILE, Records ), &BytesWritten, NULL );
}
FileSize = SetFilePointer( hf, 0, NULL, FILE_END );
if (p != NULL) {
if (!WriteFile( hf, p, sizeof( *p ), &BytesWritten, NULL )) {
CloseHandle( hf );
return NULL;
}
SetEndOfFile( hf );
FileSize += BytesWritten;
}
retrymap:
hm = CreateFileMapping( hf,
NULL,
WriteAccess ? PAGE_READWRITE | SEC_COMMIT : PAGE_READONLY,
0,
0,
NULL
);
if (hm == NULL) {
CloseHandle( hf );
return NULL;
}
pf = MapViewOfFile( hm, WriteAccess ? FILE_MAP_WRITE : FILE_MAP_READ, 0, 0, 0 );
if (pf != NULL) {
if (p != NULL) {
pf->NumberOfRecords += 1;
}
if (FileSize > FIELD_OFFSET( TIMEIT_FILE, Records )) {
*ActualNumberOfRecords = (FileSize - FIELD_OFFSET( TIMEIT_FILE, Records )) / sizeof( TIMEIT_RECORD );
}
else {
*ActualNumberOfRecords = 0;
}
if (WriteAccess && RemoveKeys && KeyNameToRemove != NULL) {
if (pf->NumberOfRecords != *ActualNumberOfRecords) {
pf->NumberOfRecords = *ActualNumberOfRecords;
}
KeyEntriesLeftToTrim = KeyEntriesToTrim;
RecordsDeleted = FALSE;
for (i=0; i<pf->NumberOfRecords; i++) {
if ((*KeyNameToRemove == '\0'&& pf->Records[i].KeyName[0] == '\0') ||
!_strnicmp( KeyNameToRemove, pf->Records[i].KeyName, 24 )) {
DeleteRecord = FALSE;
BackupFlag = FALSE;
if (KeyEntriesToTrim) {
if (KeyEntriesLeftToTrim < 0) {
DeleteRecord = TRUE;
KeyEntriesLeftToTrim += 1;
}
else
if (KeyEntriesLeftToTrim > 0) {
if ((i+1) == pf->NumberOfRecords ||
_strnicmp( KeyNameToRemove, pf->Records[i+1].KeyName, 24 )
) {
DeleteRecord = TRUE;
KeyEntriesLeftToTrim -= 1;
BackupFlag = TRUE;
}
}
}
else {
DeleteRecord = TRUE;
}
if (DeleteRecord) {
RecordsDeleted = TRUE;
FileSize -= sizeof( TIMEIT_RECORD );
if (i < --(pf->NumberOfRecords)) {
memmove( &pf->Records[i],
&pf->Records[i+1],
(pf->NumberOfRecords - i + 1) * sizeof( TIMEIT_RECORD )
);
}
i -= 1;
if (BackupFlag) {
i -= 1;
}
}
}
}
if (RecordsDeleted) {
RemoveKeys = FALSE;
CloseHandle( hm );
UnmapDataBase( pf );
SetFilePointer( hf, FileSize, NULL, FILE_BEGIN );
SetEndOfFile( hf );
goto retrymap;
}
}
if (WriteAccess) {
qsort( pf->Records,
pf->NumberOfRecords,
sizeof( TIMEIT_RECORD ),
TimeitSortRoutine
);
}
}
CloseHandle( hm );
CloseHandle( hf );
return pf;
}
void
AccumulateRecord(
PTIMEIT_RECORD pavg,
PTIMEIT_RECORD p
)
{
ULONG n;
if (p != NULL) {
if (!IgnoreNonZeroExitCodes || p->ExitCode != 0) {
pavg->ElapsedTime.QuadPart += p->ElapsedTime.QuadPart;
pavg->ProcessTime.QuadPart += p->ProcessTime.QuadPart;
if (p->Flags & TIMEIT_RECORD_SYSTEMINFO) {
pavg->Flags |= TIMEIT_RECORD_SYSTEMINFO;
pavg->SystemCalls += p->SystemCalls;
pavg->ContextSwitches += p->ContextSwitches;
pavg->InterruptCount += p->InterruptCount;
pavg->PageFaultCount += p->PageFaultCount;
pavg->IoReadTransferCount.QuadPart += p->IoReadTransferCount.QuadPart;
pavg->IoWriteTransferCount.QuadPart += p->IoWriteTransferCount.QuadPart;
pavg->IoOtherTransferCount.QuadPart += p->IoOtherTransferCount.QuadPart;
}
pavg->NumberOfRecordsInAverage += 1;
}
}
else {
n = pavg->NumberOfRecordsInAverage;
if (n > 1 && n != 0xFFFF) {
pavg->ElapsedTime.QuadPart /= n;
pavg->ProcessTime.QuadPart /= n;
if (pavg->Flags & TIMEIT_RECORD_SYSTEMINFO) {
pavg->SystemCalls /= n;
pavg->ContextSwitches /= n;
pavg->InterruptCount /= n;
pavg->PageFaultCount /= n;
pavg->IoReadTransferCount.QuadPart /= n;
pavg->IoWriteTransferCount.QuadPart /= n;
pavg->IoOtherTransferCount.QuadPart /= n;
}
}
}
return;
}
void
UpdateDataBase(
LPSTR DataBaseFileName,
PTIMEIT_RECORD p,
PTIMEIT_RECORD pavg
)
{
PTIMEIT_FILE pf;
PTIMEIT_RECORD p1;
ULONG i, NumberOfRecords;
pf = MapDataBase( DataBaseFileName, TRUE, p, &NumberOfRecords );
if (pf == NULL) {
fprintf( stderr, "Unable to access data base file '%s' (%u)\n",
DataBaseFileName, GetLastError()
);
return;
}
if (pf->VersionNumber != TIMEIT_VERSION) {
fprintf( stderr, "Invalid version number (%u) in data base file '%s'\n",
pf->VersionNumber, DataBaseFileName
);
exit( 1 );
}
if (pavg != NULL) {
memset( pavg, 0, sizeof( *pavg ) );
pavg->GetVersionNumber = GetVersion();
for (i=0; i<pf->NumberOfRecords; i++) {
p1 = &pf->Records[ i ];
if (!_stricmp( p1->KeyName, p->KeyName )) {
AccumulateRecord( pavg, p1 );
}
}
AccumulateRecord( pavg, NULL );
}
UnmapDataBase( pf );
DisplayRecord( p, pavg );
return;
}
char *PlatformStrings[] = {
"Windows NT",
"Win32s",
"Windows 95",
"unknown"
};
char *Days[] = {
"Sunday",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday"
};
char *Months[] = {
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December"
};
void
DisplayRecord(
PTIMEIT_RECORD p,
PTIMEIT_RECORD pavg
)
{
LARGE_INTEGER LocalExitTime;
TIME_FIELDS ExitTime, ElapsedTime, ProcessTime;
fprintf( stderr, "\n" );
fprintf( stderr,
"Version Number: %s %u.%u (Build %u)\n",
PlatformStrings[ (p->GetVersionNumber >> 30) & 0x3 ],
p->GetVersionNumber & 0xFF,
(p->GetVersionNumber >> 8) & 0xFF,
(p->GetVersionNumber >> 16) & 0x3FFF
);
if (pavg != NULL) {
fprintf( stderr, "Average is for %u runs\n", pavg->NumberOfRecordsInAverage );
}
FileTimeToLocalFileTime( (LPFILETIME)&p->ExitTime, (LPFILETIME)&LocalExitTime );
RtlTimeToTimeFields( &LocalExitTime, &ExitTime );
fprintf( stderr,
"Exit Time: %u:%02u %s, %s, %s %u %u\n",
ExitTime.Hour > 12 ? ExitTime.Hour - 12 : ExitTime.Hour,
ExitTime.Minute,
ExitTime.Hour > 12 ? "pm" : "am",
Days[ ExitTime.Weekday ],
Months[ ExitTime.Month-1 ],
ExitTime.Day,
ExitTime.Year
);
RtlTimeToTimeFields( &p->ElapsedTime, &ElapsedTime );
fprintf( stderr,
"Elapsed Time: %u:%02u:%02u.%03u",
ElapsedTime.Hour,
ElapsedTime.Minute,
ElapsedTime.Second,
ElapsedTime.Milliseconds
);
if (pavg != NULL) {
RtlTimeToTimeFields( &pavg->ElapsedTime, &ElapsedTime );
fprintf( stderr,
" Average: %u:%02u:%02u.%03u",
ElapsedTime.Hour,
ElapsedTime.Minute,
ElapsedTime.Second,
ElapsedTime.Milliseconds
);
}
fprintf( stderr, "\n" );
RtlTimeToTimeFields( &p->ProcessTime, &ProcessTime );
fprintf( stderr,
"Process Time: %u:%02u:%02u.%03u",
ProcessTime.Hour,
ProcessTime.Minute,
ProcessTime.Second,
ProcessTime.Milliseconds
);
if (pavg != NULL) {
RtlTimeToTimeFields( &pavg->ProcessTime, &ProcessTime );
fprintf( stderr,
" Average: %u:%02u:%02u.%03u",
ProcessTime.Hour,
ProcessTime.Minute,
ProcessTime.Second,
ProcessTime.Milliseconds
);
}
fprintf( stderr, "\n" );
if (p->Flags & TIMEIT_RECORD_SYSTEMINFO) {
fprintf( stderr, "System Calls: %-11u", p->SystemCalls );
if (pavg != NULL) {
fprintf( stderr, " Average: %u", pavg->SystemCalls );
}
fprintf( stderr, "\n" );
fprintf( stderr, "Context Switches: %-11u", p->ContextSwitches );
if (pavg != NULL) {
fprintf( stderr, " Average: %u", pavg->ContextSwitches );
}
fprintf( stderr, "\n" );
fprintf( stderr, "Page Faults: %-11u", p->PageFaultCount );
if (pavg != NULL) {
fprintf( stderr, " Average: %u", pavg->PageFaultCount );
}
fprintf( stderr, "\n" );
fprintf( stderr, "Bytes Read: %-11u", p->IoReadTransferCount.QuadPart );
if (pavg != NULL) {
fprintf( stderr, " Average: %u", pavg->IoReadTransferCount.QuadPart );
}
fprintf( stderr, "\n" );
fprintf( stderr, "Bytes Written: %-11u", p->IoWriteTransferCount.QuadPart );
if (pavg != NULL) {
fprintf( stderr, " Average: %u", pavg->IoReadTransferCount.QuadPart );
}
fprintf( stderr, "\n" );
fprintf( stderr, "Bytes Other: %-11u", p->IoOtherTransferCount.QuadPart );
if (pavg != NULL) {
fprintf( stderr, " Average: %u", pavg->IoOtherTransferCount.QuadPart );
}
fprintf( stderr, "\n" );
}
}
BOOLEAN DisplayDataBaseFirstTime;
void
DisplayDataBaseAverage(
PTIMEIT_RECORD p,
BOOLEAN ShowDetailForAverage,
BOOLEAN TabularOutput
)
{
LARGE_INTEGER LocalExitTime;
TIME_FIELDS ExitTime, ElapsedTime, ProcessTime;
AccumulateRecord( p, NULL );
if (DisplayDataBaseFirstTime) {
if (TabularOutput) {
fprintf( stderr, "Runs Name Elapsed Time Process Time System Context Page Total I/O\n" );
fprintf( stderr, " Calls Switches Faults\n" );
}
DisplayDataBaseFirstTime = FALSE;
}
else
if (!TabularOutput) {
fprintf( stderr, "\n\n" );
}
if (TabularOutput) {
if (p->KeyName[0] == '\0') {
FileTimeToLocalFileTime( (LPFILETIME)&p->ExitTime, (LPFILETIME)&LocalExitTime );
RtlTimeToTimeFields( &LocalExitTime, &ExitTime );
sprintf( p->KeyName,
"%02u/%02u/%4u %02u:%02u %s",
ExitTime.Month,
ExitTime.Day,
ExitTime.Year,
ExitTime.Hour > 12 ? ExitTime.Hour - 12 : ExitTime.Hour,
ExitTime.Minute,
ExitTime.Hour > 12 ? "pm" : "am"
);
}
if (p->NumberOfRecordsInAverage == 0) {
fprintf( stderr, " " );
}
else
if (p->NumberOfRecordsInAverage == 0xFFFF) {
fprintf( stderr, "EXCL" );
}
else {
fprintf( stderr, "%-4u", p->NumberOfRecordsInAverage );
}
RtlTimeToTimeFields( &p->ElapsedTime, &ElapsedTime );
fprintf( stderr, " %-24s %3u:%02u:%02u.%03u",
p->KeyName,
ElapsedTime.Hour,
ElapsedTime.Minute,
ElapsedTime.Second,
ElapsedTime.Milliseconds
);
RtlTimeToTimeFields( &p->ProcessTime, &ProcessTime );
fprintf( stderr, " %3u:%02u:%02u.%03u",
ProcessTime.Hour,
ProcessTime.Minute,
ProcessTime.Second,
ProcessTime.Milliseconds
);
if (p->Flags & TIMEIT_RECORD_SYSTEMINFO) {
fprintf( stderr, " %9u %9u %8u%12u",
p->SystemCalls,
p->ContextSwitches,
p->PageFaultCount,
p->IoReadTransferCount.QuadPart +
p->IoWriteTransferCount.QuadPart +
p->IoOtherTransferCount.QuadPart
);
}
fprintf( stderr, "\n" );
if (ShowDetailForAverage &&
p->NumberOfRecordsInAverage != 0 &&
p->NumberOfRecordsInAverage != 0xFFFF
) {
fprintf( stderr, "\n" );
}
}
else {
if (p->NumberOfRecordsInAverage == 0) {
fprintf( stderr, "Detail Record included in average below\n" );
}
else
if (p->NumberOfRecordsInAverage == 0xFFFF) {
fprintf( stderr, "Detail Record excluded from average below\n" );
}
else {
fprintf( stderr,
"Average for %s key over %u runs\n",
p->KeyName,
p->NumberOfRecordsInAverage
);
}
DisplayRecord( p, NULL );
}
}
void
DisplayDataBase(
LPSTR DataBaseFileName,
LPSTR KeyName,
BOOLEAN ShowDetailForAverage,
BOOLEAN TabularOutput
)
{
TIMEIT_RECORD Averages, Detail;
PTIMEIT_RECORD p, pPrev, pNext;
PTIMEIT_FILE pf;
ULONG i, j;
ULONG NumberOfRecords, NumberOfRecordsInGroup, CurrentRecordInGroup, NumberOfRecordsToTrim;
pf = MapDataBase( DataBaseFileName,
(BOOLEAN)(ForceSort | RemoveKeys),
NULL,
&NumberOfRecords
);
if (pf == NULL) {
fprintf( stderr, "Unable to access data base file '%s' (%u)\n",
DataBaseFileName, GetLastError()
);
exit( 1 );
}
if (pf->VersionNumber != TIMEIT_VERSION) {
fprintf( stderr, "Invalid version number (%u) in data base file '%s'\n",
pf->VersionNumber, DataBaseFileName
);
exit( 1 );
}
pPrev = NULL;
DisplayDataBaseFirstTime = TRUE;
for (i=0; i<NumberOfRecords; i++) {
p = &pf->Records[ i ];
if (i == 0 || _stricmp( p->KeyName, Averages.KeyName )) {
if (i != 0 && (KeyName == NULL || !_stricmp( KeyName, Averages.KeyName ))) {
DisplayDataBaseAverage( &Averages, ShowDetailForAverage, TabularOutput );
}
pNext = p+1;
NumberOfRecordsInGroup = 1;
for (j=i+1; j<NumberOfRecords; j++) {
if (!_stricmp( p->KeyName, pNext->KeyName )) {
NumberOfRecordsInGroup += 1;
}
else {
break;
}
pNext += 1;
}
CurrentRecordInGroup = 0;
NumberOfRecordsToTrim = NumberOfRecordsInGroup / 10;
memset( &Averages, 0, sizeof( Averages ) );
strcpy( Averages.KeyName, p->KeyName );
Averages.GetVersionNumber = p->GetVersionNumber;
}
Detail = *p;
Detail.KeyName[0] = '\0';
CurrentRecordInGroup += 1;
if (CurrentRecordInGroup <= NumberOfRecordsToTrim ||
CurrentRecordInGroup > (NumberOfRecordsInGroup-NumberOfRecordsToTrim)
) {
//
// Ignore fastest and slowest records
//
Detail.NumberOfRecordsInAverage = 0xFFFF;
}
else {
Detail.NumberOfRecordsInAverage = 0;
}
if (ShowDetailForAverage && (KeyName == NULL || !_stricmp( KeyName, p->KeyName ))) {
DisplayDataBaseAverage( &Detail, ShowDetailForAverage, TabularOutput );
}
if (Detail.NumberOfRecordsInAverage != 0xFFFF) {
AccumulateRecord( &Averages, p );
}
}
if (i != 0 && (KeyName == NULL || !_stricmp( KeyName, Averages.KeyName ))) {
DisplayDataBaseAverage( &Averages, ShowDetailForAverage, TabularOutput );
}
}
int
__cdecl
main(
int argc,
char *argv[],
char *envp[]
)
{
LPSTR s, s1, KeyName, CommandLine, DataBaseFileName;
BOOL b;
NTSTATUS Status;
STARTUPINFO StartupInfo;
PROCESS_INFORMATION ProcessInformation;
KERNEL_USER_TIMES Times;
TIMEIT_RECORD t, Averages;
BOOLEAN DisplayAverage;
BOOLEAN ShowDetailForAverage;
BOOLEAN TabularOutput;
BOOLEAN SuppressSystemInfo;
//
// Console API's are OEM, so make it so for us as well
//
ConvertAppToOem( argc, argv );
// printf( "sizeof( TIMEIT_RECORD ) == 0x%x\n", sizeof( TIMEIT_RECORD ) );
IgnoreNonZeroExitCodes = FALSE;
ShowDetailForAverage = FALSE;
SuppressSystemInfo = FALSE;
TabularOutput = FALSE;
DisplayAverage = FALSE;
DataBaseFileName = "timeit.dat";
CommandLine = NULL;
KeyName = NULL;
RemoveKeys = FALSE;
while (--argc) {
s = *++argv;
if (*s == '-') {
while (*++s) {
switch(tolower( *s )) {
case 'd':
ShowDetailForAverage = TRUE;
break;
case 's':
SuppressSystemInfo = TRUE;
break;
case 't':
TabularOutput = TRUE;
break;
case 'a':
DisplayAverage = TRUE;
break;
case 'f':
if (--argc) {
DataBaseFileName = *++argv;
}
else {
fprintf( stderr, "Missing parameter to -f switch\n" );
Usage();
}
break;
case 'i':
IgnoreNonZeroExitCodes = TRUE;
break;
case 'k':
if (--argc) {
KeyName = *++argv;
}
else {
fprintf( stderr, "Missing parameter to -k switch\n" );
Usage();
}
break;
case 'c':
ForceSort = TRUE;
break;
case 'r':
RemoveKeys = TRUE;
if (--argc) {
KeyNameToRemove = *++argv;
if (s1 = strchr(KeyNameToRemove, ',')) {
*s1++ = '\0';
KeyEntriesToTrim = atoi(s1);
}
}
else {
fprintf( stderr, "Missing parameter to -r switch\n" );
Usage();
}
break;
default:
fprintf( stderr, "Invalid switch -%c\n", *s );
Usage();
break;
}
}
}
else {
if (KeyName == NULL) {
KeyName = s;
}
CommandLine = GetCommandLine();
while (*CommandLine && *CommandLine <= ' ') {
CommandLine += 1;
}
while (*CommandLine && *CommandLine > ' ') {
CommandLine += 1;
}
CommandLine = strstr( CommandLine, s );
break;
}
}
if (CommandLine == NULL) {
DisplayDataBase( DataBaseFileName, KeyName, ShowDetailForAverage, TabularOutput );
exit( 0 );
}
memset( &StartupInfo,0,sizeof( StartupInfo ) );
StartupInfo.cb = sizeof(StartupInfo);
Status = NtQuerySystemInformation( SystemBasicInformation,
&BasicInfo,
sizeof( BasicInfo ),
NULL
);
if (!NT_SUCCESS( Status )) {
fprintf( stderr, "Unable to query basic system information (%x)\n", Status );
exit( RtlNtStatusToDosError( Status ) );
}
if (!SuppressSystemInfo) {
Status = NtQuerySystemInformation( SystemPerformanceInformation,
(PVOID)&SystemInfoStart,
sizeof( SystemInfoStart ),
NULL
);
if (!NT_SUCCESS( Status )) {
fprintf( stderr, "Unable to query system performance data (%x)\n", Status );
exit( RtlNtStatusToDosError( Status ) );
}
}
if (!CreateProcess( NULL,
CommandLine,
NULL,
NULL,
TRUE,
0,
NULL,
NULL,
&StartupInfo,
&ProcessInformation
)
) {
fprintf( stderr,
"CreateProcess( '%s' ) failed (%u)\n",
CommandLine,
GetLastError()
);
exit( GetLastError() );
}
SetConsoleCtrlHandler( CtrlcHandler, TRUE );
WaitForSingleObject( ProcessInformation.hProcess, INFINITE );
Status = NtQueryInformationProcess( ProcessInformation.hProcess,
ProcessTimes,
&Times,
sizeof( Times ),
NULL
);
if (!NT_SUCCESS( Status )) {
fprintf( stderr, "Unable to query process times (%x)\n", Status );
exit( RtlNtStatusToDosError( Status ) );
}
if (!SuppressSystemInfo) {
Status = NtQuerySystemInformation( SystemPerformanceInformation,
(PVOID)&SystemInfoDone,
sizeof( SystemInfoDone ),
NULL
);
if (!NT_SUCCESS( Status )) {
fprintf( stderr, "Unable to query system performance data (%x)\n", Status );
exit( RtlNtStatusToDosError( Status ) );
}
}
memset( &t, 0, sizeof( t ) );
strcpy( t.KeyName, KeyName );
t.GetVersionNumber = GetVersion();
GetExitCodeProcess( ProcessInformation.hProcess, &t.ExitCode );
t.ExitTime.QuadPart = Times.ExitTime.QuadPart;
t.ElapsedTime.QuadPart = Times.ExitTime.QuadPart - Times.CreateTime.QuadPart;
// total process time ...
t.ProcessTime.QuadPart = Times.KernelTime.QuadPart + Times.UserTime.QuadPart;
if (!SuppressSystemInfo) {
t.SystemCalls = SystemInfoDone.SystemCalls - SystemInfoStart.SystemCalls;
t.ContextSwitches = SystemInfoDone.ContextSwitches - SystemInfoStart.ContextSwitches;
t.PageFaultCount = SystemInfoDone.PageFaultCount - SystemInfoStart.PageFaultCount;
t.IoReadTransferCount.QuadPart = SystemInfoDone.IoReadTransferCount.QuadPart -
SystemInfoStart.IoReadTransferCount.QuadPart;
t.IoReadTransferCount.QuadPart += (SystemInfoDone.PageReadCount -
SystemInfoStart.PageReadCount) * BasicInfo.PageSize;
t.IoReadTransferCount.QuadPart += (SystemInfoDone.CacheReadCount -
SystemInfoStart.CacheReadCount) * BasicInfo.PageSize;
t.IoWriteTransferCount.QuadPart = SystemInfoDone.IoWriteTransferCount.QuadPart -
SystemInfoStart.IoWriteTransferCount.QuadPart;
t.IoWriteTransferCount.QuadPart += (SystemInfoDone.DirtyPagesWriteCount -
SystemInfoStart.DirtyPagesWriteCount) * BasicInfo.PageSize;
t.IoWriteTransferCount.QuadPart += (SystemInfoDone.MappedPagesWriteCount -
SystemInfoStart.MappedPagesWriteCount) * BasicInfo.PageSize;
t.IoOtherTransferCount.QuadPart = SystemInfoDone.IoOtherTransferCount.QuadPart -
SystemInfoStart.IoOtherTransferCount.QuadPart;
t.Flags |= TIMEIT_RECORD_SYSTEMINFO;
}
UpdateDataBase( DataBaseFileName, &t, DisplayAverage ? &Averages : NULL );
exit( t.ExitCode );
return 0;
}