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.
561 lines
16 KiB
561 lines
16 KiB
//+-------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1992 - 1992.
|
|
//
|
|
// File: profile.c
|
|
//
|
|
// Contents: PROFILER code
|
|
//
|
|
// History: 22-Nov-93 SethuR created
|
|
//
|
|
// Notes:
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
// #include <ctype.h>
|
|
// #include <sys\types.h>
|
|
// #include <sys\stat.h>
|
|
// #include "ntsdp.h"
|
|
|
|
#include <profile.h>
|
|
|
|
#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);
|
|
|
|
}
|