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

839 lines
28 KiB
C

#include <assert.h>
#include <stdio.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 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;
}