418 lines
7.0 KiB
C
418 lines
7.0 KiB
C
|
||
/*++
|
||
|
||
|
||
Copyright (c) 1989 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
napdump.c
|
||
|
||
|
||
Module Description:
|
||
|
||
Dump Nt Api Profiling data.
|
||
|
||
This is a perverted derivative of teh apfdump.* utility.
|
||
|
||
|
||
Environment:
|
||
|
||
Windows command line.
|
||
|
||
Author:
|
||
|
||
Russ Blake (russbl) 25-Apr-1991
|
||
|
||
|
||
Revision History:
|
||
|
||
|
||
--*/
|
||
|
||
|
||
|
||
#include <excpt.h>
|
||
#include <nt.h>
|
||
#include <ntrtl.h>
|
||
#include <nturtl.h>
|
||
#undef NULL
|
||
#include <stdio.h>
|
||
#include <string.h>
|
||
#include <direct.h>
|
||
#include <sys\types.h>
|
||
#include <sys\stat.h>
|
||
#include <io.h>
|
||
#include <conio.h>
|
||
#include <errno.h>
|
||
#include <process.h>
|
||
#include <ctype.h>
|
||
#include <windows.h>
|
||
#include <tools.h>
|
||
|
||
|
||
#define MICROSEC_FACTOR 1000000
|
||
#define MAX_TIME 0x7fffffffL
|
||
|
||
//
|
||
// Dump switches and defaults
|
||
//
|
||
BOOLEAN Dump = TRUE; // if not dumping,
|
||
// then clearing
|
||
|
||
CHAR DumpFileName[256] = "ntdll.nap"; // default name
|
||
|
||
CHAR OutputRecord[132];
|
||
|
||
CHAR *rgstrUsage[] = {
|
||
"Usage: napdump [ {/c | filename } ]",
|
||
" Default - data dumped to file named ntdll.nap",
|
||
" /c - clear data already collected",
|
||
" filename - dump data to filename",
|
||
0};
|
||
|
||
FILE *DumpFile;
|
||
ULONG ApiCount;
|
||
NAPDATA *ApiProfilingData;
|
||
PCHAR *ApiNames;
|
||
PLARGE_INTEGER CounterFreq;
|
||
|
||
/*++
|
||
|
||
Usage takes a variable number of strings, terminated by zero,
|
||
e.g. Usage ("first ", "second ", 0);
|
||
|
||
--*/
|
||
|
||
void
|
||
Usage (
|
||
CHAR *p,
|
||
...
|
||
)
|
||
{
|
||
CHAR **rgstr;
|
||
|
||
rgstr = &p;
|
||
if (*rgstr) {
|
||
fprintf (stderr, "TC: ");
|
||
while (*rgstr)
|
||
fprintf(stderr, "%s", *rgstr++);
|
||
fprintf(stderr, "\n");
|
||
}
|
||
rgstr = rgstrUsage;
|
||
while (*rgstr)
|
||
fprintf(stderr, "%s\n", *rgstr++);
|
||
|
||
exit (1);
|
||
}
|
||
|
||
/*++
|
||
|
||
ExtractTime()
|
||
|
||
Extract a time from a large integer, converting it to microseconds
|
||
|
||
--*/
|
||
|
||
|
||
VOID
|
||
ExtractTime (
|
||
PLARGE_INTEGER RealTime,
|
||
LARGE_INTEGER Time,
|
||
LONG Overhead,
|
||
ULONG Count
|
||
)
|
||
{
|
||
LARGE_INTEGER ElapsedTime;
|
||
LARGE_INTEGER OverheadTime;
|
||
|
||
|
||
|
||
ElapsedTime = RtlExtendedIntegerMultiply (Time, MICROSEC_FACTOR);
|
||
|
||
ElapsedTime = RtlExtendedLargeIntegerDivide (ElapsedTime,
|
||
CounterFreq->LowPart,
|
||
NULL);
|
||
|
||
OverheadTime.HighPart = 0;
|
||
OverheadTime.LowPart = Count * Overhead;
|
||
|
||
*RealTime = RtlLargeIntegerSubtract(ElapsedTime, OverheadTime);
|
||
}
|
||
|
||
/*++
|
||
|
||
AdjustTime
|
||
|
||
This routine is called to convert long times to smaller times
|
||
expressed as multiples of 1024 (= 1K).
|
||
|
||
--*/
|
||
|
||
VOID
|
||
AdjustTime (
|
||
PLARGE_INTEGER Time,
|
||
PCHAR Kchar
|
||
)
|
||
{
|
||
if (Time->HighPart != 0) {
|
||
|
||
if (Time->HighPart>>10 > 0) {
|
||
|
||
fprintf(DumpFile,
|
||
"*** Unexpected timer overflow: %ld \t %lu ***\n",
|
||
Time->HighPart,
|
||
Time->LowPart);
|
||
|
||
Time->HighPart = 0;
|
||
Time->LowPart = 0;
|
||
*Kchar = '?';
|
||
|
||
} else if (Time->HighPart>>10 < 0) {
|
||
|
||
fprintf(DumpFile,
|
||
"*** Unexpected timer underflow: %ld \t %lu ***\n",
|
||
Time->HighPart,
|
||
Time->LowPart);
|
||
|
||
Time->HighPart = 0;
|
||
Time->LowPart = 0;
|
||
*Kchar = '?';
|
||
|
||
} else {
|
||
|
||
Time->LowPart = ((ULONG)(Time->HighPart))<<22 +
|
||
Time->LowPart>>10;
|
||
|
||
*Kchar = 'K';
|
||
}
|
||
|
||
} else {
|
||
|
||
*Kchar = ' ';
|
||
}
|
||
}
|
||
|
||
|
||
|
||
/*++
|
||
|
||
Main Routine
|
||
|
||
--*/
|
||
|
||
|
||
VOID
|
||
main (c, v)
|
||
int c;
|
||
CHAR *v[];
|
||
{
|
||
ULONG Api;
|
||
CHAR *p;
|
||
LONG Overhead;
|
||
|
||
struct {
|
||
LONG TotalTime;
|
||
LONG FirstTime;
|
||
LONG MaxTime;
|
||
LONG MinTime;
|
||
} ProfData;
|
||
|
||
LARGE_INTEGER OverheadTime;
|
||
LARGE_INTEGER TotalTime;
|
||
LARGE_INTEGER PerCallTime;
|
||
LARGE_INTEGER FirstTime;
|
||
LARGE_INTEGER MaxTime;
|
||
LARGE_INTEGER MinTime;
|
||
|
||
CHAR TotalSuffix;
|
||
CHAR PerCallSuffix;
|
||
CHAR FirstSuffix;
|
||
CHAR MaxSuffix;
|
||
CHAR MinSuffix;
|
||
|
||
|
||
SHIFT(c,v);
|
||
while (c && fSwitChr (*v[0])) {
|
||
p = v[0];
|
||
SHIFT(c,v);
|
||
while (*++p) {
|
||
switch (*p) {
|
||
case 'c':
|
||
Dump = FALSE;
|
||
break;
|
||
case '?':
|
||
case 'h':
|
||
case 'H':
|
||
Usage(0);
|
||
break;
|
||
default:
|
||
Usage("Invalid switch - ", p, 0);
|
||
}
|
||
}
|
||
}
|
||
|
||
if (c > 1) {
|
||
Usage("Too many parameters - ", p, 0);
|
||
}
|
||
|
||
if (c == 1) {
|
||
strcpy(DumpFileName, v[0]);
|
||
}
|
||
|
||
if (!Dump) {
|
||
NapPause();
|
||
NapClearData();
|
||
NapResume();
|
||
}
|
||
else {
|
||
|
||
//
|
||
// Stop recording
|
||
//
|
||
|
||
NapPause();
|
||
|
||
DumpFile = fopen(DumpFileName, "w");
|
||
if (!DumpFile) {
|
||
Usage("Unable to open output file %s\n", DumpFileName);
|
||
}
|
||
|
||
NapGetApiCount(&ApiCount);
|
||
|
||
ApiProfilingData = (NAPDATA *) malloc((ApiCount+1) * sizeof(NAPDATA));
|
||
|
||
|
||
NapRetrieveData(ApiProfilingData, &ApiNames, &CounterFreq);
|
||
|
||
ExtractTime(&OverheadTime, ApiProfilingData[0].MinTime, 0, 0);
|
||
|
||
//
|
||
// Now dump the data; start with the header info
|
||
//
|
||
|
||
fprintf(DumpFile,
|
||
"%s: Api profile of nt service routines.\n",
|
||
DumpFileName);
|
||
|
||
fprintf(DumpFile, "All times are in microseconds.\n");
|
||
|
||
|
||
fprintf(DumpFile,
|
||
"All times (except Calibration) corrected "
|
||
"by %lu microseconds/call.\n",
|
||
OverheadTime.LowPart);
|
||
|
||
fprintf(DumpFile,
|
||
"(Note: First Time not included in Max or Min Times)\n\n\n");
|
||
|
||
fprintf(DumpFile,
|
||
"%-32s\t%10s\t%10s\t%10s\t%10s\t%10s\t%10s\t\n\n",
|
||
"API Name",
|
||
"Num Calls",
|
||
"Total Time",
|
||
"Time/Call",
|
||
"First Time",
|
||
"Max Time",
|
||
"Min Time");
|
||
|
||
//
|
||
// Dump the data for each api that got used
|
||
//
|
||
|
||
//
|
||
// The Calibration Counters (Api = 0) should not be adjusted for
|
||
// overhead.
|
||
// Note: this is repaired for all api at the end of the for loop.
|
||
//
|
||
|
||
Overhead = 0;
|
||
|
||
for (Api = 0; Api <= ApiCount; Api++) {
|
||
|
||
|
||
if (ApiProfilingData[Api].Calls >= 1) {
|
||
|
||
ExtractTime(&TotalTime,
|
||
ApiProfilingData[Api].TotalTime,
|
||
Overhead,
|
||
ApiProfilingData[Api].Calls);
|
||
|
||
ExtractTime(&FirstTime,
|
||
ApiProfilingData[Api].FirstTime,
|
||
Overhead, 1);
|
||
|
||
AdjustTime(&TotalTime,&TotalSuffix);
|
||
|
||
PerCallTime.HighPart = 0;
|
||
|
||
PerCallTime.LowPart = TotalTime.LowPart /
|
||
ApiProfilingData[Api].Calls;
|
||
|
||
PerCallSuffix = TotalSuffix;
|
||
|
||
AdjustTime(&FirstTime,&FirstSuffix);
|
||
|
||
if (ApiProfilingData[Api].Calls > 1) {
|
||
|
||
ExtractTime(&MaxTime,
|
||
ApiProfilingData[Api].MaxTime,
|
||
Overhead, 1);
|
||
|
||
ExtractTime(&MinTime,
|
||
ApiProfilingData[Api].MinTime,
|
||
Overhead, 1);
|
||
|
||
AdjustTime(&MaxTime,&MaxSuffix);
|
||
|
||
AdjustTime(&MinTime,&MinSuffix);
|
||
|
||
fprintf(DumpFile,
|
||
"%-32s\t%10lu\t%10lu%1c\t%10lu%1c\t%10lu%1c"
|
||
"\t%10lu%1c\t%10lu%1c\n",
|
||
ApiNames[Api],
|
||
ApiProfilingData[Api].Calls,
|
||
TotalTime.LowPart,
|
||
TotalSuffix,
|
||
PerCallTime.LowPart,
|
||
PerCallSuffix,
|
||
FirstTime.LowPart,
|
||
FirstSuffix,
|
||
MaxTime.LowPart,
|
||
MaxSuffix,
|
||
MinTime.LowPart,
|
||
MinSuffix);
|
||
}
|
||
else if (ApiProfilingData[Api].Calls == 1) {
|
||
fprintf(DumpFile,
|
||
"%-32s\t%10lu\t%10lu%1c\t%10lu%1c\t%10lu%1c"
|
||
"\t%10s\t%10s\n",
|
||
ApiNames[Api],
|
||
ApiProfilingData[Api].Calls,
|
||
TotalTime.LowPart,
|
||
TotalSuffix,
|
||
PerCallTime.LowPart,
|
||
PerCallSuffix,
|
||
FirstTime.LowPart,
|
||
FirstSuffix,
|
||
"n/a",
|
||
"n/a");
|
||
|
||
}
|
||
}
|
||
|
||
Overhead = OverheadTime.LowPart;
|
||
}
|
||
|
||
//
|
||
// Start collecting data again
|
||
//
|
||
|
||
free(ApiProfilingData);
|
||
|
||
NapResume();
|
||
|
||
fclose(DumpFile);
|
||
}
|
||
}
|