Click here to Skip to main content
15,896,500 members
Articles / Programming Languages / C++

Show Debug Messages from Release Mode Programs

Rate me:
Please Sign up or sign in to vote.
3.53/5 (7 votes)
9 Oct 2009CPOL13 min read 43.4K   156   22   13
A small utility that shows debug messages from programs compiled using release mode

Introduction

This article introduces a small utility that can be used to show debug messages from a program built in release mode.

Background

Sometimes a program runs well in debug mode, but behaves strangely in release mode (even though it compiles with no problem). Sometimes, the program cannot be debugged in debug mode from an IDE. For example, entering a break point may mess up the timing, window focus, painting order of the window, or even the control of flow. In other cases, the program may depend on the actual running environment, that the breaking of execution in the IDE will create a different environment, which in turn makes the program behave differently. This utility allows a program to output debug messages in its actual running state. The program to be debugged runs normally without interruption. The utility has minimum impact on the program’s execution, so the program behaves the same as it should without the added code.

There are plenty of resources on the Internet talking about how to check the various aspects of a program to find out why it behaves differently in debug and release modes. Other people have also developed utilities to show debug messages in release mode. Do a search on “debug release” and you will find some of them. For example, Paul DiLascia has a utility called “TraceWin” that is very useful in debugging. The utility I developed is just another attempt at solving the problem in a very simple way. Which solution you want to use is completely up to you.

Using the Code

Three VS 2005 projects are included in the file attached to this article. Two of them are VC++ MFC projects. The third one is a WinForms project. The first project (DLeeDebug) compiles to the utility, which is the main topic of this article. The second and third projects are demonstration programs that show how to use the utility.

To get a feel of how things work, do the following:

  • Download the attached file and extract all the files to a folder on your hard drive.
  • Run the program DLeeDebug.exe found in the extracted folder dleedebug_package\DLeeDebug\Release. The program may ask you to create a certain folder. You do need to create that folder when prompted.
  • Run the program test_dleedebug.exe found in the extracted folder dleedebug_package\test_dleedebug\Release.
  • Click the button labelled Show Something in the Debug Window.

What you should see in the debug window should be a line of text that says: “Hi there. The value of i is 0”. You can click the button multiple times to see what happens. Now, let me explain in detail how you can use this utility.

When the utility is launched, the following window is shown:

Image 1

Note: 300kkms, inc. is a fictitious company name that does not mean much. If you don't like it, you can remove it from the source code. I put it there to make the utility look cool. Also, many words in the program’s interface should start with a capital letter. I was so lazy that I did not bother to capitalize them.

This program is primarily for debugging C++ programs. To use this program with a C++ program, you need to follow the directions in the help window. The help window is shown by clicking the “?...” button in the above window. It looks like this:

Image 2

I want to emphasize that the following text talks about using this utility to debug your C++ programs. So when I am talking about making changes to the C++ project, adding files to the C++ project, etc., I am talking about your project, not the DLeeDebug project included in the file you downloaded. The DLeeDebug project is the source code for the utility. You don't need to modify it if you are happy with its current functionality.

I use this utility often and I am lazy. I often don't remember where I put the two files needed by the project to be debugged. These two files are named dlee_debug.h and dlee_debug.cpp. That's why I included buttons here so this utility can actually generate these two files for you. These two files should be added to your C++ project that you want to debug (from within Visual Studio, choose “Project”, then “Add Existing Item...”). Then, the file dlee_debug.h should be #included to your files (the #include statement can be copied from the above window, another way to save you some time). In your program where you want to show a debug message, just add a line like this:

C++
dleemsg(_T("Hi, I am here.")); 

Note: I am using a generic form in my source code so your project can be compiled with or without Unicode enabled. If you don't use Unicode and you don't like generic forms, you don’t have to use the macro _T().

You can use the stuff you normally use in calls to the C library's printf(). So the following will also work:

C++
dleemsg(_T("The value of iCounter is %d"), iCounter); 

Notice that the program has a "dir:" box that allows you to put a folder name in it. That should be an existing folder. The default folder is “C:\Temp”. I recommend that you create that folder if it does not exist. Debug messages are stored in a file in the folder specified here. After further investigation, you may decide to change it to a different folder. Read the comment lines in the generated files to learn more about that.

The next thing you need to do is define a symbol dlee_debug in the project you want to debug. That symbol will make the whole thing work. To define the symbol in Visual Studio 2005, open the project properties window by choosing the appropriate item from the "Project" menu. Navigate to “C/C++”, “Preprocessor” page, and put dlee_debug into the “Preprocessor Definitions” box, as shown below. Again, to save you some typing, the symbols that can be used in your project can also be copied from the help window.

Image 3

Now compile your program in whichever mode (debug or release). With DLeeDebug running, run your program. When your program’s execution reaches the dleemsg calls, the message will be shown in the DLeeDebug window.

