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.
1045 lines
33 KiB
1045 lines
33 KiB
/*++
|
|
|
|
Module Description:
|
|
|
|
chrono.c
|
|
|
|
|
|
Revision History:
|
|
|
|
2-Feb-94 a-robw (Bob Watson)
|
|
Added this module header
|
|
Replaced DbgPrint functions with CapDbgPrint for Windows95
|
|
compatiblity.
|
|
|
|
--*/
|
|
|
|
#include "cap.h"
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: DumpChronoFuncs
|
|
//
|
|
// Synopsis: Dump the function chrono listings
|
|
//
|
|
// Arguments: [pThdblk] -- Pointer to current thread block
|
|
// [lpstrBuff] -- Buffer to print from
|
|
//
|
|
// Returns: nothing
|
|
//
|
|
// History: 05/31/92 HoiV Created
|
|
//
|
|
// Notes:
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
void DumpChronoFuncs(PTHDBLK pthdblk, LPSTR lpstrBuff)
|
|
{
|
|
PCHRONOCELL pChronoCell;
|
|
TCHAR ptchSym [FILENAMELENGTH];
|
|
PTCHAR ptchFuncName;
|
|
PTCHAR ptchModule;
|
|
ULONG ulTotalCalls;
|
|
int iNest;
|
|
PTCHAR ptchChronoModule;
|
|
PTCHAR ptchChronoFuncName;
|
|
PTCHAR ptchMatch;
|
|
LONGLONG liTime;
|
|
TCHAR chRuntimeSuffix;
|
|
|
|
|
|
if (cChars) // if Count is not 0, we have to flush everything
|
|
{
|
|
if ( !WriteFile (hOutFile, lpstrBuff, cChars, &cChars, NULL) )
|
|
{
|
|
CapDbgPrint ("CAP: DumpChronoFuncs() - "
|
|
"Error writing to %s - 0x%lx\n",
|
|
atchOutFileName, GetLastError());
|
|
}
|
|
|
|
cChars = 0;
|
|
}
|
|
|
|
//CalcIncompleteChronoCalls(pthdblk);
|
|
GetTotalRuntime(pthdblk);
|
|
|
|
cChars = sprintf (lpstrBuff,
|
|
"\r\n\n_________________________________"
|
|
"________________________________________________"
|
|
"________________________________________________"
|
|
"________________________________________\r\n\n\n\n"
|
|
"CHRONOLOGICAL FUNCTION LISTINGS\r\n"
|
|
"===============================\r\n");
|
|
|
|
if (fChronoDump)
|
|
{
|
|
pChronoCell = pthdblk->pChronoHeadCell;
|
|
|
|
while (pChronoCell->ulSymbolAddr != 0L)
|
|
{
|
|
//
|
|
// Get the symbol name using the function address
|
|
//
|
|
strcpy(ptchSym, GetFunctionName (pChronoCell->ulSymbolAddr,
|
|
ulLocProfBlkOff,
|
|
NULL));
|
|
|
|
_strupr(ptchSym);
|
|
_strupr(ptchChronoFuncs);
|
|
ptchFuncName = strchr(ptchSym, ':') + 1;
|
|
ptchModule = ptchSym;
|
|
// ???? Check ptchFuncName
|
|
*(ptchFuncName - 1) = '\0';
|
|
|
|
if (ptchChronoFuncs[0] != EMPTY_STRING) // Empty list ?
|
|
{
|
|
ptchChronoModule = (PTCHAR) ptchChronoFuncs;
|
|
|
|
while (*ptchChronoModule != '\0')
|
|
{
|
|
ptchChronoFuncName = strchr(ptchChronoModule, INI_DELIM) + 1;
|
|
*(ptchChronoFuncName - 1) = '\0';
|
|
|
|
// Look for the Module name
|
|
ptchMatch = strstr(ptchModule, ptchChronoModule);
|
|
if (ptchMatch && (*(ptchMatch - 1) != COMMENT_CHAR))
|
|
{
|
|
// We have found the module, now check the func name
|
|
if (strstr(ptchFuncName, ptchChronoFuncName))
|
|
{
|
|
*(ptchChronoFuncName - 1) = INI_DELIM;
|
|
|
|
// Dump everything call from this cell only
|
|
// until the nesting depth is < or == to this cell
|
|
//
|
|
DumpChronoEntry(pthdblk,
|
|
lpstrBuff,
|
|
&pChronoCell,
|
|
FALSE);
|
|
|
|
// if found then break out of the
|
|
// while (ptchChronoModule) loop
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If we get here that means we fail to match a module
|
|
// name and the func name in the [CHRONO FUNCS] section.
|
|
// Just bump to next entry
|
|
//
|
|
*(ptchChronoFuncName - 1) = INI_DELIM;
|
|
ptchChronoModule += strlen(ptchChronoModule) + 1;
|
|
}
|
|
|
|
if (*ptchChronoModule == '\0') // If we did not match
|
|
{ // anything then bump to
|
|
pChronoCell++; // next cell
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Dump everything
|
|
DumpChronoEntry(pthdblk, lpstrBuff, &pChronoCell, TRUE);
|
|
}
|
|
|
|
// At this point, pChronoCell has been incremented correctly
|
|
// by DumpChronoEntry or inside the searching loop for
|
|
// " while (* ptchChronoListItem != EMPTY_STRING) ".
|
|
// We just need to loop back.
|
|
}
|
|
}
|
|
else
|
|
{
|
|
cChars += sprintf (
|
|
lpstrBuff + cChars,
|
|
"\n\n <<< CHRONO INFO COLLECTED BUT NOT DUMPED >>>\n\n"
|
|
"================================="
|
|
"================================================"
|
|
"================================================"
|
|
"========================================\r\n\n\n");
|
|
|
|
}
|
|
|
|
cChars += sprintf(lpstrBuff + cChars,
|
|
"\n\n______________________________________\n\n\n"
|
|
" Summary Statistics\n"
|
|
" ==================\n\n\n");
|
|
|
|
ulTotalCalls = 0L; // Reset our total count for each thread
|
|
for (iNest = 0 ;
|
|
( (iNest < MAX_NESTING) &&
|
|
(pthdblk->aulDepth[iNest] != 0) ) ;
|
|
iNest++)
|
|
{
|
|
ulTotalCalls += pthdblk->aulDepth[iNest];
|
|
cChars += sprintf(lpstrBuff + cChars,
|
|
" Total calls Depth [%3d] = [%8lu]\n",
|
|
iNest,
|
|
pthdblk->aulDepth[iNest]);
|
|
}
|
|
|
|
liTime = liTotalRunTime;
|
|
AdjustTime(&liTime, &chRuntimeSuffix);
|
|
|
|
cChars += sprintf(lpstrBuff + cChars,
|
|
"\n\n______________________________________\n\n"
|
|
" Total Calls = [ %8lu]\n"
|
|
" Total Time-Callees = [%9lu]%1c\n\n",
|
|
ulTotalCalls,
|
|
(ULONG)liTime,
|
|
chRuntimeSuffix);
|
|
|
|
} /* DumpChronoFuncs */
|
|
|
|
|
|
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: GetTotalRuntime
|
|
//
|
|
// Synopsis: Compute the total time the program is running.
|
|
//
|
|
// Arguments: [pThdblk] -- Pointer to current thread block
|
|
//
|
|
// Returns: nothing
|
|
//
|
|
// History: 05/31/92 HoiV Created
|
|
//
|
|
// Notes:
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
void GetTotalRuntime(PTHDBLK pthdblk)
|
|
{
|
|
LONGLONG liElapsed,
|
|
liRealTime,
|
|
liSaveRealTime;
|
|
CHAR chRealTimeSuffix;
|
|
PCHRONOCELL pChronoCell;
|
|
|
|
pChronoCell = pthdblk->pChronoHeadCell;
|
|
|
|
do
|
|
{
|
|
liElapsed = pChronoCell->liElapsed;
|
|
|
|
if (liElapsed == 0L)
|
|
{
|
|
liRealTime = liElapsed;
|
|
}
|
|
else
|
|
{
|
|
liRealTime = liElapsed - pChronoCell->liCallees;
|
|
}
|
|
|
|
liSaveRealTime = liRealTime;
|
|
|
|
AdjustTime (&liRealTime, &chRealTimeSuffix);
|
|
|
|
if (chRealTimeSuffix != 'o' ||
|
|
chRealTimeSuffix != 'u') // don't add if Under/Overflow
|
|
{
|
|
liTotalRunTime += liSaveRealTime;
|
|
}
|
|
|
|
pChronoCell++; // bump to next entry
|
|
|
|
} while (pChronoCell->ulSymbolAddr != 0L);
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: DumpChronoEntry
|
|
//
|
|
// Synopsis: Dump the Calls listings starting from one particular entry
|
|
// and stops only when the depth is greater or end of list.
|
|
//
|
|
// Arguments: [pThdblk] -- Pointer to current thread block
|
|
// [lpstrBuff] -- Buffer to print from
|
|
//
|
|
// Returns: nothing
|
|
//
|
|
// History: 05/31/92 HoiV Created
|
|
//
|
|
// Notes:
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
void DumpChronoEntry(PTHDBLK pthdblk,
|
|
LPSTR lpstrBuff,
|
|
PCHRONOCELL * ppChronoCell,
|
|
BOOL fDumpAll)
|
|
{
|
|
PCHRONOCELL pChronoCell;
|
|
LONGLONG liElapsed,
|
|
liRealTime;
|
|
TCHAR chElapsedSuffix,
|
|
chRealTimeSuffix;
|
|
TCHAR pIndentation [MAX_NESTING * 2];
|
|
TCHAR ptchSym [FILENAMELENGTH];
|
|
int i;
|
|
int iMinimumDepth;
|
|
// TCHAR ptchCallerSym [FILENAMELENGTH];
|
|
ULONG ulSymbolAddress;
|
|
|
|
|
|
if (fDumpAll)
|
|
{
|
|
pChronoCell = pthdblk->pChronoHeadCell;
|
|
cChars += sprintf(lpstrBuff + cChars,
|
|
"\n\n------------------------------------------------"
|
|
"------------------------------------------------"
|
|
"----------------------------------------\r\n\n"
|
|
" Complete Dump of Chronological Listings\n\n"
|
|
" Sym Address [+Callee] [-Callee] Nesting Depth"
|
|
" <RepCnt> - Symbol Name\n"
|
|
" ___________ _________ _________ _____________"
|
|
" ______________________\n\n");
|
|
}
|
|
else
|
|
{
|
|
pChronoCell = * ppChronoCell;
|
|
cChars += sprintf(lpstrBuff + cChars,
|
|
"\n\n------------------------------------------------"
|
|
"------------------------------------------------"
|
|
"----------------------------------------\r\n\n"
|
|
" Dump Chrono listing for Entry:"
|
|
" %-*.*s\n\n"
|
|
" Sym Address [+Callee] [-Callee] Nesting Depth"
|
|
" <RepCnt> - Symbol Name\n"
|
|
" ___________ _________ _________ _____________"
|
|
" ______________________\n\n",
|
|
iNameLength,
|
|
iNameLength,
|
|
GetFunctionName(pChronoCell->ulSymbolAddr,
|
|
ulLocProfBlkOff,
|
|
NULL));
|
|
}
|
|
|
|
iMinimumDepth = pChronoCell->nNestedCalls;
|
|
|
|
do
|
|
{
|
|
//
|
|
// Get the symbol name using the function address
|
|
//
|
|
strcpy(ptchSym, GetFunctionName (pChronoCell->ulSymbolAddr,
|
|
ulLocProfBlkOff,
|
|
&ulSymbolAddress));
|
|
|
|
// The following caller's symbol somehow could not currently be
|
|
// correctly resolved. More investigation to figure out how
|
|
// BUGBUG
|
|
// strcpy(ptchCallerSym, GetFunctionName (
|
|
// pChronoCell->ulCallRetAddr,
|
|
// MKPPROFBLK(ulLocProfBlkOff)));
|
|
|
|
pIndentation[0] = '\0';
|
|
for (i = 0 ; i < pChronoCell->nNestedCalls ; i++)
|
|
{
|
|
strcat(pIndentation, " ");
|
|
}
|
|
|
|
liElapsed = pChronoCell->liElapsed;
|
|
if (liElapsed == 0L)
|
|
{
|
|
liRealTime = liElapsed;
|
|
}
|
|
else
|
|
{
|
|
liRealTime = liElapsed - pChronoCell->liCallees;
|
|
}
|
|
|
|
AdjustTime (&liRealTime, &chRealTimeSuffix);
|
|
AdjustTime (&liElapsed, &chElapsedSuffix);
|
|
|
|
// Setup our string
|
|
cChars += sprintf (
|
|
lpstrBuff + cChars,
|
|
" <%8lx> %9lu%1c %9lu%1c%s%3d "
|
|
"<%2d> %-*.*s\n",
|
|
ulSymbolAddress,
|
|
(ULONG)liElapsed, chElapsedSuffix,
|
|
(ULONG)liRealTime, chRealTimeSuffix,
|
|
pIndentation,
|
|
pChronoCell->nNestedCalls,
|
|
pChronoCell->nRepetitions,
|
|
iNameLength,
|
|
iNameLength,
|
|
ptchSym);
|
|
|
|
if (cChars > BUFFER_SIZE)
|
|
{
|
|
if ( !WriteFile(hOutFile, lpstrBuff, cChars, &cChars, NULL))
|
|
{
|
|
CapDbgPrint ("CAP: DumpChronoFuncs() - ChronoDump - "
|
|
"Error writing to %s - 0x%lx\n",
|
|
atchOutFileName, GetLastError());
|
|
}
|
|
|
|
cChars = 0;
|
|
}
|
|
|
|
pChronoCell++;
|
|
}
|
|
while ( (pChronoCell->ulSymbolAddr != 0L) && // End Of list?
|
|
((pChronoCell->nNestedCalls > iMinimumDepth) || // Nest ?
|
|
(fDumpAll)) ); // Override
|
|
|
|
if (cChars) // if Count is not 0, we have to flush everything
|
|
{
|
|
if ( !WriteFile (hOutFile, lpstrBuff, cChars, &cChars, NULL) )
|
|
{
|
|
CapDbgPrint ("CAP: DumpChronoFuncs() - "
|
|
"Error writing to %s - 0x%lx\n",
|
|
atchOutFileName, GetLastError());
|
|
}
|
|
|
|
cChars = 0;
|
|
}
|
|
|
|
*ppChronoCell = pChronoCell;
|
|
|
|
} /* DumpChronoEntry */
|
|
|
|
|
|
|
|
#ifdef NOT_YET
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: CalcIncompleteChronoCalls
|
|
//
|
|
// Synopsis: Takes care of imcomplete chono cells which are not finished
|
|
// by using liIncompleteTicks as the end time.
|
|
//
|
|
// Arguments: [pThdblk] -- Pointer to current thread block
|
|
// [lpstrBuff] -- Buffer to print from
|
|
//
|
|
// Returns: nothing
|
|
//
|
|
// History: 05/31/92 HoiV Created
|
|
//
|
|
// Notes:
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
void CalcIncompleteChronoCalls (PTHDBLK pthdblk)
|
|
{
|
|
LONGLONG liElapsed = 0L;
|
|
PCHRONOCELL pChronoCell;
|
|
|
|
|
|
// Start at the last one
|
|
pChronoCell = pthdblk->pCurrentChronoCell;
|
|
|
|
//
|
|
// Check the chrono cells that have incomplete timings.
|
|
//
|
|
while (pChronoCell != pthdblk->pChronoHeadCell == 0L)
|
|
{
|
|
|
|
if (pChronoCell->liElapsed == 0L)
|
|
{
|
|
//
|
|
// Get the difference in ticks
|
|
//
|
|
liElapsed = liIncompleteTicks - pdatacell->liStartCount;
|
|
//
|
|
// Subtract the overhead and any waste time for this call
|
|
//
|
|
liElapsed -= liCalibTicks;
|
|
liElapsed -= pthdblk->liWasteCount;
|
|
|
|
if (liElapsed < 0L)
|
|
{
|
|
liElapsed = 0L;
|
|
}
|
|
}
|
|
pChronoCell--;
|
|
}
|
|
|
|
//
|
|
// Make recursive calls
|
|
//
|
|
if (pdatacell->ulNestedCell != 0L)
|
|
{
|
|
CalcIncompleteChronoCalls (pthdblk, pdatacell->ulNestedCell);
|
|
}
|
|
|
|
if (pdatacell->ulNextCell != 0L)
|
|
{
|
|
CalcIncompleteChronoCalls (pthdblk, pdatacell->ulNextCell);
|
|
}
|
|
|
|
} /* CalcIncompleteChronoCalls() */
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: DumpFuncCalls
|
|
//
|
|
// Synopsis: Dump the Calls listings per function
|
|
//
|
|
// Arguments: [pThdblk] -- Pointer to current thread block
|
|
// [lpstrBuff] -- Buffer to print from
|
|
//
|
|
// Returns: nothing
|
|
//
|
|
// History: 05/31/92 HoiV Created
|
|
//
|
|
// Notes:
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
void DumpFuncCalls(PTHDBLK pthdblk, LPSTR lpstrBuff)
|
|
{
|
|
PCHRONOCELL pChronoCell, pCurrentChronoCell;
|
|
ULONG ulTotalCalls;
|
|
ULONG ulCurrentSymbol;
|
|
LONGLONG liTotalElapsed,
|
|
liTotalRealTime;
|
|
DOUBLE dblTotalPercentage,
|
|
dblSinglePercentage;
|
|
TCHAR chElapsedSuffix,
|
|
chRealTimeSuffix,
|
|
chTotalRuntimeSuffix;
|
|
ULONG ulTotalPercentage,
|
|
ulSinglePercentage;
|
|
|
|
AdjustTime(&liTotalRunTime, &chTotalRuntimeSuffix);
|
|
|
|
cChars += sprintf (lpstrBuff + cChars,
|
|
"\r\n\n_________________________________"
|
|
"________________________________________________"
|
|
"________________________________________________"
|
|
"________________________________________\r\n\n\n\n"
|
|
" SUMMARY OF CALLS PER FUNCTION\r\n"
|
|
" =============================\r\n\n\n\n"
|
|
" Count [+Callee] [-Callee] %%Total | %%Single "
|
|
" Function Name\n"
|
|
" __________ _________ _________ __________________ "
|
|
"_______________\n\n");
|
|
|
|
if ( !WriteFile (hOutFile, lpstrBuff, cChars, &cChars, NULL) )
|
|
{
|
|
CapDbgPrint ("CAP: DumpFuncCalls() - "
|
|
"Error writing to %s - 0x%lx\n",
|
|
atchOutFileName, GetLastError());
|
|
}
|
|
|
|
cChars = 0;
|
|
|
|
ulTotalCalls = 0L;
|
|
pChronoCell = pthdblk->pChronoHeadCell;
|
|
while (pChronoCell->ulSymbolAddr != 0L)
|
|
{
|
|
liTotalRealTime = 0L;
|
|
liTotalElapsed = 0L;
|
|
|
|
ulCurrentSymbol = pChronoCell->ulSymbolAddr;
|
|
pCurrentChronoCell = pChronoCell;
|
|
pChronoCell->nNestedCalls = pChronoCell->nRepetitions;
|
|
liTotalRealTime += pChronoCell->liCallees;
|
|
liTotalElapsed += pChronoCell->liElapsed;
|
|
pCurrentChronoCell++;
|
|
|
|
// Walk the list and accumulate the counts
|
|
while (pCurrentChronoCell->ulSymbolAddr != 0L)
|
|
{
|
|
if (pCurrentChronoCell->ulSymbolAddr == ulCurrentSymbol)
|
|
{
|
|
pChronoCell->nNestedCalls += pCurrentChronoCell->nRepetitions;
|
|
liTotalRealTime += pCurrentChronoCell->liCallees;
|
|
liTotalElapsed += pCurrentChronoCell->liElapsed;
|
|
|
|
// Set to 0xffffffff to indicate it has been processed
|
|
pCurrentChronoCell->ulSymbolAddr = 0xffffffff;
|
|
}
|
|
|
|
pCurrentChronoCell++;
|
|
}
|
|
|
|
if (liTotalElapsed == 0 )
|
|
{
|
|
liTotalRealTime = liTotalElapsed;
|
|
}
|
|
else
|
|
{
|
|
liTotalRealTime = liTotalElapsed - liTotalRealTime;
|
|
}
|
|
|
|
AdjustTime (&liTotalElapsed, &chElapsedSuffix);
|
|
AdjustTime (&liTotalRealTime, &chRealTimeSuffix);
|
|
ulTotalCalls += pChronoCell->nNestedCalls;
|
|
|
|
if (liTotalRunTime != 0L )
|
|
{
|
|
dblTotalPercentage = (100.0 * liTotalRealTime) /
|
|
liTotalRunTime;
|
|
|
|
dblSinglePercentage = dblTotalPercentage /
|
|
pChronoCell->nNestedCalls;
|
|
|
|
// BUGBUG! This "sometimes" does not produce correct results
|
|
// for some reasons...
|
|
//
|
|
// dblSinglePercentage =
|
|
// (100.0 * liTotalRealTime.LowPart) /
|
|
// (liTotalRunTime.LowPart * pChronoCell->nNestedCalls);
|
|
}
|
|
else
|
|
{
|
|
dblTotalPercentage = 0.0;
|
|
dblSinglePercentage = 0.0;
|
|
}
|
|
|
|
ulTotalPercentage = (ULONG) (dblTotalPercentage * 1000.0);
|
|
ulSinglePercentage = (ULONG) (dblSinglePercentage * 1000.0);
|
|
|
|
cChars += sprintf(lpstrBuff + cChars,
|
|
// " <%8lu> %9lu%1c %9lu%1c %7.3f|%7.3f %-*.*s\n",
|
|
" <%8lu> %9lu%1c %9lu%1c %3lu.%03lu | %3lu.%03lu %-*.*s\n",
|
|
pChronoCell->nNestedCalls,
|
|
(ULONG)liTotalElapsed,
|
|
chElapsedSuffix,
|
|
(ULONG)liTotalRealTime,
|
|
chRealTimeSuffix,
|
|
ulTotalPercentage / 1000,
|
|
ulTotalPercentage % 1000,
|
|
ulSinglePercentage / 1000,
|
|
ulSinglePercentage % 1000,
|
|
iNameLength,
|
|
iNameLength,
|
|
GetFunctionName (pChronoCell->ulSymbolAddr,
|
|
ulLocProfBlkOff,
|
|
NULL));
|
|
|
|
|
|
if (cChars > BUFFER_SIZE)
|
|
{
|
|
if ( !WriteFile(hOutFile, lpstrBuff, cChars, &cChars, NULL))
|
|
{
|
|
CapDbgPrint ("CAP: DumpFuncCalls() - ChronoDump - "
|
|
"Error writing to %s - 0x%lx\n",
|
|
atchOutFileName, GetLastError());
|
|
}
|
|
|
|
cChars = 0;
|
|
}
|
|
|
|
pChronoCell++;
|
|
while (pChronoCell->ulSymbolAddr == 0xffffffff)
|
|
{
|
|
pChronoCell++;
|
|
}
|
|
}
|
|
|
|
cChars += sprintf(lpstrBuff + cChars,
|
|
"\n\n ________________________________ \n\n "
|
|
"<%8lu> %9lu%1c\n\n"
|
|
"\r\n\n================================="
|
|
"================================================"
|
|
"================================================"
|
|
"========================================\r\n\n\n",
|
|
ulTotalCalls,
|
|
(ULONG)liTotalRunTime,
|
|
chTotalRuntimeSuffix);
|
|
|
|
if ( !WriteFile (hOutFile, lpstrBuff, cChars, &cChars, NULL) )
|
|
{
|
|
CapDbgPrint ("CAP: DumpFuncCalls() - "
|
|
"Error writing to %s - 0x%lx\n",
|
|
atchOutFileName, GetLastError());
|
|
}
|
|
|
|
cChars = 0;
|
|
|
|
} /* DumpFuncCalls */
|
|
|
|
|
|
/******************* D u m p P r o f i l e d B i n a r y *******************
|
|
*
|
|
* DumpProfiledBinary (ptchDumpExt) -
|
|
* Dumps the BINARY profiled data to the specified output file.
|
|
*
|
|
* ENTRY ptchDumpExt - Dump file name extension
|
|
*
|
|
* EXIT -none-
|
|
*
|
|
* RETURN -none-
|
|
*
|
|
* WARNING:
|
|
* -none-
|
|
*
|
|
* COMMENT:
|
|
* Profiling is stopped while data is dumped.
|
|
*
|
|
*/
|
|
|
|
void DumpProfiledBinary (PTCHAR ptchDumpExt)
|
|
{
|
|
NTSTATUS Status;
|
|
PTCHAR ptchExtension;
|
|
PTCHAR ptchSubDir;
|
|
int iLength;
|
|
DWORD dwFilePtr;
|
|
LPSTR lpstrBuff;
|
|
HANDLE hMem;
|
|
PCHRONOCELL pChronoCell;
|
|
int iThread;
|
|
BINFILE_HEADER_INFO BinHeader;
|
|
BINFILE_THREAD_INFO ThreadHeader;
|
|
BINFILE_CELL_INFO BinChronoCell;
|
|
ULONG ulBlkOff;
|
|
PPROFBLK pProfBlk;
|
|
PROFBLOCK_INFO ProfBlkInfo;
|
|
|
|
//
|
|
// Get the GLOBAL semaphore.. (valid accross all process contexts)
|
|
//
|
|
if (WAIT_FAILED == WaitForSingleObject (hGlobalSem, INFINITE))
|
|
{
|
|
CapDbgPrint ("CAP: DumpProfiledBinary() - "
|
|
"ERROR - Wait for GLOBAL semaphore failed - 0x%lx\n",
|
|
GetLastError());
|
|
}
|
|
|
|
//
|
|
// Allocate memory for building output data
|
|
//
|
|
hMem = GlobalAlloc (GMEM_FIXED, BUFFER_SIZE + MAXNAMELENGTH+ 300);
|
|
if (hMem == NULL)
|
|
{
|
|
CapDbgPrint ("CAP: DumpProfiledBinary() - "
|
|
"Error allocating global memory - 0x%lx\n",
|
|
GetLastError());
|
|
ReleaseSemaphore (hGlobalSem, 1, NULL);
|
|
return;
|
|
}
|
|
|
|
lpstrBuff = GlobalLock (hMem);
|
|
|
|
if (lpstrBuff == NULL)
|
|
{
|
|
CapDbgPrint ("CAP: DumpProfiledBinary() - "
|
|
"Error locking global memory - 0x%lx\n",
|
|
GetLastError());
|
|
ReleaseSemaphore (hGlobalSem, 1, NULL);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Get the current date/time
|
|
//
|
|
GetLocalTime ((SYSTEMTIME * UNALIGNED)&BinHeader.SysTime);
|
|
|
|
//
|
|
// Build the call profiler output file name
|
|
//
|
|
|
|
hOutFile = INVALID_HANDLE_VALUE;
|
|
|
|
if (ptchOutputFile[0] != EMPTY_STRING)
|
|
{
|
|
strcpy ((PCHAR)atchOutFileName, (PCHAR)ptchOutputFile);
|
|
|
|
hOutFile = CreateFile(atchOutFileName,
|
|
GENERIC_WRITE,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
OPEN_ALWAYS,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL);
|
|
|
|
if (hOutFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
CapDbgPrint ("CAP: DumpProfiledBinary() - "
|
|
"ERROR - Could not create %s - 0x%lx\n",
|
|
atchOutFileName, GetLastError());
|
|
}
|
|
}
|
|
|
|
// If hOutFile has an INVALID_HANDLE_VALUE then either we have a bad
|
|
// filename in section [OUTPUT FILE] or we don't have an entry in
|
|
// [OUTPUT FILE].
|
|
|
|
if (hOutFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
ptchExtension = strrchr (ptchFullAppImageName, '.');
|
|
ptchSubDir = strrchr (ptchFullAppImageName, '\\');
|
|
|
|
//
|
|
// If there in no '.' or found one in sub-dir names, use the whole path
|
|
//
|
|
if ( (ptchExtension == NULL) || (ptchExtension < ptchSubDir) )
|
|
{
|
|
iLength = sizeof(TCHAR) * strlen(ptchFullAppImageName);
|
|
}
|
|
else
|
|
{
|
|
iLength = (int)((DWORD)ptchExtension - (DWORD)ptchFullAppImageName);
|
|
}
|
|
|
|
iLength = min (iLength, FILENAMELENGTH-5);
|
|
memcpy (atchOutFileName, ptchFullAppImageName, iLength);
|
|
atchOutFileName[iLength] = '\0';
|
|
strcat (atchOutFileName, ptchDumpExt);
|
|
|
|
hOutFile = CreateFile(atchOutFileName,
|
|
GENERIC_WRITE,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
OPEN_ALWAYS,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL);
|
|
|
|
if (hOutFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
CapDbgPrint ("CAP: DumpProfiledBinary() - "
|
|
"ERROR - Could not create %s - 0x%lx\n",
|
|
atchOutFileName, GetLastError());
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// Move to the end of the output file..
|
|
//
|
|
dwFilePtr = SetFilePointer (hOutFile, 0L, NULL, FILE_END);
|
|
if (dwFilePtr == (DWORD)INVALID_HANDLE_VALUE)
|
|
{
|
|
CapDbgPrint ("CAP: DumpProfiledBinary() - ERROR -"
|
|
"Could not move to the end of the output file - 0x%lx\n",
|
|
GetLastError());
|
|
}
|
|
|
|
cChars = 0;
|
|
|
|
memset((void *)BinHeader.ptchProfilingBinaryName,
|
|
(int)NULL,
|
|
FILENAMELENGTH);
|
|
|
|
strcpy((PCHAR UNALIGNED)BinHeader.ptchProfilingBinaryName,
|
|
ptchBaseAppImageName);
|
|
BinHeader.ulCalibTime = ulCalibTime;
|
|
BinHeader.ulCalibNestedTime = ulCalibNestedTime;
|
|
BinHeader.iTotalThreads = iThdCnt;
|
|
BinHeader.ulCairoFlags = 0xffffffff;
|
|
|
|
// Write out the BinHeader
|
|
cChars = sizeof(BINFILE_HEADER_INFO);
|
|
|
|
if ( !WriteFile (hOutFile,
|
|
(PCHAR UNALIGNED)&BinHeader,
|
|
cChars,
|
|
&cChars,
|
|
NULL) )
|
|
{
|
|
CapDbgPrint ("CAP: DumpProfiledBinary() of BinHeader - "
|
|
"Error writing to %s - 0x%lx\n",
|
|
atchOutFileName, GetLastError());
|
|
}
|
|
|
|
// Loop through all profblks and dump out the characteristics of
|
|
// each one
|
|
ulBlkOff = ulLocProfBlkOff;
|
|
|
|
while (ulBlkOff != 0)
|
|
{
|
|
pProfBlk = MKPPROFBLK(ulBlkOff);
|
|
|
|
// Write it out
|
|
ProfBlkInfo.ImageBase = pProfBlk->ImageBase;
|
|
ProfBlkInfo.CodeStart = pProfBlk->CodeStart;
|
|
ProfBlkInfo.CodeLength = pProfBlk->CodeLength;
|
|
strcpy((PCHAR UNALIGNED)ProfBlkInfo.pImageName,
|
|
(PCHAR UNALIGNED)pProfBlk->atchImageName);
|
|
|
|
cChars = sizeof(PROFBLOCK_INFO);
|
|
if ( !WriteFile (hOutFile,
|
|
(PCHAR UNALIGNED)&ProfBlkInfo,
|
|
cChars,
|
|
&cChars,
|
|
NULL) )
|
|
{
|
|
CapDbgPrint ("CAP: DumpProfiledBinary() of BinHeader - "
|
|
"Error writing to %s - 0x%lx\n",
|
|
atchOutFileName, GetLastError());
|
|
}
|
|
|
|
// Bump to next ProfBlk
|
|
ulBlkOff = pProfBlk->ulNxtBlk;
|
|
}
|
|
|
|
// Write out the last dummy ProfBlock to signal the last one
|
|
ProfBlkInfo.ImageBase = NULL;
|
|
ProfBlkInfo.CodeStart = NULL;
|
|
ProfBlkInfo.CodeLength = STUB_SIGNATURE;
|
|
|
|
cChars = sizeof(PROFBLOCK_INFO);
|
|
if ( !WriteFile (hOutFile,
|
|
(PCHAR UNALIGNED)&ProfBlkInfo,
|
|
cChars,
|
|
&cChars,
|
|
NULL) )
|
|
{
|
|
CapDbgPrint ("CAP: DumpProfiledBinary() of BinHeader - "
|
|
"Error writing to %s - 0x%lx\n",
|
|
atchOutFileName, GetLastError());
|
|
}
|
|
|
|
// Loop through all threads and write out all ChronoData
|
|
for (iThread = 0; iThread < iThdCnt; iThread++)
|
|
{
|
|
// Write out the Section header
|
|
cChars = sizeof(BINFILE_THREAD_INFO);
|
|
ThreadHeader.hPid = aSecInfo[iThread].hPid;
|
|
ThreadHeader.hTid = aSecInfo[iThread].hTid;
|
|
ThreadHeader.hClientPid = aSecInfo[iThread].hClientPid;
|
|
ThreadHeader.hClientTid = aSecInfo[iThread].hClientTid;
|
|
ThreadHeader.ulTotalCells = aSecInfo[iThread].pthdblk->ulTotalChronoCells;
|
|
|
|
if ( !WriteFile (hOutFile,
|
|
(PCHAR UNALIGNED)&ThreadHeader,
|
|
cChars,
|
|
&cChars,
|
|
NULL) )
|
|
{
|
|
CapDbgPrint ("CAP: DumpProfiledBinary() of ThreadHeader - "
|
|
"Error writing to %s - 0x%lx\n",
|
|
atchOutFileName, GetLastError());
|
|
}
|
|
|
|
// Write out all ChronoCells for this Section (or Thread)
|
|
pChronoCell = aSecInfo[iThread].pthdblk->pChronoHeadCell;
|
|
while (pChronoCell->ulSymbolAddr != 0L)
|
|
{
|
|
ULONG ulRealFuncAddr = pChronoCell->ulSymbolAddr;
|
|
|
|
// Dump out each chronocell
|
|
#ifdef i386
|
|
// If this is a stub, find out the real address
|
|
if (*((PDWORD)(pChronoCell->ulSymbolAddr + 7)) == STUB_SIGNATURE)
|
|
{
|
|
ulRealFuncAddr = (ULONG)
|
|
(*(PDWORD)(pChronoCell->ulSymbolAddr + 1));
|
|
}
|
|
#endif
|
|
|
|
#ifdef MIPS
|
|
{
|
|
ULONG ulOffsetFromTopRoutine;
|
|
ULONG ulFuncAddr = pChronoCell->ulSymbolAddr;
|
|
|
|
#ifdef MIPS_VC40_INTERFACE
|
|
// Check for stub signature at end of stub patch
|
|
if (*((PULONG)ulFuncAddr + 5) == STUB_SIGNATURE)
|
|
{
|
|
PATCHCODE *pPatchStub;
|
|
|
|
// extract real function address from stub
|
|
pPatchStub = (PPATCHCODE)(ulFuncAddr - 5 * INST_SIZE);
|
|
ulFuncAddr = (pPatchStub->Lui_t0 << 16);
|
|
ulFuncAddr |= (pPatchStub->Ori_t0 & 0x0000ffff);
|
|
}
|
|
else
|
|
{
|
|
// Normal function - subtract offset of penter call
|
|
ulFuncAddr = ulFuncAddr - 12;
|
|
}
|
|
#else
|
|
//
|
|
// Compute the real address of the function since the penter
|
|
// stub is not located at the beginning of the code as in x86
|
|
//
|
|
ulOffsetFromTopRoutine = *((PULONG) (ulFuncAddr - INST_SIZE));
|
|
ulOffsetFromTopRoutine &= 0x000ff00;
|
|
ulOffsetFromTopRoutine >>= 8;
|
|
ulRealFuncAddr = ulFuncAddr - ulOffsetFromTopRoutine;
|
|
|
|
// We have to distinguish between a stub and a regular function
|
|
// since a stub has a different setup than a regular function.
|
|
|
|
if (*( (PULONG) ulRealFuncAddr - 1 +
|
|
(sizeof(PATCHCODE) / INST_SIZE) ) == STUB_SIGNATURE)
|
|
{
|
|
PATCHCODE *pPatchStub;
|
|
|
|
// These are the stubs we made up for Dll Patching
|
|
pPatchStub = (PPATCHCODE) ulRealFuncAddr;
|
|
ulRealFuncAddr = (pPatchStub->Lui_t0 << 16);
|
|
ulRealFuncAddr |= (pPatchStub->Ori_t0 & 0x0000ffff);
|
|
}
|
|
#endif // MIPS_VC40_INTERFACE
|
|
}
|
|
#endif // MIPS
|
|
|
|
BinChronoCell.liElapsed = pChronoCell->liElapsed;
|
|
BinChronoCell.liCallees = pChronoCell->liCallees;
|
|
BinChronoCell.ulSymbolAddr = ulRealFuncAddr;
|
|
BinChronoCell.ulCallRetAddr = pChronoCell->ulCallRetAddr;
|
|
BinChronoCell.nNestedCalls = pChronoCell->nNestedCalls;
|
|
BinChronoCell.nRepetitions = pChronoCell->nRepetitions;
|
|
|
|
cChars = sizeof(BINFILE_CELL_INFO);
|
|
|
|
if ( !WriteFile (hOutFile,
|
|
(PCHAR UNALIGNED)&BinChronoCell,
|
|
cChars,
|
|
&cChars,
|
|
NULL) )
|
|
{
|
|
CapDbgPrint ("CAP: DumpProfiledBinary() of ChronoCell - "
|
|
"Error writing to %s - 0x%lx\n",
|
|
atchOutFileName, GetLastError());
|
|
}
|
|
|
|
pChronoCell++;
|
|
}
|
|
}
|
|
|
|
if ( !CloseHandle (hOutFile) )
|
|
{
|
|
CapDbgPrint ("CAP: DumpProfiledBinary() - "
|
|
"Error closing %s - 0x%lx\n",
|
|
atchOutFileName, GetLastError());
|
|
}
|
|
|
|
//
|
|
// Free allocated memory for building output data
|
|
//
|
|
if (!GlobalUnlock (hMem))
|
|
{
|
|
CapDbgPrint ("CAP: DumpProfiledBinary() - "
|
|
"Error ulocking global memory - 0x%lx\n",
|
|
GetLastError());
|
|
}
|
|
|
|
if (GlobalFree (hMem))
|
|
{
|
|
CapDbgPrint ("CAP: DumpProfiledBinary() - "
|
|
"Error freeing global memory - 0x%lx\n",
|
|
GetLastError());
|
|
}
|
|
|
|
SETUPPrint (("CAP: DumpProfiledBinary() - ...done\n"));
|
|
|
|
//
|
|
// Release the GLOBAL semaphore so other processes can dump data
|
|
//
|
|
Status = ReleaseSemaphore (hGlobalSem, 1, NULL);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
CapDbgPrint ("CAP: DumpProfiledBinary() - "
|
|
"Error releasing GLOBAL semaphore - 0x%lx\n", Status);
|
|
}
|
|
} /* DumpProfiledBinary() */
|