// Copyright (c) 1998 John Panzer. Permission is granted to
// use, copy, modify, distribute, and sell this source code as
// long as this copyright notice appears in all source files.
// Utility functions
void indent(int level)
{
for(int i=0;i<level;i++) putchar('\t');
}
//
// class CallMonitor
//
DWORD CallMonitor::tlsSlot=0xFFFFFFFF;
CallMonitor::CallMonitor() {queryTicks(&threadStartTime);}
CallMonitor::~CallMonitor() {}
void CallMonitor::threadAttach(CallMonitor *newObj)
{
if (tlsSlot==0xFFFFFFFF) tlsSlot = TlsAlloc();
TlsSetValue(tlsSlot,newObj);
}
void CallMonitor::threadDetach()
{
delete &threadObj();
}
CallMonitor &CallMonitor::threadObj()
{
CallMonitor *self = (CallMonitor *)
TlsGetValue(tlsSlot);
return *self;
}
// Performs standard entry processing
void CallMonitor::enterProcedure(ADDR parentFramePtr,
ADDR funcAddr,
ADDR *retAddrPtr,
const TICKS &entryTime)
{
// Record procedure entry on shadow stack
callInfoStack.push_back(CallInfo());
CallInfo &ci = callInfoStack.back();
ci.funcAddr = funcAddr;
ci.parentFrame = parentFramePtr;
ci.origRetAddr = *retAddrPtr,
ci.entryTime = entryTime;
logEntry(ci); // Log procedure entry event
// Redirect eventual return to local thunk
*retAddrPtr = (ADDR)_pexitThunk;
queryTicks(&ci.startTime); // Track approx. start time
}
// Performs standard exit processing
void CallMonitor::exitProcedure(ADDR parentFramePtr,
ADDR *retAddrPtr,
const TICKS &endTime)
{
// Pops shadow stack until finding a call record
// that matches the current stack layout.
bool inSync=false;
while(1)
{
// Retrieve original call record
CallInfo &ci = callInfoStack.back();
ci.endTime = endTime;
*retAddrPtr = ci.origRetAddr;
if (ci.parentFrame==parentFramePtr)
{
logExit(ci,true); // Record normal exit
callInfoStack.pop_back();
return;
}
logExit(ci,false); // Record exceptional exit
callInfoStack.pop_back();
}
}
// Default entry logging procedure
void CallMonitor::logEntry(CallInfo &ci)
{
indent(callInfoStack.size()-1);
string module,name;
getFuncInfo(ci.funcAddr,module,name);
printf("%s!%s (%08X)\n",module.c_str(),
name.c_str(),ci.funcAddr);
}
// Default exit logging procedure
void CallMonitor::logExit(CallInfo &ci,bool normalRet)
{
indent(callInfoStack.size()-1);
if (!normalRet) printf("exception ");
printf("exit %08X, elapsed time=%I64d\n",ci.funcAddr,
ci.endTime-ci.startTime);
}
void CallMonitor::getFuncInfo(ADDR addr,
string &module,
string &funcName)
{
SymInitialize(GetCurrentProcess(),NULL,FALSE);
TCHAR moduleName[MAX_PATH];
TCHAR modShortNameBuf[MAX_PATH];
MEMORY_BASIC_INFORMATION mbi;
VirtualQuery((void*)addr,&mbi,sizeof(mbi));
GetModuleFileName((HMODULE)mbi.AllocationBase,
moduleName, MAX_PATH );
_splitpath(moduleName,NULL,NULL,modShortNameBuf,NULL);
BYTE symbolBuffer[ sizeof(IMAGEHLP_SYMBOL) + 1024 ];
PIMAGEHLP_SYMBOL pSymbol =
(PIMAGEHLP_SYMBOL)&symbolBuffer[0];
// Following not per docs, but per example...
pSymbol->SizeOfStruct = sizeof(symbolBuffer);
pSymbol->MaxNameLength = 1023;
DWORD symDisplacement = 0;
SymLoadModule(GetCurrentProcess(),
NULL,
moduleName,
NULL,
(DWORD)mbi.AllocationBase,
0);
SymSetOptions( SymGetOptions() & ~SYMOPT_UNDNAME );
char undName[1024];
if (! SymGetSymFromAddr(GetCurrentProcess(), addr,
&symDisplacement, pSymbol) )
{
// Couldn't retrieve symbol (no debug info?)
strcpy(undName,"<unknown symbol>");
}
else
{
// Unmangle name, throwing away decorations
// that don't affect uniqueness:
if ( 0 == UnDecorateSymbolName(
pSymbol->Name, undName,
sizeof(undName),
UNDNAME_NO_MS_KEYWORDS |
UNDNAME_NO_ACCESS_SPECIFIERS |
UNDNAME_NO_FUNCTION_RETURNS |
UNDNAME_NO_ALLOCATION_MODEL |
UNDNAME_NO_ALLOCATION_LANGUAGE |
UNDNAME_NO_MEMBER_TYPE))
strcpy(undName,pSymbol->Name);
}
SymUnloadModule(GetCurrentProcess(),
(DWORD)mbi.AllocationBase);
SymCleanup(GetCurrentProcess());
module = modShortNameBuf;
funcName = undName;
}
//End of File