A demo VC++ MFC project is included in the package you downloaded. In fact, if you have followed the directions above, you have already tried to run this demo program. The program runs and shows this window:

Image 4

In this program, I put just one statement to call dleemsg in the button’s click handler, like this:

C++
dleemsg(_T("Hi, there! The value of i is: %d\n"), i); 

When the button is clicked three times, the DLeeDebug window shows:

Image 5

These three lines of text correspond to the output of the three dleemsg calls in the program. Keep clicking to show more messages. You can test it out.

The enable checkbox will turn on/off the dleemsg calls in the program being debugged. When the window has too many lines, the “keep last line in view” is handy in keeping the latest message in view. Sometimes, messages scroll too fast and you want to temporarily pause the scrolling. You can check the box “freeze” to achieve that. The “top most” checkbox, when checked, will put this utility’s window on top of all other windows. The “clear” button will erase all messages.

Under the Hood

If all you want to do is see your debug messages in release mode, then you don't need to read this section.

The two files added to your project enable your program to write debug messages to a message file in the folder specified. This is obvious if you take a look at the source code.

dlee_debug.h
C++
#ifndef dlee_debug_h_included
#define dlee_debug_h_included

void __cdecl dlee_debug_specific_show_debug_message(TCHAR * format, ...);

#ifdef dlee_debug
#define dleemsg dlee_debug_specific_show_debug_message
#else
#define dleemsg /##/ //so dleemsg is reduced to a comment line
#endif
#endif // dlee_debug_h_included 
dlee_debug.cpp
C++
//comment this out if this project does not use precompiled header
#include "stdafx.h"

#include <stdio.h>
#include <tchar.h>
#include <stdlib.h>
#include <strsafe.h>

//note: use strsafe.lib in your project

//change this to something else if necessary
#define dlee_debug_specific_message_file_path _T("C:\\Temp\\")
#define dlee_debug_specific_buffer_size 2000

//if you are using an older version of vc++, uncomment the following line
//#define dlee_debug_specific_old_version_vc

#ifdef dlee_debug_specific_old_version_vc
#define additional_parameter
#define additional_parameter1
#define dlee_debug_specific_func1 _tsplitpath
#define dlee_debug_specific_func2 _stprintf
#define dlee_debug_specific_func3 FILE * f = _tfopen(pszDebugSettingFile, _T("r"));
#define dlee_debug_specific_func4 _ftscanf(f, _T("%d"), &iDebug);
#define dlee_debug_specific_func5 f = _tfopen(critical_section.m_pszMsgFile, _T("a"));
#else
#define additional_parameter dlee_debug_specific_buffer_size,
#define additional_parameter1 ,dlee_debug_specific_buffer_size
#define dlee_debug_specific_func1 _tsplitpath_s
#define dlee_debug_specific_func2 _stprintf_s
#define dlee_debug_specific_func3 FILE * f;
	_tfopen_s(&f,critical_section.m_pszDebugSettingFile, _T("r"));
#define dlee_debug_specific_func4 _ftscanf_s(f, _T("%d"), &iDebug);
#define dlee_debug_specific_func5 _tfopen_s(&f,critical_section.m_pszMsgFile, _T("a"));
#endif

class dlee_debug_specific_critical_section_host_class{
public:
    dlee_debug_specific_critical_section_host_class(){
        m_b_critical_section_created=
            //i hate TRUE and true, FALSE and false. i only want one set
            (TRUE==::InitializeCriticalSectionAndSpinCount
			(&m_critical_section,0x80000400));
        ::GetModuleFileName(NULL, m_pszFullPath, dlee_debug_specific_buffer_size);
        dlee_debug_specific_func1(
            m_pszFullPath,
            m_pszDrive,
            additional_parameter
            m_pszDirectory,
            additional_parameter
            m_pszFilename,
            additional_parameter
            m_pszExt
            additional_parameter1
        );

#ifdef dlee_debug_launch_folder
        dlee_debug_specific_func2(m_pszMsgFilePath, additional_parameter _T("%s%s"), 
	m_pszDrive, m_pszDirectory);
#else
        dlee_debug_specific_func2(m_pszMsgFilePath, 
	additional_parameter dlee_debug_specific_message_file_path);
#endif

        dlee_debug_specific_func2(m_pszMsgFile, 
	additional_parameter _T("%sdlee_debug_message.txt"), m_pszMsgFilePath);
        dlee_debug_specific_func2(m_pszDebugSettingFile, 
	additional_parameter _T("%sdlee_debug_setting.txt"), m_pszMsgFilePath);
    }
    ~dlee_debug_specific_critical_section_host_class(){
        if(m_b_critical_section_created){
            ::DeleteCriticalSection(&m_critical_section);
        }
    }
    void lock(){
        if(m_b_critical_section_created){
            ::EnterCriticalSection(&m_critical_section);
        }
    }
    void unlock(){
        if(m_b_critical_section_created){
            ::LeaveCriticalSection(&m_critical_section);
        }
    }
private:
    CRITICAL_SECTION m_critical_section;
    bool m_b_critical_section_created;
public:
    TCHAR m_pszMsgFilePath[dlee_debug_specific_buffer_size];
    TCHAR m_pszMsgFile[dlee_debug_specific_buffer_size];
    TCHAR m_pszDebugSettingFile[dlee_debug_specific_buffer_size];
    TCHAR m_pszFullPath[dlee_debug_specific_buffer_size];
    TCHAR m_pszDrive[dlee_debug_specific_buffer_size];
    TCHAR m_pszDirectory[dlee_debug_specific_buffer_size];
    TCHAR m_pszFilename[dlee_debug_specific_buffer_size];
    TCHAR m_pszExt[dlee_debug_specific_buffer_size];
    TCHAR m_pszBuffer[dlee_debug_specific_buffer_size];
};

