Overview of the Trace and Debug Classes

   

Overview of the Trace and Debug Classes

The .NET Framework provides two essentially identical classes Trace and Debug for use in debugging applications and providing error checking while developing applications. The only difference between the two classes is that the Debug class code will only run if you build your application in debug mode. Whenever you build a release version of your application, any code that references the Debug class is removed. The Trace class, on the other hand, always places executable code in the application regardless of whether the application is built in debug or release mode. The Trace class is especially useful in debugging release-only issues.

To use either the Trace or Debug class, you need to define either the TRACE or DEBUG symbol, create a new Listener object, and add Trace or Debug calls to your code.

Adding Trace Statements

Trace statements are useful in determining the flow of execution within an application without stepping through the application with a debugger. This is especially important if you are debugging an application and the timing of the execution is critical. A trace statement produces the equivalent result as MFC's TRACE macro. You can use either the Trace::WriteLine() or Debug::WriteLine() static method to output a trace statement in your code.

One thing to keep in mind is that you need to make the statements as meaningful as possible. If you place several Debug statements in your application, some possibly within loop statements, the amount of information you may get could be large. Another possibility is if you have a base class and several derived classes, and the base class issues a Debug statement, you have to use the debugger to find out which derived class has caused the Debug statement to be issued.

Within your application, you can use several predefined macros to aid in creating meaningful statements. Some of these macros include the __LINE__ macro, which signifies the current line number in the source file. Likewise, the __FILE__ macro can be used to get the current name of the source file. One of the most useful macros available is the __FUNCTION__ macro, which is used to get the current function name.

The example shown in Listing 13.3 uses the Debug class to output trace statements. When the application is compiled for debug mode, the trace statements are displayed; when it is compiled for release mode, the statements are not displayed. The reason, as stated earlier, is because the Debug class doesn't produce code in release mode. If you change the class to Trace, the statements are produced in both debug and release modes.

Listing 13.3 DivByZeroException.cpp The Debug Class Used to Display Trace Statements to the Console Window
 1: #using <mscorlib.dll>  2: #using <System.dll>  3:  4: using namespace System;  5: using namespace System::Diagnostics;  6:  7: void Calculate( int nDiv )  8: {  9:    // Indent the trace statement for clarity 10:    Debug::Indent(); 11:    Debug::WriteLine( "Entering Calculate()\n" ); 12: 13:    try 14:    { 15:       // Force the DivideByZeroException to be thrown 16:       Console::WriteLine(10/nDiv); 17:    } 18:    catch (DivideByZeroException* e) 19:    { 20:       Console::WriteLine( e->get_Message() ); 21: 22:       throw( new ArgumentException( "Divide by Zero", e ) ); 23:    } 24: 25:    Debug::WriteLine( "Leaving Calculate()" ); 26: } 27: 28: int _tmain(void) 29: { 30:     // Add a listener that sends output to the console window. 31:     Debug::Listeners->Add( new TextWriterTraceListener( 32:     System::Console::get_Out() ) ); 32: 33:     Debug::WriteLine( "Entering Main()\n" ); 34: 35:     try 36:     { 37:        Calculate( 0 ); 38:     } 39:     catch( ArgumentException* e ) 40:     { 41:        Console::WriteLine( e->InnerException->get_StackTrace() ); 42:     } 43:     __finally 44:     { 45:        Console::WriteLine( "Program Ending." ); 46:     } 47: 48:     Debug::WriteLine( "\nLeaving Main()" ); 49: 50:     return 0; 51: } 

When you execute the application in debug mode, you'll notice that the trace statements are displayed on the console window, as shown in Figure 13.2.

Figure 13.2. Trace statements created with the Debug class.

graphics/13fig02.gif

Note that the Leaving Calculate() trace statement was never displayed because the ArgumentException was thrown, which caused the function to immediately exit. This is a good example of how trace statements work. You can tell what portion of the source code was never executed based on the trace statements.

Asserting on Invalid Values

Using the Assert() method of the Debug and Trace classes is essentially the same as the ASSERT macro in MFC. The value within the parameter of the Assert() method is evaluated, and if it is false, the Assert() method displays a message. The best use of the Assert() method is to determine whether a value is what you expect it should be. If it isn't, the Assert() method's message alerts you. This is a very effective method of catching hidden problems when you are unit-testing an application.

Add an Assert() statement to the Calculate() method shown in Listing 13.3 after the two Debug statements. The Assert() statement checks for an invalid nDiv value with the following statement:

 Debug::Assert( nDiv != 0, "nDiv must not be equal to 0" ); 

After the example is compiled and run, a message box is displayed, as shown in Figure 13.3.

Figure 13.3. Assertion Failed message box with options to abort, retry, or ignore.

graphics/13fig03.gif

When an assertion fails, you have the option to quit the application immediately, enter into the debugger at the location the assertion failed, or ignore the assertion and continue executing.


   
Top


Sams Teach Yourself Visual C++. NET in 24 Hours
Sams Teach Yourself Visual C++.NET in 24 Hours
ISBN: 0672323230
EAN: 2147483647
Year: 2002
Pages: 237

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net