//
// Please do not customise this code
//
// Portions (c) Keith Westley 2000
#ifndef DEBUGTOOLKIT_H
#define DEBUGTOOLKIT_H
// This class requires templates and multithreading
#include <afxtempl.h>
#include <afxmt.h>
// we will be using the fast logger file
#include "FastLogger.h"
// we will be displaying a progress window while dumping
class CProgressWnd;
// the hang window is created to receive our timer messages.
// as this window is created in the same thread as the application
// it will stop receiving timer messages if that thread hangs
LRESULT CALLBACK TimerWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
// this class is responsible for interpretting the files debug information
// I sourced this code from an article in eith MSJ or WSJ but I can't find the reference.
class PE_Debug
{
public :
PE_Debug();
virtual ~PE_Debug();
void ClearReport();
void DumpDebugInfo(CString& dumpBuffer, const BYTE* caller, HINSTANCE hInstance);
void Display() ;
private :
// Report data
enum { MAX_MODULENAME_LEN = 512, MAX_FILENAME_LEN = 256 } ;
char latestModule[ MAX_MODULENAME_LEN ] ;
char latestFile[ MAX_FILENAME_LEN ] ;
// File mapping data
HANDLE hFile ;
HANDLE hFileMapping ;
PIMAGE_DOS_HEADER fileBase ;
// Pointers to debug information
PIMAGE_NT_HEADERS NT_Header ;
PIMAGE_COFF_SYMBOLS_HEADER COFFDebugInfo;
PIMAGE_SYMBOL COFFSymbolTable;
int COFFSymbolCount;
const char* stringTable;
void ClearFileCache();
void ClearDebugPtrs();
void MapFileInMemory( const char* module);
void FindDebugInfo();
void DumpSymbolInfo(CString& dumpBuffer, DWORD relativeAddress);
void DumpLineNumber(CString& dumpBuffer, DWORD relativeAddress);
PIMAGE_COFF_SYMBOLS_HEADER GetDebugHeader();
PIMAGE_SECTION_HEADER SectionHeaderFromName(const char* name);
const char* GetSymbolName(PIMAGE_SYMBOL sym);
};
class CDebugToolkit
{
private:
// this structure is used to pass parameters to the hang thread that watches for
// the user to tell us the program has hung.
typedef struct _USERHANGTHREADPARMS
{
HANDLE ThreadHandle;
HANDLE hWait;
int iVk1;
int iVk2;
DWORD dwWait;
} USERHANGTHREADPARMS;
// this structure is used to pass parameters to the automated hang detection thread.
typedef struct _HANGTHREADPARMS
{
HANDLE hThread;
HANDLE hWait;
DWORD dwWait;
} HANGTHREADPARMS;
BOOL m_fSuspendHang; // true if we are to ignore hangs
CLogFile m_LogFile; // our trace log file
CString m_sCWDStart; // we store the cwd when we are first created here
DWORD m_dwTraceFilter; // we store the current trace filter here
HANDLE m_hMutexOnlyInstance; // this mutex is used to see if there are any other instances of the program running on this machine
BOOL m_fOnlyInstance; // true if we are the only (or first) instance of the application running
static CDebugToolkit* m__pDebugToolkit; // static pointer to THE CDebugToolkit instance
USERHANGTHREADPARMS m_UserHangThreadParms; // user hang thread startup parameters
HANGTHREADPARMS m_HangThreadParms; // automatic hang thread startup parameters
CWinThread* m_pUserHangThread; // user hang thread
CWinThread* m_pHangThread; // automatic hang thread
PAGESETUPDLG m_psd; // printer details dialog
static CRITICAL_SECTION stackTraceSync; // critical section for stack tracing
LPTOP_LEVEL_EXCEPTION_FILTER m_oldFilter; // exception filter
CTime m_timeLastVisit; // time last visited by automatic hang thread
HWND m_hwndTimer; // timer window for automatic hang thread
CCriticalSection m_csTime; // last visit time multi thread protection.
CCriticalSection m_csSuspend; // hang thread suspend multi thread protection.
CStringArray m_asFiles; // array of files to check
typedef CArray<CLSID, CLSID> CCLSIDArray;
CCLSIDArray m_aCLSID; // array of com classes to check .. must have 1:1 with m_asCOMFiles
CStringArray m_asCOMFiles; // array of com classes to check .. must have 1:1 with m_aCLSID
BOOL m_fInitialised; // true once initialised
// OVERRIDE These Functions In Your Derived Class to do things when these events occur
virtual BOOL OnAssert(void); // called to see if you want the assert handled
virtual void OnEndAssert(void); // called once the assert has been accepted and handled and reported
virtual BOOL OnException(struct _EXCEPTION_POINTERS *pExceptionInfo); // called to see if you want to handle the exception
virtual void OnEndException(void); // called once the exception has been accepted and handled.
virtual BOOL OnHang(void); // called to see if you want to handle the hang automatically detected
virtual void OnEndHang(void); // called once the hang has been accepted and handled.
virtual BOOL OnUserHang(void); // called to see if you want to handle the user reported hang
virtual void OnEndUserHang(void); // called once the user reported hang has been accepted and handled.
virtual BOOL OnTrace(const DWORD dwFilterFlag); // called to see if you want the trace processed
virtual void OnReset(void); // called to allow you to change debug toolkit processing options before everything is restarted
virtual BOOL OnDelete(void); // called to allow you to do some processing on a delete request
virtual void OnAbnormalExit(void); // called to allow you to do some processing when we detect the program did not exit normally last time it was run
virtual void OnTestDLLFail(const CString& sFile, DWORD dwError); // called when a DLL could not be loaded.
virtual void OnTestCOMFail(const CLSID clsid, const CString& sFile, DWORD dwError); // called when a COM file could not be loaded
virtual void OnTestFileFail(const CString& sFile, DWORD dwError); // called when a file could not be opened.
virtual CString GetDumpOtherAtEvent(void) // allows you to provide additional data to write to the trace log when a hang, exception or assert event occurs
{
return "";
}
virtual CString GetDumpOtherAtSave(void) // allows you to provide additional data to write to the log file when it is being written to disk
{
return "";
}
virtual void GetOptionDescriptions(CString& sOptionDescriptions) // allows you to dump in human readable form the options currently in effect
{
sOptionDescriptions = "\r\nNo Options Implemented\r\n";
}
virtual void ExportOptions(CFile& file) // allows you to dump in machine readable (but text) form
{
// do nothing
}
// Core Functions you should never call these, even from your derived clas
static LONG __stdcall myUnhandledExceptionFilter(struct _EXCEPTION_POINTERS *pExceptionInfo);
static UINT UserHangThreadProc(LPVOID pParam);
static UINT HangThreadProc(LPVOID pParam);
void HandleException(struct _EXCEPTION_POINTERS *pExceptionInfo);
void HandleUserHang(CONTEXT& context);
void HandleHang(CONTEXT& context);
BOOL GetDiscFreeSpace(const LPCTSTR lpszPath, DWORDLONG* pnFree);
void DumpOptions(CFile& file);
DWORD GetFileCount(void);
void DumpFiles(CFile& file, CProgressWnd* pPw);
void DumpLog(CFile& file);
void DumpPrinter(CFile& file);
void DumpCPU(CFile& file);
void DumpLoadableOptions(CFile& file);
void DumpCWD(CFile& file);
void DumpWindowsVersion(CFile& file);
void Write(const CString& sOut, CFile& file);
void DumpCommandLine(CFile& file);
void DumpCOMInfo(CFile& file);
void DumpResourceSpace(CFile& file);
CString DecodePaper(short paper);
CString GetFreeSystemResources(WORD wType);
CString DumpStack(WORD* pSP, WORD* pBP);
CString DecodeExceptionRecord(EXCEPTION_RECORD* pE);
CString DumpHex(void* pMemory, WORD wSize, BOOL fCRLF = FALSE);
CString DumpChar(void* pMemory, WORD wSize, BOOL fCRLF = FALSE);
CString DumpMemory(void* pMemory, WORD wSize = 16);
CString DumpRegister(const CString& sName, DWORD dwValue, const BOOL fDumpPointer = TRUE);
CString DecodeContext(CONTEXT* pC);
CString DumpCallStack(DWORD EBP, DWORD EIP);
CString DumpLoadedModules(void);
CString DumpTasks(void);
void DumpHang(CONTEXT& context);
void NormalExit(void);
void CheckAbnormalExit(void);
BOOL IsOnlyInstance(void);
BOOL TestFile(const CString& sFile);
BOOL TestDLL(const CString& sFile);
BOOL TestCOM(const CLSID clsid, const CString& sFile);
BOOL RegisterOCX(const CString& sOCX);
BOOL UnregisterOCX(const CString& sOCX);
protected:
// Options impacting the debug toolkits runtime behaviour
BOOL m_fExceptions; // true if we are to trap exceptions
BOOL m_fAssert; // true if we are to handle asserts
BOOL m_fHangDetect; // true if we are to automatically detect main thread hangs
BOOL m_fUserHangDetect; // true if we are to look for user nominated hangs
BOOL m_fTrace; // true if we are to process trace output
// options impacting the log dump contents
BOOL m_fDumpOptions; // true if we are to dump program options
BOOL m_fDumpFiles; // true if we are to test and dump DLL and files. NOTE: For lots of files this can be slow.
BOOL m_fDumpLog; // true if we are to dump the trace log
BOOL m_fDumpPrinter; // true if we are to dump details about the default printer. NOTE: This is slow.
BOOL m_fDumpCPU; // true if we are to dump details about the machines CPU
BOOL m_fDumpCWD; // true if we are to dump current directory details
BOOL m_fDumpWindowsVersion; // true if we are to dump windows version details
BOOL m_fDumpCommandLine; // true if we are to dump command line details.
BOOL m_fDumpCOMInfo; // true if we are to test and dump com object details. NOTE: For lots of com object this can be slow.
BOOL m_fDumpLoadableOptions; // true if we are to dump machine loadable (but text) options NOTE: This would typically be the options in INI file format
// values which you need to set in your derived class
CString m_sAppName; // the applications name
CString m_sVersion; // the applications version
int m_iUserHangVk1; // key1 the user has to press when reporting a user detected hang
int m_iUserHangVk2; // key2 the user has to press when reporting a user detected hang
DWORD m_dwUserHangWait; // maximum milliseconds the user has to press the keys for the user detected hang to be acknowledged. Smaller settings increase CPU load.
DWORD m_dwHangWait; // amount of time to wait before automatically detecting a hang
BOOL m_fPrefixMultilineTrace; // true if we are to prefix 2nd & subsequent lines in a multiline trace output.
// you should never need to call this
BOOL GetSuspendHang(void)
{
// this needs thread protection
CSingleLock sl(&m_csSuspend, TRUE);
return m_fSuspendHang;
}
// you should never need to call this
CTime& GetLastVisitTime(void)
{
// this needs thread protection
CSingleLock sl(&m_csTime, TRUE);
return m_timeLastVisit;
}
// used to get the application name
CString& GetAppName(void)
{
return m_sAppName;
}
// constructor
CDebugToolkit(void);
// initialises the debug toolkit. Should be called at the end of your constructor once all options have been set
BOOL Initialise(void);
public:
// destructor
virtual ~CDebugToolkit(void);
// add a file to the list of DLLs and files to check
BOOL AddFile(const CString& sFile, const BOOL fTestDLL = FALSE, const BOOL fTestFile = FALSE);
// add a com object to the list to check
BOOL AddCOM(const CString& sFile, const CLSID clsid, const BOOL fTest = FALSE);
// assert a condition
virtual void Assert(DWORD dwLine, const CString& sFile, BOOL fCondition, const CString& sCondition, const CString& sDescription = "");
// trace some output
virtual void Trace(const CString& sOut, const DWORD dwFilterFlag = 0xFFFFFFFF, const BOOL fForce = FALSE);
// dumps debug details to a file
virtual BOOL DumpToFile(const CString& sFile, const CString& sPre = "");
virtual void DumpToFile(CFile& file, const CString& sPre = "");
// deletes debug objects such as the trace log
void Delete(void);
// allows the debug toolkit to reset itself based on any changes in the debug toolkit options.
// eg. if you want to turn hang detection on mid session ... change the hang flag then call reset
void Reset(void);
// set the 32 bit flag filter for tracing
void SetTraceFilter(const DWORD dwFilter)
{
m_dwTraceFilter = dwFilter;
}
// call this function prior to lengthy processing tasks to prevent false automated hang detection
void SetSuspendHang(const BOOL fSuspend)
{
// this needs thread protection
CSingleLock sl(&m_csSuspend, TRUE);
m_fSuspendHang = fSuspend;
if (!m_fSuspendHang)
{
// if we are turning suspend off then reset the last visit time so we don't
// falsly detect a hang
SetLastVisitTime();
}
}
// you should never call this function
void SetLastVisitTime(void)
{
// this needs thread protection
CSingleLock sl(&m_csTime, TRUE);
m_timeLastVisit = CTime::GetCurrentTime();
}
// used to get a pointer to THE debug toolkit object
static CDebugToolkit* GetDebugToolkit(void)
{
ASSERT(m__pDebugToolkit != NULL);
return m__pDebugToolkit;
}
};
// macros to make using the toolkit easier
// these macros trace out a string and work exactly like MFCs TRACE0 ... TRACE3
#define DT_TRACE0(s)\
CDebugToolkit::GetDebugToolkit()->Trace(s);
#define DT_TRACE1(s, p1)\
{\
CString s1;\
s1.Format(s, p1);\
CDebugToolkit::GetDebugToolkit()->Trace(s1);\
}
#define DT_TRACE2(s, p1, p2)\
{\
CString s1;\
s1.Format(s, p1, p2);\
CDebugToolkit::GetDebugToolkit()->Trace(s1);\
}
#define DT_TRACE3(s, p1, p2, p3)\
{\
CString s1;\
s1.Format(s, p1, p2, p3);\
CDebugToolkit::GetDebugToolkit()->Trace(s1);\
}
// these macros work like DT_TRACE0 ... DT_TRACE3 except you need to specify a 32 bit flag field that will be
// and-ed with the filter to decide if it should be displayed
#define DT_TRACEF0(f, s)\
CDebugToolkit::GetDebugToolkit()->Trace(s, f);
#define DT_TRACEF1(f, s, p1)\
{\
CString s1;\
s1.Format(s, p1);\
CDebugToolkit::GetDebugToolkit()->Trace(s1, f);\
}
#define DT_TRACEF2(f, s, p1, p2)\
{\
CString s1;\
s1.Format(s, p1, p2);\
CDebugToolkit::GetDebugToolkit()->Trace(s1, f);\
}
#define DT_TRACEF3(f, s, p1, p2, p3)\
{\
CString s1;\
s1.Format(s, p1, p2, p3);\
CDebugToolkit::GetDebugToolkit()->Trace(s1,f );\
}
// this macro asserts a condition just like MFCs ASSERT
#define DT_ASSERT(b)\
{\
CDebugToolkit::GetDebugToolkit()->Assert(__LINE__, __FILE__, b, #b);\
}
// this macro adds an explanation string which is displayed when the assert fails.
#define DT_ASSERTS(b, s)\
{\
CDebugToolkit::GetDebugToolkit()->Assert(__LINE__, __FILE__, b, #b, s);\
}
#endif