void __cdecl dlee_debug_specific_show_debug_message(TCHAR * format, ...) {
    static dlee_debug_specific_critical_section_host_class critical_section;
    
    va_list args;
    va_start (args, format);

    critical_section.lock();

    dlee_debug_specific_func3
    if (f != NULL) {
        int iDebug;
        dlee_debug_specific_func4
        fclose(f);
        if (iDebug) {
            dlee_debug_specific_func5
            if (f != NULL) {
                StringCbVPrintf(critical_section.m_pszBuffer, 
			dlee_debug_specific_buffer_size, format, args);
#ifdef dlee_debug_self_name
                _ftprintf(f, _T("%s%s: %s"), critical_section.m_pszFilename, 
		critical_section.m_pszExt,critical_section.m_pszBuffer);
#else
                _ftprintf(f, _T("%s"), critical_section.m_pszBuffer);
#endif
                fclose(f);
            }
        }
    }

    critical_section.unlock();

    va_end(args);
}

There are a few points here that need to be explained. If you compile your project and VC++ complains about something in the above two files, you need to read the appropriate comment lines in the file. I have made things easier for you by offering the necessary lines in the file. You just need to comment/uncomment some of the lines to make the change. That’s probably what you need to do to quiet down the VC++ compiler. For example, comment out #include "stdafx.h" if you are not using it. Since this project is for Visual Studio 2005, you may have a problem if you are using earlier versions. If your project is created by an earlier version than Visual Studio 2005, uncomment the following line to solve that problem:

C++
//#define dlee_debug_specific_old_version_vc 

If you uncomment the above line even when you use Visual Studio 2005, the program still works, but you will get some compile-time warnings. That is because some of the functions are not recommended in this version, but using them in this case does no harm. New, more secure versions of these functions are added to the new version of the library in Visual Studio 2005.

I had to use some macro definitions in this program to do the trick. They don't look pretty. As pointed out by some other people, the use of #defines in this way also pollutes the global namespace and may possibly make debugging more difficult. The solution is to reduce the use of them. If you are using a specific version of Visual Studio, you can come to the DLeeDebug project and use a safer version of the dlee_debug.h and dlee_debug.cpp files. They are included as custom resources located in the res folder, so it's very easy to change them to something else. You simply need to recompile the program after you update those files. For those who are sure their program will not use names so specific to my program, they can keep using the current version of the program. It's true that my program may contain bugs and the #defines will make debugging my code difficult. I have made great effort to make sure my program is bug-free, but I am not top-notch programmer. The piece of code you need to include to your project is tiny anyway. I believe you should be able to debug that piece in case there is really a problem.

To open the projects attached to this article using versions earlier than Visual Studio 2005, you will have to recreate the project files because, for example, Visual Studio 6 will not recognize the Visual Studio 2005 project files.

Other Settings

In the C++ project, two other symbols can be used to control the behavior of the debugging. They are dlee_debug_launch_folder and dlee_debug_self_name. If dlee_debug_launch_folder is defined, the C++ program to be debugged will use the folder from where the .exe file was launched as the debug folder used by dleedebug. Therefore, in dleedebug, this folder should also be specified. If dlee_debug_self_name is defined, then each debug message will contain the program’s name from where the message was sent.

Potential Problems and Improvements

OK. You fired up DLeeDebug, generated two files, added those two files to your project, recompiled your project, then fired up your program. Nothing showed up in the DLeeDebug window. You say: “This utility does not work!” (Or maybe something uglier than that.) That may be true. But before you jump to that conclusion, check to see if you have created the folder that the utility uses. Anyway, you only need to do it once. If you have changed to some other folder, check to see if you have changed the source code to reflect that. Also check to see if you have remembered to define the symbol that the utility needs. Then, if the utility is still not functional, dump it. It’s not a good utility for you. I hope that does not happen.

