517 lines
12 KiB
C
517 lines
12 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1990 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
Wperf.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
Win32 application to display performance statictics.
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Ken Reneris
|
|||
|
|
|||
|
Environment:
|
|||
|
|
|||
|
console
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
//
|
|||
|
// set variable to define global variables
|
|||
|
//
|
|||
|
|
|||
|
#include <nt.h>
|
|||
|
#include <ntrtl.h>
|
|||
|
#include <nturtl.h>
|
|||
|
#include <windows.h>
|
|||
|
#include <errno.h>
|
|||
|
#include <malloc.h>
|
|||
|
#include <stdlib.h>
|
|||
|
#include <stdio.h>
|
|||
|
|
|||
|
#include "..\pstat.h"
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// global handles
|
|||
|
//
|
|||
|
|
|||
|
extern UCHAR Buffer[];
|
|||
|
#define INFSIZE 1024
|
|||
|
|
|||
|
UCHAR Usage[] = "pdump: [-p] [-t] second-delay [counter] [counter]...\n";
|
|||
|
|
|||
|
UCHAR NumberOfProcessors;
|
|||
|
|
|||
|
HANDLE DriverHandle;
|
|||
|
ULONG BufferStart [INFSIZE/4];
|
|||
|
ULONG BufferEnd [INFSIZE/4];
|
|||
|
|
|||
|
//
|
|||
|
// Selected Display Mode (read from wp2.ini), default set here.
|
|||
|
//
|
|||
|
|
|||
|
struct {
|
|||
|
ULONG EventId;
|
|||
|
PUCHAR ShortName;
|
|||
|
PUCHAR PerfName;
|
|||
|
} *Counters;
|
|||
|
|
|||
|
SETEVENT CounterEvent[MAX_EVENTS];
|
|||
|
|
|||
|
//
|
|||
|
// Protos..
|
|||
|
//
|
|||
|
|
|||
|
VOID GetInternalStats (PVOID Buffer);
|
|||
|
VOID SetCounterEncodings (VOID);
|
|||
|
LONG FindShortName (PSZ);
|
|||
|
VOID LI2Str (PSZ, ULONGLONG);
|
|||
|
BOOLEAN SetCounter (LONG CounterID, ULONG counter);
|
|||
|
BOOLEAN InitDriver ();
|
|||
|
VOID InitPossibleEventList();
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
int
|
|||
|
__cdecl
|
|||
|
main(USHORT argc, CHAR **argv)
|
|||
|
{
|
|||
|
ULONG i, j, len, pos, Delay;
|
|||
|
LONG cnttype;
|
|||
|
BOOLEAN CounterSet;
|
|||
|
pPSTATS ProcStart, ProcEnd;
|
|||
|
ULONGLONG ETime, ECount;
|
|||
|
UCHAR s1[40], s2[40];
|
|||
|
BOOLEAN Fail, DumpAll, ProcessorBreakout, ProcessorTotal;
|
|||
|
|
|||
|
//
|
|||
|
// Locate pentium perf driver
|
|||
|
//
|
|||
|
|
|||
|
if (!InitDriver ()) {
|
|||
|
printf ("pstat.sys is not installed\n");
|
|||
|
exit (1);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Initialize supported event list
|
|||
|
//
|
|||
|
|
|||
|
InitPossibleEventList();
|
|||
|
if (!Counters) {
|
|||
|
printf ("No events to monitor\n");
|
|||
|
exit (1);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Check args
|
|||
|
//
|
|||
|
|
|||
|
if (argc < 2) {
|
|||
|
printf (Usage);
|
|||
|
for (i=0; Counters[i].ShortName; i++) {
|
|||
|
printf (" %-20s\t%s\n", Counters[i].ShortName, Counters[i].PerfName);
|
|||
|
}
|
|||
|
exit (1);
|
|||
|
}
|
|||
|
|
|||
|
pos = 1;
|
|||
|
|
|||
|
Fail = FALSE;
|
|||
|
Delay = 0;
|
|||
|
DumpAll = FALSE;
|
|||
|
ProcessorBreakout = FALSE;
|
|||
|
ProcessorTotal = FALSE;
|
|||
|
|
|||
|
while (pos < argc && argv[pos][0] == '-') {
|
|||
|
switch (argv[pos][1]) {
|
|||
|
case 't':
|
|||
|
ProcessorTotal = TRUE;
|
|||
|
break;
|
|||
|
|
|||
|
case 'p':
|
|||
|
ProcessorBreakout = TRUE;
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
printf ("pdump: unkown switch '%c'\n", argv[pos][1]);
|
|||
|
Fail = TRUE;
|
|||
|
break;
|
|||
|
}
|
|||
|
pos += 1;
|
|||
|
}
|
|||
|
|
|||
|
if (pos < argc) {
|
|||
|
Delay = atoi (argv[pos]) * 1000;
|
|||
|
pos += 1;
|
|||
|
}
|
|||
|
|
|||
|
if (Fail /* || Delay == 0 */) {
|
|||
|
printf (Usage);
|
|||
|
exit (1);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Raise to highest priority
|
|||
|
//
|
|||
|
|
|||
|
if (!SetPriorityClass(GetCurrentProcess(),REALTIME_PRIORITY_CLASS)) {
|
|||
|
printf("Failed to raise to realtime priority\n");
|
|||
|
}
|
|||
|
|
|||
|
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Loop for every pentium count desired
|
|||
|
//
|
|||
|
|
|||
|
if (pos >= argc) {
|
|||
|
pos = 0;
|
|||
|
DumpAll = TRUE;
|
|||
|
}
|
|||
|
|
|||
|
printf (" %-30s %17s %17s\n", "", "Cycles", "Count");
|
|||
|
|
|||
|
for (; ;) {
|
|||
|
//
|
|||
|
// Set MAX_EVENTS
|
|||
|
//
|
|||
|
|
|||
|
CounterSet = FALSE;
|
|||
|
i = 0;
|
|||
|
while (i < MAX_EVENTS) {
|
|||
|
cnttype = -1;
|
|||
|
if (DumpAll) {
|
|||
|
//
|
|||
|
// Dump all - get next counter
|
|||
|
//
|
|||
|
|
|||
|
if (Counters[pos].ShortName) {
|
|||
|
cnttype = pos;
|
|||
|
pos++;
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// process command line args
|
|||
|
//
|
|||
|
|
|||
|
if (pos < argc) {
|
|||
|
cnttype = FindShortName (argv[pos]);
|
|||
|
if (cnttype == -1) {
|
|||
|
printf ("Counter '%s' not found\n", argv[pos]);
|
|||
|
pos++;
|
|||
|
continue;
|
|||
|
}
|
|||
|
pos++;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
CounterSet |= SetCounter (cnttype, i);
|
|||
|
i++;
|
|||
|
}
|
|||
|
|
|||
|
if (!CounterSet) {
|
|||
|
// done
|
|||
|
exit (1);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Call driver and perform the setting
|
|||
|
//
|
|||
|
|
|||
|
SetCounterEncodings ();
|
|||
|
if ( Delay == 0 ) {
|
|||
|
printf( "Counters set\n" );
|
|||
|
// done
|
|||
|
exit(1);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Snap begining & ending counts
|
|||
|
//
|
|||
|
|
|||
|
Sleep (50); // slight settle
|
|||
|
GetInternalStats (BufferStart); // snap current values
|
|||
|
Sleep (Delay); // sleep desired time
|
|||
|
GetInternalStats (BufferEnd); // snap ending values
|
|||
|
|
|||
|
//
|
|||
|
// Calculate each counter and print it
|
|||
|
//
|
|||
|
|
|||
|
for (i=0; i < MAX_EVENTS; i++) {
|
|||
|
if (!CounterEvent[i].Active) {
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
len = *((PULONG) BufferStart);
|
|||
|
|
|||
|
if (ProcessorBreakout) {
|
|||
|
//
|
|||
|
// Print stat for each processor
|
|||
|
//
|
|||
|
|
|||
|
ProcStart = (pPSTATS) ((PUCHAR) BufferStart + sizeof(ULONG));
|
|||
|
ProcEnd = (pPSTATS) ((PUCHAR) BufferEnd + sizeof(ULONG));
|
|||
|
|
|||
|
for (j=0; j < NumberOfProcessors; j++) {
|
|||
|
ETime = ProcEnd->TSC - ProcStart->TSC;
|
|||
|
ECount = ProcEnd->Counters[i] - ProcStart->Counters[i];
|
|||
|
|
|||
|
ProcStart = (pPSTATS) (((PUCHAR) ProcStart) + len);
|
|||
|
ProcEnd = (pPSTATS) (((PUCHAR) ProcEnd) + len);
|
|||
|
|
|||
|
LI2Str (s1, ETime);
|
|||
|
LI2Str (s2, ECount);
|
|||
|
printf (" P%d %-30s %s %s\n",
|
|||
|
j,
|
|||
|
Counters[CounterEvent[i].AppReserved].PerfName,
|
|||
|
s1, s2
|
|||
|
);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (!ProcessorBreakout || ProcessorTotal) {
|
|||
|
//
|
|||
|
// Sum processor's and print it
|
|||
|
//
|
|||
|
|
|||
|
ProcStart = (pPSTATS) ((PUCHAR) BufferStart + sizeof(ULONG));
|
|||
|
ProcEnd = (pPSTATS) ((PUCHAR) BufferEnd + sizeof(ULONG));
|
|||
|
|
|||
|
ETime = 0;
|
|||
|
ECount = 0;
|
|||
|
|
|||
|
for (j=0; j < NumberOfProcessors; j++) {
|
|||
|
ETime = ETime + ProcEnd->TSC;
|
|||
|
ETime = ETime - ProcStart->TSC;
|
|||
|
|
|||
|
ECount = ECount + ProcEnd->Counters[i];
|
|||
|
ECount = ECount - ProcStart->Counters[i];
|
|||
|
|
|||
|
ProcStart = (pPSTATS) (((PUCHAR) ProcStart) + len);
|
|||
|
ProcEnd = (pPSTATS) (((PUCHAR) ProcEnd) + len);
|
|||
|
}
|
|||
|
|
|||
|
LI2Str (s1, ETime);
|
|||
|
LI2Str (s2, ECount);
|
|||
|
printf (" %-30s %s %s\n",
|
|||
|
Counters[CounterEvent[i].AppReserved].PerfName,
|
|||
|
s1, s2
|
|||
|
);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
InitDriver ()
|
|||
|
{
|
|||
|
UNICODE_STRING DriverName;
|
|||
|
NTSTATUS status;
|
|||
|
OBJECT_ATTRIBUTES ObjA;
|
|||
|
IO_STATUS_BLOCK IOSB;
|
|||
|
SYSTEM_BASIC_INFORMATION BasicInfo;
|
|||
|
PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION PPerfInfo;
|
|||
|
int i;
|
|||
|
|
|||
|
//
|
|||
|
// Init Nt performance interface
|
|||
|
//
|
|||
|
|
|||
|
NtQuerySystemInformation(
|
|||
|
SystemBasicInformation,
|
|||
|
&BasicInfo,
|
|||
|
sizeof(BasicInfo),
|
|||
|
NULL
|
|||
|
);
|
|||
|
|
|||
|
NumberOfProcessors = BasicInfo.NumberOfProcessors;
|
|||
|
|
|||
|
if (NumberOfProcessors > MAX_PROCESSORS) {
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// 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
|
|||
|
);
|
|||
|
|
|||
|
return NT_SUCCESS(status) ? TRUE : FALSE;
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
InitPossibleEventList()
|
|||
|
{
|
|||
|
UCHAR buffer[400];
|
|||
|
ULONG i, Count;
|
|||
|
NTSTATUS status;
|
|||
|
PEVENTID Event;
|
|||
|
IO_STATUS_BLOCK IOSB;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Initialize possible counters
|
|||
|
//
|
|||
|
|
|||
|
// determine how many events there are
|
|||
|
|
|||
|
Event = (PEVENTID) buffer;
|
|||
|
Count = 0;
|
|||
|
do {
|
|||
|
*((PULONG) buffer) = Count;
|
|||
|
Count += 1;
|
|||
|
|
|||
|
status = NtDeviceIoControlFile(
|
|||
|
DriverHandle,
|
|||
|
(HANDLE) NULL, // event
|
|||
|
(PIO_APC_ROUTINE) NULL,
|
|||
|
(PVOID) NULL,
|
|||
|
&IOSB,
|
|||
|
PSTAT_QUERY_EVENTS,
|
|||
|
buffer, // input buffer
|
|||
|
sizeof (buffer),
|
|||
|
NULL, // output buffer
|
|||
|
0
|
|||
|
);
|
|||
|
} while (NT_SUCCESS(status));
|
|||
|
|
|||
|
Counters = malloc(sizeof(*Counters) * Count);
|
|||
|
if (Counters == NULL) {
|
|||
|
printf ("Memory allocation failure initializing event list\n");
|
|||
|
exit(1);
|
|||
|
}
|
|||
|
|
|||
|
Count -= 1;
|
|||
|
for (i=0; i < Count; i++) {
|
|||
|
*((PULONG) buffer) = i;
|
|||
|
NtDeviceIoControlFile(
|
|||
|
DriverHandle,
|
|||
|
(HANDLE) NULL, // event
|
|||
|
(PIO_APC_ROUTINE) NULL,
|
|||
|
(PVOID) NULL,
|
|||
|
&IOSB,
|
|||
|
PSTAT_QUERY_EVENTS,
|
|||
|
buffer, // input buffer
|
|||
|
sizeof (buffer),
|
|||
|
NULL, // output buffer
|
|||
|
0
|
|||
|
);
|
|||
|
|
|||
|
Counters[i].EventId = Event->EventId;
|
|||
|
Counters[i].ShortName = _strdup (Event->Buffer);
|
|||
|
Counters[i].PerfName = _strdup (Event->Buffer + Event->DescriptionOffset);
|
|||
|
}
|
|||
|
|
|||
|
Counters[i].EventId = 0;
|
|||
|
Counters[i].ShortName = NULL;
|
|||
|
Counters[i].PerfName = NULL;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
VOID LI2Str (PSZ s, ULONGLONG li)
|
|||
|
{
|
|||
|
if (li > 0xFFFFFFFF) {
|
|||
|
sprintf (s, "%08x:%08x", (ULONG) (li >> 32), (ULONG) li);
|
|||
|
} else {
|
|||
|
sprintf (s, " %08x", (ULONG) li);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
LONG FindShortName (PSZ name)
|
|||
|
{
|
|||
|
LONG i;
|
|||
|
|
|||
|
for (i=0; Counters[i].ShortName; i++) {
|
|||
|
if (strcmp (Counters[i].ShortName, name) == 0) {
|
|||
|
return i;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return -1;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
VOID GetInternalStats (PVOID Buffer)
|
|||
|
{
|
|||
|
IO_STATUS_BLOCK IOSB;
|
|||
|
|
|||
|
NtDeviceIoControlFile(
|
|||
|
DriverHandle,
|
|||
|
(HANDLE) NULL, // event
|
|||
|
(PIO_APC_ROUTINE) NULL,
|
|||
|
(PVOID) NULL,
|
|||
|
&IOSB,
|
|||
|
PSTAT_READ_STATS,
|
|||
|
Buffer, // input buffer
|
|||
|
INFSIZE,
|
|||
|
NULL, // output buffer
|
|||
|
0
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
VOID SetCounterEncodings (VOID)
|
|||
|
{
|
|||
|
IO_STATUS_BLOCK IOSB;
|
|||
|
|
|||
|
NtDeviceIoControlFile(
|
|||
|
DriverHandle,
|
|||
|
(HANDLE) NULL, // event
|
|||
|
(PIO_APC_ROUTINE) NULL,
|
|||
|
(PVOID) NULL,
|
|||
|
&IOSB,
|
|||
|
PSTAT_SET_CESR,
|
|||
|
CounterEvent, // input buffer
|
|||
|
sizeof (CounterEvent),
|
|||
|
NULL, // output buffer
|
|||
|
0
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
BOOLEAN SetCounter (LONG CounterId, ULONG counter)
|
|||
|
{
|
|||
|
if (CounterId == -1) {
|
|||
|
CounterEvent[counter].Active = FALSE;
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
CounterEvent[counter].EventId = Counters[CounterId].EventId;
|
|||
|
CounterEvent[counter].AppReserved = (ULONG) CounterId;
|
|||
|
CounterEvent[counter].Active = TRUE;
|
|||
|
CounterEvent[counter].UserMode = TRUE;
|
|||
|
CounterEvent[counter].KernelMode = TRUE;
|
|||
|
|
|||
|
return TRUE;
|
|||
|
}
|