Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

3391 lines
99 KiB

//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1997.
//
// File: T R A C E . C P P
//
// Contents: The actual tracing code (loading from ini, calling the
// trace routines, etc.
//
// Notes:
//
// Author: jeffspr 9 Apr 1997
//
//----------------------------------------------------------------------------
#include <pch.h>
#pragma hdrstop
#ifdef ENABLETRACE
#include <crtdbg.h>
#include "ncdebug.h"
#include "ncmisc.h"
#include <ntrtl.h>
#include <avrf.h>
DWORD g_dwTlsTracing = 0;
LPCRITICAL_SECTION g_csTracing = NULL;
#define MAX_TRACE_LEN 4096
EXTERN_C
IMAGE_DOS_HEADER __ImageBase;
//---[ CTracing class ]-------------------------------------------------------
//
// Don't give this class a constructor or destructor. We declare a global
// (static to this module) instance of this class and, by definition, static
// variables are automatically initialized to zero.
//
class CTracing
{
public:
CTracing();
~CTracing();
// Initialize/Deinitialize this class
//
private:
HRESULT HrInit();
HRESULT HrUnInit();
public:
VOID Trace( TraceTagId ttid,
PCSTR pszaTrace );
private:
BOOL m_fInitialized; // Has the object been initialized
BOOL m_fAttemptedLogFileOpen; // Already attempted to open log
BOOL m_fDisableLogFile; // Disable use of file logging?
UINT m_uiAllocOnWhichToBreak; // For _CrtSetBreakAlloc
HANDLE m_hLogFile; // Handle for debug output file
CHAR m_szLogFilePath[MAX_PATH+1]; // File for debug output
BOOL m_fDebugFlagsLoaded; // Have these been loaded yet.
VOID CorruptionCheck(); // Validate the tracetag structure
HRESULT HrLoadOptionsFromIniFile();
HRESULT HrLoadSectionsFromIniFile();
HRESULT HrLoadDebugFlagsFromIniFile();
HRESULT HrWriteDebugFlagsToIniFile();
HRESULT HrOpenLogFile();
HRESULT HrProcessTagSection(TraceTagElement * ptte);
HRESULT HrGetPrivateProfileString( PCSTR lpAppName,
PCSTR lpKeyName,
PCSTR lpDefault,
PSTR lpReturnedString,
DWORD nSize,
PCSTR lpFileName,
DWORD * pcchReturn );
HRESULT FIniFileInit(); // Returns S_OK if the file exist
};
//---[ Static Variables ]-----------------------------------------------------
#pragma warning(disable:4073) // warning about the following init_seg statement
#pragma init_seg(lib)
static CTracing g_Tracing; // Our global tracing object
//---[ Constants ]------------------------------------------------------------
static const WCHAR c_szDebugIniFileName[] = L"netcfg.ini"; // .INI file
CHAR c_szDebugIniFileNameA[MAX_PATH]; // .INI file
static const CHAR c_szTraceLogFileNameA[] = "nctrace.log"; // .LOG file
// constants for the INI file labels
static const CHAR c_szaOptions[] = "Options";
static const CHAR c_szaLogFilePath[] = "LogFilePath";
static const CHAR c_szaDisableLogFile[] = "DisableLogFile";
const INT c_iDefaultDisableLogFile = 0;
static CHAR c_szLowMemory[] = "<low on memory>";
//+---------------------------------------------------------------------------
//
// Function: HrInitTracing
//
// Purpose: Initialize the Tracing object and other random data.
//
// Arguments:
// bDisableFaultInjection [in] Disable App Verifier Fault injection for tracing
//
// Returns: S_OK or valid Win32 HRESULT
//
// Author: jeffspr 9 Apr 1997
//
// Notes:
//
HRESULT HrInitTracing(BOOL bDisableFaultInjection)
{
HMODULE hVrfLib;
VERIFIER_ENABLE_FAULT_INJECTION_EXCLUSION_RANGE_FUNCTION pfnVrfEnable;
ULONG Index;
if (bDisableFaultInjection)
{
if (NtCurrentPeb()->NtGlobalFlag & FLG_APPLICATION_VERIFIER)
{
hVrfLib = LoadLibrary (L"verifier.dll");
if (hVrfLib)
{
// If appverifier is not enabled on this process, the LoadLibrary will fail.
pfnVrfEnable = (VERIFIER_ENABLE_FAULT_INJECTION_EXCLUSION_RANGE_FUNCTION)GetProcAddress(hVrfLib, "VerifierEnableFaultInjectionExclusionRange");
if (pfnVrfEnable)
{
Index = pfnVrfEnable(HrInitTracing, HrUnInitTracing);
WCHAR szTmp[MAX_PATH * 2];
DWORD dwProcessId = GetCurrentProcessId();
WCHAR szModuleName[MAX_PATH];
ZeroMemory(szModuleName, MAX_PATH);
if (!GetModuleFileName(reinterpret_cast<HMODULE>(&__ImageBase), szModuleName, MAX_PATH))
{
if (!GetModuleFileName(reinterpret_cast<HMODULE>(::GetModuleHandle(NULL)), szModuleName, MAX_PATH))
{
wsprintfW(szModuleName, L"<unknown>");
}
}
if (0 == Index)
{
wsprintfW(szTmp, L"NETCFG: %s (%x) Tracing initialized. Problem disabling AVRF fault injection.\n", szModuleName, dwProcessId);
}
else
{
wsprintfW(szTmp, L"NETCFG: %s (%x) Tracing initialized. Disabled AVRF fault injection for tracing. Index = %x\n", szModuleName, dwProcessId, Index);
}
OutputDebugString(szTmp);
}
}
}
}
return S_OK;
}
extern HANDLE g_hHeap;
//+---------------------------------------------------------------------------
//
// Function: HrMallocNoFI
//
// Purpose: HRESULT returning version of malloc. This version is immune
// against AVRF fault injection due to the
// VerifierEnableFaultInjectionExclusionRange call in
// HrInitTracing above.
//
// Arguments:
// cb [in] Count of bytes to allocate.
// ppv [out] Address of returned allocation.
//
// Returns: S_OK or E_OUTOFMEMORY;
//
// Author: deonb 6/24/02
//
// Notes: Free the returned buffer with MemFree.
//
HRESULT HrMallocNoFI(size_t cb, PVOID* ppv) throw()
{
Assert(ppv);
*ppv = NULL;
if (!g_hHeap)
{
g_hHeap = GetProcessHeap();
if (!g_hHeap)
{
return E_UNEXPECTED;
}
}
*ppv = HeapAlloc (g_hHeap, 0, cb);
if (!*ppv)
{
return E_OUTOFMEMORY;
}
return S_OK;
}
class CNoFaultInject {} NO_FI;
class CThrowOnFail {} THROW_ON_FAIL;
NOTHROW VOID* __cdecl operator new (
size_t cb,
CNoFaultInject &NoFi
) throw()
{
LPVOID lpv = NULL;
HRESULT hr = HrMallocNoFI (cb, &lpv);
if (SUCCEEDED(hr))
{
return lpv;
}
else
{
return NULL;
}
}
NOTHROW VOID __cdecl operator delete (
void* pv,
CNoFaultInject &NoFi
) throw()
{
LPVOID lpv = NULL;
MemFree(pv);
}
VOID*
__cdecl
operator new (
size_t cb,
CNoFaultInject &NoFault,
CThrowOnFail &
)
{
LPVOID lpv = NULL;
HRESULT hr = HrMallocNoFI (cb, &lpv);
if (SUCCEEDED(hr))
{
return lpv;
}
else
{
throw std::bad_alloc();
}
}
namespace NOFAULT_ALLOC
{
template<class T> inline
T _FARQ *_NoFaultAllocate(_PDFT nCount, T _FARQ *)
{
if (nCount < 0)
{
nCount = 0;
}
// Call our throwing form of operator new. This will throw a bad_alloc on failure.
return ((T _FARQ *)operator new((_SIZT)nCount * sizeof (T), NO_FI, THROW_ON_FAIL));
}
// TEMPLATE FUNCTION _Construct
// See comments for _Allocate
template<class T1, class T2> inline
void _NoFaultConstruct(T1 _FARQ *p, const T2& v)
{
// Placement new only. No memory allocation, hence no need to throw.
new ((void _FARQ *)p) T1(v);
}
// TEMPLATE FUNCTION _Destroy
// See comments for _Allocate
template<class T>
inline void _NoFaultDestroy(T _FARQ *p)
{
(p)->~T(); // call the destructor
}
// FUNCTION _Destroy
// See comments for _Allocate
inline void _NoFaultDestroy(char _FARQ *p)
{
(void *)p;
}
// FUNCTION _Destroy
// See comments for _Allocate
inline void _NoFaultDestroy(wchar_t _FARQ *p)
{
(void *)p;
}
template<class T>
class nofault_allocator
{
public:
typedef _SIZT size_type;
typedef _PDFT difference_type;
typedef T _FARQ *pointer;
typedef const T _FARQ *const_pointer;
typedef T _FARQ& reference;
typedef const T _FARQ& const_reference;
typedef T value_type;
pointer address(reference x) const
{
return (&x);
}
const_pointer address(const_reference x) const
{
return (&x);
}
pointer allocate(size_type nCount, const void *)
{
return (_NoFaultAllocate((difference_type)nCount, (pointer)0));
}
char _FARQ *_Charalloc(size_type nCount)
{
return (_NoFaultAllocate((difference_type)nCount, (char _FARQ *)0));
}
void deallocate(void _FARQ *p, size_type)
{
operator delete(p);
}
void construct(pointer p, const T& v)
{
_NoFaultConstruct(p, v);
}
void destroy(pointer p)
{
_NoFaultDestroy(p);
}
_SIZT max_size() const
{
_SIZT nCount = (_SIZT)(-1) / sizeof (T);
return (0 < nCount ? nCount : 1);
}
};
template<class T, class U>
bool operator ==(const nofault_allocator<T>&, const nofault_allocator<U>&)
{
return (true);
}
}
//+---------------------------------------------------------------------------
//
// Function: HrUnInitTracing
//
// Purpose: Uninitialize the tracing object.
//
// Arguments:
// (none)
//
// Returns: S_OK or a valid Win32 HRESULT
//
// Author: jeffspr 14 Apr 1997
//
// Notes:
//
HRESULT HrUnInitTracing()
{
return S_OK;
}
const DWORD TI_HRESULT = 0x00000001;
const DWORD TI_WIN32 = 0x00000002;
const DWORD TI_IGNORABLE = 0x00000004;
//+---------------------------------------------------------------------------
//
// Function: TraceInternal
//
// Purpose: The one and only place that a string to be traced is formed
// and traced.
//
// Arguments:
//
// Returns: nothing.
//
// Author: shaunco 13 Mar 1998
//
// Notes: Restructured from lots of other code that was added to this
// module over the past year.
//
VOID
TraceInternal (
TRACETAGID ttid,
PCSTR pszaFile,
INT nLine,
DWORD dwFlags,
PCSTR pszaCallerText,
DWORD dwErrorCode,
BOOL bTraceStackOnError)
{
// If this tracetag is turned off, don't do anything.
//
if (!g_TraceTags[ttid].fOutputDebugString &&
!g_TraceTags[ttid].fOutputToFile)
{
return;
}
BOOL fError = dwFlags & (TI_HRESULT | TI_WIN32);
BOOL fIgnorable = dwFlags & TI_IGNORABLE;
HRESULT hr = (dwFlags & TI_HRESULT) ? dwErrorCode : S_OK;
DWORD dwWin32Error = (dwFlags & TI_WIN32) ? dwErrorCode : ERROR_SUCCESS;
// Ignore if told and we're not set to trace ignored errors or warnings.
//
if (fError && fIgnorable &&
!FIsDebugFlagSet (dfidShowIgnoredErrors) &&
!FIsDebugFlagSet (dfidExtremeTracing))
{
return;
}
// Don't do anything if we're tracing for an error and we don't have one,
// unless the "Extreme Tracing" flag is on, in which case we trace
// everything in the world (for debugger use only, really)
// This is the path taken by TraceError ("...", S_OK) or
// TraceLastWin32Error when there is no last Win32 error.
//
if (fError && !dwErrorCode && !FIsDebugFlagSet(dfidExtremeTracing))
{
return;
}
CHAR *pszaBuf = new(NO_FI) CHAR[MAX_TRACE_LEN * 2];
PSTR pcha = pszaBuf;
// Form the prefix, process id and thread id.
//
static const CHAR c_szaFmtPrefix [] = "NETCFG";
lstrcpyA (pcha, c_szaFmtPrefix);
pcha += lstrlenA (c_szaFmtPrefix);
// Add process and thread ids if the debug flags indicate to do so.
//
if (FIsDebugFlagSet (dfidShowProcessAndThreadIds))
{
static const CHAR c_szaFmtPidAndTid [] = " %03x.%03x";
pcha += _snprintf (pcha, MAX_TRACE_LEN, c_szaFmtPidAndTid,
GetCurrentProcessId (),
GetCurrentThreadId ());
}
// Add a time stamp if the debug flags indicate to do so.
//
if (FIsDebugFlagSet (dfidTracingTimeStamps))
{
static const CHAR c_szaFmtTime [] = " [%02dh%02d:%02d.%03d]";
SYSTEMTIME stLocal;
GetLocalTime (&stLocal);
pcha += _snprintf (pcha, MAX_TRACE_LEN, c_szaFmtTime,
stLocal.wHour,
stLocal.wMinute,
stLocal.wSecond,
stLocal.wMilliseconds);
}
// Add a severity indicator if this trace is for an error or warning.
//
if (fError || (ttidError == ttid))
{
static const CHAR c_szaSevIgnored [] = " Ignored:";
static const CHAR c_szaSevError [] = " *ERROR*:";
static const CHAR c_szaSevWarning [] = " Warning:";
PCSTR pszaSev = NULL;
if (fError && SUCCEEDED(hr) && !dwWin32Error && !fIgnorable)
{
pszaSev = c_szaSevWarning;
}
else
{
if (fIgnorable && FIsDebugFlagSet (dfidShowIgnoredErrors))
{
pszaSev = c_szaSevIgnored;
}
else
{
pszaSev = c_szaSevError;
}
}
Assert (pszaSev);
lstrcatA (pcha, pszaSev);
pcha += lstrlenA (pszaSev);
}
// Add the tracetag short name. Don't do this for ttidError if
// we already have the severity indicator from above.
//
if (ttid && (ttid < g_nTraceTagCount) && (ttid != ttidError))
{
if (FIsDebugFlagSet(dfidTraceMultiLevel))
{
static const CHAR c_szaFmtTraceTag [] = " (%-16s)";
pcha += _snprintf (pcha, MAX_TRACE_LEN, c_szaFmtTraceTag,
g_TraceTags[ttid].szShortName);
}
else
{
static const CHAR c_szaFmtTraceTag [] = " (%s)";
pcha += _snprintf (pcha, MAX_TRACE_LEN, c_szaFmtTraceTag,
g_TraceTags[ttid].szShortName);
}
*pcha = ' ';
pcha++;
if (FIsDebugFlagSet(dfidTraceMultiLevel))
{
// Add the indentation text.
DWORD dwNumSpaces = CTracingIndent::getspaces();
Assert(dwNumSpaces >= 2);
pcha += _snprintf(pcha, MAX_TRACE_LEN, "%1x", dwNumSpaces - 2);
memset(pcha, '-', dwNumSpaces-1 );
pcha += dwNumSpaces-1;
}
}
else
{
*pcha = ' ';
pcha++;
}
// Add the caller's text.
//
if (pszaCallerText)
{
static const CHAR c_szaFmtCallerText [] = "%s";
pcha += _snprintf (pcha, MAX_TRACE_LEN, c_szaFmtCallerText,
pszaCallerText);
Assert (pcha > pszaBuf);
if ('\n' == *(pcha-1))
{
pcha--;
*pcha = 0;
}
}
// Add descriptive error text if this is an error and we can get some.
//
if (FAILED(hr) || dwWin32Error)
{
BOOL fFacilityWin32 = (FACILITY_WIN32 == HRESULT_FACILITY(hr));
// dwError will be the error code we pass to FormatMessage. It may
// come from hr or dwWin32Error. Give preference to hr.
//
DWORD dwError = 0;
if (fFacilityWin32)
{
dwError = HRESULT_CODE(hr);
}
else if (FAILED(hr))
{
dwError = hr;
}
else
{
dwError = dwWin32Error;
}
Assert (dwError);
if (!FIsDebugFlagSet (dfidNoErrorText))
{
PSTR pszaErrorText = NULL;
FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM,
NULL, dwError,
MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
(PSTR)&pszaErrorText, 0, NULL);
if (pszaErrorText)
{
// Strip off newline characters.
//
PSTR pchText = pszaErrorText;
while (*pchText && (*pchText != '\r') && (*pchText != '\n'))
{
pchText++;
}
*pchText = 0;
// Add the error text.
//
static const CHAR c_szaFmtErrorText [] = " [%s]";
pcha += _snprintf (pcha, MAX_TRACE_LEN, c_szaFmtErrorText,
pszaErrorText);
LocalFree (pszaErrorText);
}
}
// Add the Win32 error code.
//
if (fFacilityWin32 || dwWin32Error)
{
static const CHAR c_szaFmtWin32Error [] = " Win32=%d,0x%08X";
pcha += _snprintf (pcha, MAX_TRACE_LEN, c_szaFmtWin32Error,
dwError, dwError);
}
}
// Add the HRESULT.
//
if (S_OK != hr)
{
static const CHAR c_szaFmtHresult [] = " hr=0x%08X";
pcha += _snprintf (pcha, MAX_TRACE_LEN, c_szaFmtHresult,
hr);
}
// Add the file and line.
//
if (pszaFile)
{
static const CHAR c_szaFmtFileAndLine [] = " File:%s,%d";
pcha += _snprintf (pcha, MAX_TRACE_LEN, c_szaFmtFileAndLine,
pszaFile, nLine);
}
// Add the newline.
//
lstrcatA (pcha, "\n");
g_Tracing.Trace (ttid, pszaBuf);
// Now that the message is on the debugger, break if we have an error
// and the debug flag to break on error is set.
//
if ((FAILED(hr) || dwWin32Error || (ttidError == ttid)) &&
!fIgnorable && FIsDebugFlagSet(dfidBreakOnError))
{
DebugBreak();
}
if ( (bTraceStackOnError) && FIsDebugFlagSet(dfidTraceCallStackOnError) && (ttid == ttidError) )
{
CTracingIndent::TraceStackFn(ttidError);
}
delete[] pszaBuf;
}
//+---------------------------------------------------------------------------
//
// Function: TraceInternal
//
// Purpose: The one and only place that a string to be traced is formed
// and traced.
//
// Arguments:
//
// Returns: nothing.
//
// Author: shaunco 13 Mar 1998
//
// Notes: Restructured from lots of other code that was added to this
// module over the past year.
//
VOID
TraceInternal (
TRACETAGID ttid,
PCSTR pszaFile,
INT nLine,
PCSTR pszaFunc,
DWORD dwFlags,
PCSTR pszaCallerText,
DWORD dwErrorCode,
BOOL bTraceStackOnError)
{
// If this tracetag is turned off, don't do anything.
//
if (!g_TraceTags[ttid].fOutputDebugString &&
!g_TraceTags[ttid].fOutputToFile)
{
return;
}
BOOL fError = dwFlags & (TI_HRESULT | TI_WIN32);
BOOL fIgnorable = dwFlags & TI_IGNORABLE;
HRESULT hr = (dwFlags & TI_HRESULT) ? dwErrorCode : S_OK;
DWORD dwWin32Error = (dwFlags & TI_WIN32) ? dwErrorCode : ERROR_SUCCESS;
// Ignore if told and we're not set to trace ignored errors or warnings.
//
if (fError && fIgnorable &&
!FIsDebugFlagSet (dfidShowIgnoredErrors) &&
!FIsDebugFlagSet (dfidExtremeTracing))
{
return;
}
// Don't do anything if we're tracing for an error and we don't have one,
// unless the "Extreme Tracing" flag is on, in which case we trace
// everything in the world (for debugger use only, really)
// This is the path taken by TraceError ("...", S_OK) or
// TraceLastWin32Error when there is no last Win32 error.
//
if (fError && !dwErrorCode && !FIsDebugFlagSet(dfidExtremeTracing))
{
return;
}
CHAR *pszaBuf = new(NO_FI) CHAR[MAX_TRACE_LEN * 2];
PSTR pcha = pszaBuf;
// Form the prefix, process id and thread id.
//
static const CHAR c_szaFmtPrefix [] = "NETCFG";
lstrcpyA (pcha, c_szaFmtPrefix);
pcha += lstrlenA (c_szaFmtPrefix);
// Add process and thread ids if the debug flags indicate to do so.
//
if (FIsDebugFlagSet (dfidShowProcessAndThreadIds))
{
static const CHAR c_szaFmtPidAndTid [] = " %03d.%03d";
pcha += _snprintf (pcha, MAX_TRACE_LEN, c_szaFmtPidAndTid,
GetCurrentProcessId (),
GetCurrentThreadId ());
}
// Add a time stamp if the debug flags indicate to do so.
//
if (FIsDebugFlagSet (dfidTracingTimeStamps))
{
static const CHAR c_szaFmtTime [] = " [%02d:%02d:%02d.%03d]";
SYSTEMTIME stLocal;
GetLocalTime (&stLocal);
pcha += _snprintf (pcha, MAX_TRACE_LEN, c_szaFmtTime,
stLocal.wHour,
stLocal.wMinute,
stLocal.wSecond,
stLocal.wMilliseconds);
}
// Add a severity indicator if this trace is for an error or warning.
//
if (fError || (ttidError == ttid))
{
static const CHAR c_szaSevIgnored [] = " Ignored:";
static const CHAR c_szaSevError [] = " *ERROR*:";
static const CHAR c_szaSevWarning [] = " Warning:";
PCSTR pszaSev = NULL;
if (fError && SUCCEEDED(hr) && !dwWin32Error && !fIgnorable)
{
pszaSev = c_szaSevWarning;
}
else
{
if (fIgnorable && FIsDebugFlagSet (dfidShowIgnoredErrors))
{
pszaSev = c_szaSevIgnored;
}
else
{
pszaSev = c_szaSevError;
}
}
Assert (pszaSev);
lstrcatA (pcha, pszaSev);
pcha += lstrlenA (pszaSev);
}
// Add the tracetag short name. Don't do this for ttidError if
// we already have the severity indicator from above.
//
if (ttid && (ttid < g_nTraceTagCount) && (ttid != ttidError))
{
static const CHAR c_szaFmtTraceTag [] = " (%s)";
pcha += _snprintf (pcha, MAX_TRACE_LEN, c_szaFmtTraceTag,
g_TraceTags[ttid].szShortName);
}
// Add the caller's text.
//
if (pszaCallerText)
{
static const CHAR c_szaFmtCallerText [] = " %s";
pcha += _snprintf (pcha, MAX_TRACE_LEN, c_szaFmtCallerText,
pszaCallerText);
Assert (pcha > pszaBuf);
if ('\n' == *(pcha-1))
{
pcha--;
*pcha = 0;
}
}
// Add descriptive error text if this is an error and we can get some.
//
if (FAILED(hr) || dwWin32Error)
{
BOOL fFacilityWin32 = (FACILITY_WIN32 == HRESULT_FACILITY(hr));
// dwError will be the error code we pass to FormatMessage. It may
// come from hr or dwWin32Error. Give preference to hr.
//
DWORD dwError = 0;
if (fFacilityWin32)
{
dwError = HRESULT_CODE(hr);
}
else if (FAILED(hr))
{
dwError = hr;
}
else
{
dwError = dwWin32Error;
}
Assert (dwError);
if (!FIsDebugFlagSet (dfidNoErrorText))
{
PSTR pszaErrorText = NULL;
FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM,
NULL, dwError,
MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
(PSTR)&pszaErrorText, 0, NULL);
if (pszaErrorText)
{
// Strip off newline characters.
//
PSTR pchText = pszaErrorText;
while (*pchText && (*pchText != '\r') && (*pchText != '\n'))
{
pchText++;
}
*pchText = 0;
// Add the error text.
//
static const CHAR c_szaFmtErrorText [] = " [%s]";
pcha += _snprintf (pcha, MAX_TRACE_LEN, c_szaFmtErrorText,
pszaErrorText);
LocalFree (pszaErrorText);
}
}
// Add the Win32 error code.
//
if (fFacilityWin32 || dwWin32Error)
{
static const CHAR c_szaFmtWin32Error [] = " Win32=%d,0x%08X";
pcha += _snprintf (pcha, MAX_TRACE_LEN, c_szaFmtWin32Error,
dwError, dwError);
}
}
// Add the HRESULT.
//
if (S_OK != hr)
{
static const CHAR c_szaFmtHresult [] = " hr=0x%08X";
pcha += _snprintf (pcha, MAX_TRACE_LEN, c_szaFmtHresult,
hr);
}
// Add the file and line.
//
if (pszaFile)
{
static const CHAR c_szaFmtFileAndLine [] = " File:%s,%d";
pcha += _snprintf (pcha, MAX_TRACE_LEN, c_szaFmtFileAndLine,
pszaFile, nLine);
}
// Add the function name
//
if (pszaFunc)
{
static const CHAR c_szaFmtFunc[] = ":";
pcha += _snprintf (pcha, MAX_TRACE_LEN, c_szaFmtFunc, pszaFunc);
}
// Add the newline.
//
lstrcatA (pcha, "\n");
g_Tracing.Trace (ttid, pszaBuf);
// Now that the message is on the debugger, break if we have an error
// and the debug flag to break on error is set.
//
if ((FAILED(hr) || dwWin32Error || (ttidError == ttid)) &&
!fIgnorable && FIsDebugFlagSet(dfidBreakOnError))
{
DebugBreak();
}
if ( (bTraceStackOnError) && FIsDebugFlagSet(dfidTraceCallStackOnError) && (ttid == ttidError) )
{
CTracingIndent::TraceStackFn(ttidError);
}
delete[] pszaBuf;
}
//+---------------------------------------------------------------------------
//
// Function: TraceErrorFn
//
// Purpose: Output debug trace of an HRESULT, allowing an additional
// caller-defined error string.
//
// Arguments:
// sz [] Caller-defined additional error text
// hr [] The error HRESULT.
//
// Returns:
//
// Author: jeffspr 14 Apr 1997
//
// Notes:
//
VOID
WINAPI
TraceErrorFn (
PCSTR pszaFile,
INT nLine,
PCSTR psza,
HRESULT hr)
{
DWORD dwFlags = TI_HRESULT;
if (S_FALSE == hr)
{
dwFlags |= TI_IGNORABLE;
}
TraceInternal (ttidError, pszaFile, nLine, dwFlags, psza, hr, TRUE);
}
//+---------------------------------------------------------------------------
//
// Function: TraceErrorOptionalFn
//
// Purpose: Implements TraceErrorOptional macro
//
// Arguments:
// pszaFile [in] __FILE__ value
// nLine [in] __LINE__ value
// psza [in] String to trace.
// hr [in] HRESULT value to trace.
// fOpt [in] TRUE if error should be treated as optional, FALSE if
// ERROR is not optional and should be reported thru
// TraceError().
//
// Returns: Nothing.
//
// Author: danielwe 12 May 1997
//
// Notes:
//
VOID
WINAPI
TraceErrorOptionalFn (
PCSTR pszaFile,
INT nLine,
PCSTR psza,
HRESULT hr,
BOOL fIgnorable)
{
DWORD dwFlags = TI_HRESULT;
if (fIgnorable)
{
dwFlags |= TI_IGNORABLE;
}
TraceInternal (ttidError, pszaFile, nLine, dwFlags, psza, hr, TRUE);
}
//+---------------------------------------------------------------------------
//
// Function: TraceErrorSkipFn
//
// Purpose: Implements TraceErrorOptional macro
//
// Arguments:
// pszaFile [in] __FILE__ value
// nLine [in] __LINE__ value
// psza [in] String to trace.
// hr [in] HRESULT value to trace.
// c [in] count of pass-through Hresults. if hr is any of these
// the error is treated as optional.
// ... [in] list of hresults.
//
// Returns: Nothing.
//
// Author: sumitc 08 Jan 1998
//
// Notes:
//
VOID WINAPI
TraceErrorSkipFn (
PCSTR pszaFile,
INT nLine,
PCSTR psza,
HRESULT hr,
UINT c, ...)
{
va_list valMarker;
BOOL fIgnorable = FALSE;
va_start(valMarker, c);
for (UINT i = 0; i < c; ++i)
{
fIgnorable = (va_arg(valMarker, HRESULT) == hr);
if (fIgnorable)
{
break;
}
}
va_end(valMarker);
DWORD dwFlags = TI_HRESULT;
if (fIgnorable)
{
dwFlags |= TI_IGNORABLE;
}
TraceInternal (ttidError, pszaFile, nLine, dwFlags, psza, hr, TRUE);
}
//+---------------------------------------------------------------------------
//
// Function: TraceLastWin32ErrorFn
//
// Purpose: Trace the last Win32 error, which we get with GetLastError().
// Not a whole lot to it.
//
// Arguments:
// sz [] Additional error text.
//
// Returns:
//
// Author: jeffspr 14 Apr 1997
//
// Notes:
//
VOID
WINAPIV
TraceLastWin32ErrorFn (
PCSTR pszaFile,
INT nLine,
PCSTR psza)
{
TraceInternal (ttidError, pszaFile, nLine, TI_WIN32, psza, GetLastError(), TRUE);
}
//+---------------------------------------------------------------------------
//
// Function: TraceHrFn
//
// Purpose: Generic replacement for the TraceErrorOptional, TraceError,
// and a couple other random functions.
//
// Arguments:
// ttid [] TraceTag to use for the debug output
// pszaFile [] Source file to log
// nLine [] Line number to log
// hr [] Error to log
// fIgnorable [] Ignore this error? (The optional bit)
// pszaFmt [] Format of the vargs
//
// Returns:
//
// Author: jeffspr 10 Oct 1997
//
// Notes:
//
VOID
WINAPIV
TraceHrFn (
TRACETAGID ttid,
PCSTR pszaFile,
INT nLine,
HRESULT hr,
BOOL fIgnorable,
PCSTR pszaFmt,
...)
{
// If this tracetag is turned off, don't do anything.
//
if (!g_TraceTags[ttid].fOutputDebugString &&
!g_TraceTags[ttid].fOutputToFile)
{
return;
}
CHAR *pszaBuf = new(NO_FI) CHAR[MAX_TRACE_LEN];
// Build the string from the varg list
//
va_list valMarker;
va_start (valMarker, pszaFmt);
vsprintf (pszaBuf, pszaFmt, valMarker);
va_end (valMarker);
DWORD dwFlags = TI_HRESULT;
if (fIgnorable)
{
dwFlags |= TI_IGNORABLE;
}
TraceInternal (ttid, pszaFile, nLine, dwFlags, pszaBuf, hr, TRUE);
delete[] pszaBuf;
}
//+---------------------------------------------------------------------------
//
// Function: TraceHrFn
//
// Purpose: Generic replacement for the TraceErrorOptional, TraceError,
// and a couple other random functions.
//
// Arguments:
// ttid [] TraceTag to use for the debug output
// pszaFile [] Source file to log
// nLine [] Line number to log
// hr [] Error to log
// fIgnorable [] Ignore this error? (The optional bit)
// pszaFmt [] Format of the vargs
//
// Returns:
//
// Author: jeffspr 10 Oct 1997
//
// Notes:
//
VOID
WINAPIV
TraceHrFn (
TRACETAGID ttid,
PCSTR pszaFile,
INT nLine,
PCSTR pszaFunc,
HRESULT hr,
BOOL fIgnorable,
PCSTR pszaFmt,
...)
{
// If this tracetag is turned off, don't do anything.
//
if (!g_TraceTags[ttid].fOutputDebugString &&
!g_TraceTags[ttid].fOutputToFile)
{
return;
}
CHAR *pszaBuf = new(NO_FI) CHAR[MAX_TRACE_LEN];
// Build the string from the varg list
//
va_list valMarker;
va_start (valMarker, pszaFmt);
_vsnprintf (pszaBuf, MAX_TRACE_LEN, pszaFmt, valMarker);
va_end (valMarker);
DWORD dwFlags = TI_HRESULT;
if (fIgnorable)
{
dwFlags |= TI_IGNORABLE;
}
TraceInternal (ttid, pszaFile, nLine, pszaFunc, dwFlags, pszaBuf, hr, TRUE);
delete[] pszaBuf;
}
//+---------------------------------------------------------------------------
//
// Function: TraceTagFn
//
// Purpose: Output a debug trace to one or more trace targets (ODS,
// File, COM port, etc.). This function determines the targets
// and performs the actual trace.
//
// Arguments:
// ttid [] TraceTag to use for the debug output
// pszaFmt [] Format of the vargs.
//
// Returns:
//
// Author: jeffspr 14 Apr 1997
//
// Notes:
//
VOID
WINAPIV
TraceTagFn (
TRACETAGID ttid,
PCSTR pszaFmt,
...)
{
// If this tracetag is turned off, don't do anything.
//
if (!g_TraceTags[ttid].fOutputDebugString &&
!g_TraceTags[ttid].fOutputToFile)
{
return;
}
CHAR *pszaBuf = new(NO_FI) CHAR[MAX_TRACE_LEN];
// Build the string from the varg list
//
va_list valMarker;
va_start (valMarker, pszaFmt);
_vsnprintf (pszaBuf, MAX_TRACE_LEN, pszaFmt, valMarker);
va_end (valMarker);
TraceInternal (ttid, NULL, 0, 0, pszaBuf, 0, TRUE);
delete[] pszaBuf;
}
//+---------------------------------------------------------------------------
//
// Member: CTracing::CTracing
//
// Purpose: Constructor for CTracing. Initialize all vars.
//
// Arguments:
// (none)
//
// Returns:
//
// Author: jeffspr 23 Jan 1999
//
// Notes:
//
CTracing::CTracing()
{
m_fInitialized = FALSE; // Has the object been initialized
m_fAttemptedLogFileOpen = FALSE; // Already attempted to open log
m_fDisableLogFile = FALSE; // Disable use of file logging?
m_uiAllocOnWhichToBreak = 0; // For _CrtSetBreakAlloc
m_hLogFile = NULL; // Handle for debug output file
m_szLogFilePath[0] = '\0'; // File for debug output
m_fDebugFlagsLoaded = FALSE; // Have these been loaded yet.
g_dwTlsTracing = NULL;
HrInit();
}
CTracing::~CTracing()
{
HrUnInit();
}
//+---------------------------------------------------------------------------
//
// Member: CTracing::HrInit
//
// Purpose: Initialize the CTracing object.
//
// Arguments:
// (none)
//
// Returns: S_OK or valid Win32 HRESULT
//
// Author: jeffspr 9 Apr 1997
//
// Notes: This should get called from some standard exe initialization
// point. And make sure to call HrDeinit when you're done, eh?
//
HRESULT CTracing::HrInit()
{
HRESULT hr = S_OK;
AssertSz(!m_fInitialized,
"CTracing::HrInit -- Let's not go overboard. Already initialized");
g_csTracing = new(NO_FI) CRITICAL_SECTION;
Assert(g_csTracing);
InitializeCriticalSection(g_csTracing);
g_dwTlsTracing = TlsAlloc();
AssertSz(g_dwTlsTracing, "g_dwTlsTracing could not aquire a TLS slot");
hr = FIniFileInit();
if (FAILED(hr))
{
goto Exit;
}
// Temporarily set this so the called functions don't believe that we're
// uninitialized. At Exit, if we fail, we'll set it back so no-one tries
// to call these functions when uninitialized.
//
m_fInitialized = TRUE;
// Check for corruptions in the tracing structure. This can't fail, but
// it will send up asserts all over the place if something is amiss.
//
CorruptionCheck();
// Load the "options" section from the ini file
//
hr = HrLoadOptionsFromIniFile();
if (FAILED(hr))
{
goto Exit;
}
// Load the DebugFlags section from the ini file.
//
hr = HrLoadDebugFlagsFromIniFile();
if (FAILED(hr))
{
goto Exit;
}
// Load the tracetag sections from the ini file.
// Make sure this is called after HrLoadDebugFlagsFromIniFile(),
// as those options will affect the tracetag sections (we also
// assert on this)
//
hr = HrLoadSectionsFromIniFile();
if (FAILED(hr))
{
goto Exit;
}
// If certain tracetags are on, we want others to be off because some
// encompase the functionality of others.
//
if (g_TraceTags[ttidBeDiag].fOutputDebugString)
{
g_TraceTags[ttidNetCfgPnp].fOutputDebugString = FALSE;
}
#ifdef ENABLELEAKDETECTION
if (FIsDebugFlagSet(dfidTrackObjectLeaks))
{
g_pObjectLeakTrack = new(NO_FI) CObjectLeakTrack;
Assert(g_pObjectLeakTrack);
}
#endif
Exit:
if (FAILED(hr))
{
m_fInitialized = FALSE;
}
return hr;
}
//+---------------------------------------------------------------------------
//
// Member: CTracing::HrUnInit
//
// Purpose: Uninitialize the Tracing object
//
// Arguments:
// (none)
//
// Returns: S_OK or valid Win32 HRESULT
//
// Author: jeffspr 12 Apr 1997
//
// Notes:
//
HRESULT CTracing::HrUnInit()
{
HRESULT hr = S_OK;
#ifdef ENABLELEAKDETECTION
if (FIsDebugFlagSet(dfidTrackObjectLeaks))
{
BOOL fAsserted = g_pObjectLeakTrack->AssertIfObjectsStillAllocated(NULL);
if (fAsserted)
{
// AssertSz(FALSE, "Spew is completed - press RETRY to look at spew and map ReturnAddr values to symbols");
}
delete g_pObjectLeakTrack;
}
#endif
if (g_dwTlsTracing)
{
TlsFree(g_dwTlsTracing);
g_dwTlsTracing = 0;
}
if (g_csTracing)
{
{
__try
{
EnterCriticalSection(g_csTracing);
}
__finally
{
LeaveCriticalSection(g_csTracing);
}
}
DeleteCriticalSection(g_csTracing);
delete g_csTracing;
g_csTracing = NULL;
}
// Don't assert on m_fInitialized here, because we allow this to
// be called even if initialization failed.
//
if (m_fInitialized)
{
hr = HrWriteDebugFlagsToIniFile();
if (FAILED(hr))
{
// continue on, but I want to know why this is failing.
AssertSz(FALSE, "Whoa, why can't we write the debug flags?");
}
// Close the log file, if there's one open
//
if (m_hLogFile)
{
CloseHandle(m_hLogFile);
m_hLogFile = NULL;
}
// Mark us as being uninitialized.
//
m_fInitialized = FALSE;
}
return hr;
}
//+---------------------------------------------------------------------------
//
// Member: CTracing::HrGetPrivateProfileString
//
// Purpose:
//
// Arguments:
// lpAppName [] points to section name
// lpKeyName [] points to key name
// lpDefault [] points to default string
// lpReturnedString [] points to destination buffer
// nSize [] size of destination buffer
// lpFileName [] points to initialization filename
// pcchReturn return buffer for the old Win32 API return code
//
//
// Returns: S_OK or valid Win32 HRESULT
//
// Author: jeffspr 12 Apr 1997
//
// Notes:
//
HRESULT CTracing::HrGetPrivateProfileString( PCSTR lpAppName,
PCSTR lpKeyName,
PCSTR lpDefault,
PSTR lpReturnedString,
DWORD nSize,
PCSTR lpFileName,
DWORD* pcchReturn
)
{
HRESULT hr = S_OK;
Assert(m_fInitialized);
// Assert on the known conditions required for this API call
//
Assert(lpDefault);
Assert(lpFileName);
// Call the Win32 API
//
DWORD dwGPPSResult = GetPrivateProfileStringA(
lpAppName,
lpKeyName,
lpDefault,
lpReturnedString,
nSize,
lpFileName);
// Check to see if we've gotten a string-size error
if (lpAppName && lpKeyName)
{
// If we get back (nSize - 1), then our string buffer wasn't
// large enough
//
if (dwGPPSResult == (nSize - 1))
{
hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
goto Exit;
}
}
else
{
// Since either of the app name or key name are NULL, then
// we're supposed to be receiving a doubly-NULL terminated
// list of strings. If we're at (nSize - 2), that means
// our buffer was too small.
//
if (dwGPPSResult == (nSize - 2))
{
hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
goto Exit;
}
}
Exit:
*pcchReturn = dwGPPSResult;
return hr;
}
//+---------------------------------------------------------------------------
//
// Member: CTracing::HrLoadOptionsFromIniFile
//
// Purpose: Load the options section from the ini file, and set our
// state accordingly
//
// Arguments:
// (none)
//
// Returns: S_OK or valid Win32 HRESULT
//
// Author: jeffspr 10 Apr 1997
//
// Notes:
//
HRESULT CTracing::HrLoadOptionsFromIniFile()
{
HRESULT hr = S_OK;
DWORD cchReturnBufferSize = 0;
WCHAR szLogFilePath[MAX_PATH+1] = { 0 };
DWORD dwTempPathLength = 0;
// Get the explicit log file path, if any. If it doesn't exist, then
// use the default path, which is the temp file path plus the default
// trace file name
//
// Get the location of the "temporary files" path
dwTempPathLength = GetTempPath(MAX_PATH, szLogFilePath);
if ((dwTempPathLength == 0) ||
(dwTempPathLength > MAX_PATH))
{
TraceLastWin32Error("GetTempPath failure");
hr = HrFromLastWin32Error();
goto Exit;
}
// Tack the log file name onto the end.
//
_snprintf(m_szLogFilePath, MAX_TRACE_LEN, "%s%s", szLogFilePath, c_szTraceLogFileNameA);
// This will overwrite the log file path if one exists in the INI file
//
hr = HrGetPrivateProfileString(
c_szaOptions, // "Options"
c_szaLogFilePath, // "LogFilePath
m_szLogFilePath, // Default string, already filled
m_szLogFilePath, // Return string (same string)
MAX_PATH+1,
c_szDebugIniFileNameA,
&cchReturnBufferSize);
if (FAILED(hr))
{
// This should not cause problems with recursive failure, since
// Traces will work regardless of the state of trace initialization.
//
TraceError(
"GetPrivateProfileString failed on Options::LogFilePath", hr);
goto Exit;
}
// Get the "disable log file option". No return code here.
m_fDisableLogFile = GetPrivateProfileIntA(
c_szaOptions, // "Options"
c_szaDisableLogFile, // "DisableLogFile"
c_iDefaultDisableLogFile,
c_szDebugIniFileNameA);
if (FAILED(hr))
{
TraceError(
"GetPrivateProfileInt failed on Options::DisableLogFile", hr);
goto Exit;
}
Exit:
return hr;
}
//+---------------------------------------------------------------------------
//
// Member: CTracing::HrLoadSectionsFromIniFile
//
// Purpose: Load the individual tracetag sections from the ini file, and
// set our array elements accordingly, defaulting if necessary.
//
// Arguments:
// (none)
//
// Returns: S_OK or valid Win32 HRESULT
//
// Author: jeffspr 10 Apr 1997
//
// Notes:
//
HRESULT CTracing::HrLoadSectionsFromIniFile()
{
HRESULT hr = S_OK;
// Make sure that we've loaded the debug flags first, as they can
// affect each tracetag section
//
Assert(m_fDebugFlagsLoaded);
// Loop through the array and load the data.
//
for (INT nLoop = 0; nLoop < g_nTraceTagCount; nLoop++ )
{
// Process the individual lines from the section
hr = HrProcessTagSection(&(g_TraceTags[nLoop]));
if (FAILED(hr))
{
break;
}
}
return hr;
}
//+---------------------------------------------------------------------------
//
// Member: CTracing::HrLoadDebugFlagsFromIniFile
//
// Purpose: Load the individual debugflag values from the ini file, and
// set our array elements accordingly, defaulting if necessary.
//
// Arguments:
// (none)
//
// Returns: S_OK or valid Win32 HRESULT
//
// Author: jeffspr 10 Apr 1997
//
// Notes:
//
HRESULT CTracing::HrLoadDebugFlagsFromIniFile()
{
HRESULT hr = S_OK;
INT nLoop;
// Loop through the array and load the data.
//
for (nLoop = 0; nLoop < g_nDebugFlagCount; nLoop++)
{
switch(nLoop)
{
case dfidBreakOnAlloc:
// Get the "break on alloc" alloc count.
//
m_uiAllocOnWhichToBreak = GetPrivateProfileIntA(
"DebugFlags",
g_DebugFlags[nLoop].szShortName,
FALSE,
c_szDebugIniFileNameA);
g_DebugFlags[nLoop].dwValue = (m_uiAllocOnWhichToBreak > 0);
// If there was a value set, set the break..
//
if (m_uiAllocOnWhichToBreak != 0)
_CrtSetBreakAlloc(m_uiAllocOnWhichToBreak);
break;
default:
// Get the enabled file param
//
g_DebugFlags[nLoop].dwValue = GetPrivateProfileIntA(
"DebugFlags",
g_DebugFlags[nLoop].szShortName,
FALSE,
c_szDebugIniFileNameA);
break;
}
}
if (SUCCEEDED(hr))
{
m_fDebugFlagsLoaded = TRUE;
}
return hr;
}
HRESULT CTracing::FIniFileInit()
{
HRESULT hr = E_FAIL;
WCHAR szWindowsPath[MAX_PATH+1] = L"";
WCHAR szPath[MAX_PATH+1] = L"";
UINT uiCharsReturned = 0;
HANDLE hFile = INVALID_HANDLE_VALUE;
uiCharsReturned = GetWindowsDirectory(szWindowsPath, MAX_PATH);
if ((uiCharsReturned == 0) || (uiCharsReturned > MAX_PATH))
{
AssertSz(FALSE, "GetWindowsDirectory failed in CTracing::FIniFileInit");
hr = E_UNEXPECTED;
goto Exit;
}
wcscpy (szPath, szWindowsPath);
wcscat (szPath, L"\\");
wcscat (szPath, c_szDebugIniFileName);
hFile = CreateFile(
szPath,
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
DWORD dwLastError = GetLastError();
if (dwLastError != ERROR_FILE_NOT_FOUND)
{
AssertSz(FALSE, "FIniFileInit failed for some reason other than FILE_NOT_FOUND");
hr = HRESULT_FROM_WIN32(dwLastError);
goto Exit;
}
}
else
{
hr = S_OK;
wcstombs(c_szDebugIniFileNameA, szPath, MAX_PATH);
goto Exit;
}
_wsplitpath(szWindowsPath, szPath, NULL, NULL, NULL);
wcscat (szPath, L"\\");
wcscat (szPath, c_szDebugIniFileName);
hFile = CreateFile(
szPath,
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
DWORD dwLastError = GetLastError();
hr = HRESULT_FROM_WIN32(dwLastError);
if (dwLastError != ERROR_FILE_NOT_FOUND)
{
AssertSz(FALSE, "FIniFileInit failed for some reason other than FILE_NOT_FOUND");
}
}
else
{
wcstombs(c_szDebugIniFileNameA, szPath, MAX_PATH);
hr = S_OK;
}
Exit:
if (hFile)
{
CloseHandle(hFile);
hFile = NULL;
}
return hr;
}
HRESULT CTracing::HrWriteDebugFlagsToIniFile()
{
HRESULT hr = S_OK;
// First, check to see if the file exists. If it doesn't, then we don't want
// to write the entries.
//
if (FIniFileInit())
{
// Loop through the array and write the data.
//
for (INT nLoop = 0; nLoop < g_nDebugFlagCount; nLoop++)
{
CHAR szInt[16]; // Sure, it's arbitrary, but it's also OK.
switch(nLoop)
{
// BreakOnAlloc is special case -- no associated flag entry
//
case dfidBreakOnAlloc:
_snprintf(szInt, MAX_TRACE_LEN, "%d", m_uiAllocOnWhichToBreak);
break;
// These store a DWORD in its standard form
//
case dfidBreakOnHr:
case dfidBreakOnHrIteration:
case dfidBreakOnIteration:
_snprintf( szInt, MAX_TRACE_LEN, "%d", g_DebugFlags[nLoop].dwValue);
break;
// default are treated as boolean, and stored that way
//
default:
// !! means it will always be 1 or 0.
_snprintf( szInt, MAX_TRACE_LEN, "%d", (!!g_DebugFlags[nLoop].dwValue));
break;
}
// Write the param to the ini file
WritePrivateProfileStringA(
"DebugFlags",
g_DebugFlags[nLoop].szShortName,
szInt,
c_szDebugIniFileNameA);
}
}
// For now, this is always S_OK, since there's nothing above that can
// fail.
//
return hr;
}
//+---------------------------------------------------------------------------
//
// Member: CTracing::HrProcessTagSection
//
// Purpose: Grab the parameters from the ini file. If they're not
// available, then use the settings in default. Note - this
// will always work because ttidDefault will always be the first
// element. If a [default] section wasn't present, then it will
// be using the settings that were in the struct initialization,
// which is also fine.
//
// Arguments:
// ptte [] TraceTag element to load
//
// Returns:
//
// Author: jeffspr 15 Apr 1997
//
// Notes:
//
HRESULT CTracing::HrProcessTagSection( TraceTagElement * ptte )
{
HRESULT hr = S_OK;
AssertSz(m_fInitialized,
"CTracing::HrProcessTagSection. Class not initialized");
AssertSz(ptte, "CTracing::HrProcessTagSection -- invalid ptte");
// Get the output to file param
//
ptte->fOutputToFile = GetPrivateProfileIntA(
ptte->szShortName,
"OutputToFile",
ptte->fVerboseOnly ?
FALSE : g_TraceTags[ttidDefault].fOutputToFile,
c_szDebugIniFileNameA);
// Get the OutputDebugString param. Require that the error tag
// always has at least output debug string on.
//
if (ptte->ttid == ttidError)
{
ptte->fOutputDebugString = TRUE;
}
else
{
// Load the OutputToDebug
ptte->fOutputDebugString = GetPrivateProfileIntA(
ptte->szShortName,
"OutputToDebug",
ptte->fVerboseOnly ?
FALSE : g_TraceTags[ttidDefault].fOutputDebugString,
c_szDebugIniFileNameA);
}
return hr;
}
//+---------------------------------------------------------------------------
//
// Member: CTracing::CorruptionCheck
//
// Purpose: Validate the tracetag array. Check to see that the
// shortnames are valid, that the descriptions are valid,
// and that the tracetag elements are not out of order.
// Also verify that the correct number of tracetag elements
// exist.
//
// Arguments:
// (none)
//
// Returns:
//
// Author: jeffspr 15 Apr 1997
//
// Notes:
// (shaunco) 16 Jul 1997: This is #if'defd out until JVert
// gives us a fix for the alpha compiler. It blows up compiling this
// function in retail.
//
// (jeffspr) Tough noogies for JVert - I need this code. Hopefully
// this has been fixed by now.
//
VOID CTracing::CorruptionCheck()
{
INT nLoop = 0;
// Validate the tracetag structure
//
for (nLoop = 0; nLoop < g_nTraceTagCount; nLoop++)
{
// Verify that we're not out of order or missing ttids
//
AssertSz(g_TraceTags[nLoop].ttid == nLoop,
"Invalid ttid in the tracetag structure. Out of order. " \
"CTracing::CorruptionCheck");
AssertSz(g_TraceTags[nLoop].ttid < g_nTraceTagCount,
"Invalid ttid (out of range) in CTracing::CorruptionCheck");
// Validate the shortname (verify not NULL or empty strings)
//
AssertSz(g_TraceTags[nLoop].szShortName,
"Invalid tracetag short name (NULL) in CTracing::CorruptionCheck");
AssertSz(g_TraceTags[nLoop].szShortName[0] != 0,
"Invalid tracetagshort name (empty) in CTracing::CorruptionCheck");
// Validate the descriptions (verify not NULL or empty strings)
//
AssertSz(g_TraceTags[nLoop].szDescription,
"Invalid tracetagdescription in CTracing::CorruptionCheck");
AssertSz(g_TraceTags[nLoop].szDescription[0] != 0,
"Invalid tracetagdescription (empty) in CTracing::CorruptionCheck");
}
// Validate the debug flags structure
//
for (nLoop = 0; nLoop < g_nDebugFlagCount; nLoop++)
{
// Verify that we're not out of order or missing dfids
//
AssertSz(g_DebugFlags[nLoop].dfid == nLoop,
"Invalid dfid in the debugflag structure. Out of order. " \
"CTracing::CorruptionCheck");
AssertSz(g_DebugFlags[nLoop].dfid < g_nDebugFlagCount,
"Invalid dfid (out of range) in CTracing::CorruptionCheck");
// Validate the shortname (verify not NULL or empty strings)
//
AssertSz(g_DebugFlags[nLoop].szShortName,
"Invalid debug flag short name (NULL) in CTracing::CorruptionCheck");
AssertSz(g_DebugFlags[nLoop].szShortName[0] != 0,
"Invalid debug flag short name (empty) in CTracing::CorruptionCheck");
// Validate the descriptions (verify not NULL or empty strings)
//
AssertSz(g_DebugFlags[nLoop].szDescription,
"Invalid debug flag description in CTracing::CorruptionCheck");
AssertSz(g_DebugFlags[nLoop].szDescription[0] != 0,
"Invalid debug flag description (empty) in CTracing::CorruptionCheck");
}
return;
}
//+---------------------------------------------------------------------------
//
// Member: CTracing::Trace
//
// Purpose: The actual trace call that takes care of doing the output
// to each trace target (file, OutputDebugString, etc.)
//
// Arguments:
// ttid [] The tracetag to use for output
// pszaTrace [] The trace string itself.
//
// Returns:
//
// Author: jeffspr 12 Apr 1997
//
// Notes:
//
VOID CTracing::Trace( TraceTagId ttid,
PCSTR pszaTrace )
{
// HrInit should have called a corruption checker for the entire trace
// block, but we'll check again just to make sure.
//
AssertSz(g_nTraceTagCount > ttid, "ttid out of range in CTracing::Trace");
AssertSz(g_TraceTags[ttid].ttid == ttid,
"TraceTag structure is corrupt in CTracing::Trace");
// If they want debug string output
//
if (g_TraceTags[ttid].fOutputDebugString)
{
// Then output the string
//
OutputDebugStringA(pszaTrace);
}
// If they want file output
if (g_TraceTags[ttid].fOutputToFile)
{
if (!m_hLogFile)
{
// Assuming that we haven't already tried to open the file
// and failed, open it.
if (!m_fAttemptedLogFileOpen)
{
HRESULT hr = HrOpenLogFile();
if (FAILED(hr))
{
AssertSz(FALSE, "Failed to open log file for tracing. No, "
"this isn't a coding error, but hey, figured that "
"you'd want to know...");
}
}
}
// If we were already open, or the open has now succeeded, do the
// trace
//
if (m_hLogFile)
{
Assert(pszaTrace);
// Since pszTrace is guaranteed to be a single-byte trace, we
// don't need to do the WCHAR multiply on the length, just
// a char multiply.
//
DWORD dwBytesToWrite = lstrlenA(pszaTrace) * sizeof(CHAR);
DWORD dwBytesWritten = 0;
BOOL fWriteResult = FALSE;
fWriteResult = WriteFile(
m_hLogFile, // handle to file to write to
pszaTrace, // pointer to data to write to file
dwBytesToWrite, // size of trace
&dwBytesWritten, // Bytes actually written.
NULL ); // No overlapped
if (!fWriteResult || (dwBytesToWrite != dwBytesWritten))
{
AssertSz(FALSE, "CTracing failure: Can't write to log file."
" Can't trace or we'll be recursing on this failure.");
}
}
}
}
HRESULT CTracing::HrOpenLogFile()
{
HRESULT hr = S_OK;
AssertSz(m_fInitialized,
"CTracing not initialized in HrOpenLogFile()");
AssertSz(!m_hLogFile,
"File already open before call to HrOpenLogFile()");
// Mark us as having attempted to open the file, so we don't call this
// function everytime we log, if we can't open it.
//
m_fAttemptedLogFileOpen = TRUE;
// $$TODO (jeffspr) - Allow flags in the Options section of the ini
// file specify the create flags and attributes, which would allow
// us to control the overwriting of log files and/or the write-through
// properties.
//
// Actually open the file, creating if necessary.
//
m_hLogFile = CreateFileA(
m_szLogFilePath, // Pointer to name of file
GENERIC_WRITE, // access (read-write) mode
FILE_SHARE_READ, // share mode (allow read access)
NULL, // pointer to security attributes
CREATE_ALWAYS, // how to create
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH,
NULL);
if (INVALID_HANDLE_VALUE == m_hLogFile)
{
m_hLogFile = NULL;
hr = HrFromLastWin32Error();
goto Exit;
}
Exit:
return hr;
}
using namespace std;
typedef deque<CTracingFuncCall, NOFAULT_ALLOC::nofault_allocator<CTracingFuncCall> > TRACING_FUNCTIONSTACK;
CTracingFuncCall::CTracingFuncCall(const CTracingFuncCall& TracingFuncCall)
{
Assert(g_csTracing);
m_szFunctionName = new(NO_FI) CHAR[strlen(TracingFuncCall.m_szFunctionName)+1];
if (m_szFunctionName)
{
strcpy(m_szFunctionName, TracingFuncCall.m_szFunctionName);
}
else
{
m_szFunctionName = c_szLowMemory;
}
m_szFunctionDName = new(NO_FI) CHAR[strlen(TracingFuncCall.m_szFunctionDName)+1];
if (m_szFunctionDName)
{
strcpy(m_szFunctionDName, TracingFuncCall.m_szFunctionDName);
}
else
{
m_szFunctionDName = c_szLowMemory;
}
m_szFile = new(NO_FI) CHAR[strlen(TracingFuncCall.m_szFile)+1];
if (m_szFile)
{
strcpy(m_szFile, TracingFuncCall.m_szFile);
}
else
{
m_szFile = c_szLowMemory;
}
m_dwLine = TracingFuncCall.m_dwLine;
m_dwFramePointer = TracingFuncCall.m_dwFramePointer;
m_dwThreadId = TracingFuncCall.m_dwThreadId;
m_ReturnAddress = TracingFuncCall.m_ReturnAddress;
#if defined(_X86_) || defined(_AMD64_)
m_arguments[0] = TracingFuncCall.m_arguments[0];
m_arguments[1] = TracingFuncCall.m_arguments[1];
m_arguments[2] = TracingFuncCall.m_arguments[2];
#elif defined (_IA64_)
m_arguments[0] = TracingFuncCall.m_arguments[0];
m_arguments[1] = TracingFuncCall.m_arguments[1];
m_arguments[2] = TracingFuncCall.m_arguments[2];
#else
// add other processors here
#endif
}
#if defined (_X86_) || defined (_AMD64_)
CTracingFuncCall::CTracingFuncCall(LPCSTR szFunctionName, LPCSTR szFunctionDName, LPCSTR szFile, const DWORD dwLine, DWORD_PTR dwpReturnAddress, const DWORD_PTR dwFramePointer)
#elif defined (_IA64_)
CTracingFuncCall::CTracingFuncCall(LPCSTR szFunctionName, LPCSTR szFunctionDName, LPCSTR szFile, const DWORD dwLine, DWORD_PTR dwpReturnAddress, const __int64 Args1, const __int64 Args2, const __int64 Args3)
#else
CTracingFuncCall::CTracingFuncCall(LPCSTR szFunctionName, LPCSTR szFunctionDName, LPCSTR szFile, const DWORD dwLine)
#endif
{
Assert(g_csTracing);
m_szFunctionName = new(NO_FI) CHAR[strlen(szFunctionName)+1];
if (m_szFunctionName)
{
strcpy(m_szFunctionName, szFunctionName);
}
else
{
m_szFunctionName = c_szLowMemory;
}
m_szFunctionDName = new(NO_FI) CHAR[strlen(szFunctionDName)+1];
if (m_szFunctionDName)
{
strcpy(m_szFunctionDName, szFunctionDName);
}
else
{
m_szFunctionDName = c_szLowMemory;
}
m_szFile = new(NO_FI) CHAR[strlen(szFile)+1];
if (m_szFile)
{
strcpy(m_szFile, szFile);
}
else
{
m_szFile = c_szLowMemory;
}
m_dwLine = dwLine;
m_ReturnAddress = dwpReturnAddress;
#if defined (_X86_) || defined (_AMD64_)
m_dwFramePointer = dwFramePointer;
if (dwFramePointer)
{
PDWORD_PTR pdwEbp = reinterpret_cast<PDWORD_PTR>(dwFramePointer);
pdwEbp++; // advance pass BaseEBP
pdwEbp++; // advance pass ReturnIP
m_arguments[0] = *pdwEbp; pdwEbp++;
m_arguments[1] = *pdwEbp; pdwEbp++;
m_arguments[2] = *pdwEbp;
}
else
{
m_arguments[0] = 0;
m_arguments[1] = 0;
m_arguments[2] = 0;
}
#elif defined (_IA64_)
m_dwFramePointer = 0;
m_arguments[0] = Args1;
m_arguments[1] = Args2;
m_arguments[2] = Args3;
#else
m_dwFramePointer = 0;
#endif
m_dwThreadId = GetCurrentThreadId();
}
CTracingFuncCall::~CTracingFuncCall()
{
Assert(g_csTracing);
if (c_szLowMemory != m_szFile)
{
delete[] m_szFile;
}
if (c_szLowMemory != m_szFunctionDName)
{
delete[] m_szFunctionDName;
}
if (c_szLowMemory != m_szFunctionName)
{
delete[] m_szFunctionName;
}
}
CTracingThreadInfo::CTracingThreadInfo()
{
Assert(g_csTracing);
m_dwLevel = 1;
m_dwThreadId = GetCurrentThreadId();
m_pfnStack = new(NO_FI) TRACING_FUNCTIONSTACK;
}
CTracingThreadInfo::~CTracingThreadInfo()
{
Assert(g_csTracing);
TRACING_FUNCTIONSTACK *pfnStack = reinterpret_cast<TRACING_FUNCTIONSTACK *>(m_pfnStack);
delete pfnStack;
}
CTracingThreadInfo* CTracingIndent::GetThreadInfo()
{
CTracingThreadInfo *pThreadInfo = NULL;
AssertSz(g_dwTlsTracing, "Tracing not initialized... Did RawDllMain run?");
AssertSz(g_csTracing, "Tracing not initialized... Did RawDllMain run?");
pThreadInfo = reinterpret_cast<CTracingThreadInfo *>(TlsGetValue(g_dwTlsTracing));
if (!pThreadInfo)
{
pThreadInfo = new(NO_FI) CTracingThreadInfo;
TlsSetValue(g_dwTlsTracing, pThreadInfo);
Assert(pThreadInfo == reinterpret_cast<CTracingThreadInfo *>(TlsGetValue(g_dwTlsTracing)));
}
Assert(pThreadInfo);
return pThreadInfo;
}
void CTracingIndent::FreeThreadInfo()
{
CTracingThreadInfo *pThreadInfo = NULL;
pThreadInfo = reinterpret_cast<CTracingThreadInfo *>(TlsGetValue(g_dwTlsTracing));
if (pThreadInfo)
{
TraceStackFn(ttidError);
AssertSz(pThreadInfo->m_dwLevel == 1, "Thread is being terminated that didn't complete (ignore will spew remaining stack if you attach a debugger)");
TraceStackFn(ttidError);
AssertSz(pThreadInfo->m_dwLevel == 1, "Thread is being terminated that didn't complete");
delete pThreadInfo;
TlsSetValue(g_dwTlsTracing, reinterpret_cast<LPVOID>(-1));
}
}
CTracingIndent::CTracingIndent()
{
bFirstTrace = TRUE;
m_szFunctionDName = NULL;
m_dwFramePointer = NULL;
}
#if defined (_X86_) || defined (_AMD64_)
void CTracingIndent::AddTrace(LPCSTR szFunctionName, LPCSTR szFunctionDName, LPCSTR szFile, const DWORD dwLine, LPCVOID pReturnAddress, const DWORD_PTR dwFramePointer)
#elif defined (_IA64_)
void CTracingIndent::AddTrace(LPCSTR szFunctionName, LPCSTR szFunctionDName, LPCSTR szFile, const DWORD dwLine, LPCVOID pReturnAddress, const __int64 Args1, const __int64 Args2, const __int64 Args3)
#else
void CTracingIndent::AddTrace(LPCSTR szFunctionName, LPCSTR szFunctionDName, LPCSTR szFile, const DWORD dwLine)
#endif
{
Assert(szFunctionName);
Assert(szFunctionDName);
Assert(szFile);
if (!bFirstTrace)
{
#if defined (_X86_) || defined (_AMD64_)
RemoveTrace(szFunctionDName, dwFramePointer);
#elif defined (_IA64_)
RemoveTrace(szFunctionDName, 0);
#else
RemoveTrace(szFunctionDName, 0);
#endif
}
else
{
bFirstTrace = FALSE;
}
volatile CTracingThreadInfo *pThreadInfo = GetThreadInfo();
TRACING_FUNCTIONSTACK &fnStack = *reinterpret_cast<TRACING_FUNCTIONSTACK *>(pThreadInfo->m_pfnStack);
Assert(g_csTracing);
try
{
EnterCriticalSection(g_csTracing);
#if defined (_X86_) || defined (_AMD64_)
CTracingFuncCall fnCall(szFunctionName, szFunctionDName, szFile, dwLine, reinterpret_cast<DWORD_PTR>(pReturnAddress), dwFramePointer);
#elif defined (_IA64_)
CTracingFuncCall fnCall(szFunctionName, szFunctionDName, szFile, dwLine, reinterpret_cast<DWORD_PTR>(pReturnAddress), Args1, Args2, Args3);
#else
CTracingFuncCall fnCall(szFunctionName, szFunctionDName, szFile, dwLine);
#endif
if (fnStack.size() == 0)
{
pThreadInfo->m_dwLevel++;
}
else
{
const CTracingFuncCall& fnTopOfStack = fnStack.front();
if ( (fnCall.m_dwFramePointer != fnTopOfStack.m_dwFramePointer) ||
strcmp(fnCall.m_szFunctionDName, fnTopOfStack.m_szFunctionDName))
{
pThreadInfo->m_dwLevel++;
}
}
m_szFunctionDName = new(NO_FI) CHAR[strlen(fnCall.m_szFunctionDName)+1];
if (m_szFunctionDName)
{
strcpy(m_szFunctionDName, fnCall.m_szFunctionDName);
}
else
{
m_szFunctionDName = c_szLowMemory;
}
m_dwFramePointer = fnCall.m_dwFramePointer;
fnStack.push_front(fnCall);
}
catch (bad_alloc)
{
}
LeaveCriticalSection(g_csTracing);
}
CTracingIndent::~CTracingIndent()
{
AssertSz(g_csTracing, "Tracing not initialized");
RemoveTrace(m_szFunctionDName, m_dwFramePointer);
}
void CTracingIndent::RemoveTrace(LPCSTR szFunctionDName, const DWORD_PTR dwFramePointer)
{
__try
{
EnterCriticalSection(g_csTracing);
volatile CTracingThreadInfo *pThreadInfo = GetThreadInfo();
TRACING_FUNCTIONSTACK &fnStack = *reinterpret_cast<TRACING_FUNCTIONSTACK *>(pThreadInfo->m_pfnStack);
Assert(szFunctionDName);
Assert(m_szFunctionDName);
Assert(g_csTracing);
if (
(fnStack.size() == 0)
||
(
(
strcmp(m_szFunctionDName, szFunctionDName)
||
strcmp(m_szFunctionDName, fnStack.front().m_szFunctionDName)
)
&&
(
(c_szLowMemory != m_szFunctionDName)
&&
(c_szLowMemory != fnStack.front().m_szFunctionDName)
&&
(c_szLowMemory != szFunctionDName)
)
)
||
(m_dwFramePointer != fnStack.front().m_dwFramePointer)
||
(dwFramePointer != m_dwFramePointer)
)
{
// Make sure to leave the critical section during the assert, so that it does not cause a deadlock.
LeaveCriticalSection(g_csTracing);
// This will trace the stack:
if (IsDebuggerPresent())
{
TraceTagFn(ttidError, "Tracing self-inconsistent - either a stack over/underwrite occurred or an exception was thrown in faulting stack:");
TraceStackFn(ttidError);
}
else
{
AssertSz(FALSE, "Tracing self-inconsistent - either a stack over/underwrite occurred or an exception was thrown.\r\nPlease attach a debugger and hit Ignore on this assert to spew info to the debugger (it will assert again).");
TraceTagFn(ttidError, "Tracing self-inconsistent - either a stack over/underwrite occurred or an exception was thrown in faulting stack:");
TraceStackFn(ttidError);
}
TraceTagFn(ttidError, "1) For complete stack info, .frame down to CTracingIndent__RemoveTrace, dv and find dwFramePointer (2nd parameter to CTracingIndent__RemoveTrace)");
TraceTagFn(ttidError, " Then do a kb=(value of dwFramePointer)");
TraceTagFn(ttidError, "2) For even more complete stack info, .frame down to CTracingIndent__RemoveTrace, dv and then dt -r on fnStack");
TraceTagFn(ttidError, " Then find the _Next, where m_szFunctionName == 'CTracingIndent::RemoveTrace'");
TraceTagFn(ttidError, " If it exists, find the value of m_dwFramePointer under _Next");
TraceTagFn(ttidError, " Then do a kb=(value of m_dwFramePointer)");
DebugBreak();
// Try to recover.
if (fnStack.size() > 0)
{
fnStack.pop_front();
}
EnterCriticalSection(g_csTracing);
}
else
{
DWORD_PTR dwOldFramePointer = fnStack.front().m_dwFramePointer;
fnStack.pop_front();
if ( (fnStack.size() == 0) ||
(dwOldFramePointer != fnStack.front().m_dwFramePointer) ||
strcmp(m_szFunctionDName, fnStack.front().m_szFunctionDName) )
{
pThreadInfo->m_dwLevel--;
Assert(pThreadInfo->m_dwLevel);
}
}
if (c_szLowMemory != m_szFunctionDName)
{
delete [] m_szFunctionDName;
}
}
__finally
{
LeaveCriticalSection(g_csTracing);
}
}
DWORD CTracingIndent::getspaces()
{
volatile CTracingThreadInfo *pThreadInfo = GetThreadInfo();
return pThreadInfo->m_dwLevel;
}
void CTracingIndent::TraceStackFn(TRACETAGID TraceTagId)
{
if (!g_TraceTags[TraceTagId].fOutputDebugString &&
!g_TraceTags[TraceTagId].fOutputToFile)
{
return;
}
volatile CTracingThreadInfo *pThreadInfo = GetThreadInfo();
TRACING_FUNCTIONSTACK &fnStack = *reinterpret_cast<TRACING_FUNCTIONSTACK *>(pThreadInfo->m_pfnStack);
Assert(g_csTracing);
__try
{
EnterCriticalSection(g_csTracing);
if (fnStack.size() == 0)
{
return;
}
#if defined (_X86_) || defined (_AMD64_)
TraceInternal(TraceTagId, NULL, 0, 0, "ChildEBP RetAddr Args to Child (reconstructed - ChildEBP is invalid now)", 0, FALSE);
#elif defined (_IA64_)
TraceInternal(TraceTagId, NULL, 0, 0, "RetAddr Args to Child (reconstructed)", 0, FALSE);
#else
TraceInternal(TraceTagId, NULL, 0, 0, "Function stack", 0, FALSE);
#endif
for (TRACING_FUNCTIONSTACK::const_iterator i = fnStack.begin(); i != fnStack.end(); i++)
{
CHAR szBuffer[MAX_TRACE_LEN];
#if defined (_X86_) || defined (_AMD64_)
_snprintf(szBuffer, MAX_TRACE_LEN, "%08x %08x %08x %08x %08x %s [%s @ %d]", i->m_dwFramePointer, i->m_ReturnAddress, i->m_arguments[0], i->m_arguments[1], i->m_arguments[2], i->m_szFunctionName, i->m_szFile, i->m_dwLine);
#elif defined (_IA64_)
_snprintf(szBuffer, MAX_TRACE_LEN, "%016I64x %016I64x 0x%016I64x 0x%016I64x %s [%s @ %d]", i->m_ReturnAddress, i->m_arguments[0], i->m_arguments[1], i->m_arguments[2], i->m_szFunctionName, i->m_szFile, i->m_dwLine);
#else
_snprintf(szBuffer, MAX_TRACE_LEN, "%s", i->m_szFunctionName);
#endif
TraceInternal (TraceTagId, NULL, 0, 0, szBuffer, 0, FALSE);
}
}
__finally
{
LeaveCriticalSection(g_csTracing);
}
}
void CTracingIndent::TraceStackFn(IN OUT LPSTR szString, IN OUT LPDWORD pdwSize)
{
volatile CTracingThreadInfo *pThreadInfo = GetThreadInfo();
TRACING_FUNCTIONSTACK &fnStack = *reinterpret_cast<TRACING_FUNCTIONSTACK *>(pThreadInfo->m_pfnStack);
Assert(g_csTracing);
__try
{
EnterCriticalSection(g_csTracing);
ZeroMemory(szString, *pdwSize);
if (fnStack.size() == 0)
{
return;
}
Assert(*pdwSize > MAX_TRACE_LEN);
LPSTR pszString = szString;
#if defined (_X86_) || defined (_AMD64_)
pszString += _snprintf(pszString, MAX_TRACE_LEN, " ChildEBP RetAddr Args to Child (reconstructed - ChildEBP is invalid now)\r\n");
#elif defined (_IA64_)
pszString += _snprintf(pszString, MAX_TRACE_LEN, " RetAddr Args to Child (reconstructed)\r\n");
#else
pszString += _snprintf(pszString, MAX_TRACE_LEN, " Function stack\r\n");
#endif
DWORD dwSizeIn = *pdwSize;
for (TRACING_FUNCTIONSTACK::const_iterator i = fnStack.begin(); i != fnStack.end(); i++)
{
CHAR szBuffer[1024];
#if defined (_X86_) || defined (_AMD64_)
_snprintf(szBuffer, MAX_TRACE_LEN, " %08x %08x %08x %08x %08x %s [%s @ %d]", i->m_dwFramePointer, i->m_ReturnAddress, i->m_arguments[0], i->m_arguments[1], i->m_arguments[2], i->m_szFunctionName, i->m_szFile, i->m_dwLine);
#elif defined (_IA64_)
_snprintf(szBuffer, MAX_TRACE_LEN, " %016I64x %016I64x 0x%016I64x 0x%016I64x %s [%s @ %d]", i->m_ReturnAddress, i->m_arguments[0], i->m_arguments[1], i->m_arguments[2], i->m_szFunctionName, i->m_szFile, i->m_dwLine);
#else
_snprintf(szBuffer, MAX_TRACE_LEN, " %s", i->m_szFunctionName);
#endif
pszString += _snprintf(pszString, MAX_TRACE_LEN, "%s\r\n", szBuffer);
if (pszString > (szString + (*pdwSize - celems(szBuffer))) ) // Can't use strlen since I need to know the length of the
// next element - not this one. Hence just take the maximum size.
{
pszString += _snprintf(pszString, MAX_TRACE_LEN, "...", szBuffer);
*pdwSize = dwSizeIn * 2; // Tell the caller to allocate more memory and call us back if they want more info.
break;
}
}
if (*pdwSize < dwSizeIn)
{
*pdwSize = (DWORD)(pszString - szString);
}
}
__finally
{
LeaveCriticalSection(g_csTracing);
}
}
VOID
WINAPIV
TraceFileFuncFn (TRACETAGID ttid)
{
if (FIsDebugFlagSet (dfidTraceFileFunc))
{
CHAR szBuffer[MAX_TRACE_LEN];
volatile CTracingThreadInfo *pThreadInfo = CTracingIndent::GetThreadInfo();
TRACING_FUNCTIONSTACK &fnStack = *reinterpret_cast<TRACING_FUNCTIONSTACK *>(pThreadInfo->m_pfnStack);
Assert(g_csTracing);
__try
{
EnterCriticalSection(g_csTracing);
const CTracingFuncCall& fnCall = fnStack.front();
if (fnStack.size() != 0)
{
if (FIsDebugFlagSet (dfidTraceSource))
{
#if defined (_X86_) || defined (_AMD64_)
_snprintf(szBuffer, MAX_TRACE_LEN, "%s [0x%08x 0x%08x 0x%08x] %s:%d", fnCall.m_szFunctionName, fnCall.m_arguments[0], fnCall.m_arguments[1], fnCall.m_arguments[2], fnCall.m_szFile, fnCall.m_dwLine);
#elif defined (_IA64_)
_snprintf(szBuffer, MAX_TRACE_LEN, "%s [0x%016I64x 0x%016I64x 0x%016I64x] %s:%d", fnCall.m_szFunctionName, fnCall.m_arguments[0], fnCall.m_arguments[1], fnCall.m_arguments[2], fnCall.m_szFile, fnCall.m_dwLine);
#else
_snprintf(szBuffer, MAX_TRACE_LEN, "%s %s:%d", fnCall.m_szFunctionName, fnCall.m_szFile, fnCall.m_dwLine);
#endif
}
else
{
#if defined (_X86_) || defined (_AMD64_)
_snprintf(szBuffer, MAX_TRACE_LEN, "%s [0x%08x 0x%08x 0x%08x]", fnCall.m_szFunctionName, fnCall.m_arguments[0], fnCall.m_arguments[1], fnCall.m_arguments[2]);
#elif defined (_IA64_)
_snprintf(szBuffer, MAX_TRACE_LEN, "%s [0x%016I64x 0x%016I64x 0x%016I64x]", fnCall.m_szFunctionName, fnCall.m_arguments[0], fnCall.m_arguments[1], fnCall.m_arguments[2]);
#else
_snprintf(szBuffer, MAX_TRACE_LEN, "%s", fnCall.m_szFunctionName);
#endif
}
TraceTagFn(ttid, szBuffer);
}
else
{
AssertSz(FALSE, "Trace failure");
}
}
__finally
{
LeaveCriticalSection(g_csTracing);
}
}
}
LPCSTR DBG_EMNAMES[] =
{
"INVALID_EVENTMGR",
"EVENTMGR_CONMAN",
"EVENTMGR_EAPOLMAN"
};
LPCSTR DBG_CMENAMES[] =
{
"INVALID_TYPE",
"CONNECTION_ADDED",
"CONNECTION_BANDWIDTH_CHANGE",
"CONNECTION_DELETED",
"CONNECTION_MODIFIED",
"CONNECTION_RENAMED",
"CONNECTION_STATUS_CHANGE",
"REFRESH_ALL",
"CONNECTION_ADDRESS_CHANGE"
};
LPCSTR DBG_NCMNAMES[] =
{
"NCM_NONE",
"NCM_DIRECT",
"NCM_ISDN",
"NCM_LAN",
"NCM_PHONE",
"NCM_TUNNEL",
"NCM_PPPOE",
"NCM_BRIDGE",
"NCM_SHAREDACCESSHOST_LAN",
"NCM_SHAREDACCESSHOST_RAS"
};
LPCSTR DBG_NCSMNAMES[] =
{
"NCSM_NONE",
"NCSM_LAN",
"NCSM_WIRELESS",
"NCSM_ATM",
"NCSM_ELAN",
"NCSM_1394",
"NCSM_DIRECT",
"NCSM_IRDA",
"NCSM_CM",
};
LPCSTR DBG_NCSNAMES[] =
{
"NCS_DISCONNECTED",
"NCS_CONNECTING",
"NCS_CONNECTED",
"NCS_DISCONNECTING",
"NCS_HARDWARE_NOT_PRESENT",
"NCS_HARDWARE_DISABLED",
"NCS_HARDWARE_MALFUNCTION",
"NCS_MEDIA_DISCONNECTED",
"NCS_AUTHENTICATING",
"NCS_AUTHENTICATION_SUCCEEDED",
"NCS_AUTHENTICATION_FAILED",
"NCS_INVALID_ADDRESS",
"NCS_CREDENTIALS_REQUIRED"
};
// Shorten these to fit more in.
LPCSTR DBG_NCCSFLAGS[] =
{
"_NONE",
"_ALL_USERS",
"_ALLOW_DUPLICATION",
"_ALLOW_REMOVAL",
"_ALLOW_RENAME",
"_SHOW_ICON",
"_INCOMING_ONLY",
"_OUTGOING_ONLY",
"_BRANDED",
"_SHARED",
"_BRIDGED",
"_FIREWALLED",
"_DEFAULT"
};
LPCSTR DbgEvents(DWORD Event)
{
if (Event < celems(DBG_CMENAMES))
{
return DBG_CMENAMES[Event];
}
else
{
return "UNKNOWN Event: Update DBG_CMENAMES table.";
}
}
LPCSTR DbgEventManager(DWORD EventManager)
{
if (EventManager < celems(DBG_EMNAMES))
{
return DBG_EMNAMES[EventManager];
}
else
{
return "UNKNOWN Event: Update DBG_EMNAMES table.";
}
}
LPCSTR DbgNcm(DWORD ncm)
{
if (ncm < celems(DBG_NCMNAMES))
{
return DBG_NCMNAMES[ncm];
}
else
{
return "UNKNOWN NCM: Update DBG_NCMNAMES table.";
}
}
LPCSTR DbgNcsm(DWORD ncsm)
{
if (ncsm < celems(DBG_NCSMNAMES))
{
return DBG_NCSMNAMES[ncsm];
}
else
{
return "UNKNOWN NCM: Update DBG_NCSMNAMES table.";
}
}
LPCSTR DbgNcs(DWORD ncs)
{
if (ncs < celems(DBG_NCSNAMES))
{
return DBG_NCSNAMES[ncs];
}
else
{
return "UNKNOWN NCS: Update DBG_NCSNAMES table.";
}
}
LPCSTR DbgNccf(DWORD nccf)
{
static CHAR szName[MAX_PATH];
if (nccf >= (1 << celems(DBG_NCCSFLAGS)) )
{
return "UNKNOWN NCCF: Update DBG_NCCSFLAGS table.";
}
if (0 == nccf)
{
strcpy(szName, DBG_NCCSFLAGS[0]);
}
else
{
szName[0] = '\0';
LPSTR szTemp = szName;
BOOL bFirst = TRUE;
for (DWORD x = 0; x < celems(DBG_NCCSFLAGS); x++)
{
if (nccf & (1 << x))
{
if (!bFirst)
{
szTemp += _snprintf(szTemp, MAX_TRACE_LEN, "+");
}
else
{
szTemp += _snprintf(szTemp, MAX_TRACE_LEN, "NCCF:");
}
bFirst = FALSE;
szTemp += _snprintf(szTemp, MAX_TRACE_LEN, "%s", DBG_NCCSFLAGS[x+1]);
}
}
}
return szName;
}
#ifdef ENABLELEAKDETECTION
CObjectLeakTrack *g_pObjectLeakTrack = NULL;
// First LPSTR is classname
// Second LPSTR is stack trace
class CObjectAllocationInfo
{
private:
void InitializeFromClass(const CObjectAllocationInfo & ObjectAllocationInfo);
public:
CObjectAllocationInfo & operator =(const CObjectAllocationInfo & ObjectAllocationInfo);
CObjectAllocationInfo();
CObjectAllocationInfo(LPCSTR szClassName, LPCSTR szStackInfo);
CObjectAllocationInfo(const CObjectAllocationInfo & ObjectAllocationInfo);
~CObjectAllocationInfo();
LPSTR m_szClassName;
LPSTR m_szStackInfo;
LPVOID m_ppvStackTrace[32];
};
CObjectAllocationInfo::CObjectAllocationInfo()
{
Assert(FIsDebugFlagSet(dfidTrackObjectLeaks));
m_szClassName = NULL;
m_szStackInfo = NULL;
ZeroMemory(m_ppvStackTrace, sizeof(m_ppvStackTrace));
}
CObjectAllocationInfo & CObjectAllocationInfo::operator =(const CObjectAllocationInfo & ObjectAllocationInfo)
{
Assert(FIsDebugFlagSet(dfidTrackObjectLeaks));
if (m_szClassName)
{
delete[] m_szClassName;
m_szClassName = NULL;
}
if (m_szStackInfo)
{
delete[] m_szStackInfo;
m_szStackInfo = NULL;
}
ZeroMemory(m_ppvStackTrace, sizeof(m_ppvStackTrace));
InitializeFromClass(ObjectAllocationInfo);
return *this;
}
CObjectAllocationInfo::CObjectAllocationInfo(LPCSTR szClassName, LPCSTR szStackInfo)
{
Assert(FIsDebugFlagSet(dfidTrackObjectLeaks));
if (szClassName)
{
DWORD dwLen = strlen(szClassName) + 1;
m_szClassName = new(NO_FI) CHAR[dwLen];
if (szClassName)
{
strncpy(m_szClassName, szClassName, dwLen);
}
}
if (szStackInfo)
{
DWORD dwLen = strlen(szStackInfo) + 1;
m_szStackInfo = new(NO_FI) CHAR[dwLen];
if (m_szStackInfo)
{
strncpy(m_szStackInfo, szStackInfo, dwLen);
}
}
ZeroMemory(m_ppvStackTrace, sizeof(m_ppvStackTrace));
}
void CObjectAllocationInfo::InitializeFromClass(const CObjectAllocationInfo & ObjectAllocationInfo)
{
Assert(FIsDebugFlagSet(dfidTrackObjectLeaks));
m_szClassName = NULL;
m_szStackInfo = NULL;
if (ObjectAllocationInfo.m_szClassName)
{
DWORD dwLen = strlen(ObjectAllocationInfo.m_szClassName) + 1;
m_szClassName = new(NO_FI) CHAR[dwLen];
if (m_szClassName)
{
strncpy(m_szClassName, ObjectAllocationInfo.m_szClassName, dwLen);
}
}
if (ObjectAllocationInfo.m_szStackInfo)
{
DWORD dwLen = strlen(ObjectAllocationInfo.m_szStackInfo) + 1;
m_szStackInfo = new(NO_FI) CHAR[dwLen];
if (m_szStackInfo)
{
strncpy(m_szStackInfo, ObjectAllocationInfo.m_szStackInfo, dwLen);
}
}
memcpy(m_ppvStackTrace, ObjectAllocationInfo.m_ppvStackTrace, sizeof(m_ppvStackTrace));
}
CObjectAllocationInfo::CObjectAllocationInfo(const CObjectAllocationInfo & ObjectAllocationInfo)
{
Assert(FIsDebugFlagSet(dfidTrackObjectLeaks));
InitializeFromClass(ObjectAllocationInfo);
}
CObjectAllocationInfo::~CObjectAllocationInfo()
{
Assert(FIsDebugFlagSet(dfidTrackObjectLeaks));
if (m_szStackInfo)
{
delete[] m_szStackInfo;
m_szStackInfo = NULL;
}
if (m_szClassName)
{
delete[] m_szClassName;
m_szClassName = NULL;
}
}
typedef map<LPCVOID, CObjectAllocationInfo, NOFAULT_ALLOC::nofault_allocator<CTracingFuncCall> > MAPOBJLEAK;
//+---------------------------------------------------------------------------
//
// Member: CObjectLeakTrack::CObjectLeakTrack
//
// Purpose: Constructor
//
// Arguments:
//
// Returns:
//
// Author: deonb 7 July 2001
//
// Notes: We are allocating our g_mapObjLeak here
//
CObjectLeakTrack::CObjectLeakTrack()
{
Assert(FIsDebugFlagSet(dfidTrackObjectLeaks));
g_mapObjLeak = new(NO_FI) MAPOBJLEAK;
}
//+---------------------------------------------------------------------------
//
// Member: CObjectLeakTrack::CObjectLeakTrack
//
// Purpose: Destructor
//
// Arguments:
//
// Returns: none
//
// Author: deonb 7 July 2001
//
// Notes: We are deleting our g_mapObjLeak here. We have to typecast it
// first since the data types exported by trace.h to the world is
// LPVOID, to minimize dependencies in order to include tracing.
//
CObjectLeakTrack::~CObjectLeakTrack()
{
Assert(FIsDebugFlagSet(dfidTrackObjectLeaks));
delete reinterpret_cast<MAPOBJLEAK *>(g_mapObjLeak);
}
//+---------------------------------------------------------------------------
//
// Member: CObjectLeakTrack::Insert
//
// Purpose: Insert an object instance in the list
//
// Arguments: [in] pThis - This pointer of the object instance. This must
// be the same as the this pointer of the ::Remove
// [in] szdbgClassName - The classname of the object
// [in own] pszConstructionStack - The stacktrace of the object constructor.
// (or any other information that is useful to describe
// the origin of the object).
// This must be allocated with the global new operator
// since we will take ownership and free this
// Returns: none
//
// Author: deonb 7 July 2001
//
// Notes:
//
void CObjectLeakTrack::Insert(IN LPCVOID pThis, IN LPCSTR szdbgClassName, IN TAKEOWNERSHIP LPSTR pszConstructionStack)
{
Assert(FIsDebugFlagSet(dfidTrackObjectLeaks));
if (g_mapObjLeak)
{
MAPOBJLEAK &rmapObjLeak = *reinterpret_cast<MAPOBJLEAK *>(g_mapObjLeak);
CObjectAllocationInfo ObjectAllocationInfo(szdbgClassName, pszConstructionStack);
delete[] pszConstructionStack;
RtlWalkFrameChain(ObjectAllocationInfo.m_ppvStackTrace, celems(ObjectAllocationInfo.m_ppvStackTrace), 0);
rmapObjLeak[pThis] = ObjectAllocationInfo;
}
}
//+---------------------------------------------------------------------------
//
// Member: CObjectLeakTrack::Remove
//
// Purpose: Remove an object instance from the list
//
// Arguments: [in] pThis - This pointer of the object instance. This must
// be the same as the this pointer of the ::Insert
//
// Returns:
//
// Author: deonb 7 July 2001
//
// Notes:
//
void CObjectLeakTrack::Remove(IN LPCVOID pThis)
{
Assert(FIsDebugFlagSet(dfidTrackObjectLeaks));
if (g_mapObjLeak)
{
MAPOBJLEAK &rmapObjLeak = *reinterpret_cast<MAPOBJLEAK *>(g_mapObjLeak);
MAPOBJLEAK::iterator iter = rmapObjLeak.find(pThis);
if (iter != rmapObjLeak.end())
{
rmapObjLeak.erase(iter);
}
}
}
void RemoveKnownleakFn(LPCVOID pThis)
{
if (FIsDebugFlagSet(dfidTrackObjectLeaks) && g_pObjectLeakTrack)
{
__try
{
EnterCriticalSection(g_csTracing);
TraceTag(ttidAllocations, "An object at '0x%08x' was marked as a known leak", pThis);
g_pObjectLeakTrack->Remove(pThis);
}
__finally
{
LeaveCriticalSection(g_csTracing);
}
}
}
//+---------------------------------------------------------------------------
//
// Member: CObjectLeakTrack::AssertIfObjectsStillAllocated
//
// Purpose: Assert if the the list of allocated objects in the process is not NULL.
// and call DumpAllocatedObjects to dump out this list.
//
// Arguments: [in] szClassName. The classname of the objects to assert that there are nothing of.
// This classname can be obtained by calling typeid(CLASS).name()
// on your class. (E.g. typeid(CConnectionManager).name() )
//
// Can also be NULL to ensure that there are NO objects allocated
// Returns: none
//
// Author: deonb 7 July 2001
//
// Notes: Don't call this from outside tracing - rather call AssertNoAllocatedInstances
// which is safe to call in CHK and FRE.
BOOL CObjectLeakTrack::AssertIfObjectsStillAllocated(IN LPCSTR szClassName)
{
if (!FIsDebugFlagSet(dfidTrackObjectLeaks))
{
return FALSE;
}
if (g_mapObjLeak)
{
__try
{
EnterCriticalSection(g_csTracing);
MAPOBJLEAK &rmapObjLeak = *reinterpret_cast<MAPOBJLEAK *>(g_mapObjLeak);
BOOL fFoundObjectOfType = FALSE;
for (MAPOBJLEAK::const_iterator iter = rmapObjLeak.begin(); iter != rmapObjLeak.end(); iter++)
{
if (szClassName)
{
if (0 == strcmp(iter->second.m_szClassName, szClassName) )
{
fFoundObjectOfType = TRUE;
break;
}
}
else
{
fFoundObjectOfType = TRUE;
break;
}
}
WCHAR szModuleName[MAX_PATH];
ZeroMemory(szModuleName, MAX_PATH);
if (!GetModuleFileName(reinterpret_cast<HMODULE>(&__ImageBase), szModuleName, MAX_PATH))
{
if (GetModuleFileName(reinterpret_cast<HMODULE>(::GetModuleHandle(NULL)), szModuleName, MAX_PATH))
{
WCHAR szTemp[MAX_PATH];
wcscpy(szTemp, szModuleName);
wsprintf(szModuleName, L"at address 0x%08x inside %s", __ImageBase, szTemp);
}
else
{
wsprintf(szModuleName, L"at address 0x%08x", __ImageBase);
}
}
if (fFoundObjectOfType)
{
if (!IsDebuggerPresent())
{
CHAR szDescription[MAX_PATH];
sprintf(szDescription,
"An object leak has been detected in %S. Please attach a user or kernel mode debugger to process id %d (0x%04x) and hit IGNORE to dump the offending stacks.\r\nE.g. ntsd -Gg -p %d",
szModuleName,
GetCurrentProces
GetCurrentProcessId(),
GetCurrentProcessId(),
GetCurrentProcessId());
AssertSz(FALSE, szDescription);
}
DumpAllocatedObjects(ttidError, szClassName);
AssertSz(FALSE, "An object leak has been detected. This leak has been spewed to the debugger - press RETRY to look at spew and map ReturnAddr values to symbols.");
return TRUE;
}
else
{
CHAR szSpewText[MAX_PATH];
TraceTag(ttidError, "No leaks were detected inside module %S", szModuleName);
// sprintf(szSpewText, "No leaks were detected inside module %S", szModuleName);
// AssertSz(NULL, szSpewText);
}
}
__finally
{
LeaveCriticalSection(g_csTracing);
}
}
return FALSE;
}
//+---------------------------------------------------------------------------
//
// Member: CObjectLeakTrack::DumpAllocatedObjects
//
// Purpose: Dump the list of the objects and their construction stacks
// for the objects that were allocated but not deleted yet. Dumps
// to the debugger.
//
// Arguments: [in] TraceTagId. The TraceTag to trace it to.
// [in] szClassName. The classname of the objects to dump out.
// This classname can be obtained by calling typeid(CLASS).name()
// on your class. (E.g. typeid(CConnectionManager).name() )
//
// Can also be NULL to dump out objects of ALL types.
//
// Returns: none
//
// Author: deonb 7 July 2001
//
// Notes: Don't call this from outside - rather call TraceAllocatedObjects
// which is safe to call in CHK and FRE.
//
void CObjectLeakTrack::DumpAllocatedObjects(IN TRACETAGID TraceTagId, IN LPCSTR szClassName)
{
if (!FIsDebugFlagSet(dfidTrackObjectLeaks))
{
return;
}
if (g_mapObjLeak)
{
__try
{
EnterCriticalSection(g_csTracing);
MAPOBJLEAK &rmapObjLeak = *reinterpret_cast<MAPOBJLEAK *>(g_mapObjLeak);
for (MAPOBJLEAK::const_iterator iter = rmapObjLeak.begin(); iter != rmapObjLeak.end(); iter++)
{
BOOL fMustSpew = TRUE;
if (szClassName)
{
if (0 != strcmp(iter->second.m_szClassName, szClassName) )
{
fMustSpew = FALSE;
}
}
if (fMustSpew)
{
TraceTag(TraceTagId, "The object of type '%s' allocated at 0x%08x has not been freed:",
iter->second.m_szClassName, iter->first);
BOOL bHasDetailedStack = FALSE;
#if defined (_X86_)
if (iter->second.m_ppvStackTrace[0] != NULL)
{
bHasDetailedStack = TRUE;
}
#endif
if (*iter->second.m_szStackInfo)
{
TraceTag (TraceTagId, "Callstack below:\r\n%s", iter->second.m_szStackInfo);
}
else
{
if (!bHasDetailedStack)
{
TraceTag(TraceTagId, " <call stack information not available. See comments inside trace.h on how to increase your call stack information>.\r\n");
}
}
if (bHasDetailedStack)
{
DWORD dwCount = 0;
while ( (dwCount < celems(iter->second.m_ppvStackTrace)) &&
(iter->second.m_ppvStackTrace[dwCount]) )
{
dwCount++;
}
TraceTag (TraceTagId, "Detailed callstack information is available through the debugger. Execute the folowing command:");
TraceTag (TraceTagId, "dds 0x%08x L 0x%02x\r\n", iter->second.m_ppvStackTrace, dwCount);
}
}
}
}
__finally
{
LeaveCriticalSection(g_csTracing);
}
}
}
#endif // ENABLELEAKDETECTION
#endif // ENABLETRACE