#include #include #include #include #include #include #include #include #include <..\ztools\inc\tools.h> #define NUMBER_SERVICE_TABLES 2 #define BUFFER_SIZE 1024 VOID SortUlongData ( IN ULONG Count, IN ULONG Index[], IN ULONG Data[] ); extern UCHAR *CallTable[]; ULONG SystemCallBufferStart[BUFFER_SIZE]; ULONG SystemCallBufferDone[BUFFER_SIZE]; ULONG Index[BUFFER_SIZE]; ULONG CallData[BUFFER_SIZE]; #define MAX_PROCESSOR 16 SYSTEM_BASIC_INFORMATION BasicInfo; SYSTEM_PERFORMANCE_INFORMATION SystemInfoStart; SYSTEM_PERFORMANCE_INFORMATION SystemInfoDone; SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION ProcessorInfoStart[MAX_PROCESSOR]; SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION ProcessorInfoDone[MAX_PROCESSOR]; #define vdelta(FLD) (VdmInfoDone.FLD - VdmInfoStart.FLD) #ifdef i386 SYSTEM_VDM_INSTEMUL_INFO VdmInfoStart; SYSTEM_VDM_INSTEMUL_INFO VdmInfoDone; #endif HANDLE GetServerProcessHandle( VOID ) { HANDLE Process; OBJECT_ATTRIBUTES Obja; UNICODE_STRING Unicode; NTSTATUS Status; RtlInitUnicodeString(&Unicode, L"\\WindowsSS"); InitializeObjectAttributes( &Obja, &Unicode, 0, NULL, NULL ); Status = NtOpenProcess( &Process, PROCESS_ALL_ACCESS, &Obja, NULL ); if ( !NT_SUCCESS(Status) ) { printf("OpenProcess Failed %lx\n",Status); Process = NULL; } return Process; } 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; } int __cdecl main( int argc, char *argv[], char *envp[] ) { LPSTR s; BOOL bFull; BOOL bOneLine; BOOL bSyscall; STARTUPINFO StartupInfo; PROCESS_INFORMATION ProcessInformation; BOOL b; VM_COUNTERS ServerVmInfoStart, ServerVmInfoDone, ProcessVmInfoStart, ProcessVmInfoDone; KERNEL_USER_TIMES Times, ServerStart, ServerDone, ProcessStart, ProcessDone; NTSTATUS Status; TIME_FIELDS Etime,Utime,Ktime,Itime; LARGE_INTEGER RunTime; LARGE_INTEGER IdleTime; HANDLE OtherProcess; ULONG i; CHAR ch; ULONG ProcessId; HANDLE ProcessHandle; ULONG Temp; ULONG ContextSwitches; ULONG FirstLevelFills; ULONG SecondLevelFills; PSYSTEM_CALL_COUNT_INFORMATION SystemCallInfoStart; PSYSTEM_CALL_COUNT_INFORMATION SystemCallInfoDone; PULONG SystemCallTableStart; PULONG SystemCallTableDone; ULONG NumberOfCounts; PULONG p; BOOL bShowHelpMsg = FALSE; argv; envp; ConvertAppToOem( argc, argv ); OtherProcess = NULL; ProcessHandle = NULL; if ( (argc < 2) ) { bShowHelpMsg = TRUE; } SetConsoleCtrlHandler( CtrlcHandler, TRUE ); ProcessId = 0; s = GetCommandLine(); if( s != NULL ) { CharToOem( s, s ); } bFull = FALSE; bOneLine = FALSE; bSyscall = FALSE; // // skip blanks // while(*s>' ')s++; // // get to next token // while(*s<=' ')s++; while (( *s == '-' ) || ( *s == '/' )) { s++; while (*s > ' ') { switch (*s) { case '1' : bOneLine = TRUE; break; case 'c' : case 'C' : bSyscall = TRUE; break; case 'f' : case 'F' : bFull = TRUE; break; case 's' : case 'S' : OtherProcess = GetServerProcessHandle(); break; case 'P': case 'p': // pid takes decimal argument s++; do ch = *s++; while (ch == ' ' || ch == '\t'); while (ch >= '0' && ch <= '9') { Temp = ProcessId * 10 + ch - '0'; if (Temp < ProcessId) { printf("pid number overflow\n"); ExitProcess(1); } ProcessId = Temp; ch = *s++; } if (!ProcessId) { printf("bad pid '%ld'\n", ProcessId); ExitProcess(1); } s--; if ( *s == ' ' ) s--; break; case 'h': case 'H': case '?': bShowHelpMsg = TRUE; break; default : break; } s++; } // // get to next token // while(*s<=' ')s++; } // see if this is just a request for command line help. if ( bShowHelpMsg ) { puts("\n" "Usage: ntimer [-1 -f -s] name-of-image [parameters]...\n" "\n" " Displays the Elapsed, Kernel, User and Idle time of the\n" " image specified in the command line\n" "\n" " -1 displays output on one line\n" " -f displays the process page faults, total\n" " system interrupts, context switches and system\n" " calls\n" " -s indicates the name of the image is that of a\n" " server process. Press Ctrl-C to get the times."); ExitProcess(1); } if ( ProcessId ) { ProcessHandle = OpenProcess(PROCESS_ALL_ACCESS,FALSE,ProcessId); } memset(&StartupInfo,0,sizeof(StartupInfo)); StartupInfo.cb = sizeof(StartupInfo); if (bOneLine) { bSyscall = FALSE; } if (bSyscall) { Status = NtQuerySystemInformation(SystemCallCountInformation, (PVOID)SystemCallBufferStart, BUFFER_SIZE * sizeof(ULONG), NULL); bSyscall = FALSE; if (!NT_SUCCESS(Status)) { printf("Failed to query system call performance information: %x\n", Status); } else { SystemCallInfoStart = (PVOID)SystemCallBufferStart; SystemCallInfoDone = (PVOID)SystemCallBufferDone; // // Make sure that the number of tables reported by the kernel matches // our list. // if (SystemCallInfoStart->NumberOfTables != NUMBER_SERVICE_TABLES) { printf("System call table count (%d) doesn't match NTIMER's count (%d)\n", SystemCallInfoStart->NumberOfTables, NUMBER_SERVICE_TABLES); } else { // // Make sure call count information is available for base services. // p = (PULONG)(SystemCallInfoStart + 1); SystemCallTableStart = (PULONG)(SystemCallInfoStart + 1) + NUMBER_SERVICE_TABLES; SystemCallTableDone = (PULONG)(SystemCallInfoDone + 1) + NUMBER_SERVICE_TABLES; if (p[0] == 0) { printf("No system call count information available for base services\n"); } else { // // If there is a hole in the count information (i.e., one set of services // doesn't have counting enabled, but a subsequent one does, then our // indexes will be off, and we'll display the wrong service names. // i = 2; for ( ; i < NUMBER_SERVICE_TABLES; i++ ) { if ((p[i] != 0) && (p[i-1] == 0)) { printf("One or more call count tables empty. NTIMER can't report\n"); break; } } if ( i >= NUMBER_SERVICE_TABLES ) { bSyscall = TRUE; NumberOfCounts = SystemCallInfoStart->Length - sizeof(SYSTEM_CALL_COUNT_INFORMATION) - NUMBER_SERVICE_TABLES * sizeof(ULONG); } } } } } if ( OtherProcess ) { Status = NtQueryInformationProcess( OtherProcess, ProcessTimes, &ServerStart, sizeof(Times), NULL ); if ( !NT_SUCCESS(Status) ) { ExitProcess((DWORD)Status); } } if ( ProcessHandle ) { Status = NtQueryInformationProcess( ProcessHandle, ProcessTimes, &ProcessStart, sizeof(Times), NULL ); if ( !NT_SUCCESS(Status) ) { ExitProcess((DWORD)Status); } } if ( bFull ) { if ( OtherProcess ) { Status = NtQueryInformationProcess( OtherProcess, ProcessVmCounters, &ServerVmInfoStart, sizeof(VM_COUNTERS), NULL ); if ( !NT_SUCCESS(Status) ) { ExitProcess((DWORD)Status); } } if ( ProcessHandle ) { Status = NtQueryInformationProcess( ProcessHandle, ProcessVmCounters, &ProcessVmInfoStart, sizeof(VM_COUNTERS), NULL ); if ( !NT_SUCCESS(Status) ) { ExitProcess((DWORD)Status); } } else { ZeroMemory(&ProcessVmInfoStart,sizeof(VM_COUNTERS)); } #ifdef i386 Status = NtQuerySystemInformation( SystemVdmInstemulInformation, &VdmInfoStart, sizeof(VdmInfoStart), NULL ); if (!NT_SUCCESS(Status)) { printf("Failed to query vdm information\n"); ExitProcess((DWORD)Status); } #endif Status = NtQuerySystemInformation( SystemBasicInformation, &BasicInfo, sizeof(BasicInfo), NULL ); if (!NT_SUCCESS(Status)) { printf("Failed to query basic information\n"); ExitProcess((DWORD)Status); } Status = NtQuerySystemInformation(SystemProcessorPerformanceInformation, (PVOID)&ProcessorInfoStart, sizeof (SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION) * MAX_PROCESSOR, NULL); if (!NT_SUCCESS(Status)) { printf("Failed to query preocessor performance information\n"); ExitProcess((DWORD)Status); } } Status = NtQuerySystemInformation(SystemPerformanceInformation, (PVOID)&SystemInfoStart, sizeof(SYSTEM_PERFORMANCE_INFORMATION), NULL); ContextSwitches = SystemInfoStart.ContextSwitches; FirstLevelFills = SystemInfoStart.FirstLevelTbFills; SecondLevelFills = SystemInfoStart.SecondLevelTbFills; b = CreateProcess( NULL, s, NULL, NULL, TRUE, 0, NULL, NULL, &StartupInfo, &ProcessInformation ); if ( !b ) { printf("CreateProcess(%s) failed %lx\n",s,GetLastError()); ExitProcess(GetLastError()); } WaitForSingleObject(ProcessInformation.hProcess,(DWORD)-1); Status = NtQuerySystemInformation(SystemPerformanceInformation, (PVOID)&SystemInfoDone, sizeof(SYSTEM_PERFORMANCE_INFORMATION), NULL); if (!bOneLine) { printf("\nContextSwitches - %d\nFirst level fills = %d\nSecond level fills = %d\n", SystemInfoDone.ContextSwitches - ContextSwitches, SystemInfoDone.FirstLevelTbFills - FirstLevelFills, SystemInfoDone.SecondLevelTbFills - SecondLevelFills); } if ( OtherProcess ) { Status = NtQueryInformationProcess( OtherProcess, ProcessTimes, &ServerDone, sizeof(Times), NULL ); if ( !NT_SUCCESS(Status) ) { ExitProcess((DWORD)Status); } } if ( ProcessHandle ) { Status = NtQueryInformationProcess( ProcessHandle, ProcessTimes, &ProcessDone, sizeof(Times), NULL ); if ( !NT_SUCCESS(Status) ) { ExitProcess((DWORD)Status); } } else { Status = NtQueryInformationProcess( ProcessInformation.hProcess, ProcessTimes, &Times, sizeof(Times), NULL ); if ( !NT_SUCCESS(Status) ) { ExitProcess((DWORD)Status); } } if ( bFull ) { if ( OtherProcess ) { Status = NtQueryInformationProcess( OtherProcess, ProcessVmCounters, &ServerVmInfoDone, sizeof(VM_COUNTERS), NULL ); if ( !NT_SUCCESS(Status) ) { ExitProcess((DWORD)Status); } } if ( ProcessHandle ) { Status = NtQueryInformationProcess( ProcessHandle, ProcessVmCounters, &ProcessVmInfoDone, sizeof(VM_COUNTERS), NULL ); if ( !NT_SUCCESS(Status) ) { ExitProcess((DWORD)Status); } } else { Status = NtQueryInformationProcess( ProcessInformation.hProcess, ProcessVmCounters, &ProcessVmInfoDone, sizeof(VM_COUNTERS), NULL ); if ( !NT_SUCCESS(Status) ) { ExitProcess((DWORD)Status); } } #ifdef i386 Status = NtQuerySystemInformation( SystemVdmInstemulInformation, &VdmInfoDone, sizeof(VdmInfoStart), NULL ); if (!NT_SUCCESS(Status)) { printf("Failed to query vdm information\n"); ExitProcess((DWORD)Status); } #endif Status = NtQuerySystemInformation(SystemProcessorPerformanceInformation, (PVOID)&ProcessorInfoDone, sizeof (SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION) * MAX_PROCESSOR, NULL); if (!NT_SUCCESS(Status)) { printf("Failed to query preocessor performance information\n"); ExitProcess((DWORD)Status); } } if ( bSyscall ) { Status = NtQuerySystemInformation(SystemCallCountInformation, (PVOID)SystemCallBufferDone, BUFFER_SIZE * sizeof(ULONG), NULL); if (!NT_SUCCESS(Status)) { printf("Failed to query system call performance information: %x\n", Status); bSyscall = FALSE; } } RunTime.QuadPart = Times.ExitTime.QuadPart - Times.CreateTime.QuadPart; IdleTime.QuadPart = SystemInfoDone.IdleProcessTime.QuadPart - SystemInfoStart.IdleProcessTime.QuadPart; RtlTimeToTimeFields ( (PLARGE_INTEGER)&IdleTime, &Itime); if ( ProcessHandle ) { RunTime.QuadPart = ProcessDone.UserTime.QuadPart - ProcessStart.UserTime.QuadPart; RtlTimeToTimeFields ( (PLARGE_INTEGER)&RunTime, &Utime); RunTime.QuadPart = ProcessDone.KernelTime.QuadPart - ProcessStart.KernelTime.QuadPart; RtlTimeToTimeFields ( (PLARGE_INTEGER)&RunTime, &Ktime); if (bOneLine) { printf("%3ld:%02ld:%02ld.%03ld %3ld:%02ld:%02ld.%03ld %3ld:%02ld:%02ld.%03ld", Utime.Hour, Utime.Minute, Utime.Second, Utime.Milliseconds, Ktime.Hour, Ktime.Minute, Ktime.Second, Ktime.Milliseconds, Itime.Hour, Itime.Minute, Itime.Second, Itime.Milliseconds ); } else { printf("ProcessTimes "); printf("UTime( %3ld:%02ld:%02ld.%03ld ) ", Utime.Hour, Utime.Minute, Utime.Second, Utime.Milliseconds ); printf("KTime( %3ld:%02ld:%02ld.%03ld ) ", Ktime.Hour, Ktime.Minute, Ktime.Second, Ktime.Milliseconds ); printf("ITime( %3ld:%02ld:%02ld.%03ld )\n", Itime.Hour, Itime.Minute, Itime.Second, Itime.Milliseconds ); } } else { RtlTimeToTimeFields((PLARGE_INTEGER) &RunTime, &Etime); RtlTimeToTimeFields(&Times.UserTime, &Utime); RtlTimeToTimeFields(&Times.KernelTime, &Ktime); if (bOneLine) { printf("%3ld:%02ld:%02ld.%03ld %3ld:%02ld:%02ld.%03ld %3ld:%02ld:%02ld.%03ld %3ld:%02ld:%02ld.%03ld", Etime.Hour, Etime.Minute, Etime.Second, Etime.Milliseconds, Utime.Hour, Utime.Minute, Utime.Second, Utime.Milliseconds, Ktime.Hour, Ktime.Minute, Ktime.Second, Ktime.Milliseconds, Itime.Hour, Itime.Minute, Itime.Second, Itime.Milliseconds ); } else { printf("\nETime( %3ld:%02ld:%02ld.%03ld ) ", Etime.Hour, Etime.Minute, Etime.Second, Etime.Milliseconds ); printf("UTime( %3ld:%02ld:%02ld.%03ld ) ", Utime.Hour, Utime.Minute, Utime.Second, Utime.Milliseconds ); printf("KTime( %3ld:%02ld:%02ld.%03ld )\n", Ktime.Hour, Ktime.Minute, Ktime.Second, Ktime.Milliseconds ); printf("ITime( %3ld:%02ld:%02ld.%03ld )\n", Itime.Hour, Itime.Minute, Itime.Second, Itime.Milliseconds ); } } if ( OtherProcess ) { RunTime.QuadPart = ServerDone.UserTime.QuadPart - ServerStart.UserTime.QuadPart; RtlTimeToTimeFields ( (PLARGE_INTEGER)&RunTime, &Utime); RunTime.QuadPart = ServerDone.KernelTime.QuadPart - ServerStart.KernelTime.QuadPart; RtlTimeToTimeFields ( (PLARGE_INTEGER)&RunTime, &Ktime); printf("ServerTimes "); if (bOneLine) { printf("%3ld:%02ld:%02ld.%03ld %3ld:%02ld:%02ld.%03ld %3ld:%02ld:%02ld.%03ld", Utime.Hour, Utime.Minute, Utime.Second, Utime.Milliseconds, Ktime.Hour, Ktime.Minute, Ktime.Second, Ktime.Milliseconds, Itime.Hour, Itime.Minute, Itime.Second, Itime.Milliseconds ); } else { printf("UTime( %3ld:%02ld:%02ld.%03ld ) ", Utime.Hour, Utime.Minute, Utime.Second, Utime.Milliseconds ); printf("KTime( %3ld:%02ld:%02ld.%03ld )\n", Ktime.Hour, Ktime.Minute, Ktime.Second, Ktime.Milliseconds ); printf("ITime( %3ld:%02ld:%02ld.%03ld )\n", Itime.Hour, Itime.Minute, Itime.Second, Itime.Milliseconds ); } } if ( bFull ) { ULONG InterruptCount; ULONG PreviousInterruptCount; #ifdef i386 ULONG EmulationTotal; #endif PreviousInterruptCount = 0; for (i=0; i < (ULONG)BasicInfo.NumberOfProcessors; i++) { PreviousInterruptCount += ProcessorInfoStart[i].InterruptCount; } InterruptCount = 0; for (i=0; i < (ULONG)BasicInfo.NumberOfProcessors; i++) { InterruptCount += ProcessorInfoDone[i].InterruptCount; } if (bOneLine) { printf(" %ld",ProcessVmInfoDone.PageFaultCount - ProcessVmInfoStart.PageFaultCount); if (OtherProcess) { printf(" %ld",ServerVmInfoDone.PageFaultCount - ServerVmInfoStart.PageFaultCount); } printf(" %ld", InterruptCount - PreviousInterruptCount); printf(" %ld", SystemInfoDone.ContextSwitches - SystemInfoStart.ContextSwitches); printf(" %ld", SystemInfoDone.SystemCalls - SystemInfoStart.SystemCalls); } else { printf("\n"); printf("Process PageFaultCount %ld\n",ProcessVmInfoDone.PageFaultCount - ProcessVmInfoStart.PageFaultCount); if (OtherProcess) { printf("Server PageFaultCount %ld\n",ServerVmInfoDone.PageFaultCount - ServerVmInfoStart.PageFaultCount); } printf("Total Interrupts %ld\n", InterruptCount - PreviousInterruptCount); printf("Total Context Switches %ld\n", SystemInfoDone.ContextSwitches - SystemInfoStart.ContextSwitches); printf("Total System Calls %ld\n", SystemInfoDone.SystemCalls - SystemInfoStart.SystemCalls); } if (ProcessHandle) { #ifdef i386 printf("\n"); printf("Total OpcodeHLT %ld\n", vdelta(OpcodeHLT )); printf("Total OpcodeCLI %ld\n", vdelta(OpcodeCLI )); printf("Total OpcodeSTI %ld\n", vdelta(OpcodeSTI )); printf("Total BopCount %ld\n", vdelta(BopCount )); printf("Total SegmentNotPresent %ld\n", vdelta(SegmentNotPresent )); printf("Total OpcodePUSHF %ld\n", vdelta(OpcodePUSHF )); printf("Total OpcodePOPF %ld\n", vdelta(OpcodePOPF )); printf("Total VdmOpcode0F %ld\n", vdelta(VdmOpcode0F )); printf("Total OpcodeINSB %ld\n", vdelta(OpcodeINSB )); printf("Total OpcodeINSW %ld\n", vdelta(OpcodeINSW )); printf("Total OpcodeOUTSB %ld\n", vdelta(OpcodeOUTSB )); printf("Total OpcodeOUTSW %ld\n", vdelta(OpcodeOUTSW )); printf("Total OpcodeINTnn %ld\n", vdelta(OpcodeINTnn )); printf("Total OpcodeINTO %ld\n", vdelta(OpcodeINTO )); printf("Total OpcodeIRET %ld\n", vdelta(OpcodeIRET )); printf("Total OpcodeINBimm %ld\n", vdelta(OpcodeINBimm )); printf("Total OpcodeINWimm %ld\n", vdelta(OpcodeINWimm )); printf("Total OpcodeOUTBimm %ld\n", vdelta(OpcodeOUTBimm )); printf("Total OpcodeOUTWimm %ld\n", vdelta(OpcodeOUTWimm )); printf("Total OpcodeINB %ld\n", vdelta(OpcodeINB )); printf("Total OpcodeINW %ld\n", vdelta(OpcodeINW )); printf("Total OpcodeOUTB %ld\n", vdelta(OpcodeOUTB )); printf("Total OpcodeOUTW %ld\n", vdelta(OpcodeOUTW )); EmulationTotal = vdelta(OpcodeHLT )+ vdelta(OpcodeCLI )+ vdelta(OpcodeSTI )+ vdelta(BopCount )+ vdelta(SegmentNotPresent )+ vdelta(OpcodePUSHF )+ vdelta(OpcodePOPF )+ vdelta(VdmOpcode0F )+ vdelta(OpcodeINSB )+ vdelta(OpcodeINSW )+ vdelta(OpcodeOUTSB )+ vdelta(OpcodeOUTSW )+ vdelta(OpcodeINTnn )+ vdelta(OpcodeINTO )+ vdelta(OpcodeIRET )+ vdelta(OpcodeINBimm )+ vdelta(OpcodeINWimm )+ vdelta(OpcodeOUTBimm )+ vdelta(OpcodeOUTWimm )+ vdelta(OpcodeINB )+ vdelta(OpcodeINW )+ vdelta(OpcodeOUTB )+ vdelta(OpcodeOUTW ) ; if (bOneLine) { printf(" %ld %ld", EmulationTotal, EmulationTotal*515); } else { printf("\n"); printf("Total Emulation %ld * 515clocks = %ld cycles\n", EmulationTotal, EmulationTotal*515); } #endif } if (bSyscall) { for (i = 0; i < NumberOfCounts; i += 1) { CallData[i] = SystemCallTableDone[i] - SystemCallTableStart[i]; } SortUlongData(NumberOfCounts, Index, CallData); for (i = 0; i < NumberOfCounts; i += 1) { if (CallData[Index[i]] == 0) { break; } printf("%8ld calls to %s\n", CallData[Index[i]], CallTable[Index[i]]); } } if (bOneLine) { printf("\n"); } } else { printf("\n"); } return 0; }