windows-nt/Source/XPSP1/NT/ds/adsi/nocairo/otrack.cxx
2020-09-26 16:20:57 +08:00

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