479 lines
16 KiB
C++
479 lines
16 KiB
C++
//+---------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1997.
|
|
//
|
|
// File: T R A C E . H
|
|
//
|
|
// Contents: Class definition for CTracing
|
|
//
|
|
// Notes:
|
|
//
|
|
// Author: jeffspr 15 Apr 1997
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
#pragma once
|
|
|
|
#include "tracetag.h"
|
|
#include "stlalgor.h"
|
|
#include "stldeque.h"
|
|
#include "stlmap.h"
|
|
|
|
// ISSUE: The customized STL that we have conflicts with the declarations pulled in
|
|
// from stdexcpt.h which gets included by typeinfo.h. This sucks!
|
|
// Hence we can't include typeinfo.h to get RTTI's type_info structure,
|
|
// so we have to declare type_info ourselves. This is a well documented
|
|
// structure inside MSDN though.
|
|
class type_info {
|
|
public:
|
|
_CRTIMP virtual ~type_info();
|
|
_CRTIMP int operator==(const type_info& rhs) const;
|
|
_CRTIMP int operator!=(const type_info& rhs) const;
|
|
_CRTIMP int before(const type_info& rhs) const;
|
|
_CRTIMP const char* name() const;
|
|
_CRTIMP const char* raw_name() const;
|
|
private:
|
|
void *_m_data;
|
|
char _m_d_name[1];
|
|
type_info(const type_info& rhs);
|
|
type_info& operator=(const type_info& rhs);
|
|
};
|
|
|
|
#define TAKEOWNERSHIP
|
|
|
|
#ifdef ENABLETRACE
|
|
|
|
// This is needed for TraceHr, since we can't use a macro (vargs), but we
|
|
// need to get the file and line from the source.
|
|
#define FAL __FILE__,__LINE__,__FUNCTION__
|
|
|
|
// The Trace Stack functions
|
|
#if defined (_IA64_)
|
|
#include <ia64reg.h>
|
|
|
|
extern "C" unsigned __int64 __getReg(int whichReg);
|
|
extern "C" void __setReg(int whichReg, __int64 value);
|
|
#pragma intrinsic(__getReg)
|
|
#pragma intrinsic(__setReg)
|
|
|
|
#define GetR32 __getReg(CV_IA64_IntR32)
|
|
#define GetR33 __getReg(CV_IA64_IntR33)
|
|
#define GetR34 __getReg(CV_IA64_IntR34)
|
|
#endif // defined(_IA64_)
|
|
|
|
extern "C" void* _ReturnAddress(void);
|
|
#pragma intrinsic(_ReturnAddress)
|
|
|
|
extern LPCRITICAL_SECTION g_csTracing;
|
|
|
|
class CTracingIndent;
|
|
|
|
class CTracingFuncCall
|
|
{
|
|
public:
|
|
#if defined (_X86_) || defined (_AMD64_)
|
|
CTracingFuncCall(LPCSTR szFunctionName, LPCSTR szFunctionDName, LPCSTR szFile, const DWORD dwLine, const DWORD_PTR ReturnAddress, const DWORD_PTR dwFramePointer);
|
|
#elif defined (_IA64_)
|
|
CTracingFuncCall(LPCSTR szFunctionName, LPCSTR szFunctionDName, LPCSTR szFile, const DWORD dwLine, const DWORD_PTR ReturnAddress, const __int64 Args1, const __int64 Args2, const __int64 Args3);
|
|
#else
|
|
CTracingFuncCall(LPCSTR szFunctionName, LPCSTR szFunctionDName, LPCSTR szFile, const DWORD dwLine);
|
|
#endif
|
|
|
|
CTracingFuncCall(const CTracingFuncCall& TracingFuncCall);
|
|
~CTracingFuncCall();
|
|
|
|
public:
|
|
LPSTR m_szFunctionName;
|
|
LPSTR m_szFunctionDName;
|
|
LPSTR m_szFile;
|
|
DWORD_PTR m_ReturnAddress;
|
|
|
|
#if defined (_X86_) || defined (_AMD64_)
|
|
DWORD m_arguments[3];
|
|
#elif defined (_IA64_ )
|
|
__int64 m_arguments[3];
|
|
#else
|
|
// ... add other processors here
|
|
#endif
|
|
|
|
DWORD m_dwFramePointer;
|
|
DWORD m_dwThreadId;
|
|
DWORD m_dwLine;
|
|
|
|
friend CTracingIndent;
|
|
};
|
|
|
|
class CTracingThreadInfo
|
|
{
|
|
public:
|
|
CTracingThreadInfo();
|
|
~CTracingThreadInfo();
|
|
|
|
public:
|
|
LPVOID m_pfnStack;
|
|
DWORD m_dwLevel;
|
|
DWORD m_dwThreadId;
|
|
friend CTracingIndent;
|
|
};
|
|
|
|
class CTracingIndent
|
|
{
|
|
LPSTR m_szFunctionDName;
|
|
DWORD m_dwFramePointer;
|
|
BOOL bFirstTrace;
|
|
|
|
public:
|
|
#if defined (_X86_) || defined (_AMD64_)
|
|
void AddTrace(LPCSTR szFunctionName, LPCSTR szFunctionDName, LPCSTR szFile, const DWORD dwLine, LPCVOID pReturnAddress, const DWORD_PTR dwFramePointer);
|
|
#elif defined (_IA64_)
|
|
void AddTrace(LPCSTR szFunctionName, LPCSTR szFunctionDName, LPCSTR szFile, const DWORD dwLine, LPCVOID pReturnAddress, const __int64 Args1, const __int64 Args2, const __int64 Args3);
|
|
#else
|
|
void AddTrace(LPCSTR szFunctionName, LPCSTR szFunctionDName, LPCSTR szFile, const DWORD dwLine);
|
|
#endif
|
|
void RemoveTrace(LPCSTR szFunctionDName, const DWORD dwFramePointer);
|
|
|
|
CTracingIndent();
|
|
~CTracingIndent();
|
|
|
|
static CTracingThreadInfo* GetThreadInfo();
|
|
static DWORD getspaces();
|
|
static void TraceStackFn(TRACETAGID TraceTagId);
|
|
static void TraceStackFn(IN OUT LPSTR szString, IN OUT LPDWORD pdwSize);
|
|
};
|
|
|
|
|
|
#define IDENT_ADD2(x) indent ## x
|
|
#define IDENT_ADD(x) IDENT_ADD2(x)
|
|
#define __INDENT__ IDENT_ADD(__LINE__)
|
|
|
|
#define FP_ADD2(x) FP ## x
|
|
#define FP_ADD(x) FP_ADD2(x)
|
|
#define __FP__ FP_ADD(__LINE__)
|
|
|
|
#if defined (_X86_)
|
|
#define AddTraceLevel \
|
|
__if_not_exists(NetCfgFramePointer) \
|
|
{ \
|
|
DWORD NetCfgFramePointer; \
|
|
BOOL fForceC4715Check = TRUE; \
|
|
} \
|
|
if (fForceC4715Check) \
|
|
{ \
|
|
__asm { mov NetCfgFramePointer, ebp }; \
|
|
} \
|
|
__if_not_exists(NetCfgIndent) \
|
|
{ \
|
|
CTracingIndent NetCfgIndent; \
|
|
} \
|
|
NetCfgIndent.AddTrace(__FUNCTION__, __FUNCDNAME__, __FILE__, __LINE__, _ReturnAddress(), NetCfgFramePointer);
|
|
#elif defined (_IA64_)
|
|
#define AddTraceLevel \
|
|
__if_not_exists(NetCfgIndent) \
|
|
{ \
|
|
CTracingIndent NetCfgIndent; \
|
|
} \
|
|
NetCfgIndent.AddTrace(__FUNCTION__, __FUNCDNAME__, __FILE__, __LINE__, _ReturnAddress(), 0, 0, 0);
|
|
|
|
// NetCfgIndent.AddTrace(__FUNCTION__, __FUNCDNAME__, __FILE__, __LINE__, _ReturnAddress(), GetR32, GetR33, GetR34);
|
|
// ISSUE: GetR32, GetR33, GetR34 is inherently unsafe and can cause an STATUS_REG_NAT_CONSUMPTION exception
|
|
// if a register is being read that has the NAT bit set. Removing this for now until the compiler
|
|
// team provides a safe version of __getReg
|
|
#elif defined (_AMD64_)
|
|
#define AddTraceLevel \
|
|
__if_not_exists(NetCfgIndent) \
|
|
{ \
|
|
CTracingIndent NetCfgIndent; \
|
|
} \
|
|
NetCfgIndent.AddTrace(__FUNCTION__, __FUNCDNAME__, __FILE__, __LINE__, _ReturnAddress(), 0);
|
|
#else
|
|
#define AddTraceLevel \
|
|
__if_not_exists(NetCfgIndent) \
|
|
{ \
|
|
CTracingIndent NetCfgIndent; \
|
|
} \
|
|
NetCfgIndent.AddTrace(__FUNCTION__, __FUNCDNAME__, __FILE__, __LINE__);
|
|
#endif
|
|
|
|
// Trace error functions. The leaading _ is to establish the real function,
|
|
// while adding a new macro so we can add __FILE__ and __LINE__ to the output.
|
|
//
|
|
VOID WINAPI TraceErrorFn (PCSTR pszaFile, INT nLine, PCSTR psza, HRESULT hr);
|
|
VOID WINAPI TraceErrorOptionalFn (PCSTR pszaFile, INT nLine, PCSTR psza, HRESULT hr, BOOL fOpt);
|
|
VOID WINAPI TraceErrorSkipFn (PCSTR pszaFile, INT nLine, PCSTR psza, HRESULT hr, UINT c, ...);
|
|
VOID WINAPIV TraceLastWin32ErrorFn (PCSTR pszaFile, INT nLine, PCSTR psza);
|
|
|
|
#define TraceError(sz, hr) TraceErrorFn(__FILE__, __LINE__, sz, hr);
|
|
#define TraceErrorOptional(sz, hr, _bool) TraceErrorOptionalFn(__FILE__, __LINE__, sz, hr, _bool);
|
|
#define TraceErrorSkip1(sz, hr, hr1) TraceErrorSkipFn(__FILE__, __LINE__, sz, hr, 1, hr1);
|
|
#define TraceErrorSkip2(sz, hr, hr1, hr2) TraceErrorSkipFn(__FILE__, __LINE__, sz, hr, 2, hr1, hr2);
|
|
#define TraceErrorSkip3(sz, hr, hr1, hr2, hr3) TraceErrorSkipFn(__FILE__, __LINE__, sz, hr, 3, hr1, hr2, hr3);
|
|
#define TraceLastWin32Error(sz) TraceLastWin32ErrorFn(__FILE__,__LINE__, sz);
|
|
|
|
VOID
|
|
WINAPIV
|
|
TraceHrFn (
|
|
TRACETAGID ttid,
|
|
PCSTR pszaFile,
|
|
INT nLine,
|
|
HRESULT hr,
|
|
BOOL fIgnore,
|
|
PCSTR pszaFmt,
|
|
...);
|
|
|
|
VOID
|
|
WINAPIV
|
|
TraceHrFn (
|
|
TRACETAGID ttid,
|
|
PCSTR pszaFile,
|
|
INT nLine,
|
|
PCSTR pszaFunc,
|
|
HRESULT hr,
|
|
BOOL fIgnore,
|
|
PCSTR pszaFmt,
|
|
...);
|
|
|
|
VOID
|
|
WINAPIV
|
|
TraceTagFn (
|
|
TRACETAGID ttid,
|
|
PCSTR pszaFmt,
|
|
...);
|
|
|
|
VOID
|
|
WINAPIV
|
|
TraceFileFuncFn (
|
|
TRACETAGID ttid);
|
|
|
|
#define TraceFileFunc(ttidWhich) AddTraceLevel; TraceFileFuncFn(ttidWhich);
|
|
#define TraceStack(ttidWhich) AddTraceLevel; CTracingIndent::TraceStackFn(ttidWhich);
|
|
#define TraceStackToString(szString, nSize) AddTraceLevel; CTracingIndent::TraceStackFn(szString, nSize);
|
|
#define TraceHr AddTraceLevel; TraceHrFn
|
|
#define TraceTag AddTraceLevel; TraceTagFn
|
|
#define TraceException(hr, szExceptionName) TraceHr(ttidError, FAL, hr, FALSE, "A (%s) exception occurred", szExceptionName);
|
|
|
|
LPCSTR DbgEvents(DWORD Event);
|
|
LPCSTR DbgEventManager(DWORD EventManager);
|
|
LPCSTR DbgNcm(DWORD ncm);
|
|
LPCSTR DbgNcs(DWORD ncs);
|
|
LPCSTR DbgNccf(DWORD nccf);
|
|
LPCSTR DbgNcsm(DWORD ncsm);
|
|
|
|
#else // !ENABLETRACE
|
|
|
|
#define FAL (void)0
|
|
#define TraceError(_sz, _hr)
|
|
#define TraceErrorOptional(_sz, _hr, _bool)
|
|
#define TraceErrorSkip1(_sz, _hr, _hr1)
|
|
#define TraceErrorSkip2(_sz, _hr, _hr1, _hr2)
|
|
#define TraceErrorSkip3(_sz, _hr, _hr1, _hr2, _hr3)
|
|
#define TraceLastWin32Error(_sz)
|
|
#define TraceHr NOP_FUNCTION
|
|
#define TraceTag NOP_FUNCTION
|
|
#define TraceFileFunc(ttidWhich) NOP_FUNCTION
|
|
#define TraceException(hr, szExceptionName) NOP_FUNCTION
|
|
#define TraceStack(ttidWhich) NOP_FUNCTION
|
|
#define TraceStackToString(szString, nSize) NOP_FUNCTION
|
|
|
|
#define DbgEvents(Event) ""
|
|
#define DbgEventManager(EventManager) ""
|
|
#define DbgNcm(ncm) ""
|
|
#define DbgNcs(ncs) ""
|
|
#define DbgNccf(nccf) ""
|
|
#define DbgNcsm(nccf) ""
|
|
|
|
#endif // ENABLETRACE
|
|
|
|
#ifdef ENABLETRACE
|
|
|
|
//---[ Initialization stuff ]-------------------------------------------------
|
|
|
|
HRESULT HrInitTracing();
|
|
HRESULT HrUnInitTracing();
|
|
HRESULT HrOpenTraceUI(HWND hwndOwner);
|
|
|
|
#endif // ENABLETRACE
|
|
|
|
#define ENABLELEAKDETECTION
|
|
|
|
#if (defined(ENABLETRACE) && defined(ENABLELEAKDETECTION))
|
|
|
|
template <class T> class CNetCfgDebug; // Fwd template to make this friendly to CObjectLeakTrack
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// class: CObjectLeakTrack
|
|
//
|
|
// Purpose: Keep a list of all the CNetCfgDebug derived object instances and
|
|
// dump these out on request
|
|
//
|
|
// Author: deonb 7 July 2001
|
|
//
|
|
// Notes: The data types in here are of type LPVOID in order to minimize
|
|
// dependencies when including the trace header.
|
|
//
|
|
class CObjectLeakTrack
|
|
{
|
|
public:
|
|
CObjectLeakTrack();
|
|
~CObjectLeakTrack();
|
|
void DumpAllocatedObjects(IN TRACETAGID TraceTagId, IN LPCSTR szClassName);
|
|
BOOL AssertIfObjectsStillAllocated(IN LPCSTR szClassName);
|
|
|
|
protected:
|
|
// For these, pThis is really of type CNetCfgDebug<T> *. Since this should only ever be called
|
|
// from CNetCfgDebug or ISSUE_knownleak, we are ok with the lack of compile-time type checking.
|
|
void Insert(IN LPCVOID pThis, IN LPCSTR szdbgClassName, IN TAKEOWNERSHIP LPSTR pszConstructionStack);
|
|
void Remove(IN LPCVOID pThis);
|
|
|
|
friend class CNetCfgDebug;
|
|
friend void RemoveKnownleakFn(LPCVOID pThis);
|
|
|
|
protected:
|
|
LPVOID g_mapObjLeak; // This will actually be of type map<LPCVOID, pair<LPSTR, LPSTR> > ;
|
|
};
|
|
|
|
extern CObjectLeakTrack *g_pObjectLeakTrack; // The global list of NetConfig objects in the process.
|
|
// Call DumpAllocatedObjects on this to dump out the objects.
|
|
void RemoveKnownleakFn(LPCVOID pThis);
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// class: CNetCfgDebug
|
|
//
|
|
// Purpose: In order to debug instance leaks of your class instances, you can
|
|
// derive your class from CNetCfgDebug. This adds no data members nor
|
|
// a v-table nor virtual functions to your class. It will add
|
|
// a constructor and destructor that will be called before & after yours
|
|
// respectively, in order to keep track of your class instances.
|
|
//
|
|
// This will only happen on CHK builds. On FRE builds deriving from this
|
|
// class has no effect, and is safe.
|
|
//
|
|
// Author: deonb 7 July 2001
|
|
//
|
|
// Notes:
|
|
// This is s an ATL style parent template derive, e.g.:
|
|
// CNetCfgDbg<parent>
|
|
// e.g.
|
|
//
|
|
// class CConnectionManager : public ClassX,
|
|
// public classY,
|
|
// public CNetCfgBase<CConnectionManager>
|
|
//
|
|
// No other interaction with your class is needed. This will now automatically
|
|
// keep a list of all the instances allocated of this class (in debug mode),
|
|
// with a stack trace where they were allocated from. This is a Tracing
|
|
// stack trace so it's only successful for functions inside the callstack which
|
|
// actually used a TraceXxx statement (any trace statement) before they called
|
|
// the child functions.
|
|
//
|
|
// e.g.
|
|
// void X()
|
|
// {
|
|
// TraceFileFunc(ttidSomething);
|
|
// void Y()
|
|
// {
|
|
// void Z()
|
|
// {
|
|
// TraceTag(ttidSomething, "allocation class");
|
|
// CConnectionManager *pConnectionManager = new CConnectionManager();
|
|
// }
|
|
// TraceTag(ttidSomething, "Z called");
|
|
// }
|
|
// }
|
|
//
|
|
// This will spew the following when the process terminates (or when TraceAllocatedObjects is called):
|
|
// ASSERT: "An object leak has been detected. Please attach a user or kernel mode debugger and hit
|
|
// IGNORE to dump the offending stacks"
|
|
//
|
|
// The object of type 'class CConnectionManager' allocated at 0x7fe2345 has not been freed:
|
|
// it was constructed from the stack below:
|
|
// Z [EBP: 0x731d3128] 0x00000001 0x00000000 0x0000000a
|
|
// X [EBP: 0x731d310f] 0x0000000f 0x0000000e 0x0000000a
|
|
//
|
|
// (since Y() did not contain a trace statement before the call to Z() )
|
|
//
|
|
template <class T>
|
|
class CNetCfgDebug
|
|
{
|
|
public:
|
|
CNetCfgDebug()
|
|
{
|
|
if (FIsDebugFlagSet(dfidTrackObjectLeaks))
|
|
{
|
|
if (g_csTracing && g_pObjectLeakTrack)
|
|
{
|
|
EnterCriticalSection(g_csTracing);
|
|
|
|
DWORD dwConstructionStackSize = 16384;
|
|
LPSTR pszConstructionStack = new CHAR[dwConstructionStackSize];
|
|
if (pszConstructionStack)
|
|
{
|
|
TraceStackToString(pszConstructionStack, &dwConstructionStackSize);
|
|
|
|
if (dwConstructionStackSize < 16384)
|
|
{
|
|
// Reduce the amount of memory required
|
|
LPSTR szTemp = new CHAR[dwConstructionStackSize];
|
|
if (szTemp)
|
|
{
|
|
memcpy(szTemp, pszConstructionStack, dwConstructionStackSize);
|
|
delete[] pszConstructionStack;
|
|
|
|
pszConstructionStack = szTemp;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
}
|
|
}
|
|
|
|
TraceTag(ttidAllocations, "An object of type '%s' was allocated at '0x%08x'", typeid(T).name(), this);
|
|
g_pObjectLeakTrack->Insert(this, typeid(T).name(), pszConstructionStack); // ok to insert if pszConstructionStack is NULL.
|
|
|
|
LeaveCriticalSection(g_csTracing);
|
|
}
|
|
}
|
|
};
|
|
|
|
~CNetCfgDebug()
|
|
{
|
|
if (FIsDebugFlagSet(dfidTrackObjectLeaks))
|
|
{
|
|
if (g_csTracing && g_pObjectLeakTrack)
|
|
{
|
|
EnterCriticalSection(g_csTracing);
|
|
|
|
TraceTag(ttidAllocations, "An object of type '%s' was deleted at '0x%08x'", typeid(T).name(), this);
|
|
g_pObjectLeakTrack->Remove(this);
|
|
|
|
LeaveCriticalSection(g_csTracing);
|
|
}
|
|
}
|
|
};
|
|
};
|
|
|
|
#define ISSUE_knownleak(pThis) RemoveKnownleakFn(pThis);
|
|
|
|
#define TraceAllocatedObjects(ttidWhich, ClassName) g_pObjectLeakTrack->DumpAllocatedObjects(ttidWhich, typeid(ClassName).name());
|
|
#define AssertIfAllocatedObjects(ClassName) g_pObjectLeakTrack->AssertIfObjectsStillAllocated(typeid(ClassName).name());
|
|
|
|
#define TraceAllAllocatedObjects(ttidWhich) g_pObjectLeakTrack->DumpAllocatedObjects(ttidWhich, NULL);
|
|
#define AssertIfAnyAllocatedObjects() g_pObjectLeakTrack->AssertIfObjectsStillAllocated(NULL);
|
|
|
|
#else // ENABLETRACE && ENABLELEAKDETECTION
|
|
|
|
template <class T>
|
|
class CNetCfgDebug
|
|
{
|
|
};
|
|
|
|
#define ISSUE_knownleak(pThis) NOP_FUNCTION
|
|
#define TraceAllocatedObjects(ttidWhich, szClassName) NOP_FUNCTION
|
|
#define AssertNoAllocatedInstances(szClassName) NOP_FUNCTION
|
|
|
|
#endif // ENABLETRACE && ENABLELEAKDETECTION
|
|
|