#include #include #include #include #include #include #include #include #include #include #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; iNumberOfRecords; 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; iNumberOfRecords; 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; iRecords[ 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; jKeyName, 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; }