579 lines
15 KiB
C++
579 lines
15 KiB
C++
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Microsoft Windows
|
||
|
// Copyright (C) Microsoft Corporation, 1992 - 1992.
|
||
|
//
|
||
|
// File: otrack.cxx
|
||
|
//
|
||
|
// Contents: Object tracking system
|
||
|
//
|
||
|
// Classes: ObjectTracker
|
||
|
//
|
||
|
// History: 06-Apr-92 MikeSe Created
|
||
|
// 30-Sep-93 KyleP DEVL obsolete
|
||
|
// 15-Jul-94 DonCl grabbed from common put in forms.
|
||
|
// absolutely minimal changes to
|
||
|
// get it working.
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
#include "dswarn.h"
|
||
|
#include <ADs.hxx>
|
||
|
|
||
|
#include "symtrans.h"
|
||
|
#include "otrackp.hxx"
|
||
|
|
||
|
// This is all unused in the retail build
|
||
|
#if DBG == 1
|
||
|
CRITICAL_SECTION g_csOT;
|
||
|
|
||
|
// this is a dummy item used as the head of the doubly-linked list
|
||
|
// of TrackLinks, and a mutex to protect it
|
||
|
static TrackLink tlHead = { &tlHead, &tlHead };
|
||
|
|
||
|
// this is a dummy item used as the head of the doubly-linked list
|
||
|
// of NameEntrys
|
||
|
static NameEntry neHead = { &neHead, &neHead };
|
||
|
|
||
|
static char * apszTypes[] = {"Create", "AddRef", "Release"};
|
||
|
|
||
|
// initialize class static data
|
||
|
int ObjectTracker::_TrackAll = GetProfileInt(L"Object Track",L"DEFAULT",1);
|
||
|
|
||
|
// standard debugging stuff
|
||
|
DECLARE_INFOLEVEL(Ot)
|
||
|
|
||
|
extern void RecordAction ( TrackLink *tl, FrameType ft, ULONG cRefCount );
|
||
|
extern void _cdecl EstablishHistory ( FrameRecord * fr, int nSkip );
|
||
|
extern void DumpHistory ( unsigned long fDebugMask, TrackLink *tl );
|
||
|
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: ObjectTracker::ObjectTracker, protected
|
||
|
//
|
||
|
// Synopsis: Contructor
|
||
|
//
|
||
|
// Effects: Allocates TrackLink structure and initialises it.
|
||
|
//
|
||
|
// Arguments: None
|
||
|
//
|
||
|
// History: 6-Apr-92 MikeSe Created
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
EXPORTIMP
|
||
|
ObjectTracker::ObjectTracker ()
|
||
|
:_ulRefs(1)
|
||
|
{
|
||
|
// Allocate the structure which maintains the tracking history.
|
||
|
// We also make space for the first FrameRecord.
|
||
|
HRESULT hr = MemAlloc ( sizeof(TrackLink) + sizeof(FrameRecord),
|
||
|
(void**)&_tl);
|
||
|
if ( FAILED(hr) )
|
||
|
{
|
||
|
OtDebugOut ((DEB_OT_ERRORS,
|
||
|
"Unable to establish tracking for %lx\n",
|
||
|
this ));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
_tl->potr = this;
|
||
|
_tl->ulSig = TRACK_LINK_SIGNATURE;
|
||
|
_tl->pszName = NULL;
|
||
|
|
||
|
FrameRecord * fr = (FrameRecord*) ( _tl + 1 );
|
||
|
if ( OtInfoLevel & DEB_OT_CALLERS )
|
||
|
{
|
||
|
EstablishHistory ( fr, 1 );
|
||
|
}
|
||
|
fr->ft = FT_CREATE;
|
||
|
fr->cRefCount = 1;
|
||
|
fr->frNext = NULL;
|
||
|
_tl->frFirst = _tl->frLast = fr;
|
||
|
|
||
|
// insert at end of list, with concurrency control
|
||
|
EnterCriticalSection(&g_csOT);
|
||
|
TrackLink * tll = tlHead.tlPrev;
|
||
|
tll->tlNext = tlHead.tlPrev = _tl;
|
||
|
_tl->tlNext = &tlHead;
|
||
|
_tl->tlPrev = tll;
|
||
|
LeaveCriticalSection(&g_csOT);
|
||
|
|
||
|
OtDebugOut ((DEB_OT_ACTIONS, "New object at %lx\n", this ));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: ObjectTracker::TrackClassName, protected
|
||
|
//
|
||
|
// Synopsis: Records class name for tracked object
|
||
|
//
|
||
|
// Arguments: [pszName] -- class name
|
||
|
//
|
||
|
// History: 6-Apr-92 MikeSe Created
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
EXPORTIMP void
|
||
|
ObjectTracker::TrackClassName (
|
||
|
char * pszName )
|
||
|
{
|
||
|
if ( _tl != NULL )
|
||
|
{
|
||
|
OtAssert (( _tl->ulSig == TRACK_LINK_SIGNATURE ));
|
||
|
|
||
|
// Copy the class name so we don't lose it if the class DLL is
|
||
|
// unloaded.
|
||
|
ULONG cBytes = strlen ( pszName ) + 1;
|
||
|
HRESULT hr = MemAlloc ( cBytes, (void**)&(_tl->pszName) );
|
||
|
if ( SUCCEEDED(hr) )
|
||
|
{
|
||
|
memcpy ( _tl->pszName, pszName, cBytes );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
OtDebugOut((DEB_OT_ERRORS,"Memory allocation failure %lx\n",hr));
|
||
|
_tl->pszName = "Name lost";
|
||
|
}
|
||
|
|
||
|
_tl->fTrack = IsClassTracking(pszName);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: ObjectTracker::StdAddRef, public
|
||
|
//
|
||
|
// Synopsis: Standard implementation of AddRef for tracked objects
|
||
|
//
|
||
|
// Effects: Increments ref count, records history
|
||
|
//
|
||
|
// Returns: S_OK
|
||
|
//
|
||
|
// Modifies: _ulRefs
|
||
|
//
|
||
|
// Derivation: Never
|
||
|
//
|
||
|
// History: 31-Jul-92 MikeSe Created
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
EXPORTIMP ULONG
|
||
|
ObjectTracker::StdAddRef()
|
||
|
{
|
||
|
InterlockedIncrement ( (LONG*)&_ulRefs );
|
||
|
|
||
|
if (_tl->fTrack)
|
||
|
{
|
||
|
OtDebugOut ((DEB_OT_ACTIONS, "AddRef [%d] of object at %lx (%s)\n",
|
||
|
_ulRefs, this, _tl->pszName ));
|
||
|
|
||
|
RecordAction ( _tl, FT_ADDREF, _ulRefs );
|
||
|
}
|
||
|
|
||
|
return _ulRefs;
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: ObjectTracker::StdRelease, public
|
||
|
//
|
||
|
// Synopsis: Helper function for standard implementation of Release()
|
||
|
//
|
||
|
// Effects: Decrements ref count, records history
|
||
|
//
|
||
|
// Returns: SUCCESS_NO_MORE iff ref count reached zero.
|
||
|
// Otherwise S_OK or an error.
|
||
|
//
|
||
|
// History: 31-Jul-92 MikeSe Created
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
EXPORTIMP ULONG
|
||
|
ObjectTracker::StdRelease ()
|
||
|
{
|
||
|
LONG lResult = InterlockedDecrement((LONG*)&_ulRefs);
|
||
|
|
||
|
if (_tl->fTrack)
|
||
|
{
|
||
|
OtDebugOut ((DEB_OT_ACTIONS, "Release [%d] of object at %lx (%s)\n",
|
||
|
_ulRefs, this, _tl->pszName ));
|
||
|
|
||
|
RecordAction ( _tl, FT_RELEASE, _ulRefs );
|
||
|
}
|
||
|
|
||
|
return (lResult==0)?0:_ulRefs;
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: ObjectTracker::~ObjectTracker, protected
|
||
|
//
|
||
|
// Synopsis: Destructor
|
||
|
//
|
||
|
// Effects: Remove this item, along with all history, from the
|
||
|
// tracking list
|
||
|
//
|
||
|
// History: 6-Apr-92 MikeSe Created
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
EXPORTIMP
|
||
|
ObjectTracker::~ObjectTracker()
|
||
|
{
|
||
|
if ( _tl != NULL )
|
||
|
{
|
||
|
OtDebugOut ((DEB_OT_ACTIONS, "Delete of object at %lx [%s]\n",
|
||
|
this, _tl->pszName ));
|
||
|
OtAssert (( _tl->ulSig == TRACK_LINK_SIGNATURE ));
|
||
|
// OtAssert ( _ulRefs == 0 );
|
||
|
|
||
|
// unlink, with concurrency control
|
||
|
EnterCriticalSection(&g_csOT);
|
||
|
TrackLink * tlp = _tl->tlPrev;
|
||
|
TrackLink * tln = _tl->tlNext;
|
||
|
tln->tlPrev = tlp;
|
||
|
tlp->tlNext = tln;
|
||
|
LeaveCriticalSection(&g_csOT);
|
||
|
|
||
|
if ((_tl->fTrack) && (OtInfoLevel & DEB_OT_DELETE))
|
||
|
{
|
||
|
DumpHistory ( DEB_OT_DELETE, _tl );
|
||
|
}
|
||
|
if (_tl->pszName) {
|
||
|
MemFree(_tl->pszName);
|
||
|
}
|
||
|
MemFree ( _tl );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: ObjectTracker::DumpTrackingInfo, public
|
||
|
//
|
||
|
// Synopsis: Dumps out the tracking list
|
||
|
//
|
||
|
// History: 6-Apr-92 MikeSe Created
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
EXPORTIMP void
|
||
|
ObjectTracker::DumpTrackingInfo (
|
||
|
int fDeleteNode)
|
||
|
{
|
||
|
TrackLink * tl = tlHead.tlNext;
|
||
|
BOOL fHeader = FALSE;
|
||
|
|
||
|
while ( tl != &tlHead )
|
||
|
{
|
||
|
// This is an unreleased item. Print out the history info
|
||
|
|
||
|
if ( !fHeader )
|
||
|
{
|
||
|
OtDebugOut((DEB_OT_OBJECTS, "****************************\n"));
|
||
|
OtDebugOut((DEB_OT_OBJECTS, "* Unreleased objects found *\n"));
|
||
|
OtDebugOut((DEB_OT_OBJECTS, "****************************\n"));
|
||
|
fHeader = TRUE;
|
||
|
}
|
||
|
|
||
|
OtDebugOut ((DEB_OT_OBJECTS,
|
||
|
"Object at %lx (%s)\n",
|
||
|
tl->potr,
|
||
|
tl->pszName ));
|
||
|
OtDebugOut ((DEB_OT_OBJECTS,
|
||
|
" Reference count = %d\n",
|
||
|
tl->potr->GetRefCount() ));
|
||
|
DumpHistory ( DEB_OT_CALLERS, tl );
|
||
|
|
||
|
if (fDeleteNode)
|
||
|
{
|
||
|
// unlink, with concurrency control
|
||
|
EnterCriticalSection(&g_csOT);
|
||
|
tl->potr->_tl = NULL;
|
||
|
TrackLink * tlp = tl->tlPrev;
|
||
|
TrackLink * tln = tl->tlNext;
|
||
|
tln->tlPrev = tlp;
|
||
|
tlp->tlNext = tln;
|
||
|
LeaveCriticalSection(&g_csOT);
|
||
|
MemFree ( tl );
|
||
|
tl = tln;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
tl = tl->tlNext;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// delete all the name entries
|
||
|
if (fDeleteNode)
|
||
|
{
|
||
|
EnterCriticalSection(&g_csOT);
|
||
|
|
||
|
// find the entry if there is one
|
||
|
NameEntry *ne = neHead.neNext;
|
||
|
while (ne != &neHead)
|
||
|
{
|
||
|
// unlink, with concurrency control
|
||
|
NameEntry * nep = ne->nePrev;
|
||
|
NameEntry * nen = ne->neNext;
|
||
|
nen->nePrev = nep;
|
||
|
nep->neNext = nen;
|
||
|
MemFree ( ne );
|
||
|
ne = nen;
|
||
|
}
|
||
|
LeaveCriticalSection(&g_csOT);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: TrackClass
|
||
|
//
|
||
|
// Synopsis: Tells the object tracker to start/stop tracking the specified
|
||
|
// class of objects.
|
||
|
//
|
||
|
// Arguments: [fTrack -- debug mask controlling the output
|
||
|
// [pszName] -- TrackLink record
|
||
|
//
|
||
|
// History: 14-Apr-93 Rickhi Created
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
EXPORTIMP
|
||
|
void ObjectTracker::TrackClass(int fTrack, char * pszClassName)
|
||
|
{
|
||
|
if (pszClassName == NULL)
|
||
|
{
|
||
|
// set default for ALL classes
|
||
|
_TrackAll = fTrack;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
|
||
|
// find the entry if there is one
|
||
|
NameEntry *ne = neHead.neNext;
|
||
|
|
||
|
while (ne != &neHead)
|
||
|
{
|
||
|
if (!strcmp(ne->pszName, pszClassName))
|
||
|
{
|
||
|
// found our entry, update the flag
|
||
|
ne->fTrack = fTrack;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
ne = ne->neNext;
|
||
|
}
|
||
|
|
||
|
// its not in the list
|
||
|
HRESULT hr = MemAlloc( sizeof(NameEntry), (void **)&ne);
|
||
|
if ( FAILED(hr) )
|
||
|
{
|
||
|
OtDebugOut((DEB_OT_ERRORS,
|
||
|
"Unable to record class for tracking %s\n",
|
||
|
pszClassName));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ne->pszName = pszClassName;
|
||
|
ne->fTrack = fTrack;
|
||
|
|
||
|
// insert at end of list, with concurrency control
|
||
|
EnterCriticalSection(&g_csOT);
|
||
|
NameEntry *neH = neHead.nePrev;
|
||
|
neH->neNext = neHead.nePrev = ne;
|
||
|
ne->neNext = &neHead;
|
||
|
ne->nePrev = ne;
|
||
|
LeaveCriticalSection(&g_csOT);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: IhrlassTracking, private
|
||
|
//
|
||
|
// Synopsis: returns TRUE if the object is currently tracked
|
||
|
//
|
||
|
// Arguments: [pszClassName] -- class name
|
||
|
//
|
||
|
// History: 14-Apr-93 Rickhi Created
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
EXPORTIMP
|
||
|
int ObjectTracker::IsClassTracking(char * pszClassName)
|
||
|
{
|
||
|
// find the entry if there is one
|
||
|
NameEntry *ne = neHead.neNext;
|
||
|
while (ne != &neHead)
|
||
|
{
|
||
|
if (!strcmp(ne->pszName, pszClassName))
|
||
|
{
|
||
|
return ne->fTrack;
|
||
|
}
|
||
|
ne = ne->neNext;
|
||
|
}
|
||
|
|
||
|
return GetProfileIntA("Object Track",pszClassName,_TrackAll);
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: DumpHistory
|
||
|
//
|
||
|
// Synopsis: Dumps the call history represented by a particular TrackLink
|
||
|
//
|
||
|
// Arguments: [fDebugMask] -- debug mask controlling the output
|
||
|
// [tl] -- TrackLink record
|
||
|
//
|
||
|
// History: 28-Jul-92 MikeSe Created
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
void DumpHistory ( unsigned long fDebugMask, TrackLink * tl )
|
||
|
{
|
||
|
// we can't call TranslateAddress without access to NT header files
|
||
|
#ifndef MSVC
|
||
|
#ifndef WIN95
|
||
|
//
|
||
|
// Only do all of this work if it will output anything!
|
||
|
//
|
||
|
if (OtInfoLevel & fDebugMask)
|
||
|
{
|
||
|
OtDebugOut ((fDebugMask, " Call history follows:\n" ));
|
||
|
FrameRecord * fr = tl->frFirst;
|
||
|
while ( fr != NULL )
|
||
|
{
|
||
|
char achBuffer[MAX_TRANSLATED_LEN];
|
||
|
|
||
|
OtDebugOut ((fDebugMask,
|
||
|
"\t%s [%d]\n",
|
||
|
apszTypes[fr->ft],
|
||
|
fr->cRefCount ));
|
||
|
|
||
|
for ( int I=0; (I < MAX_CALLERS) && (fr->callers[I]); I++ )
|
||
|
{
|
||
|
TranslateAddress ( fr->callers[I], achBuffer );
|
||
|
OtDebugOut ((fDebugMask, "\t %s\n", achBuffer));
|
||
|
}
|
||
|
|
||
|
fr = fr->frNext;
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: RecordAction
|
||
|
//
|
||
|
// Synopsis: Record an AddRef/Release
|
||
|
//
|
||
|
// Effects: Allocates and fills in a new frame record
|
||
|
//
|
||
|
// Arguments: [tl] -- TrackLink for object being tracked
|
||
|
// [ft] -- Frame type (FT_ADDREF, FT_RELEASE)
|
||
|
// [cRefCount] -- current ref count
|
||
|
//
|
||
|
// History: 6-Apr-92 MikeSe Created
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
void RecordAction ( TrackLink * tl, FrameType ft, ULONG cRefCount )
|
||
|
{
|
||
|
// Record the activity only if DEB_OT_CALLERS is set
|
||
|
|
||
|
if ( tl != NULL && (OtInfoLevel & DEB_OT_CALLERS))
|
||
|
{
|
||
|
OtAssert(tl->ulSig == TRACK_LINK_SIGNATURE );
|
||
|
FrameRecord * fr;
|
||
|
HRESULT hr;
|
||
|
|
||
|
hr = MemAlloc(sizeof(FrameRecord), (void **)&fr);
|
||
|
|
||
|
if ( FAILED(hr) )
|
||
|
{
|
||
|
OtDebugOut((DEB_OT_ERRORS,
|
||
|
"Unable to record history for %lx\n",
|
||
|
tl->potr));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Save call history
|
||
|
EstablishHistory ( fr, 2 );
|
||
|
fr->ft = ft;
|
||
|
fr->cRefCount = cRefCount;
|
||
|
fr->frNext = NULL;
|
||
|
|
||
|
// Add to list, with concurrency control
|
||
|
EnterCriticalSection(&g_csOT);
|
||
|
FrameRecord * frl = tl->frLast;
|
||
|
frl->frNext = tl->frLast = fr;
|
||
|
LeaveCriticalSection(&g_csOT);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: EstablishHistory
|
||
|
//
|
||
|
// Synopsis: Records calling history for an operation
|
||
|
//
|
||
|
// Effects: Walks back through call frames recording caller addresses.
|
||
|
//
|
||
|
// Arguments: [fr] -- FrameRecord in which to save history
|
||
|
// [nFramesSkip] -- number of frames to skip before
|
||
|
// recording.
|
||
|
//
|
||
|
// History: 6-Apr-92 MikeSe Created [from PaulC's imalloc code]
|
||
|
// 19-Apr-94 MikeSe Converted to use RtlCaptureStackBacktrace
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
void _cdecl
|
||
|
EstablishHistory (
|
||
|
FrameRecord * fr,
|
||
|
int nFramesSkip
|
||
|
)
|
||
|
{
|
||
|
|
||
|
#if (defined(i386) && !defined(WIN95))
|
||
|
|
||
|
memset ( fr->callers, 0, MAX_CALLERS * sizeof(void*) );
|
||
|
|
||
|
ULONG ulHash;
|
||
|
RtlCaptureStackBackTrace ( nFramesSkip, MAX_CALLERS,
|
||
|
fr->callers, &ulHash );
|
||
|
#endif // i386
|
||
|
}
|
||
|
|
||
|
#endif // DBG == 1
|