mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
417 lines
7.0 KiB
417 lines
7.0 KiB
|
|
/*++
|
|
|
|
|
|
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);
|
|
}
|
|
}
|