/*++ Copyright (c) 1990 Microsoft Corporation Module Name: kernprof.c Abstract: This module contains the implementation of a kernel profiler. It uses dbghelp for symbols and image information and creates profile objects for each modules it finds loaded when it starts. Usage: See below Author: Lou Perazzoli (loup) 29-Sep-1990 Envirnoment: Revision History: --*/ #include #include #include #include #include #include #include #include #include #include #include #include <..\pperf\pstat.h> #define SYM_HANDLE INVALID_HANDLE_VALUE #define DBG_PROFILE 0 #define MAX_BYTE_PER_LINE 72 #define MAX_PROFILE_COUNT 100 #define MAX_BUCKET_SHIFT 31 // 2GBytes #define MAX_BUCKET_SIZE 0x80000000U typedef struct _PROFILE_BLOCK { HANDLE Handle[MAXIMUM_PROCESSORS]; PVOID ImageBase; PULONG CodeStart; SIZE_T CodeLength; PULONG Buffer[MAXIMUM_PROCESSORS]; ULONG BufferSize; ULONG BucketSize; LPSTR ModuleName; ULONG ModuleHitCount[MAXIMUM_PROCESSORS]; BOOLEAN SymbolsLoaded; } PROFILE_BLOCK; // // This really should go into a header file but.... // typedef struct _PROFILE_CONTROL_BLOCK { BOOLEAN Stop; char FileName[MAX_PATH]; } PROFILE_CONTROL_BLOCK; typedef PROFILE_CONTROL_BLOCK * PPROFILE_CONTROL_BLOCK; #define PRFEVENT_START_EVENT "PrfEventStartedEvent" #define PRFEVENT_STOP_EVENT "PrfEventStopEvent" #define PRFEVENT_SHARED_MEMORY "PrfEventSharedMemory" // // End header file // #define MAX_SYMNAME_SIZE 1024 CHAR symBuffer[sizeof(IMAGEHLP_SYMBOL)+MAX_SYMNAME_SIZE]; PIMAGEHLP_SYMBOL ThisSymbol = (PIMAGEHLP_SYMBOL) symBuffer; CHAR LastSymBuffer[sizeof(IMAGEHLP_SYMBOL)+MAX_SYMNAME_SIZE]; PIMAGEHLP_SYMBOL LastSymbol = (PIMAGEHLP_SYMBOL) LastSymBuffer; VOID InitializeProfileSourceMapping ( VOID ); NTSTATUS InitializeKernelProfile( VOID ); NTSTATUS RunEventLoop( VOID ); NTSTATUS RunStdProfile( VOID ); NTSTATUS StartProfile( VOID ); NTSTATUS StopProfile( VOID ); NTSTATUS AnalyzeProfile( ULONG Threshold, PSYSTEM_CONTEXT_SWITCH_INFORMATION StartContext, PSYSTEM_CONTEXT_SWITCH_INFORMATION StopContext ); VOID OutputSymbolCount( IN ULONG CountAtSymbol, IN ULONG TotalCount, IN PROFILE_BLOCK *ProfileObject, IN PIMAGEHLP_SYMBOL SymbolInfo, IN ULONG Threshold, IN PULONG CounterStart, IN PULONG CounterStop, IN ULONG Va, IN ULONG BytesPerBucket ); #ifdef _ALPHA_ #define PAGE_SIZE 8192 #else #define PAGE_SIZE 4096 #endif FILE *fpOut = NULL; PROFILE_BLOCK ProfileObject[MAX_PROFILE_COUNT]; DWORD *UserModeBuffer[MAXIMUM_PROCESSORS]; ULONG NumberOfProfileObjects = 0; ULONG MaxProcessors = 1; ULONG ProfileInterval = 10000; CHAR SymbolSearchPathBuf[4096]; LPSTR lpSymbolSearchPath = SymbolSearchPathBuf; // display flags BOOLEAN bDisplayAddress=FALSE; BOOLEAN bDisplayDensity=FALSE; BOOLEAN bDisplayCounters=FALSE; BOOLEAN bDisplayContextSwitch=FALSE; BOOLEAN bPerProcessor = FALSE; BOOLEAN bWaitForInput = FALSE; BOOLEAN bEventLoop = FALSE; BOOLEAN bPrintPercentages = FALSE; BOOLEAN Verbose = FALSE; // // Image name to perform kernel mode analysis upon. // #define IMAGE_NAME "\\SystemRoot\\system32\\ntoskrnl.exe" HANDLE DoneEvent; HANDLE DelayEvent; KPROFILE_SOURCE ProfileSource = ProfileTime; ULONG Seconds = (ULONG)-1; ULONG Threshold = 100; ULONG DelaySeconds = (ULONG)-1; // // define the mappings between arguments and KPROFILE_SOURCE types // typedef struct _PROFILE_SOURCE_MAPPING { PCHAR ShortName; PCHAR Description; KPROFILE_SOURCE Source; } PROFILE_SOURCE_MAPPING, *PPROFILE_SOURCE_MAPPING; #if defined(_ALPHA_) PROFILE_SOURCE_MAPPING ProfileSourceMapping[] = { {"align", "", ProfileAlignmentFixup}, {"totalissues", "", ProfileTotalIssues}, {"pipelinedry", "", ProfilePipelineDry}, {"loadinstructions", "", ProfileLoadInstructions}, {"pipelinefrozen", "", ProfilePipelineFrozen}, {"branchinstructions", "", ProfileBranchInstructions}, {"totalnonissues", "", ProfileTotalNonissues}, {"dcachemisses", "", ProfileDcacheMisses}, {"icachemisses", "", ProfileIcacheMisses}, {"branchmispredicts", "", ProfileBranchMispredictions}, {"storeinstructions", "", ProfileStoreInstructions}, {NULL,0} }; #elif defined(_X86_) PPROFILE_SOURCE_MAPPING ProfileSourceMapping; #else PROFILE_SOURCE_MAPPING ProfileSourceMapping[] = { {NULL,0} }; #endif BOOL CtrlcH( DWORD dwCtrlType ) { if ( dwCtrlType == CTRL_C_EVENT ) { SetEvent(DoneEvent); return TRUE; } return FALSE; } void PrintUsage (void) { fputs ("Kernel Profiler Usage:\n\n" "Kernprof [-acdpnrx] [-w ] [-s Source] [-t ] []\n" " -a - display function address and length and bucket size\n" " -c - display individual counters\n" " -d - compute hit Density for each function\n" //UNDOC " -e - use special event syncronization for start and stop\n" " -f filename - output file (Default stdout)\n" " -i (Default 10000)\n" " -n - print hit percentages\n" " -p - Per-processor profile objects\n" " -r - wait for a before starting collection\n" " -s Source - use Source instead of clock as profile source\n" " ? lists Sources\n" " -t - Minimum number of counts to report.\n" " Defaults is 100\n" " -v - Display verbose symbol information\n" " -w - wait for before starting collection\n" " -x - display context switch counters\n" " - Specify, in seconds, how long to collect\n" " profile information.\n" " Default is wait until Ctrl-C\n\n" #if defined (_ALPHA_) "Currently supported profile sources are 'align', 'totalissues', 'pipelinedry'\n" " 'loadinstructions', 'pipelinefrozen', 'branchinstructions', 'totalnonissues',\n" " 'dcachemisses', 'icachemisses', 'branchmispredicts', 'storeinstructions'\n" #endif , stderr); } __cdecl main( int argc, char *argv[], char *envp[] ) { int j; NTSTATUS status; PPROFILE_SOURCE_MAPPING ProfileMapping; SYSTEM_INFO SystemInfo; fpOut = stdout; ThisSymbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL); ThisSymbol->MaxNameLength = MAX_SYMNAME_SIZE; LastSymbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL); LastSymbol->MaxNameLength = MAX_SYMNAME_SIZE; SymSetOptions( SYMOPT_UNDNAME | SYMOPT_CASE_INSENSITIVE | SYMOPT_OMAP_FIND_NEAREST ); SymInitialize( SYM_HANDLE, NULL, FALSE ); SymGetSearchPath( SYM_HANDLE, SymbolSearchPathBuf, sizeof(SymbolSearchPathBuf) ); // // Parse the input string. // DoneEvent = CreateEvent(NULL,FALSE,FALSE,NULL); if (argc > 1) { if (((argv[1][0] == '-') || (argv[1][0] == '/')) && ((argv[1][1] == '?' ) || (argv[1][1] == 'H') || (argv[1][1] == 'H')) ) { PrintUsage(); return ERROR_SUCCESS; } for (j = 1; j < argc; j++) { BOOLEAN NextArg; char *p; if (argv[j][0] == '-') { NextArg = FALSE; for (p = &argv[j][1] ; *p && !NextArg ; p++) { switch (toupper(*p)) { case 'A': bDisplayAddress = TRUE; break; case 'C': bDisplayCounters = TRUE; break; case 'D': bDisplayDensity = TRUE; break; case 'E': bEventLoop = TRUE; break; case 'F': NextArg = TRUE; fpOut = fopen(argv[++j], "w"); break; case 'I': NextArg = TRUE; ProfileInterval = atoi(argv[++j]); break; case 'N': bPrintPercentages = TRUE; break; case 'P': GetSystemInfo(&SystemInfo); MaxProcessors = SystemInfo.dwNumberOfProcessors; bPerProcessor = TRUE; break; case 'R': bWaitForInput = TRUE; break; case 'S': NextArg = TRUE; if (!ProfileSourceMapping) { InitializeProfileSourceMapping(); } if (!argv[j+1]) { break; } if (argv[j+1][0] == '?') { ProfileMapping = ProfileSourceMapping; if (ProfileMapping) { fprintf (stderr, "kernprof: profile sources\n"); while (ProfileMapping->ShortName != NULL) { fprintf (stderr, " %-10s %s\n", ProfileMapping->ShortName, ProfileMapping->Description ); ++ProfileMapping; } } else { fprintf (stderr, "kernprof: no alternative profile sources\n"); } return 0; } ProfileMapping = ProfileSourceMapping; if (ProfileMapping) { while (ProfileMapping->ShortName != NULL) { if (_stricmp(ProfileMapping->ShortName, argv[j+1])==0) { ProfileSource = ProfileMapping->Source; fprintf (stderr, "ProfileSource %x\n", ProfileMapping->Source); ++j; break; } ++ProfileMapping; } } break; case 'T': NextArg = TRUE; Threshold = atoi(argv[++j]); break; case 'V': Verbose = TRUE; break; case 'W': NextArg = TRUE; DelaySeconds = atoi(argv[++j]); DelayEvent = CreateEvent(NULL,FALSE,FALSE,NULL); break; case 'X': bDisplayContextSwitch = TRUE; break; } } } else { Seconds = atoi(argv[j]); } } } if (bEventLoop || (DelaySeconds != -1)) { SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS); SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); } status = InitializeKernelProfile (); if (!NT_SUCCESS(status)) { fprintf(stderr, "initialize failed status - %lx\n",status); return(status); } if (bEventLoop) RunEventLoop(); else RunStdProfile(); return STATUS_SUCCESS; } NTSTATUS RunEventLoop() { NTSTATUS status; SYSTEM_CONTEXT_SWITCH_INFORMATION StartContext; SYSTEM_CONTEXT_SWITCH_INFORMATION StopContext; HANDLE hStartedEvent = NULL; HANDLE hStopEvent = NULL; HANDLE hMap = NULL; PPROFILE_CONTROL_BLOCK pShared = NULL; // Create the events and shared memory hStartedEvent = CreateEvent (NULL, FALSE, FALSE, PRFEVENT_START_EVENT); if (hStartedEvent == NULL) { fprintf(stderr, "Failed to create started event - 0x%lx\n", GetLastError()); return(GetLastError()); } hStopEvent = CreateEvent (NULL, FALSE, FALSE, PRFEVENT_STOP_EVENT); if (hStopEvent == NULL) { fprintf(stderr, "Failed to create stop event - 0x%lx\n", GetLastError()); return(GetLastError()); } hMap = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE | SEC_COMMIT, 0, sizeof(PROFILE_CONTROL_BLOCK), PRFEVENT_SHARED_MEMORY); if (hMap == NULL) { fprintf(stderr, "Failed to create the file mapping - 0x%lx\n", GetLastError()); return(GetLastError()); } pShared = (PPROFILE_CONTROL_BLOCK) MapViewOfFile(hMap, FILE_MAP_WRITE, 0,0, sizeof(PROFILE_CONTROL_BLOCK)); if (pShared == NULL) { fprintf(stderr, "Failed to map the shared memory view - 0x%lx\n", GetLastError()); return(GetLastError()); } // Wait for start i.e., the stop event WaitForSingleObject(hStopEvent, INFINITE); while (TRUE) { if (bDisplayContextSwitch) { NtQuerySystemInformation(SystemContextSwitchInformation, &StartContext, sizeof(StartContext), NULL); } status = StartProfile (); if (!NT_SUCCESS(status)) { fprintf(stderr, "start profile failed status - %lx\n",status); break; } // Signal started SetEvent(hStartedEvent); // Wait for stop WaitForSingleObject(hStopEvent, INFINITE); status = StopProfile (); if (!NT_SUCCESS(status)) { fprintf(stderr, "stop profile failed status - %lx\n",status); break; } if (bDisplayContextSwitch) { status = NtQuerySystemInformation(SystemContextSwitchInformation, &StopContext, sizeof(StopContext), NULL); if (!NT_SUCCESS(status)) { fprintf(stderr, "QuerySystemInformation for context switch information failed %08lx\n",status); bDisplayContextSwitch = FALSE; } } fpOut = fopen(pShared->FileName, "w"); status = AnalyzeProfile (Threshold, &StartContext, &StopContext); fclose(fpOut); if (!NT_SUCCESS(status)) { fprintf(stderr, "analyze profile failed status - %lx\n",status); } if (pShared->Stop == TRUE) break; } SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS); SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL); UnmapViewOfFile((void*)pShared); CloseHandle(hMap); CloseHandle(hStopEvent); CloseHandle(hStartedEvent); return(status); } NTSTATUS RunStdProfile() { NTSTATUS status; SYSTEM_CONTEXT_SWITCH_INFORMATION StartContext; SYSTEM_CONTEXT_SWITCH_INFORMATION StopContext; SetConsoleCtrlHandler(CtrlcH,TRUE); if (DelaySeconds != -1) { fprintf(stderr, "starting profile after %d seconds\n",DelaySeconds); WaitForSingleObject(DelayEvent, DelaySeconds*1000); } if (bDisplayContextSwitch) { NtQuerySystemInformation(SystemContextSwitchInformation, &StartContext, sizeof(StartContext), NULL); } status = StartProfile (); if (!NT_SUCCESS(status)) { fprintf(stderr, "start profile failed status - %lx\n",status); return(status); } if ( Seconds == -1 ) { fprintf(stderr, "delaying until ^C\n"); } else { fprintf(stderr, "delaying for %ld seconds... " "report on values with %ld hits\n", Seconds, Threshold ); } if ( Seconds ) { if ( Seconds != -1 ) { Seconds = Seconds * 1000; } if ( DoneEvent ) { WaitForSingleObject(DoneEvent,Seconds); } else { Sleep(Seconds); } } else { getchar(); } fprintf (stderr, "end of delay\n"); status = StopProfile (); if (!NT_SUCCESS(status)) { fprintf(stderr, "stop profile failed status - %lx\n",status); return(status); } SetConsoleCtrlHandler(CtrlcH,FALSE); if (bDisplayContextSwitch) { status = NtQuerySystemInformation(SystemContextSwitchInformation, &StopContext, sizeof(StopContext), NULL); if (!NT_SUCCESS(status)) { fprintf(stderr, "QuerySystemInformation for context switch information failed %08lx\n",status); bDisplayContextSwitch = FALSE; } } if (DelaySeconds != -1) { SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS); SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL); } status = AnalyzeProfile (Threshold, &StartContext, &StopContext); if (!NT_SUCCESS(status)) { fprintf(stderr, "analyze profile failed status - %lx\n",status); } return(status); } VOID InitializeProfileSourceMapping ( VOID ) { #if defined(_X86_) UNICODE_STRING DriverName; NTSTATUS status; OBJECT_ATTRIBUTES ObjA; IO_STATUS_BLOCK IOSB; UCHAR buffer[400]; ULONG i, j, Count; PEVENTID Event; HANDLE DriverHandle; // // 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 ); if (!NT_SUCCESS(status)) { return ; } // // 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)); ProfileSourceMapping = malloc(sizeof(*ProfileSourceMapping) * Count); Count -= 1; for (i=0, j=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 ); if (Event->ProfileSource > ProfileTime) { ProfileSourceMapping[j].Source = Event->ProfileSource; ProfileSourceMapping[j].ShortName = _strdup (Event->Buffer); ProfileSourceMapping[j].Description = _strdup (Event->Buffer + Event->DescriptionOffset); j++; } } ProfileSourceMapping[j].Source = (KPROFILE_SOURCE) 0; ProfileSourceMapping[j].ShortName = NULL; ProfileSourceMapping[j].Description = NULL; NtClose (DriverHandle); #endif } NTSTATUS InitializeKernelProfile ( VOID ) /*++ Routine Description: This routine initializes profiling for the kernel for the current process. Arguments: None. Return Value: Returns the status of the last NtCreateProfile. --*/ { ULONG i; ULONG ModuleNumber; SIZE_T ViewSize; PULONG CodeStart; ULONG CodeLength; NTSTATUS LocalStatus; NTSTATUS status; HANDLE CurrentProcessHandle; QUOTA_LIMITS QuotaLimits; PVOID Buffer; DWORD Cells; ULONG BucketSize; WCHAR StringBuf[500]; PCHAR ModuleInfoBuffer; ULONG ModuleInfoBufferLength; ULONG ReturnedLength; PRTL_PROCESS_MODULES Modules; PRTL_PROCESS_MODULE_INFORMATION Module; UNICODE_STRING Sysdisk; UNICODE_STRING Sysroot; UNICODE_STRING Sysdll; UNICODE_STRING NameString; BOOLEAN PreviousProfilePrivState; BOOLEAN PreviousQuotaPrivState; CHAR ImageName[256]; HANDLE hFile; HANDLE hMap; PVOID MappedBase; PIMAGE_NT_HEADERS NtHeaders; CurrentProcessHandle = NtCurrentProcess(); // // Locate system drivers. // ModuleInfoBufferLength = 0; ModuleInfoBuffer = NULL; while (1) { status = NtQuerySystemInformation (SystemModuleInformation, ModuleInfoBuffer, ModuleInfoBufferLength, &ReturnedLength); if (NT_SUCCESS (status)) { break; } if (ModuleInfoBuffer != NULL) { RtlFreeHeap (RtlProcessHeap (), 0, ModuleInfoBuffer); } if (status == STATUS_INFO_LENGTH_MISMATCH && ReturnedLength > ModuleInfoBufferLength) { ModuleInfoBufferLength = ReturnedLength; ModuleInfoBuffer = RtlAllocateHeap (RtlProcessHeap(), 0, ModuleInfoBufferLength); if (ModuleInfoBuffer == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } } else if (!NT_SUCCESS(status)) { fprintf(stderr, "query system info failed status - %lx\n",status); return(status); } } RtlInitUnicodeString (&Sysdisk,L"\\SystemRoot\\"); RtlInitUnicodeString (&Sysroot,L"\\SystemRoot\\System32\\Drivers\\"); RtlInitUnicodeString (&Sysdll, L"\\SystemRoot\\System32\\"); NameString.Buffer = StringBuf; NameString.Length = 0; NameString.MaximumLength = sizeof( StringBuf ); status = RtlAdjustPrivilege( SE_SYSTEM_PROFILE_PRIVILEGE, TRUE, //Enable FALSE, //not impersonating &PreviousProfilePrivState ); if (!NT_SUCCESS(status) || status == STATUS_NOT_ALL_ASSIGNED) { fprintf(stderr, "Enable system profile privilege failed - status 0x%lx\n", status); } status = RtlAdjustPrivilege( SE_INCREASE_QUOTA_PRIVILEGE, TRUE, //Enable FALSE, //not impersonating &PreviousQuotaPrivState ); if (!NT_SUCCESS(status) || status == STATUS_NOT_ALL_ASSIGNED) { fprintf(stderr, "Unable to increase quota privilege (status=0x%lx)\n", status); } Modules = (PRTL_PROCESS_MODULES)ModuleInfoBuffer; Module = &Modules->Modules[ 0 ]; for (ModuleNumber=0; ModuleNumber < Modules->NumberOfModules; ModuleNumber++,Module++) { #if DBG_PROFILE fprintf(stderr, "module base %p\n",Module->ImageBase); fprintf(stderr, "module full path name: %s (%u)\n", Module->FullPathName, Module->OffsetToFileName); #endif if (SymLoadModule( SYM_HANDLE, NULL, &Module->FullPathName[Module->OffsetToFileName], NULL, (ULONG_PTR)Module->ImageBase, Module->ImageSize )) { ProfileObject[NumberOfProfileObjects].SymbolsLoaded = TRUE; if (Verbose) { fprintf(stderr, "Symbols loaded: %p %s\n", Module->ImageBase, &Module->FullPathName[Module->OffsetToFileName] ); } } else { ProfileObject[NumberOfProfileObjects].SymbolsLoaded = FALSE; if (Verbose) { fprintf(stderr, "*** Could not load symbols: %p %s\n", Module->ImageBase, &Module->FullPathName[Module->OffsetToFileName] ); } } hFile = FindExecutableImage( &Module->FullPathName[Module->OffsetToFileName], lpSymbolSearchPath, ImageName ); if (!hFile) { continue; } hMap = CreateFileMapping( hFile, NULL, PAGE_READONLY, 0, 0, NULL ); if (!hMap) { CloseHandle( hFile ); continue; } MappedBase = MapViewOfFile( hMap, FILE_MAP_READ, 0, 0, 0 ); if (!MappedBase) { CloseHandle( hMap ); CloseHandle( hFile ); continue; } NtHeaders = ImageNtHeader( MappedBase ); CodeLength = NtHeaders->OptionalHeader.SizeOfImage; CodeStart = (PULONG)Module->ImageBase; UnmapViewOfFile( MappedBase ); CloseHandle( hMap ); CloseHandle( hFile ); if (CodeLength > 1024*512) { // // Just create a 512K byte buffer. // ViewSize = 1024 * 512; } else { ViewSize = CodeLength + PAGE_SIZE; } ProfileObject[NumberOfProfileObjects].CodeStart = CodeStart; ProfileObject[NumberOfProfileObjects].CodeLength = CodeLength; ProfileObject[NumberOfProfileObjects].ImageBase = Module->ImageBase; ProfileObject[NumberOfProfileObjects].ModuleName = _strdup(&Module->FullPathName[Module->OffsetToFileName]); for (i=0; i> 2)) >> 2); BucketSize = 2; while (Cells != 0) { Cells = Cells >> 1; BucketSize += 1; } ProfileObject[NumberOfProfileObjects].Buffer[i] = Buffer; ProfileObject[NumberOfProfileObjects].BufferSize = 1 + (CodeLength >> (BucketSize - 2)); ProfileObject[NumberOfProfileObjects].BucketSize = BucketSize; // // Increase the working set to lock down a bigger buffer. // status = NtQueryInformationProcess (CurrentProcessHandle, ProcessQuotaLimits, &QuotaLimits, sizeof(QUOTA_LIMITS), NULL ); if (!NT_SUCCESS(status)) { fprintf (stderr, "query process info failed %lx\n",status); RtlFreeHeap (RtlProcessHeap (), 0, ModuleInfoBuffer); return(status); } QuotaLimits.MaximumWorkingSetSize += ViewSize; QuotaLimits.MinimumWorkingSetSize += ViewSize; status = NtSetInformationProcess (CurrentProcessHandle, ProcessQuotaLimits, &QuotaLimits, sizeof(QUOTA_LIMITS)); #if DBG_PROFILE fprintf(stderr, "code start %p len %p, bucksize %lx buffer %p bsize %08x\n", ProfileObject[NumberOfProfileObjects].CodeStart, ProfileObject[NumberOfProfileObjects].CodeLength, ProfileObject[NumberOfProfileObjects].BucketSize, ProfileObject[NumberOfProfileObjects].Buffer , ProfileObject[NumberOfProfileObjects].BufferSize); #endif if (bPerProcessor) { status = NtCreateProfile ( &ProfileObject[NumberOfProfileObjects].Handle[i], 0, ProfileObject[NumberOfProfileObjects].CodeStart, ProfileObject[NumberOfProfileObjects].CodeLength, ProfileObject[NumberOfProfileObjects].BucketSize, ProfileObject[NumberOfProfileObjects].Buffer[i] , ProfileObject[NumberOfProfileObjects].BufferSize, ProfileSource, 1 << i); } else { status = NtCreateProfile ( &ProfileObject[NumberOfProfileObjects].Handle[i], 0, ProfileObject[NumberOfProfileObjects].CodeStart, ProfileObject[NumberOfProfileObjects].CodeLength, ProfileObject[NumberOfProfileObjects].BucketSize, ProfileObject[NumberOfProfileObjects].Buffer[i] , ProfileObject[NumberOfProfileObjects].BufferSize, ProfileSource, (KAFFINITY)-1); } if (status != STATUS_SUCCESS) { fprintf(stderr, "create kernel profile %s failed - status %lx\n", ProfileObject[NumberOfProfileObjects].ModuleName, status); } } NumberOfProfileObjects += 1; if (NumberOfProfileObjects == MAX_PROFILE_COUNT) { RtlFreeHeap (RtlProcessHeap (), 0, ModuleInfoBuffer); return STATUS_SUCCESS; } } if (NumberOfProfileObjects < MAX_PROFILE_COUNT) { // // Add in usermode object // 0x00000000 -> SystemRangeStart // ULONG_PTR SystemRangeStart; ULONG UserModeBucketCount; status = NtQuerySystemInformation(SystemRangeStartInformation, &SystemRangeStart, sizeof(SystemRangeStart), NULL); // // How many buckets to cover the range // UserModeBucketCount = (ULONG)(1 + ((SystemRangeStart - 1) / MAX_BUCKET_SIZE)); if (!NT_SUCCESS(status)) { fprintf(stderr, "NtQuerySystemInformation failed - status %lx\n", status); RtlFreeHeap (RtlProcessHeap (), 0, ModuleInfoBuffer); return status; } ProfileObject[NumberOfProfileObjects].SymbolsLoaded = FALSE; ProfileObject[NumberOfProfileObjects].CodeStart = 0; ProfileObject[NumberOfProfileObjects].CodeLength = SystemRangeStart; ProfileObject[NumberOfProfileObjects].ImageBase = 0; ProfileObject[NumberOfProfileObjects].ModuleName = "User Mode"; ProfileObject[NumberOfProfileObjects].BufferSize = UserModeBucketCount * sizeof(DWORD); ProfileObject[NumberOfProfileObjects].BucketSize = MAX_BUCKET_SHIFT; for (i=0; i Buffer) { Counter -= 1; ProfileObject[i].ModuleHitCount[Processor] += *Counter; } ProcessorTotalHits[Processor] += ProfileObject[i].ModuleHitCount[Processor]; } if (bPerProcessor) { fprintf(fpOut, "Processor %d: %d Total hits\n", Processor, ProcessorTotalHits[Processor]); } TotalHits += ProcessorTotalHits[Processor]; } fprintf(fpOut, "%d Total hits\n",TotalHits); for (Processor = 0; Processor < MaxProcessors; Processor++) { if (bPerProcessor) { fprintf(fpOut, "\nPROCESSOR %d\n",Processor); } for (i = 0; i < (int)NumberOfProfileObjects; i++) { CountAtSymbol = 0; // // Sum the total number of cells written. // BufferEnd = ProfileObject[i].Buffer[Processor] + ( ProfileObject[i].BufferSize / sizeof(ULONG)); Buffer = ProfileObject[i].Buffer[Processor]; Counter = BufferEnd; if (ProfileObject[i].ModuleHitCount[Processor] < Threshold) { continue; } fprintf(fpOut, "\n%9d ", ProfileObject[i].ModuleHitCount[Processor]); if (bPrintPercentages) { fprintf(fpOut, "%5.2f ", (ProfileObject[i].ModuleHitCount[Processor] / (double)ProcessorTotalHits[Processor]) * 100); } fprintf(fpOut, "%20s --Total Hits-- %s\n", ProfileObject[i].ModuleName, ((ProfileObject[i].SymbolsLoaded) ? "" : "(NO SYMBOLS)") ); if (!ProfileObject[i].SymbolsLoaded) { RtlZeroMemory(ProfileObject[i].Buffer[Processor], ProfileObject[i].BufferSize); continue; } BytesPerBucket = (1 << ProfileObject[i].BucketSize); pInitialCounter = Buffer; for ( Counter = Buffer; Counter < BufferEnd; Counter += 1 ) { if ( *Counter ) { // // Calculate the virtual address of the counter // Va = Counter - Buffer; // Calculate buckets # Va = Va * BytesPerBucket; // convert to bytes Va = Va + (ULONG_PTR)ProfileObject[i].CodeStart; // add in base address if (SymGetSymFromAddr( SYM_HANDLE, Va, &Displacement, ThisSymbol )) { if (UseLastSymbol && LastSymbol->Address && (LastSymbol->Address == ThisSymbol->Address)) { CountAtSymbol += *Counter; } else { OutputSymbolCount(CountAtSymbol, ProcessorTotalHits[Processor], &ProfileObject[i], LastSymbol, Threshold, pInitialCounter, Counter, OffsetVa, BytesPerBucket); pInitialCounter = Counter; OffsetVa = (DWORD) Displacement; // Images aren't > 2g so this cast s/b O.K. CountAtSymbol = *Counter; memcpy( LastSymBuffer, symBuffer, sizeof(symBuffer) ); UseLastSymbol = TRUE; } } else { OutputSymbolCount(CountAtSymbol, ProcessorTotalHits[Processor], &ProfileObject[i], LastSymbol, Threshold, pInitialCounter, Counter, OffsetVa, BytesPerBucket); } // else !(NT_SUCCESS) } // if (*Counter) } // for (Counter) OutputSymbolCount(CountAtSymbol, ProcessorTotalHits[Processor], &ProfileObject[i], LastSymbol, Threshold, pInitialCounter, Counter, OffsetVa, BytesPerBucket); // // Clear after buffer's been checked and displayed // RtlZeroMemory(ProfileObject[i].Buffer[Processor], ProfileObject[i].BufferSize); } } if (bDisplayContextSwitch) { fprintf(fpOut, "\n"); fprintf(fpOut, "Context Switch Information\n"); fprintf(fpOut, " Find any processor %6ld\n", StopContext->FindAny - StartContext->FindAny); fprintf(fpOut, " Find last processor %6ld\n", StopContext->FindLast - StartContext->FindLast); fprintf(fpOut, " Idle any processor %6ld\n", StopContext->IdleAny - StartContext->IdleAny); fprintf(fpOut, " Idle current processor %6ld\n", StopContext->IdleCurrent - StartContext->IdleCurrent); fprintf(fpOut, " Idle last processor %6ld\n", StopContext->IdleLast - StartContext->IdleLast); fprintf(fpOut, " Preempt any processor %6ld\n", StopContext->PreemptAny - StartContext->PreemptAny); fprintf(fpOut, " Preempt current processor %6ld\n", StopContext->PreemptCurrent - StartContext->PreemptCurrent); fprintf(fpOut, " Preempt last processor %6ld\n", StopContext->PreemptLast - StartContext->PreemptLast); fprintf(fpOut, " Switch to idle %6ld\n", StopContext->SwitchToIdle - StartContext->SwitchToIdle); fprintf(fpOut, "\n"); fprintf(fpOut, " Total context switches %6ld\n", StopContext->ContextSwitches - StartContext->ContextSwitches); } return STATUS_SUCCESS; } VOID OutputSymbolCount( IN ULONG CountAtSymbol, IN ULONG TotalCount, IN PROFILE_BLOCK *ProfileObject, IN PIMAGEHLP_SYMBOL SymbolInfo, IN ULONG Threshold, IN PULONG CounterStart, IN PULONG CounterStop, IN ULONG Va, IN ULONG BytesPerBucket ) { ULONG Density; ULONG i; if (CountAtSymbol < Threshold) { return; } fprintf(fpOut, "%9d ", CountAtSymbol); if (bPrintPercentages) { fprintf(fpOut, "%5.2f ", (CountAtSymbol / (double) TotalCount) * 100); } if (bDisplayDensity) { // // Compute hit density = hits * 100 / function length // if (!SymbolInfo || !SymbolInfo->Size) { Density = 0; } else { Density = CountAtSymbol * 100 / SymbolInfo->Size; } fprintf(fpOut, "%5d ",Density); } if (SymbolInfo->MaxNameLength) { fprintf(fpOut, "%20s %s", ProfileObject->ModuleName, SymbolInfo->Name); } else { fprintf(fpOut, "%20s 0x%x", ProfileObject->ModuleName, SymbolInfo->Address); } if (bDisplayAddress) { fprintf(fpOut, " 0x0%p %d %d", SymbolInfo->Address, SymbolInfo->Size, ProfileObject->BucketSize); } if (bDisplayCounters) { for (i = 0 ; CounterStart < CounterStop; i++, Va += BytesPerBucket, ++CounterStart) { if ((i % 16) == 0) { fprintf (fpOut, "\n0x%08x:", Va); } fprintf(fpOut, " %5d", *CounterStart); } } fprintf (fpOut, "\n"); }