The program uses a physical file to cache debug messages. That may make the performance of your program suffer. However, because it is only used when you are debugging your program, it is not a big problem. When you are done debugging your program, all you need to do is to remove the defined symbol (dlee_debug) from your project (through the project properties window mentioned above) and recompile. Those dleemsg calls will be reduced to nothing and will impose zero impact on your program’s performance.

However, if performance is really a concern during debugging, the utility can be changed to use memory-mapped file objects to communicate with your program. This is a one time change to make the whole thing work better. I currently don’t have time to do this. I hope somebody else can contribute to make it happen.

You may have noticed that this program can also work with your C# programs. There is a button on the help window that can be used to generate a C# file for use in this situation. However, you can normally compile your C# program into a console application, and output messages to the console window, and change it back to a Windows application after you get all the bugs out. You can use this program with other types of C# applications, though. For example, you can use the program in your ASP.NET applications with no problem. The C# file generated by the utility works differently than the C++ version. It checks the enabled state of DLeeDebug only once – when the C# program is launched. So, if you want to see the debug messages, you need to check the enable box of DLeeDebug before you run your C# program. The display cannot be turned off while the program is running. This is done so that the debugging code does not pose a big impact on the performance of your program. It is still a good practice to remove the debug module before you release your program.

If you are a Visual Basic programmer, you may want to use the utility with your VB programs. A module can be added to your VB program (both VB6 and VB.NET) to achieve this. Since I don't have time to write this module, I did not offer it in DLeeDebug. (There is no button on the help window that generates the VB module for you.) Sorry for this. It is a simple task. I hope somebody else can finish it.

Conclusion

The utility presented here can be used to show debug messages in release-built programs. I know that lots of research has been done on this topic. You may argue about the usefulness of this utility. Let me tell you this. You can buy a milling machine that is controlled by a computer and capable of doing very complicated and precise jobs. That may cost you thousands of dollars. It does not mean that you can't have a $100 drill press, or even a $20 hand drill in your shop. Sometimes a hand drill can do a specific job better then a big million-dollar machine. What I want to do here is to add a simple tool to your tool box so you have one more option when you are frustrated with strange problems in your programs. If it does not work well, you can always go look for other solutions. I welcome any comments (especially negative ones). Good luck and happy programming!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Other Scania AB
Sweden Sweden
I have been a carpenter and machine operator since I graduated from high school. I like working on things I can see. At age 7 I was playing in a machine shop in my dad's school and injured myself on my head by a lathe. I have a scar on my head with no hair in that area. However, my love of machines did not go away.

At my spare time I read some programming books and like some of the modern languages such as C# and C++ very much. But after repeated attempts to change myself into a "good" programmer, spanning 20 years, I am still pretty dumb. I tried once to find a job in Dallas as a programmer. They liked my demo projects but were very unhappy with the fact that I did not have a bachelor's degree in computers. Needless to say I did not get that job.

Since I am looking at programming from a non-programmer's point of view, my programs sometimes look unconventional. That drew some criticisms. I don't mind negative views on my programs. I even feel good when people say something like that.

I am the main customer of my programs. They work well for me.

I am a carpenter anyway!

Comments and Discussions

 
GeneralMy vote of 2 Pin
KarstenK6-Oct-09 1:17
mveKarstenK6-Oct-09 1:17 
General[Message Deleted] Pin
dxlee6-Oct-09 3:32
dxlee6-Oct-09 3:32 
GeneralI like that part Pin
loyal ginger6-Oct-09 4:46
loyal ginger6-Oct-09 4:46 
GeneralRe: I like that part Pin
Stefan_Lang6-Oct-09 23:45
Stefan_Lang6-Oct-09 23:45 
General[Message Deleted] Pin
dxlee7-Oct-09 1:06
dxlee7-Oct-09 1:06 
GeneralRe: I like that part Pin
loyal ginger7-Oct-09 8:52
loyal ginger7-Oct-09 8:52 
GeneralThanks Pin
theCPkid5-Oct-09 19:35
theCPkid5-Oct-09 19:35 
Generaldbgview Pin
xoox4-Oct-09 10:38
xoox4-Oct-09 10:38 
GeneralGood. Pin
dxlee5-Oct-09 4:10
dxlee5-Oct-09 4:10 
GeneralRe: Good. Pin
theCPkid5-Oct-09 20:02
theCPkid5-Oct-09 20:02 
GeneralRe: Good. Pin
Kim Togo13-Oct-09 1:29
professionalKim Togo13-Oct-09 1:29 
GeneralRe: Good. Pin
theCPkid13-Oct-09 2:08
theCPkid13-Oct-09 2:08 
GeneralGood Pin
Md. Marufuzzaman3-Oct-09 3:04
professionalMd. Marufuzzaman3-Oct-09 3:04 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.