/*++ 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 #include #include #include #undef NULL #include #include #include #include #include #include #include #include #include #include #include #include #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); } }