Click here to Skip to main content
15,885,916 members
Articles / Programming Languages / C++

Debug Toolkit

Rate me:
Please Sign up or sign in to vote.
4.20/5 (5 votes)
27 Mar 2000 179.7K   1.5K   88  
A complete debug toolkit to add intelligent debugging capability to your application.
//
// 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

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions