//+--------------------------------------------------------------------------- // // 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 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 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 *. 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 > ; }; 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 // e.g. // // class CConnectionManager : public ClassX, // public classY, // public CNetCfgBase // // 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 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 CNetCfgDebug { }; #define ISSUE_knownleak(pThis) NOP_FUNCTION #define TraceAllocatedObjects(ttidWhich, szClassName) NOP_FUNCTION #define AssertNoAllocatedInstances(szClassName) NOP_FUNCTION #endif // ENABLETRACE && ENABLELEAKDETECTION