Windows NT 4.0 source code leak
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.
 
 
 
 
 
 

2610 lines
76 KiB

/*++
Revision History:
2-Feb-95 a-robw (Bob Watson)
Added Windows95 compatibility functions
replaced DbgPrint with CapDbgPrint
replaced OutputDebugString w/ OutputCapDebugString macros
10-Feb-95 a-robw (Bob Watson)
Added ecx to list of register saved/restored in calls
--*/
#include "cap.h"
/**************************** P r e P e n t e r ****************************
*
* PrePenter (pthdblk) -
* Helper routine for _penter()..
*
* ENTRY pthdblk - pointer to the current thread block
*
* EXIT -none-
*
* RETURN -none-
*
* WARNING:
* -none-
*
* COMMENT:
* -none-
*
*/
void PrePenter (PTHDBLK pthdblk)
{
PDATACELL pcurdatacell;
PCHRONOCELL pPreviousChronoCell;
LARGE_INTEGER liTemp;
if (pthdblk->ulCurCell == 0L)
{
pthdblk->ulRootCell = GetNewCell (pthdblk);
pthdblk->ulCurCell = pthdblk->ulRootCell;
}
else
{
pthdblk->ulCurCell = GetNxtCell (pthdblk);
MKPDATACELL(pthdblk, pthdblk->ulCurCell)->ts = T1;
}
pcurdatacell = MKPDATACELL(pthdblk, pthdblk->ulCurCell);
//
// dwSYMBOLADDR and dwCALLRETADDR have been set just before
// the call to PrePenter with the values pushed on the stack
// before _penter
//
pcurdatacell->ulSymbolAddr = pthdblk->dwSYMBOLADDR;
pcurdatacell->ulCallRetAddr = pthdblk->dwCALLRETADDR;
if (fChronoCollect)
{
pPreviousChronoCell = pthdblk->pLastChronoCell;
#ifdef DEBUG_CAP
if (pPreviousChronoCell->nNestedCalls >= MAX_NESTING)
{
OutputCapDebugString("\n\n"
"CAP: **** MAX_NESTING Exceeded **** \n\n");
DebugBreak();
}
#endif
//
// Since if this is pChronoHeadCell, the pPreviousChronoCell will be
// NULL, therefore the 2nd and 3rd comparison would result in
// GPFaults. But thanks to the && operation, it will be bumped out
// already at the 1st comparison if this is pChronoHeadCell.
//
if ((pthdblk->pCurrentChronoCell != pthdblk->pChronoHeadCell) &&
(pthdblk->dwSYMBOLADDR == pPreviousChronoCell->ulSymbolAddr) &&
(pthdblk->ulNestedCalls == (ULONG)pPreviousChronoCell->nNestedCalls))
{
// Bump repeat count for last chronocell
pPreviousChronoCell->nRepetitions++;
(pthdblk->pCurrentChronoCell)->pPreviousChronoCell = pPreviousChronoCell;
// Increment depth
pthdblk->aulDepth[ (pPreviousChronoCell->nNestedCalls) ]++;
}
else
{
// Setup new Chrono cell
pthdblk->ulTotalChronoCells++;
(pthdblk->pCurrentChronoCell)->ulSymbolAddr = pthdblk->dwSYMBOLADDR;
(pthdblk->pCurrentChronoCell)->ulCallRetAddr = pthdblk->dwCALLRETADDR;
(pthdblk->pCurrentChronoCell)->nNestedCalls = pthdblk->ulNestedCalls;
(pthdblk->pCurrentChronoCell)->nRepetitions = 1;
(pthdblk->pCurrentChronoCell)->liElapsed = 0L;
(pthdblk->pCurrentChronoCell)->liCallees = 0L;
// Increment depth
pthdblk->aulDepth[ (pthdblk->ulNestedCalls) ]++;
// Allocate new cell
pthdblk->ulChronoOffset++;
pPreviousChronoCell = pthdblk->pCurrentChronoCell;
pthdblk->pLastChronoCell = pPreviousChronoCell;
pthdblk->pCurrentChronoCell = pthdblk->pChronoHeadCell +
pthdblk->ulChronoOffset;
try
{
(pthdblk->pCurrentChronoCell)->pPreviousChronoCell =
pPreviousChronoCell;
(pthdblk->pCurrentChronoCell)->ulSymbolAddr = 0L;
}
//
// + : transfer control to the handler (EXCEPTION_EXECUTE_HANDLER)
// 0 : continue search (EXCEPTION_CONTINUE_SEARCH)
// - : dismiss exception & continue (EXCEPTION_CONTINUE_EXECUTION)
//
except ( AccessXcptFilter (GetExceptionCode(),
GetExceptionInformation(),
COMMIT_SIZE) )
{
//
// Should never get here since filter never returns
// EXCEPTION_EXECUTE_HANDLER.
//
CapDbgPrint ("CAP: GetNewCell() - *LOGIC ERROR* - "
"Inside the EXCEPT: (xcpt=0x%lx)\n",
GetExceptionCode());
}
}
} // if fChronoCollect
// Bump counter for ulNestedCalls of this thread
pthdblk->ulNestedCalls++;
QueryPerformanceCounter( &liTemp );
pcurdatacell->liStartCount = liTemp.QuadPart;
//
// Subtract any accumulated waste time (if any).
// Waste time is being subtracted from end time as well. So if there
// is any additional waste time during the function (such as any
// LoadLibrary() intercepted call) it will be subtracted from elapsed
// time.
//
pcurdatacell->liStartCount = pcurdatacell->liStartCount -
pthdblk->liWasteCount;
} /* PrePenter() */
/*************************** P o s t P e n t e r ***************************
*
* PostPenter () -
* Helper routine for _penter()..
*
* ENTRY -none-
*
* EXIT -none-
*
* RETURN -none-
*
* WARNING:
* -none-
*
* COMMENT:
* -none-
*
*/
DWORD PostPenter ()
{
PDATACELL pcurdatacell;
PCHRONOCELL pPreviousChronoCell;
LARGE_INTEGER liTemp;
PTHDBLK pthdblk;
pthdblk = GETCURTHDBLK();
SETCAPINUSE(); // Called from assembly unlike PrePenter
QueryPerformanceCounter(&liTemp);
pthdblk->liStopCount = liTemp.QuadPart;
pcurdatacell = MKPDATACELL(pthdblk, pthdblk->ulCurCell);
//
// Subtract any accumulated waste time (if any).
// Waste time is being subtracted from start time as well. So if there
// is any additional waste time during the function (such as any
// LoadLibrary() intercepted call) it will be subtracted from elapsed
// time.
//
pthdblk->liStopCount = pthdblk->liStopCount - pthdblk->liWasteCount;
if (fRegularDump && (pcurdatacell->ts == RESTART))
{
pcurdatacell->liStartCount = liRestartTicks;
pcurdatacell->liStartCount = pcurdatacell->liStartCount -
pthdblk->liWasteCount;
}
// Setup real RetAddr so code after PostPenter in _penter could
// be used to return to the correct instruction before the call
//
pthdblk->dwCALLRETADDR = pcurdatacell->ulCallRetAddr;
if (fChronoCollect)
{
pPreviousChronoCell = (pthdblk->pCurrentChronoCell)->pPreviousChronoCell;
if (pPreviousChronoCell != pthdblk->pChronoHeadCell)
{
RecordInfo (pcurdatacell, pPreviousChronoCell, pthdblk);
(pthdblk->pCurrentChronoCell)->pPreviousChronoCell =
pPreviousChronoCell->pPreviousChronoCell;
}
else
{
RecordInfo (pcurdatacell, pthdblk->pChronoHeadCell, pthdblk);
}
}
else
{
// The NULL does not matter since its usage is bracketed inside
// if (fChronoCollect) clause. Consequently, it pChronoCell == NULL
// fChronoCollect is also FALSE, the 2nd parm will never be used in
// RecordInfo. Actually we can pass anything we want to and it still
// would not matter if (fChronoCollect == FALSE).
RecordInfo(pcurdatacell, NULL, pthdblk);
}
//
// We have finished this call so we can finalize the count
// on NestedCalls and set the time state to T2 which is over
//
pcurdatacell->nNestedCalls += pcurdatacell->nTmpNestedCalls;
//051993Remove pcurdatacell->ts = T2;
pthdblk->ulNestedCalls--;
if (pcurdatacell->ulParentCell != 0L) // Parent present
{
// Accumulate the Parent NestedCalls count from the current cell
// NestedCalls count
//
MKPDATACELL(pthdblk, pcurdatacell->ulParentCell)->nTmpNestedCalls +=
pcurdatacell->nTmpNestedCalls;
// Reset the current NestedCalls accumulator
pcurdatacell->nTmpNestedCalls = 0L;
// Set current to Parent and pop back to handle Parent now
pthdblk->ulCurCell = pcurdatacell->ulParentCell;
}
else // No parent cell
{
// Reset current NestedCalls accumulator
pcurdatacell->nTmpNestedCalls = 0L;
// Set CurrentCell to RootCell since we don't have a ParentCell
pthdblk->ulCurCell = pthdblk->ulRootCell;
}
RESETCAPINUSE();
return (DWORD)(pthdblk->dwCALLRETADDR);
} /* PostPenter() */
/*************************** R e c o r d I n f o ***************************
*
* RecordInfo (pCur, pChronoCell) -
* Calculates the elapsed time, first/min/max time and stores
* them in the data structure.
*
* ENTRY pCur - points to the current cell
* pChronoCell - points to current Chronological cell (if NULL
* then no chrono collection is done)
*
* EXIT -none-
*
* RETURN -none-
*
* WARNING:
* -none-
*
* COMMENT:
* Everything is stored/computed as ticks.
*
*/
void RecordInfo (PDATACELL pCur, PCHRONOCELL pChronoCell, PTHDBLK pthdblk)
{
LONGLONG liOverhead = 0L;
LONGLONG liElapsed;
LONGLONG liScrap;
PCHRONOCELL pPreviousChronoCell;
// Get the difference in ticks
//
liElapsed = pthdblk->liStopCount - pCur->liStartCount;
//
// Calculate the overhead for this call
//
liOverhead = liCalibNestedTicks * pCur->nTmpNestedCalls;
liOverhead += liCalibTicks;
liElapsed -= liOverhead;
if (liElapsed < 0L)
{
liElapsed = 0L;
}
if (fChronoCollect)
{
// Accumulate Elapsed time in pChronocell
pChronoCell->liElapsed +=liElapsed;
pPreviousChronoCell = pChronoCell->pPreviousChronoCell;
if (pChronoCell->nNestedCalls != 0)
{
pPreviousChronoCell->liCallees += liElapsed;
}
}
if (fRegularDump)
{
// Accumulate total time
//
liScrap = pCur->liTotTime + liElapsed;
pCur->liTotTime = liScrap;
pCur->nCalls++;
pCur->ts = T2; // 051993 Add
// Store the first time - first time is not included in Max/Min times
// computations.
//
if (pCur->nCalls == 1)
{
//
// Get the First time
//
pCur->liFirstTime = liElapsed;
}
else
{
//
// Check for new minimum time
//
if ( liElapsed < pCur->liMinTime )
{
pCur->liMinTime = liElapsed;
}
// Check for new maximum time
//
if ( liElapsed > pCur->liMaxTime )
{
pCur->liMaxTime = liElapsed;
}
}
}
} /* RecordInfo () */
PTHDBLK c_penter (DWORD dwSYMBOLADDR, DWORD dwCALLRETADDR)
{
PTHDBLK pthdblk = NULL;
if (fProfiling && !ISCAPINUSE())
{
SETCAPINUSE();
GetNewThdBlk(); // will save new block ptr in teb
// Get the newly created thread block or the current one
pthdblk = GETCURTHDBLK();
pthdblk->dwSYMBOLADDR = dwSYMBOLADDR;
pthdblk->dwCALLRETADDR = dwCALLRETADDR;
PrePenter (pthdblk);
RESETCAPINUSE();
}
return pthdblk;
}
/************************** G e t N e w T h d B l k *************************
*
* GetNewThdBlk () -
* Creates a new thread info structure or opens an existing one
* for the current thread if one has not been created/opened
* already.
*
* New thread info blocks are created/openned in the following
* situations:
*
* 1) Upon the very first call in the server thread. (CREATED)
* 2) Upon the very first call in the client thread. (CREATED)
* 3) The first time a client request is being handled by
* the server. (Section in use by the client is OPENNED)
*
* ENTRY -none-
*
* EXIT -none-
*
* RETURN -none-
*
* WARNING:
* -none-
*
* COMMENT:
* -none-
*
*/
void GetNewThdBlk ()
{
//
// CURTHDBLD(pteb) refers to the *ULONG Instrumentation[0]
// area of the current thread. This is local reserved area of
// a particular thread
//
if (!GETCURTHDBLK())
{
SETCURTHDBLK(CreateDataSec( GetCurrentProcessId(),
GetCurrentThreadId(),
0,
0));
}
#ifndef _CHICAGO_
else if (fCsrSS && !ISCLIENT())
{
PCSR_THREAD pcsrThd;
//
// If this is the csrss exe then we need to check if we're running
// on behalf of a client thread. If so open a the section which
// it has created so that csr's data will be stuffed in with the
// clients.
//
// This is a quick check in the Teb. This is how we find out if there
// is a client thread who caused this thread to be running. If
// it is then this thread is the server thread and care should
// be taken so that data could be written into the correct location.
//
pcsrThd = CSR_SERVER_QUERYCLIENTTHREAD();
if (pcsrThd != NULL)
{
SETCLIENT();
SETCURTHDBLK(CreateDataSec( GetCurrentProcessId(),
GetCurrentThreadId(),
(DWORD)pcsrThd->ClientId.UniqueProcess,
(DWORD)pcsrThd->ClientId.UniqueThread));
}
}
#endif
return;
} /* GetNewThdBlk () */
/************************ C r e a t e D a t a S e c *************************
*
* CreateDataSec () -
* Creates data section for the thread info block accessable by
* all processes for read/write operations.
*
* ENTRY hPid - current thread's unique process id
* hTid - current thread's unique thread id
* hClientPid - client thread's unique process id
* hClientTid - client thread's unique thread id
*
* EXIT -none-
*
* RETURN pthdblk - contains pointer to the thread info block address
*
* WARNING:
* -none-
*
* COMMENT:
* New thread info blocks are created/openned in the following
* situations:
*
* 1) Upon the very first call in the server thread. (CREATED)
* 2) Upon the very first call in the client thread. (CREATED)
* 3) The first time a client request is being handled by
* the server. (Section in use by the client is OPENNED)
*
* Client thread (if one exists) or current thread unique
* pid/tid is used to make up the section name.
*
*/
PTHDBLK CreateDataSec (DWORD hPid,
DWORD hTid,
DWORD hClientPid,
DWORD hClientTid)
{
NTSTATUS Status;
ANSI_STRING SectionName;
UNICODE_STRING SectionUnicodeName;
OBJECT_ATTRIBUTES SectionAttributes;
LARGE_INTEGER AllocationSize;
ULONG ulViewSize;
DWORD hThdUnq;
DWORD hPrcUnq;
TCHAR atchUnqId[80]=DATASECNAME;
PTHDBLK pthdblk;
HANDLE hMapObject;
TCHAR pszChronoSecName[80] = CHRONOSECNAME;
HANDLE hChronoMapObject;
int iNest;
CHAR PidStr [20]; // HWC added 11/18/93
CHAR TidStr [20]; // HWC added 11/18/93
CHAR SeqNumStr [20]; // HWC added 11/18/93
int LocalThdCnt;
#ifdef STUFF_OUT_BECAUSE_IT_IS_NOT_WORKING
PCSR_PROCESS Process; // HWC added 11/18/93
#endif
if (hClientPid)
{
hPrcUnq = hClientPid;
hThdUnq = hClientTid;
}
else
{
hPrcUnq = hPid;
hThdUnq = hTid;
}
_ultoa ((ULONG)hPrcUnq, PidStr, 10); // HWC added 11/18/93
_ultoa ((ULONG)hThdUnq, TidStr, 10); // HWC added 11/18/93
strcat (atchUnqId, PidStr); // HWC added 11/18/93
strcat (atchUnqId, TidStr); // HWC added 11/18/93
// get the Process Sequence Number to make the Id more unique since
// process id and thread id are re-used frequently. HWC added 11/18/93
SeqNumStr[0] = '\0';
#ifdef STUFF_OUT_BECAUSE_IT_IS_NOT_WORKING
Status = CsrLockProcessByClientId ((HANDLE)hPrcUnq, &Process);
if (NT_SUCCESS(Status))
{
_ultoa ((ULONG)Process->SequenceNumber, SeqNumStr, 10);
strcat (atchUnqId, SeqNumStr);
CsrUnlockProcess (Process);
}
#endif
SETUPPrint (("CAP: CreateDataSec() - %s\n", atchUnqId));
//
// Create a read-write section
//
hMapObject = CreateFileMapping((HANDLE)0xFFFFFFFF,
&SecAttributes,
PAGE_READWRITE | SEC_RESERVE,
0,
ulPerThdAllocSize,
atchUnqId);
if (NULL == hMapObject)
{
CapDbgPrint ("CAP: CreateDataSec() - "
"CreateFileMapping() failed - 0x%lx\n", GetLastError());
}
pthdblk = MapViewOfFile(hMapObject, FILE_MAP_WRITE, 0, 0,
ulPerThdAllocSize);
if (NULL == pthdblk)
{
CapDbgPrint ("CAP: CreateDataSec() - "
"MapViewOfFile() failed - 0x%lx\n", GetLastError());
}
//
// Commit the first COMMIT_SIZE pages
//
if (!VirtualAlloc(pthdblk, COMMIT_SIZE, MEM_COMMIT, PAGE_READWRITE))
{
CapDbgPrint ("CAP: CreateDataSec() - "
"VirtualAlloc() commit failed - 0x%lx\n", GetLastError());
}
if (fChronoCollect)
{
// Initialize object attributes
//
strcat (pszChronoSecName, PidStr); // HWC added 11/18/93
strcat (pszChronoSecName, TidStr); // HWC added 11/18/93
if (SeqNumStr[0])
{
strcat (pszChronoSecName, SeqNumStr); // HWC added 11/18/93
}
hChronoMapObject = CreateFileMapping((HANDLE)0xFFFFFFFF,
&SecAttributes,
PAGE_READWRITE | SEC_RESERVE,
0,
ulPerThdAllocSize,
pszChronoSecName);
if (NULL == hChronoMapObject)
{
CapDbgPrint ("CAP: CreateDataSec() - "
"CreateFileMapping() failed - 0x%lx\n", GetLastError());
}
pthdblk->pChronoHeadCell = MapViewOfFile(hChronoMapObject, FILE_MAP_WRITE, 0, 0,
ulPerThdAllocSize);
if (NULL == pthdblk->pChronoHeadCell)
{
CapDbgPrint ("CAP: CreateDataSec() - "
"MapViewOfFile() failed - 0x%lx\n", GetLastError());
}
//
// Commit the first 4*COMMIT_SIZE pages
//
if (!VirtualAlloc(pthdblk->pChronoHeadCell, 4*COMMIT_SIZE, MEM_COMMIT, PAGE_READWRITE))
{
CapDbgPrint ("CAP: CreateDataSec() - "
"VirtualAlloc() commit failed - 0x%lx\n", GetLastError());
}
aSecInfo[iThdCnt].hChronoMapObject = hChronoMapObject;
(pthdblk->pChronoHeadCell)->ulSymbolAddr = 0L;
(pthdblk->pChronoHeadCell)->pPreviousChronoCell =
pthdblk->pChronoHeadCell;
pthdblk->pCurrentChronoCell = pthdblk->pChronoHeadCell;
pthdblk->pLastChronoCell = pthdblk->pChronoHeadCell;
for (iNest = 0 ; iNest < MAX_NESTING ; iNest++)
{
pthdblk->aulDepth[iNest] = 0;
}
}
if (pthdblk->ulMemOff == 0L)
{
//
// New section - initialize next available mem location in
// the section
//
pthdblk->ulMemOff = sizeof(THDBLK);
pthdblk->ulChronoOffset = 0L;
}
else
{
//
// If no client-server relationship, clear the root cell to indicate
// end of an already dead thread data and beginning of the new thread
// data. This is needed since id of a dead thread will be assigned
// to a new thread by the system.
//
if (hClientPid == 0)
{
pthdblk->ulRootCell = 0L;
pthdblk->ulCurCell = 0L;
pthdblk->liWasteCount = 0L;
#ifdef i386
pthdblk->jmpinfo.nJmpCnt = 0;
#endif
}
SETUPPrint (("CAP: CreateDataSec() - ulMemOff != 0 (0x%lx)\n",
pthdblk->ulMemOff));
}
//
// Update global section information
//
// Get the LOCAL semaphore.. (valid in this process context only)
//
if (WAIT_FAILED == WaitForSingleObject (hLocalSem, INFINITE))
{
CapDbgPrint ("CAP: CreateDataSec() - "
"Wait for LOCAL semaphore failed - 0x%lx\n",
GetLastError());
}
// BUGBUG
// This code should be changed to not have a limit on the
// number of threads that can be attached.
//
if ((unsigned)iThdCnt > 100)
{
CapDbgPrint("CAP: iThdCnt trashed!!\n");
}
LocalThdCnt = iThdCnt++; // get a SecInfo under the lock
Status = ReleaseSemaphore (hLocalSem, 1, NULL);
if (!NT_SUCCESS(Status))
{
CapDbgPrint ("CAP: CreateDataSec() - "
"Error releasing LOCAL semaphore - 0x%lx\n", Status);
}
SETUPPrint (("CAP: CreateDataSec() - pid|tid=0x%lx|0x%lx "
"Cpid|Ctid=0x%lx|0x%lx Thd#%d\n",
hPid, hTid, hClientPid, hClientTid, LocalThdCnt));
// Initialize aSecInfo (a SECTIONINFO structure)
aSecInfo[LocalThdCnt].hPid = hPid;
aSecInfo[LocalThdCnt].hTid = hTid;
aSecInfo[LocalThdCnt].hClientPid = hClientPid;
aSecInfo[LocalThdCnt].hClientTid = hClientTid;
aSecInfo[LocalThdCnt].pthdblk = pthdblk;
aSecInfo[LocalThdCnt].hMapObject = hMapObject;
if (hClientPid == 0)
{
aSecInfo[LocalThdCnt].ulRootCell = pthdblk->ulMemOff;
}
else
{
aSecInfo[LocalThdCnt].ulRootCell = pthdblk->ulRootCell;
}
return(pthdblk);
} /* CreateDataSec() */
/*************************** G e t N x t C e l l ***************************
*
* GetNxtCell (pthdblk) -
* Searches for the next cell based on the SYMBOLADDR. If
* none is found, a new cell is created.
*
* ENTRY pthdblk - points to the current thread block
*
* EXIT -none-
*
* RETURN ulCell - offset to the next data cell
*
* WARNING:
* -none-
*
* COMMENT:
* -none-
*
*/
ULONG GetNxtCell (PTHDBLK pthdblk)
{
// The forest of trees is connected at top level by next pointers
//
// For calltree(s):
//
// A -------+ I
// | | / \
// B ----+ F ----+ J K
// | \ | | \ |
// C D E G H null
//
// A->ulNestedCell = B
// A->ulNextCell = I
//
// B->ulNestedCell = C
// B->ulNextCell = F
//
// C->ulNextCell = D
// C->ulNestedCell = (null)
// D->ulNextCell = E
// D->ulNestedCell = (null)
// E->ulNextCell = (null)
// E->ulNestedCell = (null)
//
// F->ulNestedCell = G
// F->ulNextCell = (null)
//
// G->ulNextCell = H
// G->ulNestedCell = (null)
// H->ulNextCell = (null)
// H->ulNestedCell = (null)
//
// I->ulNextCell = (null)
// I->ulNestedCell = J
//
// J->ulNextCell = K
// J->ulNestedCell = (null)
// K->ulNextCell = (null)
// K->ulNestedCell = (null)
PDATACELL pCell;
pCell = MKPDATACELL(pthdblk, pthdblk->ulCurCell);
try // EXCEPT - to handle access violation exception.
{ // Access violation might happen if we are using client created
// section and client thread has already used more space than what
// has been commited by the server thread
//
if (pCell->ts == T2) // We finish a call ? If yes, then
{ // this is not a nested call.
//
// Not a nested call, search through sequential calls until
// we find a matched symbol address for the routine we are
// profiling.
//
// davidfie -- I believe that this can only occur at the root of the tree
// so nTmpNestedCalls can remain zero.
//
while ( (pCell->ulNextCell != 0L) &&
(pCell->ulSymbolAddr != pthdblk->dwSYMBOLADDR) )
{
pCell = MKPDATACELL(pthdblk, pCell->ulNextCell);
}
//
// No cell found, create a new one
//
if (pCell->ulSymbolAddr != pthdblk->dwSYMBOLADDR)
{
// Get a new cell
pCell->ulNextCell = GetNewCell (pthdblk);
// Set NextCell ParentCell to our current cell's parent
// cell
//
MKPDATACELL(pthdblk, pCell->ulNextCell)->ulParentCell =
pCell->ulParentCell;
// Set current cell to point to next cell
pCell = MKPDATACELL(pthdblk, pCell->ulNextCell);
}
}
else
{
//
// A nested call, search through nested call tree - if one exists
// but first increment the temporary current accumulated
// NestedCalls counter
//
pCell->nTmpNestedCalls++;
if (pCell->ulNestedCell == 0L) // No nested calls before so
{ // we create a new NestedCell
pCell->ulNestedCell = GetNewCell (pthdblk);
// Set NestedCell's parent cell to current cell
MKPDATACELL(pthdblk, pCell->ulNestedCell)->ulParentCell =
(ULONG)((PBYTE)(pCell) - (ULONG)pthdblk);
pCell = MKPDATACELL(pthdblk, pCell->ulNestedCell);
}
else
{
// If there is a NestedCell then we created a next cell
// based on that NestedCell
//
pCell = MKPDATACELL(pthdblk, pCell->ulNestedCell);
while ((pCell->ulNextCell != 0L) &&
(pCell->ulSymbolAddr != pthdblk->dwSYMBOLADDR))
{
pCell = MKPDATACELL(pthdblk, pCell->ulNextCell);
}
if (pCell->ulSymbolAddr != pthdblk->dwSYMBOLADDR)
{
//
// No cell found, create a new one
//
pCell->ulNextCell = GetNewCell (pthdblk);
// Set NextCell's parent cell to current parent cell
MKPDATACELL(pthdblk, pCell->ulNextCell)->ulParentCell =
pCell->ulParentCell;
pCell = MKPDATACELL(pthdblk, pCell->ulNextCell);
}
}
}
}
//
// + : transfer control to the handler (EXCEPTION_EXECUTE_HANDLER)
// 0 : continue search (EXCEPTION_CONTINUE_SEARCH)
// - : dismiss exception & continue (EXCEPTION_CONTINUE_EXECUTION)
//
except ( AccessXcptFilter (GetExceptionCode(),
GetExceptionInformation(),
COMMIT_SIZE) )
{
//
// Should never get here since filter never returns
// EXCEPTION_EXECUTE_HANDLER.
//
CapDbgPrint ("CAP: GetNxtCell() - *LOGIC ERROR* - "
"Inside the EXCEPT: (xcpt=0x%lx)\n", GetExceptionCode());
}
return (ULONG)((PBYTE)(pCell) - (ULONG)pthdblk);
} /* GetNxtCell () */
/*************************** G e t N e w C e l l ***************************
*
* GetNewCell (pthdblk) -
* Creates a new cell using the allocated global memory for the
* current thread. The new cell is initialized.
*
* ENTRY pthdblk - points to the current thread block
*
* EXIT -none-
*
* RETURN ulNewCell - offset to the to a new cell in memory
*
* WARNING:
* -none-
*
* COMMENT:
* -none-
*
*/
ULONG GetNewCell (PTHDBLK pthdblk)
{
PDATACELL pNewCell;
ULONG ulNewCell;
ulNewCell = pthdblk->ulMemOff;
((PDATACELL)pthdblk->ulMemOff)++;
pNewCell = MKPDATACELL(pthdblk, ulNewCell);
try // EXCEPT - to handle access violation exception
{
pNewCell->ts = T1;
pNewCell->ulSymbolAddr = 0L;
pNewCell->ulCallRetAddr = 0L;
pNewCell->liStartCount = 0L;
pNewCell->liFirstTime = 0L;
pNewCell->liMinTime = MAXLONGLONG;
pNewCell->liMaxTime = 0L;
pNewCell->liTotTime = 0L;
pNewCell->nCalls = 0;
pNewCell->nNestedCalls = 0;
pNewCell->nTmpNestedCalls = 0;
pNewCell->ulParentCell = 0L;
pNewCell->ulNextCell = 0L;
pNewCell->ulNestedCell = 0L;
pNewCell->ulProfBlkOff = ulLocProfBlkOff;
}
//
// + : transfer control to the handler (EXCEPTION_EXECUTE_HANDLER)
// 0 : continue search (EXCEPTION_CONTINUE_SEARCH)
// - : dismiss exception & continue (EXCEPTION_CONTINUE_EXECUTION)
//
except ( AccessXcptFilter (GetExceptionCode(),
GetExceptionInformation(),
COMMIT_SIZE) )
{
//
// Should never get here since filter never returns
// EXCEPTION_EXECUTE_HANDLER.
//
CapDbgPrint ("CAP: GetNewCell() - *LOGIC ERROR* - "
"Inside the EXCEPT: (xcpt=0x%lx)\n", GetExceptionCode());
}
return ulNewCell;
} /* GetNewCell () */
/******************** C l e a r P r o f i l e d I n f o ********************
*
* ClearProfiledInfo () -
* Clears the profiled data for all the threads. Current time
* is used to replace the starting time for those routines that
* are in the middle of a call.
*
* ENTRY -none-
*
* EXIT -none-
*
* RETURN -none-
*
* WARNING:
* -none-
*
* COMMENT:
* Profiling is stopped while data is cleared.
*
*/
void ClearProfiledInfo ()
{
int i;
LONGLONG liRootStartTicks;
PDATACELL pcell;
NTSTATUS Status;
PTHDBLK pthdblk;
//
// Get the GLOBAL semaphore.. (valid accross all process contexts)
// Prevents clearing data while another process is dumping data
//
if (WAIT_FAILED == WaitForSingleObject (hGlobalSem, INFINITE))
{
CapDbgPrint ("CAP: ClearProfiledInfo() - "
"ERROR - Wait for GLOBAL semaphore failed - 0x%lx\n",
GetLastError());
}
liRootStartTicks = 0L;
for (i=0; i<iThdCnt; i++)
{
pthdblk = aSecInfo[i].pthdblk;
pthdblk->liWasteCount = 0L;
pcell = MKPDATACELL(pthdblk, pthdblk->ulRootCell);
//
// Find the top of the tree start ticks
//
while (pcell != (PDATACELL) (pthdblk))
{
if (pcell->ts == T1)
{
liRootStartTicks = pcell->liStartCount;
break;
}
else
{
pcell = MKPDATACELL(pthdblk,
pcell->ulNextCell);
}
}
// The Chrono entries are sequential stamps that only make sense
// when they are preserved until the app exits. Clearing them
// during the app is running makes the output illogical and
// non-sense. I turn it off in here to avoid more problems.
if (fChronoCollect)
{
pthdblk->ulTotalChronoCells = 0L;
pthdblk->ulNestedCalls = 0L;
pthdblk->ulChronoOffset = 0L;
pthdblk->pCurrentChronoCell = pthdblk->pChronoHeadCell;
pthdblk->pLastChronoCell = pthdblk->pChronoHeadCell;
(pthdblk->pChronoHeadCell)->pPreviousChronoCell =
pthdblk->pChronoHeadCell;
(pthdblk->pCurrentChronoCell)->ulSymbolAddr = 0L; // signifies EOL
(pthdblk->pCurrentChronoCell)->ulCallRetAddr = 0L;
(pthdblk->pCurrentChronoCell)->nNestedCalls = 0;
(pthdblk->pCurrentChronoCell)->nRepetitions = 0;
}
if (aSecInfo[i].pthdblk->ulRootCell != 0L)
{
ClearRoutineInfo (pthdblk,
pthdblk->ulRootCell,
liRootStartTicks);
}
}
//
// Release the GLOBAL semaphore so other processes can dump data
//
Status = ReleaseSemaphore (hGlobalSem, 1, NULL);
if (!NT_SUCCESS(Status))
{
CapDbgPrint ("CAP: ClearProfiledInfo() - "
"Error releasing GLOBAL semaphore - 0x%lx\n", Status);
}
} /* ClearProfiledInfo() */
/*********************** C l e a r R o u t i n e I n f o *********************
*
* ClearRoutineInfo (pthdblk, uldatacell, liRootStartTicks) -
* Clears the profiled data for the specifed thread.
*
* ENTRY pthdblk - points to this thread info block
* uldatacell - offset of the next data cell
* liRootStartTicks - start time for the root cell
*
* EXIT -none-
*
* RETURN -none-
*
* WARNING:
* -none-
*
* COMMENT:
* This routine is called recursively to clear all cells.
*
*/
void ClearRoutineInfo (PTHDBLK pthdblk,
ULONG uldatacell,
LONGLONG liRootStartTicks)
{
PDATACELL pdatacell;
if (uldatacell != 0L)
{
pdatacell = MKPDATACELL(pthdblk, uldatacell);
if (pdatacell->ts == T2)
{
pdatacell->ts = CLEARED;
pdatacell->liStartCount = 0L;
pdatacell->liFirstTime = 0L;
pdatacell->liMinTime = MAXLONGLONG;
pdatacell->liMaxTime = 0L;
pdatacell->liTotTime = 0L;
pdatacell->nCalls = 0;
pdatacell->nNestedCalls = 0;
pdatacell->nTmpNestedCalls = 0;
}
else
if ( (pdatacell->ts == T1) || (pdatacell->ts == RESTART) )
{
//
// Start count could have been cleared by another process..
//
if (pdatacell->liStartCount > 0)
{
pdatacell->liTotTime = pdatacell->liStartCount -
liRootStartTicks;
}
pdatacell->ts = RESTART;
pdatacell->liStartCount = 0L;
pdatacell->liFirstTime = 0L;
pdatacell->liMinTime = MAXLONGLONG;
pdatacell->liMaxTime = 0L;
pdatacell->nCalls = 0;
pdatacell->nNestedCalls = 0;
if (pdatacell->nTmpNestedCalls > 0)
{
pdatacell->nTmpNestedCalls = 1;
}
}
//
// Make recursive calls for NESTED & NEXT call trees
//
ClearRoutineInfo (pthdblk, pdatacell->ulNestedCell, liRootStartTicks);
ClearRoutineInfo (pthdblk, pdatacell->ulNextCell, liRootStartTicks);
}
} /* ClearRoutineInfo () */
/********************** D u m p P r o f i l e d I n f o *********************
*
* DumpProfiledInfo (ptchDumpExt) -
* Dumps the 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 DumpProfiledInfo (PTCHAR ptchDumpExt)
{
NTSTATUS Status;
int i;
int iLocThdCnt;
PTCHAR ptchExtension;
PTCHAR ptchSubDir;
int iLength;
SYSTEMTIME SysTime;
DWORD dwFilePtr;
LPSTR lpstrBuff;
HANDLE hMem;
int iThread;
HANDLE hLib [MAX_PATCHES];
//
// Get the GLOBAL semaphore.. (valid accross all process contexts)
//
if (WAIT_FAILED == WaitForSingleObject (hGlobalSem, INFINITE))
{
CapDbgPrint ("CAP: DumpProfiledInfo() - "
"ERROR - Wait for GLOBAL semaphore failed - 0x%lx\n",
GetLastError());
}
cChars = 0;
//
// Allocate memory for building output data
//
hMem = GlobalAlloc (GMEM_FIXED, BUFFER_SIZE + MAXNAMELENGTH+ 300);
if (hMem == NULL)
{
CapDbgPrint ("CAP: DumpProfiledInfo() - "
"Error allocating global memory - 0x%lx\n",
GetLastError());
ReleaseSemaphore (hGlobalSem, 1, NULL);
return;
}
lpstrBuff = GlobalLock (hMem);
if (lpstrBuff == NULL)
{
CapDbgPrint ("CAP: DumpProfiledInfo() - "
"Error locking global memory - 0x%lx\n",
GetLastError());
ReleaseSemaphore (hGlobalSem, 1, NULL);
return;
}
//
// Get the current date/time
//
GetLocalTime (&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: DumpProfiledInfo() - "
"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: DumpProfiledInfo() - "
"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: DumpProfiledInfo() - ERROR -"
"Could not move to the end of the output file - 0x%lx\n",
GetLastError());
}
if (fRegularDump)
{
cChars += sprintf (
lpstrBuff+cChars,
"Call Profile of %s - %02d/%02d/%02d %02d:%02d:%02d\n\n"
"All times are in microseconds.\n"
"Profiler routine's calibration times: Top Level Calls = %lu us\n"
" Nested Calls = %lu us\n\n"
"(Note: First time is not included in Min/Max times computation)"
"\n\n\n",
ptchBaseAppImageName, SysTime.wMonth, SysTime.wDay, SysTime.wYear,
SysTime.wHour, SysTime.wMinute, SysTime.wSecond
,ulCalibTime, ulCalibNestedTime
);
}
else
{
cChars += sprintf (
lpstrBuff+cChars,
"Call Profile of %s - %02d/%02d/%02d %02d:%02d:%02d\n\n"
"All times are in microseconds.\n"
"Profiler routine's calibration times: Top Level Calls = %lu us\n"
" Nested Calls = %lu us\n\n"
,ptchBaseAppImageName, SysTime.wMonth, SysTime.wDay, SysTime.wYear,
SysTime.wHour, SysTime.wMinute, SysTime.wSecond
,ulCalibTime, ulCalibNestedTime
);
}
iLocThdCnt = 0;
SETUPPrint (("CAP: DumpProfiledInfo() - Starting for [%d] threads...\n",
iThdCnt));
for (iThread = 0 ; iThread < iThdCnt ; iThread++)
{
SETUPPrint (("CAP: T h r e a d #%d: (pid|tid=0x%lx|0x%lx "
"Client:pid|tid=0x%lx|0x%lx)\n",
iThread,
aSecInfo[iThread].hPid,
aSecInfo[iThread].hTid,
aSecInfo[iThread].hClientPid,
aSecInfo[iThread].hClientTid));
if ((fRegularDump) &&
(aSecInfo[iThread].hTid != (DWORD)DumpClientId.UniqueThread) && // BUGBUG
(aSecInfo[iThread].hTid != (DWORD)ClearClientId.UniqueThread) && // WHY???
(aSecInfo[iThread].hTid != (DWORD)PauseClientId.UniqueThread))
{
iLocThdCnt++;
cChars += sprintf(
lpstrBuff+cChars,
"\n\nT h r e a d #%d: (pid|tid=0x%lx|0x%lx "
"Client:pid|tid=0x%lx|0x%lx)\r\n"
//
// 1st header line
//
" %-*.*s "
"--- Rtn + Callees --- "
"--- Rtn - Callees ---\r\n"
//
// 2nd header line
//
"Depth%1c%-*.*s%1c"
"Calls%1c Tot Time %1c Time/Call %1c "
"Tot Time %1c Time/Call %1c "
"First Time %1c Min Time %1c Max Time\r\n\n",
iLocThdCnt, aSecInfo[iThread].hPid,
aSecInfo[iThread].hTid, aSecInfo[iThread].hClientPid,
aSecInfo[iThread].hClientTid,
iNameLength, iNameLength, " ",
cExcelDelimiter,
iNameLength - 1, iNameLength - 1, " Routine",
cExcelDelimiter,
cExcelDelimiter,
cExcelDelimiter,
cExcelDelimiter,
cExcelDelimiter,
cExcelDelimiter,
cExcelDelimiter,
cExcelDelimiter);
if ( !WriteFile (hOutFile, lpstrBuff, cChars, &cChars, NULL) )
{
CapDbgPrint ("CAP: DumpProfiledInfo() - "
"Error writing to %s - 0x%lx\n",
atchOutFileName, GetLastError());
}
cChars = 0;
if (aSecInfo[iThread].pthdblk->ulRootCell != 0L)
{
CalcIncompleteCalls (aSecInfo[iThread].pthdblk,
aSecInfo[iThread].ulRootCell,
0);
liTotalRunTime = 0L;
DumpRoutineInfo (aSecInfo[iThread].pthdblk,
aSecInfo[iThread].ulRootCell,
0,
atchOutFileName,
lpstrBuff);
}
}
else
{
cChars += sprintf (
lpstrBuff + cChars,
"\n\n <<< REGULAR DUMP NOT PRINTED >>>\n\n");
}
if (fChronoCollect)
{
DumpChronoFuncs(aSecInfo[iThread].pthdblk, lpstrBuff);
DumpFuncCalls(aSecInfo[iThread].pthdblk, lpstrBuff);
}
else
{
cChars += sprintf (
lpstrBuff + cChars,
"\n\n <<< NO CHRONO INFO COLLECTED >>>\n\n"
"================================="
"================================================"
"================================================"
"========================================\r\n\n\n");
}
}
cChars += sprintf (lpstrBuff + cChars,
"\r\n\n<<<< END OF LISTINGS >>>>\n\n"
"================================="
"================================================"
"================================================"
"========================================\r\n\n\n");
if ( !WriteFile (hOutFile, lpstrBuff, cChars, &cChars, NULL) )
{
CapDbgPrint ("CAP: DumpProfiledInfo() - "
"Error writing to %s - 0x%lx\n",
atchOutFileName, GetLastError());
}
cChars = 0;
if ( !CloseHandle (hOutFile) )
{
CapDbgPrint ("CAP: DumpProfiledInfo() - "
"Error closing %s - 0x%lx\n",
atchOutFileName, GetLastError());
}
//
// Free allocated memory for building output data
//
if (!GlobalUnlock (hMem))
{
CapDbgPrint ("CAP: DumpProfiledInfo() - "
"Error ulocking global memory - 0x%lx\n",
GetLastError());
}
if (GlobalFree (hMem))
{
CapDbgPrint ("CAP: DumpProfiledInfo() - "
"Error freeing global memory - 0x%lx\n",
GetLastError());
}
SETUPPrint (("CAP: DumpProfiledInfo() - ...done\n"));
//
// Release the GLOBAL semaphore so other processes can dump data
//
Status = ReleaseSemaphore (hGlobalSem, 1, NULL);
if (!NT_SUCCESS(Status))
{
CapDbgPrint ("CAP: DumpProfiledInfo() - "
"Error releasing GLOBAL semaphore - 0x%lx\n", Status);
}
} /* DumpProfiledInfo() */
/******************* C a l c I n c o m p l e t e C a l l s ******************
*
* CalcIncompleteCalls (pthdblk, uldatacell) -
* Takes care of imcomplete calls times by using liIncompleteTicks
* as the end time. It calculates the call over head for all
* incomplete calls as though they have been completed. This is
* a bit inaccurate but it can't hurt too much since only one call
* per level can be incomplete.
*
* ENTRY pthdblk - points to the current thread info block
* uldatacell - offset to the next data cell
* TreeDepth - current depth down a tree
*
* EXIT -none-
*
* RETURN Number of untstanding nested calls
*
* WARNING:
* -none-
*
* COMMENT:
* This routine is called recursively to take care of all cells.
*
*/
int CalcIncompleteCalls(PTHDBLK pthdblk, ULONG uldatacell, int TreeDepth)
{
LONGLONG liElapsed = 0L;
LONGLONG liOverhead = 0L;
PDATACELL pdatacell;
int nOutstandingNestedCalls = 0;
int nOutstandingNextCalls = 0;
pdatacell = MKPDATACELL(pthdblk, uldatacell);
//
// Make recursive calls
//
if (pdatacell->ulNestedCell != 0L) // go down the tree
{
nOutstandingNestedCalls = CalcIncompleteCalls (pthdblk, pdatacell->ulNestedCell, TreeDepth + 1);
}
if (pdatacell->ulNextCell != 0L) // move along the forest of trees
{
nOutstandingNextCalls = CalcIncompleteCalls (pthdblk, pdatacell->ulNextCell, TreeDepth);
}
//
// Check the cells that have incomplete timings.
//
if ( (pdatacell->ts == T1) || (pdatacell->ts == RESTART) )
{
//
// Get the difference in ticks
//
liElapsed = liIncompleteTicks - pdatacell->liStartCount;
//
// Subtract the overhead and any waste time for this call
//
nOutstandingNestedCalls += pdatacell->nTmpNestedCalls;
liOverhead = liCalibNestedTicks * nOutstandingNestedCalls;
liElapsed -= liOverhead;
liElapsed -= liCalibTicks;
liElapsed -= pthdblk->liWasteCount;
if ( liElapsed < 0 )
{
OutputCapDebugString ("CAP: CalcIncompleteCalls() - Overhead greater"
" than elapsed time.\n");
liElapsed = 0L;
}
// Accumulate total time
//
pdatacell->liTotTime += liElapsed;
pdatacell->nCalls++;
// Store the first time - first time is not included in Max/Min times
// computations.
//
if (pdatacell->nCalls == 1)
{
//
// Get the First time
//
pdatacell->liFirstTime = liElapsed;
}
}
else
{
if (pdatacell->nTmpNestedCalls || nOutstandingNestedCalls)
{
OutputCapDebugString ("CAP: CalcIncompleteCalls() - Complete cell"
" with outstanding calls\n");
}
}
return(nOutstandingNestedCalls + nOutstandingNextCalls);
} /* CalcIncompleteCalls() */
/*********************** D u m p R o u t i n e I n f o *********************
*
* DumpRoutineInfo (pthdblk, uldatacell, iDepth, ptchDumpFile, lpstrBuff) -
* Dumps the profiled data to the specified output file.
*
* ENTRY pthdblk - points to the current thread info block
* uldatacell - offset to the next data cell
* iDepth - call depth level
* ptchDumpFile - Output filename
* lpstrBuff - pointer to the formating buffer
*
* EXIT -none-
*
* RETURN -none-
*
* WARNING:
* -none-
*
* COMMENT:
* This routine is called recursively to print all cells.
*
*/
void DumpRoutineInfo (PTHDBLK pthdblk,
ULONG uldatacell,
int iDepth,
PTCHAR ptchDumpFile,
LPSTR lpstrBuff)
{
LONGLONG liTotalTime;
TCHAR chTotalTimeSuffix;
LONGLONG liTotalTPC;
TCHAR chTotalTPCSuffix;
LONGLONG liCallerTime;
TCHAR chCallerTimeSuffix;
LONGLONG liCallerTPC;
TCHAR chCallerTPCSuffix;
LONGLONG liCalleeTime;
TCHAR chCalleeTimeSuffix;
LONGLONG liFirst;
TCHAR chFirstSuffix;
LONGLONG liMin;
TCHAR chMinSuffix;
LONGLONG liMax;
TCHAR chMaxSuffix;
int iCalleeCalls;
int iCalleeNestedCalls;
PTCHAR ptchSym;
PDATACELL pdatacell;
pdatacell = MKPDATACELL(pthdblk, uldatacell);
//
// Dump data only if cell is NOT CLEARED (just initialized - no data yet)
//
if (pdatacell->ts != CLEARED)
{
//
// Get the total time and total number of calls of nested calles
// for this routine
//
liCalleeTime = GetCalleesInfo (pthdblk,
pdatacell->ulNestedCell,
&iCalleeCalls,
&iCalleeNestedCalls);
DETAILPrint (("CAP: CCalls:%d + CNCalls:%d = Calls:%d\n",
iCalleeCalls,
iCalleeNestedCalls,
pdatacell->nNestedCalls));
DETAILPrint (("CAP: Total Time=0x%x%x ; Callee Time=0x%x%x\n",
pdatacell->liTotTime,
liCalleeTime));
liTotalTime = pdatacell->liTotTime;
//
// Calculate just the routine time (not including the callees times)
//
liCallerTime = liTotalTime - liCalleeTime;
if (liCallerTime < 0L)
{
liCallerTime = 0L;
}
if (pdatacell->nCalls > 1)
{
liTotalTPC = liTotalTime / pdatacell->nCalls;
liCallerTPC = liCallerTime / pdatacell->nCalls;
}
else if (pdatacell->nCalls == 1)
{
liTotalTPC = liTotalTime;
liCallerTPC = liCallerTime;
}
else
{
liTotalTPC = 0L;
liCallerTPC = 0L;
}
//
// Get the First time.
//
liFirst = pdatacell->liFirstTime;
//
// Adjust all the times (also converts ticks to microseconds)
//
AdjustTime (&liCalleeTime, &chCalleeTimeSuffix);
AdjustTime (&liTotalTime, &chTotalTimeSuffix);
AdjustTime (&liTotalTPC, &chTotalTPCSuffix);
AdjustTime (&liCallerTime, &chCallerTimeSuffix);
AdjustTime (&liCallerTPC, &chCallerTPCSuffix);
AdjustTime (&liFirst, &chFirstSuffix);
//
// Get the symbol name using the function address
//
ptchSym = GetFunctionName (pdatacell->ulSymbolAddr,
pdatacell->ulProfBlkOff,
NULL);
//
// Did end time captured for last call? If not, indicate timing of the
// last call was incomplete
//
if ( (pdatacell->ts == T1) || (pdatacell->ts == RESTART) )
{
*ptchSym = '*';
}
if ( (pdatacell->nCalls > 1) &&
(pdatacell->liMinTime != MAXLONGLONG) ) //051993 Add
{
//
// Adjust Min/Max times - Min/Max times are computed without
// considering the first time.
//
liMin = pdatacell->liMinTime;
AdjustTime (&liMin, &chMinSuffix);
liMax = pdatacell->liMaxTime;
AdjustTime (&liMax, &chMaxSuffix);
cChars += sprintf (lpstrBuff + cChars,
"%3d%1c %-*.*s%1c%5lu%1c%9lu%1c%1c %9lu%1c"
"%1c %9lu%1c%1c %9lu%1c%1c %9lu%1c%1c "
"%9lu%1c%1c %9lu%1c\r\n",
iDepth,
cExcelDelimiter,
iNameLength,
iNameLength,
ptchSym,
cExcelDelimiter,
pdatacell->nCalls,
cExcelDelimiter,
(ULONG)liTotalTime, chTotalTimeSuffix,
cExcelDelimiter,
(ULONG)liTotalTPC, chTotalTPCSuffix,
cExcelDelimiter,
(ULONG)liCallerTime, chCallerTimeSuffix,
cExcelDelimiter,
(ULONG)liCallerTPC, chCallerTPCSuffix,
cExcelDelimiter,
(ULONG)liFirst, chFirstSuffix,
cExcelDelimiter,
(ULONG)liMin, chMinSuffix,
cExcelDelimiter,
(ULONG)liMax, chMaxSuffix);
}
else
{
cChars += sprintf (lpstrBuff+cChars,
"%3d%1c %-*.*s%1c%5lu%1c%9lu%1c%1c %9lu%1c"
"%1c %9lu%1c%1c %9lu%1c%1c %9lu%1c%1c "
"%9s %1c %9s\r\n",
iDepth,
cExcelDelimiter,
iNameLength,
iNameLength,
ptchSym,
cExcelDelimiter,
pdatacell->nCalls,
cExcelDelimiter,
(ULONG)liTotalTime, chTotalTimeSuffix,
cExcelDelimiter,
(ULONG)liTotalTPC, chTotalTPCSuffix,
cExcelDelimiter,
(ULONG)liCallerTime, chCallerTimeSuffix,
cExcelDelimiter,
(ULONG)liCallerTPC, chCallerTPCSuffix,
cExcelDelimiter,
(ULONG)liFirst, chFirstSuffix,
cExcelDelimiter,
"n/a",
cExcelDelimiter,
"n/a");
}
if (cChars > BUFFER_SIZE)
{
if ( !WriteFile (hOutFile, lpstrBuff, cChars, &cChars, NULL) )
{
CapDbgPrint ("CAP: DumpRoutineInfo() - "
"Error writing to %s - 0x%lx\n",
ptchDumpFile, GetLastError());
}
cChars = 0;
}
}
//
// Make recursive calls
//
if (pdatacell->ulNestedCell != 0L)
{
DumpRoutineInfo (pthdblk,
pdatacell->ulNestedCell,
iDepth+1,
ptchDumpFile,
lpstrBuff);
}
if (pdatacell->ulNextCell != 0L)
{
DumpRoutineInfo (pthdblk,
pdatacell->ulNextCell,
iDepth,
ptchDumpFile,
lpstrBuff);
}
} /* DumpRoutineInfo() */
/************************* G e t C a l l e e s I n f o ***********************
*
* GetCalleesInfo (pthdblk, uldatacell, piCalls, piNestedCalls) -
* Accumulates total time and total number of callee's counts.
*
* ENTRY pthdblk - points to the current thread info block
* uldatacell - offset to the data cell
*
* EXIT piCalls - contains total number callee's calls
* piNestedCalls - conatins total number callee's nested calls
*
* RETURN liAccum - conatins total callee's times (not calibrated)
*
* WARNING:
* -none-
*
* COMMENT:
* -none-
*
*/
LONGLONG GetCalleesInfo (PTHDBLK pthdblk,
ULONG uldatacell,
int *piCalls,
int *piNestedCalls)
{
LONGLONG liAccum;
PDATACELL pdatacell;
liAccum = 0L;
*piCalls = 0L;
*piNestedCalls = 0L;
while (uldatacell != 0L)
{
pdatacell = MKPDATACELL(pthdblk, uldatacell);
*piCalls += pdatacell->nCalls;
*piNestedCalls += pdatacell->nNestedCalls;
liAccum += pdatacell->liTotTime;
uldatacell = pdatacell->ulNextCell;
}
return (liAccum);
} /* GetCalleesInfo () */
/*************************** A d j u s t T i m e ***************************
*
* AdjustTime (pliTime, ptchSuffix) -
* This routine converts the time to microseconds and then
* long times to smaller times expressed as multiples of
* 1024 (= 1K).
*
* ENTRY pliTime - large integer time
*
* EXIT pliTime - converted time
* ptchSuffix - suffix character indicating "K" for multiple
* of 1K or '?' in case of over/underflow
*
* RETURN -none-
*
* WARNING:
* -none-
*
* COMMENT:
* -none-
*
*/
void AdjustTime (PLONGLONG pliTime, PTCHAR ptchSuffix)
{
LARGE_INTEGER liTime;
// Convert ticks to microseconds
//
*pliTime *= ONE_MILLION;
*pliTime /= liTimerFreq;
liTime.QuadPart = *pliTime;
if (liTime.HighPart != 0)
{
if (liTime.HighPart >> 10 > 0)
{
CapDbgPrint ("CAP: AdjustTime() - "
"ERROR - Unexpected timer overflow: %lu-%lu\n",
liTime.HighPart, liTime.LowPart);
*pliTime = 0L;
*ptchSuffix = 'o';
}
else
if (liTime.HighPart >> 10 < 0)
{
CapDbgPrint ("CAP: AdjustTime() - "
"ERROR - Unexpected timer underflow: %lu-%lu\n",
liTime.HighPart, liTime.LowPart);
*pliTime = 0L;
*ptchSuffix = 'u';
}
else
{
*pliTime = ((ULONG)(liTime.HighPart) << 22) +
(liTime.LowPart >> 10);
*ptchSuffix = 'K';
}
}
else
{
*ptchSuffix = ' ';
}
} /* AdjustTime () */
/*********************** P r e T o p L e v e l C a l i b ********************
*
* PreTopLevelCalib (pthdblk) -
* Helper routine for DoCalibrations()..
*
* ENTRY pthdblk - pointer to the current thread block
* pDataCell
*
* EXIT -none-
*
* RETURN -none-
*
* WARNING:
* -none-
*
* COMMENT:
* -none-
*
*/
/* void PreTopLevelCalib (PTHDBLK pthdblk, PDATACELL pDataCell) */
/* { */
/* LARGE_INTEGER liTemp; */
/* */
/* QueryPerformanceCounter (&liTemp ); */
/* pDataCell->liStartCount = liTemp.QuadPart; */
/* */
/* pDataCell->liStartCount -= pthdblk->liWasteCount; */
/* } /* PreTopLevelCalib() */
/********************* P o s t T o p L e v e l C a l i b *******************
*
* PostTopLevelCalib (pthdblk) -
* Helper routine for DoCalibrations()..
*
* ENTRY pthdblk - pointer to the current thread block
*
* EXIT -none-
*
* RETURN -none-
*
* WARNING:
* -none-
*
* COMMENT:
* -none-
*
*/
/* void PostTopLevelCalib (PTHDBLK pthdblk) */
/* { */
/* LARGE_INTEGER liTemp; */
/* */
/* QueryPerformanceCounter ( &liTemp ); */
/* pthdblk->liStopCount = liTemp.QuadPart; */
/* pthdblk->liStopCount -= pthdblk->liWasteCount; */
/* } /* PostTopLevelCalib() */
/* */
/************************ D o C a l i b r a t i o n s **********************
*
* DoCalibrations () -
* This routine calculates _penter / _mcount overheads
*
* ENTRY -none-
*
* EXIT -none-
*
* RETURN -none-
*
* WARNING:
* -none-
*
* COMMENT:
* -none-
*
*/
void DoCalibrations ()
{
NTSTATUS Status;
int i;
LARGE_INTEGER liStartTicks;
LONGLONG liStart;
LARGE_INTEGER liEndTicks;
LONGLONG liEnd;
LONGLONG liWaste;
LONGLONG liElapsed;
LONGLONG liQPCOverhead;
BOOL fDummy;
PTHDBLK pthdblk;
ULONG ulElapsed;
PTHDBLK pCURTHDBLK;
DWORD dwDummyLocal = 0;
PDATACELL pCurDataCell;
PDATACELL pHelperDataCell;
ULONG ulCapUse;
LARGE_INTEGER liTemp;
/*
*
* * * * N t Q u e r y P e r f C o u n t e r C a l i b r a t i o n * * * *
*
*/
//
// Calibrate NTQueryPerformanceCounter() call
//
liQPCOverhead = MAXLONGLONG;
for (i=0; i < NUM_ITERATIONS; i++)
{
QueryPerformanceCounter (&liStartTicks);
QueryPerformanceCounter (&liEndTicks);
liElapsed = liEndTicks.QuadPart - liStartTicks.QuadPart;
if (liElapsed < liQPCOverhead )
{
liQPCOverhead = liElapsed;
}
}
SETUPPrint (("CAP: DoCalibrations() - QPCOverhead=0x%x%x\n",
liQPCOverhead));
GetNewThdBlk ();
pthdblk = GETCURTHDBLK();
//
// Calibrate liWasteOverhead
//
liWasteOverhead = MAXLONGLONG;
liStart = liEnd = liWaste = 0L;
for (i=0; i < NUM_ITERATIONS; i++)
{
QueryPerformanceCounter (&liStartTicks);
// Execute waste calculation sequence
liWaste = liEnd - liStart;
liWaste += liWaste;
pthdblk->liWasteCount += liWaste;
QueryPerformanceCounter (&liEndTicks);
liElapsed = liEndTicks.QuadPart - liStartTicks.QuadPart;
if (liElapsed < liWasteOverhead )
{
liWasteOverhead = liElapsed;
}
}
#if defined(_X86_)
//
// Calibrate liWasteOverheadSavRes
//
liWasteOverheadSavRes = MAXLONGLONG;
liStart = liEnd = liWaste = 0;
for (i=0; i < NUM_ITERATIONS; i++)
{
QueryPerformanceCounter (&liStartTicks);
// Execute save/restore sequence
SaveAllRegs ();
SETCAPINUSE();
liWaste = liEnd - liStart;
liWaste += liWaste;
pthdblk->liWasteCount += liWaste;
RESETCAPINUSE();
RestoreAllRegs ();
QueryPerformanceCounter (&liEndTicks);
liElapsed = liEndTicks.QuadPart - liStartTicks.QuadPart;
if (liElapsed < liWasteOverheadSavRes)
{
liWasteOverheadSavRes = liElapsed;
}
}
#else
liWasteOverheadSavRes = 0L;
#endif // ifdef _X86_
SETUPPrint ((
"CAP: DoCalibrations() - QPCOverhead=0x%x%x - "
"WasteOverhead=0x%x%x - WasteOverheadSavRes=0x%x%x\n",
liQPCOverhead, liWasteOverhead, liWasteOverheadSavRes ));
/*
*
* * * * T o p l e v e l c a l l s ' c a l i b r a t i o n * * * *
*
*/
fProfiling = TRUE;
// Setup root cell and make it the current one
pthdblk->ulRootCell = GetNewCell (pthdblk);
pthdblk->ulCurCell = pthdblk->ulRootCell;
pthdblk->liWasteCount = 0;
// Set calib ticks to zero so there is no correction
// during the calibration
liCalibTicks = 0;
liCalibNestedTicks = 0;
// Call CalHelper1 routine many times
// CalHelper1 is an empty routine with a _penter call
for (i=0; i< NUM_ITERATIONS; i++)
{
CalHelper1();
}
// Get pointer to the nested cell that holds CalHelper1 info
pCurDataCell = MKPDATACELL(pthdblk, pthdblk->ulCurCell);
pHelperDataCell = MKPDATACELL(pthdblk, pCurDataCell->ulNestedCell);
// Take the minimum measurement as penter's overhead
liCalibTicks = pHelperDataCell->liMinTime;
//
// Convert ticks to microseconds..
//
liElapsed = liCalibTicks * ONE_MILLION;
liElapsed = liElapsed / liTimerFreq;
ulCalibTime = (ULONG)liElapsed;
/*
*
* * * * N e s t e d c a l l s ' c a l i b r a t i o n * * * *
*
*/
// Get new cell for root and make it the current one
pthdblk->ulRootCell = GetNewCell (pthdblk);
pthdblk->ulCurCell = pthdblk->ulRootCell;
// Call CalHelper2 many times
// CalHelper calls one subroutine and both call _penter
for (i=0; i<NUM_ITERATIONS; i++)
{
CalHelper2();
}
// Get pointer to nested cell that hold CalHelper2 info
pCurDataCell = MKPDATACELL(pthdblk, pthdblk->ulCurCell);
pHelperDataCell = MKPDATACELL(pthdblk,pCurDataCell->ulNestedCell);
// Take the minimum measurement as nested penter's overhead
liCalibNestedTicks = pHelperDataCell->liMinTime;
//
// Convert ticks to microseconds..
//
liElapsed = liCalibNestedTicks * ONE_MILLION;
liElapsed /= liTimerFreq;
ulCalibNestedTime = (ULONG)liElapsed;
//
// Free allocated memory. At this point iThdCnt is == to 0 since we
// have not started to do profiling yet.
//
aSecInfo[0].ulRootCell = 0L; // 051993 Add
aSecInfo[0].pthdblk->ulRootCell = 0L;
aSecInfo[0].pthdblk->ulCurCell = 0L;
aSecInfo[0].pthdblk->ulMemOff = 0L;
aSecInfo[0].pthdblk->ulChronoOffset = 0L;
if (fChronoCollect || ((aSecInfo[0].pthdblk)->pChronoHeadCell != NULL))
{
//
// Unmap section
//
if (!UnmapViewOfFile((PVOID)((aSecInfo[0].pthdblk)->pChronoHeadCell)))
{
CapDbgPrint ("CAP: DoCalibrations() - Free chronoSec"
"ERROR - UnmapViewOfFile() - 0x%lx\n", GetLastError());
}
//
// Close section
//
if (CloseHandle(aSecInfo[0].hChronoMapObject))
{
CapDbgPrint ("CAP: DoCalibrations() - "
"ERROR - CloseHandle() - 0x%lx\n", GetLastError());
}
(aSecInfo[0].pthdblk)->pChronoHeadCell = NULL;
(aSecInfo[0].pthdblk)->pCurrentChronoCell = NULL;
}
//
// Unmap section
//
if (!UnmapViewOfFile((PVOID)aSecInfo[0].pthdblk))
{
CapDbgPrint ("CAP: DoCalibrations() - "
"ERROR - UnmapViewOfSection() - 0x%lx\n", GetLastError());
}
//
// Close section
//
if (!CloseHandle(aSecInfo[0].hMapObject))
{
CapDbgPrint ("CAP: DoCalibrations() - "
"ERROR - CloseHandle() - 0x%lx\n", GetLastError());
}
aSecInfo[0].pthdblk = NULL;
//
// Reset current thread block pointer
//
SETCURTHDBLK(NULL);
RESETCLIENT();
} /* DoCalibrations () */
/****************** U n p r o t e c t T h u n k F i l t e r *****************
*
* UnprotectThunkFilter (pThunkAddress, pXcptInfo) -
* Unprotects the thunk address to be able to write to it.
*
* ENTRY pThunkAddress - thunk address which caused the exception
* pXcptInfo - exception report record info pointer
*
* EXIT -none-
*
* RETURN EXCEPTIONR_CONTINUE_EXECUTION : if mem unprotected successfully
* EXCEPTION_CONTINUE_SEARCH : if non-access violation exception
* or cannot unprotect memory
* WARNING:
* -none-
*
* COMMENT:
* -none-
*
*/
INT UnprotectThunkFilter (PVOID pThunkAddress, PEXCEPTION_POINTERS pXcptInfo)
{
PVOID FaultAddress;
NTSTATUS Status;
PVOID ThunkBase;
ULONG RegionSize;
ULONG OldProtect;
//
// If we fault on the thunk attemting to write, then set protection to allow
// writes
//
Status = STATUS_UNSUCCESSFUL;
FaultAddress = (PVOID)
(pXcptInfo->ExceptionRecord->ExceptionInformation[1] & ~0x3);
if ( pXcptInfo->ExceptionRecord->ExceptionCode ==
STATUS_ACCESS_VIOLATION )
{
if (pXcptInfo->ExceptionRecord->ExceptionInformation[0] &&
FaultAddress == pThunkAddress )
{
ThunkBase = (PVOID)
pXcptInfo->ExceptionRecord->ExceptionInformation[1];
RegionSize = sizeof(ULONG);
if (VirtualProtect(ThunkBase, RegionSize, PAGE_READWRITE,
&OldProtect))
{
return EXCEPTION_CONTINUE_EXECUTION;
}
else
{
CapDbgPrint ("CAP: UnprotectThunkFilter() - "
"Error changing memory protections @ 0x%08lx - 0x%lx\n",
ThunkBase, GetLastError());
OutputCapDebugString("CAP: UnprotectThunkFilter() - "
"fatal error changing memory protections - ");
}
}
}
return EXCEPTION_CONTINUE_SEARCH;
} /* UnprotectThunkFilter() */
/*********************** A c c e s s X c p t F i l t e r *********************
*
* AccessXcptFilter (ulXcptNo, pXcptInfoPtr, ulCommitSz) -
* Commits COMMIT_SIZE more pages of memory if exception is access
* violation.
*
* ENTRY ulXcptNo - exception number
* pXcptInfoPtr - exception report record info pointer
* ulCommitSz - Size of memory to be commited
*
* EXIT -none-
*
* RETURN EXCEPTIONR_CONTINUE_EXECUTION : if access violation exception
* and mem committed successfully
* EXCEPTION_CONTINUE_SEARCH : if non-access violation exception
* or cannot commit more memory
* WARNING:
* -none-
*
* COMMENT:
* -none-
*
*/
INT AccessXcptFilter (ULONG ulXcptNo,
PEXCEPTION_POINTERS pXcptPtr,
ULONG ulCommitSz)
{
NTSTATUS Status;
LARGE_INTEGER liStart;
LARGE_INTEGER liEnd;
LONGLONG liWaste;
PTHDBLK pthdblk;
PVOID pvMem;
QueryPerformanceCounter (&liStart);
pvMem = (PVOID)pXcptPtr->ExceptionRecord->ExceptionInformation[1];
if (ulXcptNo != EXCEPTION_ACCESS_VIOLATION)
{
return EXCEPTION_CONTINUE_SEARCH;
}
else
{
if (!VirtualAlloc(pvMem, ulCommitSz, MEM_COMMIT, PAGE_READWRITE))
{
OutputCapDebugString("CAP: AccessXcptFilter() - "
"fatal error committing more memory - ");
CapDbgPrint ("CAP: AccessXcptFilter() - "
"Error committing more memory @ 0x%08lx - 0x%lx\n",
pvMem, GetLastError());
return EXCEPTION_CONTINUE_SEARCH;
}
else
{
SETUPPrint (("CAP: AccessXcptFilter() - "
"Committed %d more page(s) @ 0x%08lx\n",
ulCommitSz/PAGE_SIZE, pvMem));
}
if ( pthdblk = GETCURTHDBLK() )
{
//
// Compute the overhead time in getting more memory and
// subtract that out of the profiling time later on
//
QueryPerformanceCounter (&liEnd);
liWaste = liEnd.QuadPart - liStart.QuadPart;
liWaste += liWasteOverhead;
pthdblk->liWasteCount += liWaste;
SETUPPrint (("CAP: AccessXcptFilter() - liWaste = 0x%x%x\n",
liWaste));
}
return EXCEPTION_CONTINUE_EXECUTION;
}
} /* AccessXcptFilter () */
/*++
Windows95 compaitibility functions:
Created 2-Feb-95 a-robw (Bob Watson)
CapInitUnicodeString: Same as RtlInitUnicodeString
CapUnicodeStringToAnsiString: Same as RtlUnicodeStringToAnsiString
IsThisWin95: returns TRUE on Windows 95 system
GetCurrentCapProcess: handles difference in PSAPI.DLL
implementations between Win95 & NT
CapDbgPrint: win95 compatible version of DbgPrint
--*/
VOID
CapInitUnicodeString (
PUNICODE_STRING DestinationString,
PCWSTR SourceString
)
{
// allocates buffer for a unicode string structure and copies
// the source string into it.
DestinationString->Length = lstrlenW(SourceString) * sizeof(WCHAR);
DestinationString->MaximumLength = DestinationString->Length + sizeof(WCHAR);
DestinationString->Buffer = GlobalAlloc (GPTR, DestinationString->MaximumLength);
if (DestinationString->Buffer != NULL) {
lstrcpyW (DestinationString->Buffer, SourceString);
} else {
DestinationString->Length = 0;
DestinationString->MaximumLength = 0;
}
}
LONG
CapUnicodeStringToAnsiString (
PANSI_STRING DestinationString,
PUNICODE_STRING SourceString,
BOOLEAN AllocateDestinationString
)
{
LONG lStatus;
if (AllocateDestinationString) {
DestinationString->Buffer =
GlobalAlloc (GPTR, (SourceString->MaximumLength / sizeof(WCHAR)));
if (DestinationString->Buffer != NULL) {
DestinationString->Length = SourceString->Length / sizeof(WCHAR);
DestinationString->MaximumLength =
(SourceString->MaximumLength / sizeof(WCHAR));
lStatus = ERROR_SUCCESS;
} else {
lStatus = ERROR_OUTOFMEMORY;
}
} else {
lStatus = ERROR_SUCCESS;
}
if (lStatus == ERROR_SUCCESS) {
if (((SourceString->Length + sizeof(WCHAR)) / sizeof(WCHAR)) <=
DestinationString->MaximumLength) {
// then there's room to copy so convert
if (wcstombs (DestinationString->Buffer,
SourceString->Buffer,
DestinationString->MaximumLength) == (size_t)-1) {
lStatus = ERROR_SUCCESS;
} else {
lStatus = ERROR_SUCCESS;
}
} else {
lStatus = ERROR_INSUFFICIENT_BUFFER;
}
}
return lStatus;
}
static
BOOL
IsThisWin95 (
VOID
)
{
OSVERSIONINFO os;
BOOL bReturn = FALSE;
os.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
os.dwMajorVersion = 0;
os.dwMinorVersion = 0;
os.dwBuildNumber = 0;
os.dwPlatformId = 0;
if (GetVersionEx(&os)) {
if (os.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
bReturn = TRUE;
}
}
return bReturn;
}
HANDLE
GetCurrentCapProcess (
VOID
)
/*++
Routine Description:
Accomodates the minor differences between WinNt & Win95 implementations
of PSAPI.DLL. This may change (i.e. be fixed) later on but for now
this is what's needed
--*/
{
if (IsThisWin95()) {
// the Win95 version of PSAPI uses the PID not Handles
return (HANDLE)GetCurrentProcessId();
} else {
return GetCurrentProcess();
}
}
void
CapDbgPrint (
PCH Format,
...
)
{
TCHAR szBuffer[256];
va_list ArgList;
va_start (ArgList, Format);
_vstprintf (szBuffer, Format, ArgList);
OutputCapDebugString (szBuffer);
va_end (ArgList);
return;
}