//+------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1992 - 1992. // // File: profile.c // // Contents: PROFILER code // // History: 22-Nov-93 SethuR created // // Notes: // //-------------------------------------------------------------------------- // #include // #include // #include // #include "ntsdp.h" #include #define MAX_STACK_HEIGHT 2000 #define INVOCATIONS_TO_PRINT 0 // Stack element used by wt profiling typedef struct _SStackElement { unsigned char Symbol[MAX_SYMBOL_LEN]; unsigned long cInstructions; unsigned long cLevel; } SStackElement; typedef SStackElement *PSStackElement; PSStackElement CallStack; LONG iStackDepth = -1; PSProfile ps_ProfileDLL; PSProfile ps_ProfileTrace; BOOLEAN fProfilingDLL; //----------------------------------------------------------------- // // Function: InitProfile // // Purpose: Initializes the data structure for Profiling. // The profiling data structure uses a hash table // to determine where to search/put new entries in // the entries table. // // Input: Profile - pointer to profiler data structure // // Output: None // //------------------------------------------------------------------ void InitProfile(PSProfile Profile) { // Set all the entries in the profiler hash table to NULL int i; if (!CallStack) { CallStack = calloc(MAX_STACK_HEIGHT, sizeof(SStackElement)); ps_ProfileDLL = calloc(1, sizeof(SProfile)); ps_ProfileTrace = calloc(1, sizeof(SProfile)); } else { for (i = 0;i < MAX_NUM_OF_BUCKETS;i++) { Profile->Buckets[i] = NULL; } // Set the used count Profile->cUsed = 0; } } //----------------------------------------------------------------- // // Function: AllocProfilerEntry // // Purpose: Allocates and initialized an entry in the profiler's // entry table. // // Input: Profile - pointer to the profiling data structure // // Output: SProfileRecord * - pointer to the new entry in the // profiler's entry table if one can // be allocated. Otherwise, NULL is // returned. // //------------------------------------------------------------------ SProfileRecord *AllocProfilerEntry(SProfile *Profile) { if (Profile->cUsed < MAX_PROFILE_ENTRIES) { SProfileRecord *pRecord = &(Profile->ProfileRecords[Profile->cUsed]); pRecord->pNext = NULL; Profile->cUsed++; // Initialize the record ... pRecord->cInvocations = 0; pRecord->cMaxInstructions = 0; pRecord->cMinInstructions = 0; pRecord->cCumInstructions = 0; return (pRecord); } else { // We need to assert the fact that the statically allocated pool // has been overrun. dprintf("Profile Allocation size exceeded, Report to SethuR\n"); return NULL; } } //----------------------------------------------------------------- // // Function: SearchStructure // // Purpose: Searches the profiler data struture for the function // name. // // Input: Profile - pointer to profiler data structure // Symbol - pointer to name to search for // // Output: SProfileRecord - pointer to the entry that contains // the name, Symbol, if found. Otherwise // NULL is returned. // //------------------------------------------------------------------ SProfileRecord * SearchStructure (SProfile *Profile, UCHAR *Symbol) { unsigned long cHashNumber; unsigned long iBucket; SProfileRecord *pProfileRecord; // Hash on the first + last letters of the symbol cHashNumber = (unsigned long)Symbol[0] + (unsigned long)Symbol[strlen(Symbol)-1]; iBucket = HASH(cHashNumber); // Search the bucket for the corresponding entry pProfileRecord = Profile->Buckets[iBucket]; while (pProfileRecord != NULL) { if (!strcmp(pProfileRecord->Symbol, Symbol)) break; else pProfileRecord = pProfileRecord->pNext; } return (pProfileRecord); } //----------------------------------------------------------------- // // Function: UpdateProfile // // Purpose: Searches the profiler data structure for the name, // Symbol. If one is found, the information in the // corresponding entry is updated. Otherwise, a new // entry is allocated and the information saved. // // For wt profiling, cValue contains the instruction // count for the function. For function profiling, // cValue contains the breakpoint number of the function. // // Input: Profile - pointer to profiling data structure // Symbol - function name // cValue - value to update // // Output: None // //------------------------------------------------------------------ void UpdateProfile( PSProfile *pProfile, unsigned char Symbol[], unsigned long cValue ) { unsigned long iBucket; SProfileRecord *pProfileRecord; unsigned long cHashNumber; PSProfile Profile; // Initialize the Profiler if not initialized already if (!*pProfile) { InitProfile(*pProfile); } Profile = *pProfile; // Search data structure pProfileRecord = SearchStructure (Profile, Symbol); // If the search was unsuccessful record the new call Site. if (pProfileRecord == NULL) { pProfileRecord = AllocProfilerEntry(Profile); // Hash on the first + last letters of the symbol cHashNumber = (unsigned long)Symbol[0] + (unsigned long)Symbol[strlen(Symbol)-1]; iBucket = HASH(cHashNumber); if (pProfileRecord != NULL) { pProfileRecord->pNext = Profile->Buckets[iBucket]; strcpy (pProfileRecord->Symbol, Symbol); if (PROFILING) { pProfileRecord->cInvocations = 0; pProfileRecord->cBrkptType = BRKPT_PROFILE; pProfileRecord->cBrkptNo = cValue; } else { pProfileRecord->cInvocations = 1; pProfileRecord->cMinInstructions = cValue; pProfileRecord->cMaxInstructions = cValue; pProfileRecord->cCumInstructions = cValue; } Profile->Buckets[iBucket] = pProfileRecord; } } else { // Augment the invocation count. pProfileRecord->cInvocations++; if (!PROFILING) { if (pProfileRecord->cMinInstructions > cValue) pProfileRecord->cMinInstructions = cValue; if (pProfileRecord->cMaxInstructions < cValue) pProfileRecord->cMaxInstructions = cValue; pProfileRecord->cCumInstructions += cValue; } } } //----------------------------------------------------------------- // // Function: DumpProfile // // Purpose: Display the information in the profiler data structure. // // Input: Profile - pointer to profiler data structure // // Output: None // //------------------------------------------------------------------ void DumpProfile( SProfile *Profile ) { unsigned long i; if (!PROFILING) dprintf ("\n\n%-30.30s Invocations MinInstr MaxInstr AvgInstr\n\n", "Function Name"); else dprintf ("\n\n%-60.60s Invocations\n\n", "FunctionName"); for (i = 0; i < Profile->cUsed; i++) { if (Profile->ProfileRecords[i].cInvocations > INVOCATIONS_TO_PRINT) { if (!PROFILING ) { dprintf("%-30.30s %8d %8d %8d %8d\n", Profile->ProfileRecords[i].Symbol, Profile->ProfileRecords[i].cInvocations, Profile->ProfileRecords[i].cMinInstructions, Profile->ProfileRecords[i].cMaxInstructions, ( Profile->ProfileRecords[i].cCumInstructions / Profile->ProfileRecords[i].cInvocations)); } else { dprintf("%-60.60s %8d\n", Profile->ProfileRecords[i].Symbol, Profile->ProfileRecords[i].cInvocations); } } Profile->ProfileRecords[i].cInvocations = 0; Profile->ProfileRecords[i].pNext = &(Profile->ProfileRecords[i+1]); } } //----------------------------------------------------------------- // // Function: Profile // // Purpose: Used by wt profiling to keep track of trace information. // A stack is used to keep track of the call tree. The // function invocation count is incremented only when // the function is removed from the stack. A function // is removed from the stack when another function of // a lower level is reached. // // Input: Profile - pointer to profiling data structure // Symbol - function name to store in profiler data structure. // cInstructions - instructions executed by the function // cLevel - level in the call tree // // Output: None // //------------------------------------------------------------------ void Profile( PSProfile *pProfile, unsigned char *Symbol, unsigned long cInstructions, unsigned long cLevel ) { long cNewLevel = -1; PSProfile Profile; if (!*pProfile) { InitProfile(*pProfile); } Profile = *pProfile; if (iStackDepth == -1) { iStackDepth = 0; cNewLevel = 0; } else { // See if this is a lower level function than the current function on // the stack. If so, pop the stack until at the same level if (CallStack[iStackDepth].cLevel > cLevel) { for ( ; CallStack[iStackDepth].cLevel > cLevel; iStackDepth -= 1) { UpdateProfile(pProfile, CallStack[iStackDepth].Symbol, CallStack[iStackDepth].cInstructions); CallStack[iStackDepth-1].cInstructions += CallStack[iStackDepth].cInstructions; } } if (CallStack[iStackDepth].cLevel == cLevel) { if (!strcmp(CallStack[iStackDepth].Symbol, Symbol)) { // Function hasn't finished executing CallStack[iStackDepth].cInstructions += cInstructions; } else { // Function finished executing. Save info in Profile UpdateProfile(pProfile, CallStack[iStackDepth].Symbol, CallStack[iStackDepth].cInstructions); CallStack[iStackDepth-1].cInstructions += CallStack[iStackDepth].cInstructions; cNewLevel = iStackDepth; } } else if (CallStack[iStackDepth].cLevel < cLevel) { // New symbol at higher level iStackDepth++; cNewLevel = iStackDepth; } } if (cNewLevel != -1) { // Initialize the new stack element strcpy(CallStack[cNewLevel].Symbol , Symbol); CallStack[cNewLevel].cInstructions = cInstructions; CallStack[cNewLevel].cLevel = cLevel; } } //----------------------------------------------------------------- // // Function: ProcDump // // Purpose: Called by wt profiling to dump out the information in // profiling data structre. The stack is cleared of // functions. // // Input: Profile - pointer to profiler data structure // // Output: None // //------------------------------------------------------------------ void ProcDump (PSProfile *Profile ) { // Clear the stack of any symbols for ( ; iStackDepth >= 0; iStackDepth -= 1) { UpdateProfile(Profile, CallStack[iStackDepth].Symbol, CallStack[iStackDepth].cInstructions); if (iStackDepth != 0) { CallStack[iStackDepth-1].cInstructions += CallStack[iStackDepth].cInstructions; } } DumpProfile (*Profile); InitProfile (*Profile); } //----------------------------------------------------------------- // // Function: UpdateBrkpt // // Purpose: Update the type of breakpoint to BrkptType. // // Input: Profile - pointer to profiling data structure // BrkptNo - break point number to update // BrkptType - break point type to update breakpoint to // // Output: BOOLEAN - TRUE for successful update // FALSE for breakpoint breakpoint number not found. // //------------------------------------------------------------------ BOOLEAN UpdateBrkpt (SProfile *Profile, ULONG BrkptNo, ULONG BrkptType) { ULONG i; if (Profile == NULL) { return (FALSE); } //Search data structure for the breakpoint for (i = 0; i < Profile->cUsed; i ++) { if (Profile->ProfileRecords[i].cBrkptNo == BrkptNo) { Profile->ProfileRecords[i].cBrkptType = BrkptType; return (TRUE); } } return (FALSE); } //----------------------------------------------------------------- // // Function: GetBrkptType // // Purpose: Get the type of breakpoint. // // Input: Profile - pointer to profiling data structure // BrkptNo - break point number to look at // // Output: BRKPT_PROFILE - breakpoint is a profiling breakpoint // BRKPT_USER - brkpt is profiling breakpoint but // it is currently set manually by user. // BRKPT_NOT_FOUND - brkpt is not a profiling breakpoint // //------------------------------------------------------------------ LONG GetBrkptType (SProfile *Profile, ULONG BrkptNo) { ULONG i; for (i = 0; i < Profile->cUsed; i++) { if (Profile->ProfileRecords[i].cBrkptNo == BrkptNo) { return (Profile->ProfileRecords[i].cBrkptType); } } return (BRKPT_NOT_FOUND); } //----------------------------------------------------------------- // // Function: fnStartProfilingDLL // // Purpose: Set up the breakpoints for profiling. The parseExamine() // routine is called to find the list of brkpts corresponding // to the user request. Breakpoints are set as each // function is found in parseExamine(). // // Input: Profile - pointer to profiling data structure // // Output: TRUE - successful // FALSE - unsuccessful - function name not entered // //------------------------------------------------------------------ BOOLEAN fnStartProfilingDLL (PSProfile *Profile) { UCHAR ch; UCHAR chDLL[50]; // Get the DLL name entered ch = PeekChar(); if (ch == '\0') { dprintf ("A DLL name must be entered with this command.\n"); return (FALSE); } // set command to invoke the 'x' functionality strcpy (chDLL, pchCommand); pchCommand = &chCommand[0]; strcpy (pchCommand, chDLL); // call function to parse the 'x' command parseExamine(); return (TRUE); } //----------------------------------------------------------------- // // Function: fnStopProfilingDLL // // Purpose: Stops profiling functions. Clears all profiling // breakpoints that are not being used by the user. // The data in profiling data structure is dumped out. // // Input: Profile - pointer to profiling data structure // // Output: None // //------------------------------------------------------------------ void fnStopProfilingDLL (PSProfile *Profile) { ULONG cEntries; // clear all breakpoints set for profiling for (cEntries = 0; cEntries < (*Profile)->cUsed; cEntries++) { if ((*Profile)->ProfileRecords[cEntries].cBrkptType == BRKPT_PROFILE) fnChangeBpState ((*Profile)->ProfileRecords[cEntries].cBrkptNo, 'c'); } DumpProfile(*Profile); InitProfile(*Profile); }