|
|
//+-------------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (C) Microsoft Corporation, 1997 - 1999
//
// File: debug.cpp
//
//--------------------------------------------------------------------------
/*----------------------------------------------------------------------------
/ Title; / debug.cpp / / Authors; / Jeffrey Saathoff (jeffreys) / / Notes; / Provides printf style debug output /----------------------------------------------------------------------------*/ #include "pch.h"
#include <stdio.h>
#include <comctrlp.h>
#pragma hdrstop
#ifdef DEBUG
DWORD g_dwTraceMask = 0; DWORD g_tlsDebug = 0xffffffffL;
#define MAX_CALL_DEPTH 64
class CDebugStack { private: DWORD m_dwThreadID; LONG m_cDepth; struct { BOOL fTracedYet; LPCTSTR pszFunctionName; DWORD dwMask; } m_CallStack[MAX_CALL_DEPTH];
public: CDebugStack() : m_dwThreadID(GetCurrentThreadId()), m_cDepth(-1) { ZeroMemory(&m_CallStack, sizeof(m_CallStack)); }
public: void _Indent(LONG iDepth, LPCTSTR pszFormat, ...); void _vIndent(LONG iDepth, LPCTSTR pszFormat, va_list *pva); BOOL _TraceProlog(LONG iDepth, BOOL fForce); void _TraceEnter(DWORD dwMask, LPCTSTR pName); void _TraceLeave(void); void _Trace(BOOL bForce, LPCTSTR pszFormat, ...); void _vTrace(BOOL bForce, LPCTSTR pszFormat, va_list *pva); void _TraceGUID(LPCTSTR pPrefix, REFGUID rGUID); void _TraceAssert(int iLine, LPTSTR pFilename, LPTSTR pCondition); }; typedef CDebugStack *PDEBUGSTACK;
class CDebugStackHolder { private: HDPA m_hDebugStackList; CRITICAL_SECTION m_csStackList;
public: CDebugStackHolder() : m_hDebugStackList(NULL) { InitializeCriticalSection(&m_csStackList); } ~CDebugStackHolder();
public: void Add(PDEBUGSTACK pDebugStack); void Remove(PDEBUGSTACK pDebugStack); }; typedef CDebugStackHolder *PDEBUGSTACKHOLDER;
PDEBUGSTACKHOLDER g_pStackHolder = NULL;
/*-----------------------------------------------------------------------------
/ _Indent / ------- / Output to the debug stream indented by n columns. / / In: / i = column to indent to. / pszFormat -> string to be indented / / Out: / - /----------------------------------------------------------------------------*/
void CDebugStack::_Indent(LONG iDepth, LPCTSTR pszFormat, ...) { va_list va;
va_start(va, pszFormat); _vIndent(iDepth, pszFormat, &va); va_end(va); }
void CDebugStack::_vIndent(LONG iDepth, LPCTSTR pszFormat, va_list *pva) { TCHAR szStringBuffer[2048]; szStringBuffer[0] = TEXT('\0');
wsprintf(szStringBuffer, TEXT("%08x "), m_dwThreadID);
iDepth = min(iDepth, MAX_CALL_DEPTH - 1); for ( ; iDepth > 0 ; iDepth-- ) lstrcat(szStringBuffer, TEXT(" "));
wvsprintf(szStringBuffer + lstrlen(szStringBuffer), pszFormat, *pva); lstrcat(szStringBuffer, TEXT("\n"));
OutputDebugString(szStringBuffer); }
/*-----------------------------------------------------------------------------
/ _TraceProlog / ------------- / Handle the prolog to a prefix string, including outputting the / function name if we haven't already. / / In: / iDepth = depth in the call stack / fForce = ignore flags / / Out: / BOOL if trace output should be made /----------------------------------------------------------------------------*/ BOOL CDebugStack::_TraceProlog(LONG iDepth, BOOL fForce) { if ( iDepth < 0 || iDepth >= MAX_CALL_DEPTH ) return FALSE;
if ( (g_dwTraceMask & m_CallStack[iDepth].dwMask) || fForce ) { if ( !m_CallStack[iDepth].fTracedYet ) { if ( iDepth > 0 ) _TraceProlog(iDepth-1, TRUE);
_Indent(iDepth, m_CallStack[iDepth].pszFunctionName); m_CallStack[iDepth].fTracedYet = TRUE; }
return TRUE; }
return FALSE; }
/*-----------------------------------------------------------------------------
/ _TraceEnter / ------------ / Set the debugging call stack up to indicate which function we are in. / / In: / pName -> function name to be displayed in subsequent trace output. / / Out: / - /----------------------------------------------------------------------------*/ void CDebugStack::_TraceEnter(DWORD dwMask, LPCTSTR pName) { m_cDepth++;
if ( m_cDepth < MAX_CALL_DEPTH ) { if ( !pName ) pName = TEXT("<no name>"); // no function name given
m_CallStack[m_cDepth].fTracedYet = FALSE; m_CallStack[m_cDepth].pszFunctionName = pName; m_CallStack[m_cDepth].dwMask = dwMask;
//if ( m_cDepth > 0 )
// _TraceProlog(m_cDepth-1, FALSE);
} }
/*-----------------------------------------------------------------------------
/ _TraceLeave / ------------ / On exit from a function this will adjust the function stack pointer to / point to our previous function. If no trace output has been made then / we will output the function name on a single line (to indicate we went somewhere). / / In: / - / Out: / - /----------------------------------------------------------------------------*/ void CDebugStack::_TraceLeave(void) { //_TraceProlog(m_cDepth, FALSE);
//if ( !m_cDepth && m_CallStack[0].fTracedYet )
// OutputDebugString(TEXT("\n"));
m_cDepth = max(m_cDepth-1, -1); // account for underflow
}
/*-----------------------------------------------------------------------------
/ _Trace / ------- / Perform printf formatting to the debugging stream. We indent the output / and stream the function name as required to give some indication of / call stack depth. / / In: / pszFormat -> printf style formatting string / ... = arguments as required for the formatting / / Out: / - /----------------------------------------------------------------------------*/ void CDebugStack::_Trace(BOOL bForce, LPCTSTR pszFormat, ...) { va_list va;
va_start(va, pszFormat); _vTrace(bForce, pszFormat, &va); va_end(va); }
void CDebugStack::_vTrace(BOOL bForce, LPCTSTR pszFormat, va_list *pva) { if ( _TraceProlog(m_cDepth, bForce) || bForce ) _vIndent(m_cDepth+1, pszFormat, pva); }
/*-----------------------------------------------------------------------------
/ _TraceGUID / ----------- / Given a GUID output it into the debug string, first we try and map it / to a name (ie. IShellFolder), if that didn't work then we convert it / to its human readable form. / / In: / pszPrefix -> prefix string / lpGuid -> guid to be streamed / / Out: / - /----------------------------------------------------------------------------*/ #ifdef UNICODE
#define MAP_GUID(x) &x, TEXT(""L#x)
#else
#define MAP_GUID(x) &x, TEXT(""#x)
#endif
#define MAP_GUID2(x,y) MAP_GUID(x), MAP_GUID(y)
const struct { const GUID* m_pGUID; LPCTSTR m_pName; } _guid_map[] = { MAP_GUID(IID_IUnknown), MAP_GUID(IID_IClassFactory), MAP_GUID(IID_IDropTarget), MAP_GUID(IID_IDataObject), MAP_GUID(IID_IPersist), MAP_GUID(IID_IOleWindow),
MAP_GUID2(IID_INewShortcutHookA, IID_INewShortcutHookW), MAP_GUID(IID_IShellBrowser), MAP_GUID(IID_IShellView), MAP_GUID(IID_IContextMenu), MAP_GUID(IID_IShellIcon), MAP_GUID(IID_IShellFolder), MAP_GUID(IID_IShellExtInit), MAP_GUID(IID_IShellPropSheetExt), MAP_GUID(IID_IPersistFolder), MAP_GUID2(IID_IExtractIconA, IID_IExtractIconW), MAP_GUID2(IID_IShellLinkA, IID_IShellLinkW), MAP_GUID2(IID_IShellCopyHookA, IID_IShellCopyHookW), MAP_GUID2(IID_IFileViewerA, IID_IFileViewerW), MAP_GUID(IID_ICommDlgBrowser), MAP_GUID(IID_IEnumIDList), MAP_GUID(IID_IFileViewerSite), MAP_GUID(IID_IContextMenu2), MAP_GUID2(IID_IShellExecuteHookA, IID_IShellExecuteHookW), MAP_GUID(IID_IPropSheetPage), MAP_GUID(IID_IShellView2), MAP_GUID(IID_IUniformResourceLocator), };
void CDebugStack::_TraceGUID(LPCTSTR pPrefix, REFGUID rGUID) { TCHAR szGUID[40]; LPCTSTR pName = NULL; int i; for ( i = 0 ; i < ARRAYSIZE(_guid_map); i++ ) { if ( IsEqualGUID(rGUID, *_guid_map[i].m_pGUID) ) { pName = _guid_map[i].m_pName; break; } }
if ( !pName ) { SHStringFromGUID(rGUID, szGUID, ARRAYSIZE(szGUID)); pName = szGUID; }
_Trace(FALSE, TEXT("%s %s"), pPrefix, pName); }
/*-----------------------------------------------------------------------------
/ _TraceAssert / ------------- / Our assert handler, always prints messages but only breaks if enabled / in the trace mask. / / In: / iLine = line / pFilename -> filename of the file we asserted in / pCondition -> assert condition that failed / / Out: / - /----------------------------------------------------------------------------*/ void CDebugStack::_TraceAssert(int iLine, LPTSTR pFilename, LPTSTR pCondition) { // nb: TRUE --> asserts always displayed
_Trace(TRUE, TEXT("Assert failed: \"%s\""), pCondition); _Trace(TRUE, TEXT("File: %s, Line %d"), pFilename, iLine);
if ( g_dwTraceMask & TRACE_COMMON_ASSERT ) DebugBreak(); }
/*-----------------------------------------------------------------------------
/ ~CDebugStackHolder / ------------------ / Free any DebugStack objects that exist / / In: / - / / Out: / - /----------------------------------------------------------------------------*/ int CALLBACK _DeleteCB(LPVOID pVoid, LPVOID /*pData*/) { PDEBUGSTACK pDebugStack = (PDEBUGSTACK)pVoid; if (pDebugStack) { //pDebugStack->_Trace(TRUE, TEXT("~CDebugStackHolder destroying DebugStack"));
delete pDebugStack; } return 1; }
CDebugStackHolder::~CDebugStackHolder() { EnterCriticalSection(&m_csStackList);
if (NULL != m_hDebugStackList) { DPA_DestroyCallback(m_hDebugStackList, _DeleteCB, NULL); m_hDebugStackList = NULL; }
LeaveCriticalSection(&m_csStackList); DeleteCriticalSection(&m_csStackList); }
/*-----------------------------------------------------------------------------
/ CDebugStackHolder::Add / ---------------------- / Saves the DebugStack object in a list / / In: / PDEBUGSTACK pointer to the thread's debug stack object / / Out: / - /----------------------------------------------------------------------------*/ void CDebugStackHolder::Add(PDEBUGSTACK pDebugStack) { EnterCriticalSection(&m_csStackList);
if (NULL == m_hDebugStackList) m_hDebugStackList = DPA_Create(4);
if (NULL != m_hDebugStackList) DPA_AppendPtr(m_hDebugStackList, pDebugStack);
LeaveCriticalSection(&m_csStackList); }
/*-----------------------------------------------------------------------------
/ CDebugStackHolder::Remove / ------------------------- / Removes the DebugStack object from the list / / In: / PDEBUGSTACK pointer to the thread's debug stack object / / Out: / - /----------------------------------------------------------------------------*/ void CDebugStackHolder::Remove(PDEBUGSTACK pDebugStack) { EnterCriticalSection(&m_csStackList);
if (NULL != m_hDebugStackList) { int iStack = DPA_GetPtrIndex(m_hDebugStackList, pDebugStack);
if (-1 != iStack) DPA_DeletePtr(m_hDebugStackList, iStack); }
LeaveCriticalSection(&m_csStackList); }
/*-----------------------------------------------------------------------------
/ GetThreadStack / -------------- / Create (if necessary) and return the per-thread debug stack object. / / In: / - / / Out: / PDEBUGSTACK pointer to the thread's debug stack object /----------------------------------------------------------------------------*/ PDEBUGSTACK GetThreadStack() { PDEBUGSTACK pDebugStack;
if (0xffffffffL == g_tlsDebug) return NULL;
pDebugStack = (PDEBUGSTACK)TlsGetValue(g_tlsDebug);
if (!pDebugStack) { pDebugStack = new CDebugStack; TlsSetValue(g_tlsDebug, pDebugStack);
if (!g_pStackHolder) g_pStackHolder = new CDebugStackHolder;
if (g_pStackHolder) g_pStackHolder->Add(pDebugStack); }
return pDebugStack; }
/*-----------------------------------------------------------------------------
/ DoTraceSetMask / -------------- / Adjust the trace mask to reflect the state given. / / In: / dwMask = mask for enabling / disable trace output / / Out: / - /----------------------------------------------------------------------------*/ void DoTraceSetMask(DWORD dwMask) { g_dwTraceMask = dwMask; }
/*-----------------------------------------------------------------------------
/ DoTraceSetMaskFromRegKey / ------------------------ / Pick up the TraceMask value from the given registry key and / set the trace mask using that. / / In: / hkRoot = handle of open key / pszSubKey = name of subkey to open / / Out: / - /----------------------------------------------------------------------------*/ void DoTraceSetMaskFromRegKey(HKEY hkRoot, LPCTSTR pszSubKey) { HKEY hKey;
if (ERROR_SUCCESS == RegOpenKey(hkRoot, pszSubKey, &hKey)) { DWORD dwTraceMask = 0; DWORD cbTraceMask = sizeof(dwTraceMask);
RegQueryValueEx(hKey, TEXT("TraceMask"), NULL, NULL, (LPBYTE)&dwTraceMask, &cbTraceMask); DoTraceSetMask(dwTraceMask); RegCloseKey(hKey); } }
/*-----------------------------------------------------------------------------
/ DoTraceSetMaskFromCLSID / ----------------------- / Pick up the TraceMask value from the given CLSID value and / set the trace mask using that. / / In: / rCLSID = CLSID to query the value from / / Out: / - /----------------------------------------------------------------------------*/ void DoTraceSetMaskFromCLSID(REFCLSID rCLSID) { TCHAR szClsidKey[48] = TEXT("CLSID\\"); int nLength = lstrlen(szClsidKey);
if (!SHStringFromGUID(rCLSID, szClsidKey + nLength, ARRAYSIZE(szClsidKey) - nLength)) return;
DoTraceSetMaskFromRegKey(HKEY_CLASSES_ROOT, szClsidKey); }
/*-----------------------------------------------------------------------------
/ DoTraceEnter / ------------ / Set the debugging call stack up to indicate which function we are in. / / In: / pName -> function name to be displayed in subsequent trace output. / / Out: / - /----------------------------------------------------------------------------*/ void DoTraceEnter(DWORD dwMask, LPCTSTR pName) { PDEBUGSTACK pDebugStack = GetThreadStack();
if (pDebugStack) pDebugStack->_TraceEnter(dwMask, pName); }
/*-----------------------------------------------------------------------------
/ DoTraceLeave / ------------ / On exit from a function this will adjust the function stack pointer to / point to our previous function. If no trace output has been made then / we will output the function name on a single line (to indicate we went somewhere). / / In: / - / Out: / - /----------------------------------------------------------------------------*/ void DoTraceLeave(void) { PDEBUGSTACK pDebugStack = GetThreadStack();
if (pDebugStack) pDebugStack->_TraceLeave(); }
/*-----------------------------------------------------------------------------
/ DoTrace / ------- / Perform printf formatting to the debugging stream. We indent the output / and stream the function name as required to give some indication of / call stack depth. / / In: / pszFormat -> printf style formatting string / ... = arguments as required for the formatting / / Out: / - /----------------------------------------------------------------------------*/ void DoTrace(LPCTSTR pszFormat, ...) { PDEBUGSTACK pDebugStack = GetThreadStack(); va_list va;
if (pDebugStack) { va_start(va, pszFormat); pDebugStack->_vTrace(FALSE, pszFormat, &va); va_end(va); } }
/*-----------------------------------------------------------------------------
/ DoTraceGuid / ----------- / Given a GUID output it into the debug string, first we try and map it / to a name (ie. IShellFolder), if that didn't work then we convert it / to its human readable form. / / In: / pszPrefix -> prefix string / lpGuid -> guid to be streamed / / Out: / - /----------------------------------------------------------------------------*/ void DoTraceGUID(LPCTSTR pPrefix, REFGUID rGUID) { PDEBUGSTACK pDebugStack = GetThreadStack();
if (pDebugStack) pDebugStack->_TraceGUID(pPrefix, rGUID); }
/*-----------------------------------------------------------------------------
/ DoTraceAssert / ------------- / Our assert handler, out faults it the trace mask as enabled assert / faulting. / / In: / iLine = line / pFilename -> filename of the file we asserted in / / Out: / - /----------------------------------------------------------------------------*/ void DoTraceAssert(int iLine, LPTSTR pFilename, LPTSTR pCondition) { PDEBUGSTACK pDebugStack = GetThreadStack();
if (pDebugStack) pDebugStack->_TraceAssert(iLine, pFilename, pCondition); }
/*-----------------------------------------------------------------------------
/ DebugThreadDetach / DebugProcessAttach / DebugProcessDetach / ------------- / These must be called from DllMain / / In: / - / / Out: / - /----------------------------------------------------------------------------*/ void DebugThreadDetach(void) { PDEBUGSTACK pDebugStack;
if (0xffffffffL == g_tlsDebug) return;
pDebugStack = (PDEBUGSTACK)TlsGetValue(g_tlsDebug);
if (pDebugStack) { if (g_pStackHolder) g_pStackHolder->Remove(pDebugStack);
delete pDebugStack; TlsSetValue(g_tlsDebug, NULL); } }
void DebugProcessAttach(void) { g_tlsDebug = TlsAlloc(); }
void DebugProcessDetach(void) { DebugThreadDetach();
if (NULL != g_pStackHolder) { delete g_pStackHolder; g_pStackHolder = NULL; }
if (0xffffffffL != g_tlsDebug) { TlsFree(g_tlsDebug); g_tlsDebug = 0xffffffffL; } }
#endif // DEBUG
|