Renamed version4 to flaim and version5 to xflaim
git-svn-id: https://svn.code.sf.net/p/flaim/code/trunk@7 0109f412-320b-0410-ab79-c3e0c5ffbbe6
This commit is contained in:
789
flaim/src/fobjtrck.cpp
Normal file
789
flaim/src/fobjtrck.cpp
Normal file
@@ -0,0 +1,789 @@
|
||||
//-------------------------------------------------------------------------
|
||||
// Desc: Object reference tracker
|
||||
// Tabs: 3
|
||||
//
|
||||
// Copyright (c) 1999-2006 Novell, Inc. All Rights Reserved.
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of version 2 of the GNU General Public
|
||||
// License as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, contact Novell, Inc.
|
||||
//
|
||||
// To contact Novell about this file by physical or electronic mail,
|
||||
// you may find current contact information at www.novell.com
|
||||
//
|
||||
// $Id: fobjtrck.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
#include "flaimsys.h"
|
||||
|
||||
#if defined( FLM_UNIX) && !defined( FLM_OSX)
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
The NetWare Internal Debugger encrypts all symbols by XORing each character
|
||||
in the symbol name with the character at the corresponding position in the
|
||||
following mask. To see how the crypt mask is defined in NetWare, see
|
||||
SYMDEB.386 in the NetWare source. We have three options:
|
||||
|
||||
1. We can just emulate the internal debugger and decrypt the symbol,
|
||||
print it, and reencrypt it each time we want to display it (less than
|
||||
efficient).
|
||||
2. We can make a decrypted copy of the symbols we are interested in on
|
||||
module init, then use our own copy and free it on module exit.
|
||||
3. We can use the symbol list, but decrypt character by character into
|
||||
an internal string buffer, then print the buffer.
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef FLM_NLM
|
||||
extern "C"
|
||||
{
|
||||
void GetClosestSymbol(
|
||||
BYTE * szBuffer,
|
||||
LONG udAddress);
|
||||
}
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
Desc:
|
||||
****************************************************************************/
|
||||
F_ObjRefTracker::F_ObjRefTracker(void)
|
||||
{
|
||||
m_hRefListMutex = F_MUTEX_NULL;
|
||||
m_pListManager = NULL;;
|
||||
m_pFileSystem = NULL;
|
||||
m_pszObjName[ 0] = '\0';
|
||||
m_bLocalFS = FALSE;
|
||||
m_pAddrFmtHook = NULL;
|
||||
m_pUserData = NULL;
|
||||
m_pModHandle = NULL;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Desc: Allocates required memory and initializes a local file system
|
||||
****************************************************************************/
|
||||
RCODE F_ObjRefTracker::setup(
|
||||
const char * pszObjName,
|
||||
FLMBOOL bLogToFile)
|
||||
{
|
||||
RCODE rc = FERR_OK;
|
||||
char pszTmpBuf[ FORTRACK_MAX_OBJ_NAME_LEN + 5];
|
||||
char * pucTmp;
|
||||
|
||||
// Allocate a mutex
|
||||
|
||||
if( RC_BAD( rc = f_mutexCreate( &m_hRefListMutex)))
|
||||
{
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
// Allocate the list
|
||||
|
||||
if( (m_pListManager = f_new F_ListMgr) == NULL)
|
||||
{
|
||||
rc = RC_SET( FERR_MEM);
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
if( RC_BAD( m_pListManager->Setup( &m_lnode, 1)))
|
||||
{
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
// Create a local file system object
|
||||
|
||||
if( bLogToFile)
|
||||
{
|
||||
if( (m_pFileSystem = f_new F_FileSystemImp) == NULL)
|
||||
{
|
||||
rc = RC_SET( FERR_MEM);
|
||||
goto Exit;
|
||||
}
|
||||
m_bLocalFS = TRUE;
|
||||
}
|
||||
|
||||
if( f_strlen( pszObjName) <= FORTRACK_MAX_OBJ_NAME_LEN)
|
||||
{
|
||||
f_strcpy( m_pszObjName, pszObjName);
|
||||
}
|
||||
else
|
||||
{
|
||||
f_sprintf( m_pszObjName, "OBJTRCK");
|
||||
}
|
||||
|
||||
// Set the log path
|
||||
|
||||
f_strcpy( pszTmpBuf, m_pszObjName);
|
||||
pucTmp = pszTmpBuf;
|
||||
while( *pucTmp)
|
||||
{
|
||||
if( *pucTmp >= 'a' && *pucTmp <= 'z')
|
||||
{
|
||||
*pucTmp = (*pucTmp - 'a') + 'A';
|
||||
}
|
||||
pucTmp++;
|
||||
}
|
||||
f_strcat( pszTmpBuf, ".OTL");
|
||||
|
||||
#ifdef FLM_NLM
|
||||
f_strcpy( m_pLogPath, "SYS:SYSTEM");
|
||||
f_pathAppend( m_pLogPath, pszTmpBuf);
|
||||
#else
|
||||
f_strcpy( m_pLogPath, pszTmpBuf);
|
||||
#endif
|
||||
|
||||
Exit:
|
||||
|
||||
return( rc);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Desc:
|
||||
****************************************************************************/
|
||||
F_ObjRefTracker::~F_ObjRefTracker(void)
|
||||
{
|
||||
if( m_pListManager)
|
||||
{
|
||||
m_pListManager->Release();
|
||||
m_pListManager = NULL;
|
||||
}
|
||||
|
||||
if( m_hRefListMutex != F_MUTEX_NULL)
|
||||
{
|
||||
f_mutexDestroy( &m_hRefListMutex);
|
||||
}
|
||||
|
||||
if( m_pFileSystem && m_bLocalFS)
|
||||
{
|
||||
m_pFileSystem->Release();
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Desc: Track the given reference.
|
||||
****************************************************************************/
|
||||
void F_ObjRefTracker::trackRef(
|
||||
void * pReferenceID,
|
||||
void * pSubrefID)
|
||||
{
|
||||
TrackingRecord * pTrackingRec = NULL;
|
||||
void ** pStack;
|
||||
|
||||
if( m_hRefListMutex == F_MUTEX_NULL)
|
||||
{
|
||||
// Reference Tracking has not been initialized, just exit.
|
||||
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
if( !pReferenceID)
|
||||
{
|
||||
// Do not track NULL references
|
||||
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
// If there is insufficient memory to allocate a tracking record,
|
||||
// then we will never know if this reference is properly released.
|
||||
|
||||
if( (pTrackingRec = f_new TrackingRecord( pReferenceID, pSubrefID)) == NULL)
|
||||
{
|
||||
char pucMessage[ 100];
|
||||
|
||||
logError( "trackRef: Insufficient memory to allocate tracking record");
|
||||
f_sprintf( pucMessage, "\treference %x.%x will not be tracked",
|
||||
(unsigned)((FLMUINT)pReferenceID), (unsigned)((FLMUINT)pSubrefID));
|
||||
logError( pucMessage);
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
if( RC_BAD( pTrackingRec->Setup( m_pListManager, &m_lnode, 1)))
|
||||
{
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
//Lock the list
|
||||
f_mutexLock( m_hRefListMutex);
|
||||
|
||||
// Add the tracking record to the list
|
||||
m_pListManager->InsertAtEnd( 0, pTrackingRec);
|
||||
|
||||
//Unlock the list
|
||||
f_mutexUnlock( m_hRefListMutex);
|
||||
|
||||
pStack = (void **)pTrackingRec->getStack();
|
||||
getCallStack( pStack, CTRC_STACK_SIZE, /*skip = */ 1);
|
||||
|
||||
Exit:
|
||||
return;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Desc: This reference has been released, don't track it any more.
|
||||
****************************************************************************/
|
||||
void F_ObjRefTracker::untrackRef(
|
||||
void * pReferenceID,
|
||||
void * pSubrefID)
|
||||
{
|
||||
TrackingRecord * pTrackingRec = NULL;
|
||||
FLMBOOL bListLocked = FALSE;
|
||||
|
||||
if( m_hRefListMutex == F_MUTEX_NULL)
|
||||
{
|
||||
// Reference Tracking has not been initialized, just exit.
|
||||
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
if( !pReferenceID)
|
||||
{
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
// Lock the list
|
||||
|
||||
f_mutexLock( m_hRefListMutex);
|
||||
bListLocked = TRUE;
|
||||
|
||||
// Try to find the reference in the list
|
||||
|
||||
pTrackingRec = (TrackingRecord *) m_pListManager->GetItem( 0, 0);
|
||||
while( pTrackingRec)
|
||||
{
|
||||
if( pTrackingRec->getReferenceID() == pReferenceID
|
||||
&& pTrackingRec->getSubrefID() == pSubrefID)
|
||||
{
|
||||
// The reference has been found.
|
||||
|
||||
pTrackingRec->RemoveFromList();
|
||||
pTrackingRec->Release();
|
||||
break;
|
||||
}
|
||||
|
||||
pTrackingRec = (TrackingRecord *) pTrackingRec->GetNextListItem();
|
||||
}
|
||||
|
||||
if( !pTrackingRec)
|
||||
{
|
||||
// The reference was never tracked. This isn't supposed to happen.
|
||||
|
||||
char pucMessage[100];
|
||||
|
||||
f_sprintf( pucMessage,
|
||||
"untrackRef: Reference %x.%x was not tracked",
|
||||
(unsigned)((FLMUINT)pReferenceID), (unsigned)((FLMUINT)pSubrefID));
|
||||
logError( pucMessage);
|
||||
logError( "\tModify code to track this reference");
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
Exit:
|
||||
|
||||
if( bListLocked)
|
||||
{
|
||||
f_mutexUnlock( m_hRefListMutex);
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Desc: Check the list for references that were never released.
|
||||
****************************************************************************/
|
||||
void F_ObjRefTracker::checkForUnreleasedRefs(
|
||||
FLMUINT * puiCount)
|
||||
{
|
||||
RCODE rc = FERR_OK;
|
||||
TrackingRecord * pTrackingRec;
|
||||
FLMUINT uiFileCursor;
|
||||
FLMUINT uiLoop;
|
||||
char pucSymbol[ 125];
|
||||
char pucBuffer[ 150];
|
||||
FLMBOOL bHeaderDisplayed;
|
||||
F_FileHdl * pFileHdl = NULL;
|
||||
FLMBOOL bListLocked = FALSE;
|
||||
FLMUINT uiCount = 0;
|
||||
|
||||
if( m_hRefListMutex == F_MUTEX_NULL)
|
||||
{
|
||||
logError( "checkForUnreleasedReferences: Reference tracking "
|
||||
"was not initialized");
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
if( m_pFileSystem)
|
||||
{
|
||||
if( RC_BAD( rc = m_pFileSystem->Open( m_pLogPath,
|
||||
F_IO_RDWR | F_IO_EXCL | F_IO_SH_DENYNONE,
|
||||
&pFileHdl)))
|
||||
{
|
||||
if( RC_BAD( rc = m_pFileSystem->Create( m_pLogPath,
|
||||
F_IO_RDWR | F_IO_EXCL | F_IO_SH_DENYNONE,
|
||||
&pFileHdl)))
|
||||
{
|
||||
goto Exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find EOF so text can be appended to the trace file.
|
||||
|
||||
if( pFileHdl)
|
||||
{
|
||||
if( RC_BAD( rc = pFileHdl->Size( &uiFileCursor)))
|
||||
{
|
||||
goto Exit;
|
||||
}
|
||||
}
|
||||
|
||||
// Lock the list
|
||||
|
||||
f_mutexLock( m_hRefListMutex);
|
||||
bListLocked = TRUE;
|
||||
bHeaderDisplayed = FALSE;
|
||||
|
||||
// Process all unreleased references
|
||||
|
||||
for( pTrackingRec = (TrackingRecord *) m_pListManager->GetItem( 0, 0);
|
||||
pTrackingRec;
|
||||
pTrackingRec = (TrackingRecord *) m_pListManager->GetItem( 0, 0))
|
||||
{
|
||||
void ** pStack;
|
||||
|
||||
uiCount++;
|
||||
if( !bHeaderDisplayed)
|
||||
{
|
||||
f_sprintf( pucBuffer, "Unreleased references of type [%s]\n",
|
||||
m_pszObjName);
|
||||
|
||||
if( RC_BAD( rc = logMessage( pucBuffer, pFileHdl, uiFileCursor)))
|
||||
{
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
bHeaderDisplayed = TRUE;
|
||||
}
|
||||
|
||||
if( RC_BAD( rc = logMessage( " ", pFileHdl, uiFileCursor)))
|
||||
{
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
f_sprintf( pucBuffer, " Unreleased reference (%X.%X) from thread: %X\n",
|
||||
(unsigned)((FLMUINT)pTrackingRec->getReferenceID()),
|
||||
(unsigned)((FLMUINT)pTrackingRec->getSubrefID()),
|
||||
(unsigned) pTrackingRec->getThreadID());
|
||||
|
||||
if( RC_BAD( rc = logMessage( pucBuffer, pFileHdl, uiFileCursor)))
|
||||
{
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
pStack = (void **) pTrackingRec->getStack();
|
||||
for( uiLoop = 0; pStack[ uiLoop]; uiLoop++ )
|
||||
{
|
||||
formatAddress( pucSymbol, sizeof( pucSymbol), pStack[ uiLoop]);
|
||||
f_sprintf( pucBuffer, " %-45.45s [addr = %8.8x]\n", pucSymbol,
|
||||
(unsigned)((FLMUINT)pStack[ uiLoop]));
|
||||
|
||||
if( RC_BAD( rc = logMessage( pucBuffer, pFileHdl, uiFileCursor)))
|
||||
{
|
||||
goto Exit;
|
||||
}
|
||||
}
|
||||
|
||||
m_pListManager->RemoveItem( 0, pTrackingRec);
|
||||
}
|
||||
|
||||
Exit:
|
||||
|
||||
if( bListLocked)
|
||||
{
|
||||
f_mutexUnlock( m_hRefListMutex);
|
||||
}
|
||||
|
||||
if( pFileHdl)
|
||||
{
|
||||
pFileHdl->Close();
|
||||
pFileHdl->Release();
|
||||
}
|
||||
|
||||
if( puiCount)
|
||||
{
|
||||
*puiCount = uiCount;
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Desc: Sets the address coonversion callback function
|
||||
****************************************************************************/
|
||||
void F_ObjRefTracker::setAddressFormatter(
|
||||
ADDR_FMT_HOOK pFunc,
|
||||
void * pvUserData)
|
||||
{
|
||||
m_pAddrFmtHook = pFunc;
|
||||
m_pUserData = pvUserData;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Desc: Converts a return address to displayable format
|
||||
****************************************************************************/
|
||||
void F_ObjRefTracker::formatAddress(
|
||||
char * pucBuffer,
|
||||
FLMUINT uiSize,
|
||||
void * pAddress)
|
||||
{
|
||||
#ifdef FLM_WIN
|
||||
PIMAGEHLP_SYMBOL pihs = NULL;
|
||||
#ifdef FLM_64BIT
|
||||
DWORD64 displacement;
|
||||
#else
|
||||
DWORD displacement;
|
||||
#endif
|
||||
RCODE rc = FERR_OK;
|
||||
#endif
|
||||
|
||||
if( m_pAddrFmtHook)
|
||||
{
|
||||
pucBuffer[ 0] = '\0';
|
||||
m_pAddrFmtHook( this, pAddress, (FLMBYTE *)pucBuffer, uiSize, m_pUserData);
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined( FLM_NLM)
|
||||
|
||||
if( uiSize == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
GetClosestSymbol( (BYTE *)pucBuffer, (LONG)pAddress);
|
||||
|
||||
return;
|
||||
|
||||
#elif defined( FLM_WIN)
|
||||
|
||||
if( RC_OK( rc = f_alloc( sizeof( IMAGEHLP_SYMBOL) + 100, &pihs)))
|
||||
{
|
||||
pihs->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL);
|
||||
pihs->Address = (FLMUINT)pAddress; //stackFrame.AddrPC.Offset;
|
||||
pihs->MaxNameLength = (FLMINT32)uiSize;
|
||||
|
||||
if ( SymGetSymFromAddr( GetCurrentProcess(), (FLMUINT)pAddress,
|
||||
&displacement, pihs ) )
|
||||
{
|
||||
wsprintf( pucBuffer, "%s + %X",
|
||||
(char *)pihs->Name, (unsigned)displacement);
|
||||
}
|
||||
else
|
||||
{
|
||||
wsprintf( pucBuffer, "0x%08X", (unsigned)((FLMUINT)pAddress));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
wsprintf( pucBuffer, "0x%08X", (unsigned)((FLMUINT)pAddress));
|
||||
}
|
||||
|
||||
f_free( &pihs);
|
||||
|
||||
#else
|
||||
#ifdef HAVE_DLADDR
|
||||
Dl_info dlip;
|
||||
if (dladdr(pAddress, &dlip) != 0 && dlip.dli_sname)
|
||||
{
|
||||
const char *filename;
|
||||
if (dlip.dli_saddr != pAddress)
|
||||
{
|
||||
filename = strrchr(dlip.dli_fname, '/');
|
||||
if (!filename)
|
||||
filename = dlip.dli_fname;
|
||||
else
|
||||
filename++; // skip over slash
|
||||
f_sprintf( pucBuffer, "0x%08x (%s)", (unsigned)((FLMUINT)pAddress),
|
||||
filename);
|
||||
}
|
||||
else
|
||||
f_sprintf( pucBuffer, "%s", dlip.dli_sname);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
f_sprintf( pucBuffer, "0x%08x", (unsigned)((FLMUINT)pAddress));
|
||||
#endif
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Desc: Walk the BP chain down the call stack, gathering return addresses.
|
||||
****************************************************************************/
|
||||
#if defined( FLM_WIN) && !defined( FLM_64BIT)
|
||||
void F_ObjRefTracker::getCallStack(
|
||||
void * stack[],
|
||||
FLMUINT uiCount,
|
||||
FLMUINT uiSkip)
|
||||
{
|
||||
STACKFRAME stackFrame;
|
||||
FLMINT32 ui32LastBP;
|
||||
FLMUINT uiLoop;
|
||||
|
||||
F_UNREFERENCED_PARM( uiSkip);
|
||||
ZeroMemory( (PVOID)&stackFrame, sizeof(STACKFRAME) );
|
||||
|
||||
// TDOMAN: do this in assembly since we aren't sure we can rely on the
|
||||
// GetThreadContext and StackWalk API's
|
||||
_asm
|
||||
{
|
||||
mov ui32LastBP, ebp // save off next bp
|
||||
}
|
||||
|
||||
// while you can continue walking the stack...
|
||||
for ( uiLoop = 0; uiLoop < uiCount; uiLoop++ )
|
||||
{
|
||||
// TDOMAN: we have to walk the stack ourselves since the VC4x API's
|
||||
// don't appear to be consistently reliable.
|
||||
__try
|
||||
{
|
||||
// TDOMAN: don't crash if the last bp wasn't want we expected
|
||||
_asm
|
||||
{
|
||||
push esi
|
||||
push edi
|
||||
mov edi, ui32LastBP
|
||||
mov esi, [edi]
|
||||
cmp esi, edi
|
||||
jbe Done
|
||||
mov ui32LastBP, esi // save off next bp
|
||||
mov esi, [edi + 4]
|
||||
mov stackFrame, esi // setup AddrPC
|
||||
pop edi
|
||||
pop esi
|
||||
}
|
||||
}
|
||||
__except (EXCEPTION_EXECUTE_HANDLER)
|
||||
{
|
||||
// If you do want to get these exceptions, you can turn off stack
|
||||
// walking by setting fStackWalk to FALSE.
|
||||
goto Done;
|
||||
}
|
||||
stack[ uiLoop] = (void *)(FLMUINT)stackFrame.AddrPC.Offset;
|
||||
}
|
||||
|
||||
Done:
|
||||
stack[ uiLoop] = (void *)0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
Desc:
|
||||
****************************************************************************/
|
||||
#if defined( FLM_UNIX) || defined( FLM_64BIT)
|
||||
void F_ObjRefTracker::getCallStack(
|
||||
void * stack[],
|
||||
FLMUINT, //uiCount,
|
||||
FLMUINT) //uiSkip)
|
||||
{
|
||||
// Not supported on this platform
|
||||
stack[0] = (void *)0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
****************************************************************************/
|
||||
#if defined( FLM_NLM)
|
||||
|
||||
void *DMGetEBP(void);
|
||||
|
||||
#if defined( __MWERKS__)
|
||||
|
||||
|
||||
void *DMGetEBP(void)
|
||||
{
|
||||
__asm
|
||||
{
|
||||
mov eax,[ebp]
|
||||
}
|
||||
} // end of assembly
|
||||
|
||||
#else
|
||||
|
||||
#pragma aux DMGetEBP = "mov eax,ebp";
|
||||
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
Desc:
|
||||
****************************************************************************/
|
||||
void * DMValueAtStackOffset(void *pos, int offset);
|
||||
|
||||
#if defined( __MWERKS__)
|
||||
|
||||
void *DMValueAtStackOffset(void *, int )
|
||||
{
|
||||
__asm
|
||||
{
|
||||
mov eax,[ebp+0x8]
|
||||
mov ebx,[ebp+0xC]
|
||||
mov eax,ss:[eax+ebx]
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#pragma aux DMValueAtStackOffset = "mov eax,ss:[eax+ebx]" parm [eax] [ebx];
|
||||
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
Desc: Traces back COUNT entries on the call stack, storing
|
||||
them in STACK. Note that this code requires that DS be build using the
|
||||
Watcom /of+ option, (or equivalent) to generate traceable stack frames by
|
||||
emitting prelude code for every function that looks something like that
|
||||
of the SubRoutine below:
|
||||
|
||||
Caller:
|
||||
push Parms ; caller pushes parameters
|
||||
call SubRoutine ; caller pushes his own return address
|
||||
add esp, parmSize ; caller clears parameters from stack
|
||||
...
|
||||
SubRoutine:
|
||||
push ebp ; pushes caller's frame pointer
|
||||
mov ebp, esp ; creates SubRoutine's frame pointer
|
||||
...
|
||||
pop ebp ; restores caller's frame pointer
|
||||
ret ; returns to caller
|
||||
|
||||
In this scheme, the MOV instruction in the prelude code always sets EBP
|
||||
pointing to the PUSHed value of the previous (caller's) frame pointer
|
||||
(see the first instruction in SubRoutine above). We discard the first
|
||||
'skipCount' + 1 return addresses because we aren't interested in the fact
|
||||
that the caller called DMAlloc, which called DMAllocFromTag,
|
||||
which called getCallStack.
|
||||
The stack trace loop terminates when it detects a return address that is
|
||||
outside of NDS code space (start and limit are stored in the load def
|
||||
struct - module handle). Some inline assembly is used to access the stack
|
||||
like data (see DMGetEBP and DMValueAtStackOffset above).
|
||||
****************************************************************************/
|
||||
void F_ObjRefTracker::getCallStack(
|
||||
void * stack[],
|
||||
FLMUINT uiCount,
|
||||
FLMUINT uiSkipCount)
|
||||
{
|
||||
FLMUINT uiLoop;
|
||||
void * rtnAddr;
|
||||
void * ebp = DMGetEBP();
|
||||
|
||||
while( uiSkipCount--)
|
||||
{
|
||||
ebp = DMValueAtStackOffset( ebp, 0);
|
||||
}
|
||||
rtnAddr = DMValueAtStackOffset( ebp, 4);
|
||||
|
||||
for( uiLoop = 0; --uiCount; )
|
||||
{
|
||||
void *oldebp;
|
||||
|
||||
stack[ uiLoop++] = rtnAddr;
|
||||
if( !ebp)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
oldebp = ebp;
|
||||
ebp = DMValueAtStackOffset( ebp, 0); // Caller's frame ptr
|
||||
|
||||
if ( !ebp || ebp <= oldebp || ebp > (void *)((char *)oldebp+3000))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
rtnAddr = DMValueAtStackOffset( ebp, 4); // Caller's return addr
|
||||
}
|
||||
stack[ uiLoop] = 0;
|
||||
return;
|
||||
}
|
||||
#endif // defined( FLM_NLM)
|
||||
|
||||
/****************************************************************************
|
||||
Desc: Log an error message
|
||||
****************************************************************************/
|
||||
void F_ObjRefTracker::logError(
|
||||
const char * pucMessage)
|
||||
{
|
||||
char pucBuffer[ 120];
|
||||
FLMUINT uiDummy = 0;
|
||||
|
||||
f_sprintf( pucBuffer, "Error: %s", pucMessage);
|
||||
logMessage( pucBuffer, NULL, uiDummy);
|
||||
|
||||
flmAssert(0);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Desc: Log a message to the trace file and to the DS trace screen
|
||||
****************************************************************************/
|
||||
RCODE F_ObjRefTracker::logMessage(
|
||||
const char * message,
|
||||
F_FileHdl * pFileHdl,
|
||||
FLMUINT & fileCursor)
|
||||
{
|
||||
RCODE rc = FERR_OK;
|
||||
FLMUINT uiBytesWritten;
|
||||
FLMBOOL bFileOpened = FALSE;
|
||||
const char * pCarriageReturn = "\n";
|
||||
|
||||
if( !pFileHdl && m_pFileSystem)
|
||||
{
|
||||
if( RC_BAD( rc = m_pFileSystem->Open(
|
||||
m_pLogPath, F_IO_RDWR | F_IO_SH_DENYNONE, &pFileHdl)))
|
||||
{
|
||||
if( RC_BAD( rc = m_pFileSystem->Create(
|
||||
m_pLogPath, F_IO_RDWR | F_IO_EXCL | F_IO_SH_DENYNONE,
|
||||
&pFileHdl)))
|
||||
{
|
||||
goto Exit;
|
||||
}
|
||||
}
|
||||
|
||||
bFileOpened = TRUE;
|
||||
flmAssert( pFileHdl);
|
||||
|
||||
//Find EOF so text can be appended to the trace file.
|
||||
if( RC_BAD( rc = pFileHdl->Size( &fileCursor)))
|
||||
{
|
||||
goto Exit;
|
||||
}
|
||||
}
|
||||
|
||||
if( pFileHdl)
|
||||
{
|
||||
if( RC_BAD( rc = pFileHdl->Write(
|
||||
fileCursor, (FLMUINT)f_strlen(message),
|
||||
(void *)message, &uiBytesWritten)))
|
||||
{
|
||||
goto Exit;
|
||||
}
|
||||
fileCursor += uiBytesWritten;
|
||||
|
||||
if( RC_BAD( rc = pFileHdl->Write(
|
||||
fileCursor, (FLMUINT)f_strlen(pCarriageReturn),
|
||||
(void *)pCarriageReturn, &uiBytesWritten)))
|
||||
{
|
||||
fileCursor += uiBytesWritten;
|
||||
}
|
||||
}
|
||||
|
||||
Exit:
|
||||
|
||||
if( bFileOpened)
|
||||
{
|
||||
pFileHdl->Close();
|
||||
pFileHdl->Release();
|
||||
}
|
||||
|
||||
return( rc);
|
||||
}
|
||||
Reference in New Issue
Block a user