Figure 2: CallMonitor class implementation

// 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