Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Debug Toolkit

0.00/5 (No votes)
27 Mar 2000 1  
A complete debug toolkit to add intelligent debugging capability to your application.
  • Download source files - 60 Kb

    Introduction

    So you have written a great application and rolled it out to all your users and a week later you get the dreaded email:

    Downloaded your program and I love it but it crashed. Can you please fix it.

    You didn't expect details ... did you? So now you go through an extended exchange with the user trying to tie down the bug and eventually you give up and add it to the too hard basket in case someone else experiences the problem.

    Of course you could ask the user to install Dr Watson or another tool which captures information when an application experiences an exception but what if the problem is a hang or if you would like internal application state information when the problem occured.

    There is a better way. Using the debug toolkit outlined in this article you can get the users to send to you a file which provides:

    • Trace Output
      Trace output can be captured even in release builds. The trace output can be filtered to allow selective tracing output. Trace output is kept in a file which ensures it is not lost even when your program hangs or has an exception and its size is constrained so it won't fill up the users hard disk.
    • Command Line
      The full command line used to run the program including any command line options.
    • Program Options
      You need to do some work here but the you can save the current program options in effect. This can be done both in a description form and a more machine readable form.
    • Check Files
      Check and dump details about any DLLs or files your program is dependent upon. You just need to tell it which files to check.
    • Check COM
      Check and dump details about any COM objects your program is dependent upon. Once again just let it know which ones to check.
    • Printer
      Dump the settings for the current default printer to help you diagnose printing related problems.
    • CPU
      Dump details about the CPU in the PC.
    • Current Directory
      Debug toolkit will dump the name of the current working directory.
    • Windows Version
      Dump the version details of windows.
    • User definable
      Of course if there are other things you would like to dump you can do that as well.

    The trace log not only contains statements you have traced out. When an error occurs such as an exception, a hang or an assert fails the following details are dumped to the trace log so you can get a picture of what was happening:

    • Call Stack
      The current call stack so you can trace back the error to an exact line of code.
    • Register Dump
      A dump of all processor registers and the 16 bytes of memory that the value in the register points to.
    • Task List
      A list of active tasks on the PC.
    • Loaded modules
      A list of DLLs and other modules loaded into your processes address space.
    • Windows Resources
      Available windows GDI and USER resources.
    • Program Stack
      A dump of the contents of the programs stack.

    These are produced based on the following events:

    • Asserts
      When your code calls the debug toolkit to assert a condition that is false the toolkit will dump the programs state.
    • Exceptions
      When windows traps an exception such as invalid memory access or divide by zero the toolkit will dump the programs state.
    • Automatically Detected Hangs
      If the thread in which you create the debug toolkit object does not process any windows messages for a given period then the toolkit will dump the programs state and kill the program.
    • User Detected Hangs
      If the user presses and holds <CTRL><F11><F12> for a given period then the toolkit will assume a user hang and dump the programs state and kill the program.

    To get all of this you actually need do very little coding and can easily enhance or limit the functionality without the need to modify directly the provided code.

    One of the design goals here was to make this class very easy to incorporate into an existing or new application. The main class is CDebugToolkit. It also has some helper classes but you should never need to access them. CDebugToolkit has been designed to form the base class for your own debug toolkit class in which you will override numerous methods and occasionally call methods on the base class. If you find yourself changing CDebugToolkit then either you have found a bug (in which case let us know) or you are providing a generic enhancement which could be used by others (in which case also let us know). In all other circumstances I would hope the functionality could live in the derived class.

    By sticking with this approach you should be able to easily incorportate enhancements if/when they are made.

    Class Overview

    Public Methods

    BOOL AddFile(const CString& sFile, const BOOL fTestDLL = FALSE, const BOOL fTestFile = FALSE);

    Adds a file to the list of files that will be dumped should a log be required.

    • sFile
      Name of the file to add. If the file must live in a specific directory then include the full path.
    • fTestDLL
      If true then the file will be treated as a DLL and the program will verify it can be loaded at startup.
    • fTestFile
      If true then the file will be tested to see if it can be reached at startup.

    Returns FALSE if the file was to be tested and the test failed.

    BOOL AddCOM(const CString& sFile, const CLSID clsid, const BOOL fTest = FALSE);

    Adds a COM objects to the list of COM objects that will be dumped should a log be required.

    • sFile
      Name of the COM object to add.
    • clsid
      CLSID of the COM object to add.
    • fTest
      If true the the COM object will be tested to see if it can be loaded at startup.

    Returns FALSE if the COM object was to be tested and it failed.

    virtual void Assert(DWORD dwLine, const CString& sFile, BOOL fCondition, const CString& sCondition, const CString& sDescription = "");

    Evaluates fCondition. If it is FALSE then it writes trace output with details of the line, the condition and an optional description. See the DT_ASSERT* macros for an easy way to use this function. This is a virtual function so you can always replace it in your derived class.

    • dwLine
      The source line number containing the assert call.
    • sFile
      The source file containing the assert call.
    • fCondition
      The condition which is being evaluated.
    • sCondition
      The condition in text format.
    • sDescription
      A description of what was being tested.

    virtual void Trace(const CString& sOut, const DWORD dwFilterFlag = 0xFFFFFFFF, const BOOL fForce = FALSE);

    Writes out a message to the trace log. See the DT_TRACE* macros for an easy way to use this function. This is a virtual function so you can always replace it in your derived class.

    • sOut
      Text to trace out.
    • dwFilterFlag
      A 32 bit field which will be and'd with the filter to see if this should be traced out.
    • fForce
      If true then the text is written to the trace log regardless of any filters of the result of the OnTrace method.

    virtual BOOL DumpToFile(const CString& sFile, const CString& sPre = "");
    virtual void DumpToFile(CFile& file, const CString& sPre = "");

    Writes all debug information to a log file. These are virtual functions so you can always replace them in your derived class.

    • sFile
      Name of the log file to create.
    • file
      An already open file to write the log contents into.
    • sPre
      A text string to insert at the beginning of the file.

    Returns FALSE if the file could not be created.

    void Delete(void);

    Deletes the cicular log file used to track trace output.

    void Reset(void);

    Calling this function causes the debug toolkit to reload the debug options. Use this function if you want to turn on/off debug capabilities at runtime.

    void SetTraceFilter(const DWORD dwFilter)

    This function sets the trace filter 32 bit flag which gets and'd with each trace request.

    • dwFiler
      32 bit flag filter.

    void SetSuspendHang(const BOOL fSuspend)

    This function suspends and re-activates the automated hang detection routine. Typically you would suspend this routine when performing particularly long tasks which do not allow the thread to process messages.

    • fSuspend
      TRUE to suspend. FALSE to re-activate.

    static CDebugToolkit* GetDebugToolkit(void)

    This function is a static function used to access the single instance of the debug toolkit. This function is used by the DT_ macros to allow you to easily write code to call instance methods.

    Protected Methods

    CDebugToolkit(void);

    It is essential you call the base class constructor when constructing your derived class. You should also set any of the Debug Toolkit member variables that you don't like the default values for:

    • m_sAppName
      Name of your application. This is used as a filename so careful what you call it.
    • m_sVersion
      Version of your application.
    • m_fExceptions
      True if we are to trap exceptions
    • m_fAssert
      True if we are to handle asserts
    • m_fHangDetect
      True if we are to automatically detect main thread hangs
    • m_fUserHangDetect
      True if we are to look for user nominated hangs
    • m_fTrace
      True if we are to process trace output
    • m_fDumpOptions
      True if we are to dump program options
    • m_fDumpFiles
      True if we are to test and dump DLL and files. NOTE: For lots of files this can be slow.
    • m_fDumpLog
      True if we are to dump the trace log
    • m_fDumpPrinter
      True if we are to dump details about the default printer. NOTE: This is slow.
    • m_fDumpCPU
      True if we are to dump details about the machines CPU
    • m_fDumpCWD
      True if we are to dump current directory details
    • m_fDumpWindowsVersion
      True if we are to dump windows version details
    • m_fDumpCommandLine
      True if we are to dump command line details.
    • m_fDumpCOMInfo
      True if we are to test and dump com object details. NOTE: For lots of com object this can be slow.
    • m_fDumpLoadableOptions
      True if we are to dump machine loadable (but text) options NOTE: This would typically be the options in INI file format
    • m_iUserHangVk1
      Key1 the user has to press when reporting a user detected hang
    • m_iUserHangVk2
      Key2 the user has to press when reporting a user detected hang
    • m_dwUserHangWait
      Maximum milliseconds the user has to press the keys for the user detected hang to be acknowledged. Smaller settings increase CPU load.
    • m_dwHangWait
      Amount of time to wait before automatically detecting a hang
    • m_fPrefixMultilineTrace
      True if we are to prefix 2nd & subsequent lines in a multiline trace output.

    You would also typically add any files and COM objects that you want checking or dumping.

    BOOL Initialise(void);

    This method should be called from your derived classes constructor just before it returns. It is critical that you have set all options prior to calling this function.

    Returns FALSE if an error occured in initialisation.

    Macros

    DT_TRACE[0..3](s, p1, p2, p3)

    These macros are shortcuts for calling the Trace method. Using this macro prevents using filtering. This macro is used in an identical manner to MFCs TRACE[0..3] macros.

    DT_TRACEF[0..3](f, s, p1, p2, p3)

    These macros are identical to DT_TRACE[0..3] except the additional f parameter allows you to pass a 32 bit flag field which will be and'd with the trace filter flag and only traced if at least 1 set bit matches.

    DT_ASSERT(b)

    This macro is a shortcut for calling the Assert method. This macro is used in an identical manner to MFCs ASSERT macro.

    DT_ASSERTS(b, s)

    This macro is a shortcut for calling the Assert method. It extends the DT_ASSERT macro in that it allows you to describe what you are asserting.

    Over-ridables

    These methods have been specifically designed to be overridden in your class that derives from CDebugToolkit. Please do not change the methods directly.

    virtual BOOL OnAssert(void);

    Called to see if you want the assert handled. If you want the assert handled return TRUE.

    virtual void OnEndAssert(void);

    Called once the assert has been accepted and handled. Allows you to do some post assert processing such as maybe saving a log file and suggesting the user mail it to you.

    virtual BOOL OnException(struct _EXCEPTION_POINTERS *pExceptionInfo);

    Called to see if you want to handle the exception. If you want the exception handled return TRUE.

    virtual void OnEndException(void);

    Called once the exception has been accepted and handled. You need to be very careful what you do here as exceptions usually mean something is quite wrong.

    virtual BOOL OnHang(void);

    Called to see if you want to handle the hang automatically detected. If you want the hang handled return TRUE. You may want to ask the user if the program appears to have hung ... just in case.

    virtual void OnEndHang(void);

    Called once the hang has been accepted and handled. Allows you to do some final processing before the task is killed. BEWARE when this method is called you are not running in the main thread.

    virtual BOOL OnUserHang(void);

    Called to see if you want to handle the user reported hang. If you want the hang handled return TRUE.

    virtual void OnEndUserHang(void);

    Called once the user reported hang has been accepted and handled. Allows you to do some final processing before the task is killed. BEWARE when this method is called you are not running in the main thread.

    virtual BOOL OnTrace(const DWORD dwFilterFlag);

    Called to see if you want the trace processed. If you want the trace handled return TRUE.

    virtual void OnReset(void);

    Called to allow you to change debug toolkit processing options before debugging is restarted.

    virtual BOOL OnDelete(void);

    Called to allow you to do some processing before the delete request is handled. Return FALSE to cancel the delete.

    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. This function is usually run prior to completion of the application constructor which limits some of the MFC functionality you can use.

    virtual void OnTestDLLFail(const CString& sFile, DWORD dwError);

    Called when a DLL could not be loaded. Override this if you want to tell the user that a critical DLL is missing.

    virtual void OnTestCOMFail(const CLSID clsid, const CString& sFile, DWORD dwError);

    Called when a COM file could not be loaded. Override this if you want to tell the user that a critical COM object is missing.

    virtual void OnTestFileFail(const CString& sFile, DWORD dwError);

    Called when a file could not be found. Override this if you want to tell the user that a critical file is missing.

    virtual CString GetDumpOtherAtEvent(void);

    Allows you to provide additional data to write to the trace log when a hang, exception or assert event occurs.

    virtual CString GetDumpOtherAtSave(void);

    Allows you to provide additional data to write to the log file when it is being written to disk.

    virtual void GetOptionDescriptions(CString& sOptionDescriptions);

    Allows you to dump in human readable form the options currently in effect.

    virtual void ExportOptions(CFile& file);

    Allows you to dump in machine readable (but text) form to options currently in effect.

    Adding Debug Toolkit To Your Project

    1. Add these files to your project.
      • DebugToolkit.cpp // main debug toolkit class - Do Not Change
      • DebugToolkit.h // main debug toolkit class - Do Not Change
      • FastLogger.cpp - Do Not Change
      • FastLogger.h - Do Not Change
      • FileInfo.cpp - Do Not Change
      • FileInfo.h - Do Not Change
      • K32exp.cpp - Do Not Change
      • K32exp.h - Do Not Change
      • MyDebugToolkit.cpp // shell for your overriden debug toolkit
      • MyDebugToolkit.h // shell for your overriden debug toolkit
      • ProcessorInfo.h - Do Not Change
      • ProgressWnd.cpp - Do Not Change
      • ProgressWnd.h - Do Not Change
      • WindowsVersion.h - Do Not Change
    2. Add VERSION.LIB to your link options.
    3. Select the Generate mapfile option in your projects link options.
    4. In C++ Listing Files options select Assembly with Source Code.
    5. Edit your CApplication derived class to add a new public member variable
      CMyDebugToolkit m_mdt;
    6. Add #include "MyDebugToolkit.h" to the top of you CApplication derived class header file. You may need to do the same in other c++ files which will call the debug toolkit.
    7. Customise your new CMyDebugToolkit class.
    8. Now replace all ASSERT() and TRACE[0..3] calls with the DT_ equivaluent.
    9. When you produce your final release build don't forget to create a zip file containing your projects executables along with the .MAP and all the .ASM files. You will need these to decode the call stack to the original source code line.

    CAUTION: When running your code under the debugger this class will not trap exception events. This is because the debugger takes priority and traps it first.

    There is obviously a lot of functionality here but there are also lots of possible enhancements. Should you make any or have any suggestions please let me know.

    Possible Enhancements

    Here are some possible enhancements I have thought of but either don't have the time or equipment to do.

    • Fix issue with the program not correctly dumping COM object registration details on NT.
    • Add task listing for NT.

    Fixes

    • 28 March 2000 : Fixed toolhelp APIs so they do not cause runtime errors on Windows NT.

    NOTE: I do post fixes to the code project site but sometimes it can take a few days for them to get there. The latest source is always available here.

  • 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