Release Date: 9/9/2004
| Update Date: 9/9/2004
John Robbins Download the code in this article: BugsLayer0102.exe (42KB)
This page
Debugger Condition Compilation Tracking and TraceSwitch Ask TraceListener - Listener is listening to BugsLayertracelistener usage and implementing more new features
Because Microsoft has released Visual Studio .NET Beta 1, many readers have begun to study .NET. But don't make a mistake - .NET is a new platform. A large number of publicity has been conducted on key features such as ASP .NET and ADO .NET. But for me, one of the biggest advantages of .NET lies in that it solves the most difficult problem in all programming issues - memory damage and leakage.
With the public language runtime library (CLR), you can concentrate on solve user issues, rather than waste time to find memory damage. In addition, Microsoft finally has a programming model for accessing the system's new system range. The consistent object-oriented base base library (BCL) can also eliminate a large number of errors.
Don't this mean that there is no need to write a bugslayer column article? There will be many problems before Visual Studio enable your ideas, such as logical errors, and misunderstanding of system, performance, and scalability.
This month, I intend to help you start the correct .NET development. You may recall, and the effective old assertion is very in line with my mind. When I first started learning .NET, I feel that if I have lost, because I love the Assert and Trace macro. Therefore, I want to explain how to assertize and track in .NET. To this end, I first need to discuss the debugger provided in the .NET SDK. Then, I will discuss assertions and conditions and provide you with a better assertion tool than BCL.
All the code in this month's column is developed with .NET SDK Beta 1, so you don't need to compile and run using complete Visual Studio .NET. This code will continue to work properly unless Microsoft changes the interface in Beta 2. What you should understand about .NET SDK is that it is extremely stable; I have been using it from the PDC version, and there is no problem.
Debugger
Usually, debugging is debugging. In other words, no matter what the operating system you use, always set breakpoints, dump memory and do some other common operations. For those developers with Win32®, background, .NET debugging has a slight function. You can add and separate in the .NET process (right, it is separated) debugger! Now, debugging the running server application should be more easier than before.
.NET SDK contains two debuggers: Cordbg.exe and DBGurt.exe. Among them, DBGurt.exe is a better GUI, Cordbg.exe is based on the console version, which has several additional features, but it is more difficult to use. Cordbg.exe Magic? Command is very important because you can use it to get help about all commands. By entering the command name later, you can get more help about a single command. Typically, if you have used debuggers such as Windbg, you should be able to speculate. However, I still intend to show you a few commands I feel interesting in Cordbg.exe.
I hope that the first command you pay attention to is WT, which will pass the application through the application, and print the calling tree of each managed method called. I found that WT is useful to understand the call relationship between various system classes. Sometimes, the wt command will display many information that is not use, but it is very useful to see who is doing any operations in Bcl. The WT command can track the program flow from the end of the current code line of the debugger to the end of the corresponding method. The simple program in Figure 1 demonstrates the WT command. Once you start performing Cordbg.exe on the program, you will start from the MAIN to start the FOO code line. If you type "WT", the output will be as follows:
(CordBG) WT
1 HappyAppy :: main
6 HappyAppy :: foo
6 happyappy :: bar
10 HappyAppy :: Baz
7 HappyAppy :: bar
7 HappyAppy :: foo
2 HappyAppy :: main
39 instructions Total
And you can see the call diagram of each element called by the foo. After executing the WT command, the instruction pointer will point to the code line after the call to the foo.
Another useful command is F (FUNCEVAL), which allows you to call the outside of your class. For non-static methods, keep in mind that the "THIS" is passed as the first parameter. There is also a group that allows you to create an object, but I have not guess their working methods.
Cordbg.exe uses a little trouble, while DBGurt is like a dream. From the information that I collected in PDC last year, DBGURT uses the same code base with Visual Studio .NET, so you can experience the final Visual Studio .NET debugger. Just from the small preview in .NET SDK, I really like what I see. First, the Visual C 6.0 debugger should be able to dialog (such as Modules, Threads, and BreakPoints), which can eventually stop, so that the debugger is more easier to use. In addition, in order to prevent all of these barrible windows from being required to use a 35-inch monitor due to too many screen areas, the debugger has a very intuitive mode. In this mode, multiple darkengers can share the same with Analog label window area. Figure 2 shows a variety of stuck windows (such as modules, threads, and breakpoints display), all windows share the area of the bottom of the screen.
Each thing that is not committed to the debugger UI exists in the SDK's beta 1 version. Specifically, DBGurt lacks C # syntax coloring and certain types of breakpoints. However, there is enough feature in dbgurt to let you get .NET debugging. DBGURT has several new skip count bitbus breakpoints very useful. Now, when the skip count is equal to a specific number, or the multiple of the specific numbers, or when it is greater than or equal to a specific number, you will be interrupted.
Since I have discussed the key points of the debugger, now I am going to discuss the conditional compilation, because I will leave it, tracking and assertion will no longer exist!
Back to top
Conditional compilation
All three language compilers (C #, Visual Basic, and C ) in .NET SDK support standard #ifdef ... # Endif style conditional compilation. However, C # and Visual Basic now have a very good custom property that supports conditional compilation in another way. When declaring the C # or Visual Basic method, you can use this conditional property to determine when the method can be called. The advantage of this conditional attribute is that the calling party does not have to perform any additional work when calling a method. If the compilation instruction in the conditional properties specified by C # DEFINE, or set the compilation instruction in the condition attribute of the C # and Visual Basic, the compile command specified by the compiler option will be modified. Otherwise, the compiler does not generate the Microsoft Intermediate Language (MSIL) required for this call. The following program shows the usage of the conditional properties in the C # example:
Using system;
Class happyAppy
{
[Conditional ("Debug")]]
Public static void debugonlymethod ()
{
Console.writeline ("Debug is Active !!");
}
Public static void main ()
{
Debugonlymethod ();
}
}
The following is the same example written in Visual Basic:
Imports system
Imports system.diagnostics
Public Module HappyAppy
SUB DebugonlyMethod ()
Console.writeline ("Debug is active !!")
End Sub
Sub main
Debugonlymethod ()
End Sub
End module
DebugonlyMethod appears in these two examples only when you define Debug when compiling. The advantage of this approach is that there is no need to use a lot of #ifdef ... # Endif when performing DebugonlyMethod calls. If you have not understood how the condition attribute works, I suggest you run the previous program. As you start, the conditional compilation feature in C # and Visual Basic makes it easy and pleasant to use a lot of assertion.
Back to top
Track and TraceSwitch
BCL provides two identical classes to process tracking and assertions: Trace and Debug, both classes come from System.Diagnostics namespace. Interestingly, these two classes have exactly the same properties and methods, but they are not derived from the other party, nor derived from any base class other than Object. The concept between these two classes is: When you define the Debug class, the Debug class is active; and when you define Trace, the Trace class is active. According to documentation, Microsoft wants you to use Debug for debug versions, and use Trace for all versions. One functionality for the .NET's network administrator is that you can enable light diagnostic tracking, so you should always define Trace. However, like the tracking in the operating system, if you don't follow the strict format settings and only the least content you need to understand the minimum content required by the program flow, the output may be too much.
Tracking methods on the Trace and Debug class include Write, WriteiF, WriteLine, and WriteLineif. The only difference between WRITE and WRITELINE is that WriteLine will place a carriage return and a newline character at the end of the output. Writeif and WriteLineif are only performed when the first parameter is calculated as true. This can provide you with conditional tracking. Although this sounds good, it is not a good idea to use Writeif and WriteLineif. Can you see what is wrong with the code snippet below?
Debug.writeLineif (Showtrace
"NUM =" Num "Value out of Range!");
The problem is to perform full computation and generation of the string to be displayed before calling Debug.WriteLineif. This means that when this line is executed, even if ShowTrace is a false, you still have all system overheads generated by parameters. Unlike this, you should use normal conditions before making calls, just like the code snippet below.
IF (True == BShowTrace)
{
Debug.writeline ("Num =" Num "Value Out of Range!");
}
Here, the system overhead generated by the string parameter is avoided before the condition is calculated as true and you will perform tracking. A disadvantage is that you have to perform more types of operations.
Because tracking is a good way to find problems in the field, Microsoft adds another class to System.Diagnostics to help you determine the tracking level: TraceSwitch. TraceSwitch is a simple condition class that makes it easier to track individual assemblies, modules, and classes. The purpose of TraceSwitch is to make it easy to determine the tracking level so that your code can instantly generate an appropriate output. You can determine the tracking level through the attribute of the TraceSwitch class, because if the level of setting is appropriate, these properties will return all true. Figure 3 shows the tracking level and the value thereof.
Creating and using traceSwitch has some trivial. The following code snippet illustrates the creation and use of TraceSwitch. I use WriteLinei to compress the code snippet.
Public static void main ()
{
Traceswitch thiswitch = new traceswitch
"Switchytheswitch", "Example Switch");
TRACE.WRITELINEIF (THHESWITCH.TRACEERROR,
"ERROR TRACING IS ON!");
TRACE.WRITELINEIF (SHESWITCH.TRACEWARNING,
"Warning Tracing is on!");
TRACE.WRITELINEIF (THESWITCH.TRACEINFO,
"Info Tracing IS ON!");
TRACE.WRITELINEIF (THESWITCH.TRACEVERBOSE,
"Verbosewitching is on!");
}
At this point, you may want to know how to set the tracking level. The TraceSwitch constructor uses two parameters: switch name and switch description. The important value is the switch name because you must use the exact string to set the tracking level. The first setting switch is to use all the switches in HKLM / Software / Microsoft / Complus / Switches use global registry entries. Just create a DWORD value that matches the switch name and sets the number to the corresponding number specified in Figure 3. Another way to set a specific tracking level is to use environment variables, ie_switch_ followed by the switch name. Take the previous code snippet as an example, the environment variable should be _Switch_Switchytheswitch. Set the environment variable to the tracking level you want to see. Keep in mind that any environment variable will rewrite the registry settings.
The idea of putting all applications in a single environment variable seems to be an accident waiting. I can easily see the very real possibility of naming conflicts between various companies. I feel that if there is a way to specify the tracking switch from the input file, it will be much better. In this month's Bugslayer code, I created a class called BugsLayertraceSwitch (all relevant code) category (all relevant code) can be found at the top of this article). The construction function of BugsLayertraceSwitch also uses the third parameter, which is used to read the files set by the tracking level. Assuming that the file name you pass to the constructor has sufficient information so that it can be found. BugsLayertraceSwitch is part of my Bugslayer assembly, so you only need to include BugsLayer as an import file. The file format is very simple, as shown in the code snippet below.
The format is =
HappyAppyclassswitch = 4
Switcheroo = 0
Please note that rows with semicolons are considered a comment line.
Since you have already understood how to use the trace function in .NET, let us discuss the output of the output. By default, the tracking output will go to the attached debugger in addition to the traditional Win32 OutputDebugstring calls. Keep in mind that .NET is not Win32-based traditional application, so the debugger output will be different from what you are used to see. Behind this column, I will discuss the output in detail.
At the beginning of this section, I mentioned that the Trace and the Debug class have tracking methods. But which method you should use? I decided to use Trace to track. In this way, I have a consistent way when they are tracked, but don't have to take a consideration before typing tracking statements. Since you have already understood the way of working, let's talk about how the assertions in .NET work.
Back to top
assertion
As I mentioned earlier, the Trace and Debug classes have an Assert method. I only use Assert in the Debug class. These methods are identical, but I don't want to pop up an unexpected message box in the process of running the application, so I insist on using the Debug version. The default assertion message box is shown in Figure 4. Please note that .NET assertions come with a ready-made stack audit (with filling source and row-finding).
Figure 4 Debug assertion
Although the C Assert macro is easier, but the debug.assert method in the C # is also good; the overloaded Assert method is only used by different parameters. The first method uses a single Boolean condition; the second method uses a Boolean condition and a message string; the last method uses a Boolean condition, a message string, and a detailed message string. Using .NET's Assert means you must complete more types of root more than traditional C Assert. Because there is no .NET macro, you need yourself into the string in order to display an assertion string in the assertion message box. The following code snippet shows all three types of work mode of C # Assert. Debug.assert (i> 3);
Debug.assert (i> 3, "I> 3");
Debug.Assert (i> 3, "I> 3", "This Means I Got Parameter");
Of course, because C # supports conditional properties, you only need to define debug to enable assert code. For Visual Basic, you need to compile each assertion with a real condition, as shown below:
#If debug the
Debug.assert (i> 3)
#End IF
As I mentioned earlier, if the code runs in interactively, the assertion in .NET will be displayed in the message box. I gave a drum in .NET, I found that the assertion of all applications can be fully reached into the file. But use these technologies require you to bother. In addition, in addition to your computer for development, do not use them on any other computer. With this premise, the following is the steps that need to follow. In HKLM / Software / Microsoft / Complus, you need to add two DWORD values (Noguionassert and LogTofile) and a string value. Set Noguionassert to 1 to disable the message box. Set the logtofile to 1 to enable the function of logging the log to the file. Set the logfile to all assertions to output the full name and path. However, before changing global settings, you should know how to better control assertion output through TraceListener.
Back to top
Tracelistener - Listener is listening
Tracking and assertions are unique in .NET because control output is quite easy. There is a member listener, which is a member of the TraceListener object. TraceListener sends the output of tracking and assertion. As you can imagine, you can have a trace listener for sending output to OutputDebugstring and a tracking listener for sending output to files. The functionality of the Trace and the Debug class is an enumerating the TraceListener class in the listeners array one by one, and allows each class to process. This allows you to add or reduce the output. The default TraceListener (DefaultTracelistener) sends trace output to OutputDebugstring through its WRITE and WRITELINE methods, as well as the LOG method of all additional debuggers. If the user logs in in interactive mode, DefaultTraceListener will send all assertions to the message box through its fail method.
BCL comes with some predefined TraceListener, you can add them to the listnes array as additional output means. The first is an EventLogTraceListener class that sends the output to the specified event log. The second is TextWritertracelistener, which can direct the output to TextWriter or Stream, such as the Console.out function of FileStream. The following code shows how to add a TextWritertraceListener to the chain. Debug.Listener.Add (New Textwritertracelistener ("trace.log");
Back to top
Usage and implementation of BugsLayertracelistener
Ability to replace the tracking output is an interesting idea. However, from the actual point of view, I would rather have a tracelistener. First, I can control it from any location in the application. If there are multiple independent tracelistener, each of them will be more difficult. Second, a TraceListener can handle all the outputs of the entire application. Therefore, I wrote BugsLayertracelistener to simplify my work. It is a fully inserted replacement of all TraceListener. Tracking outputs can go to any combination of multiple locations (additional debuggers, files, and standard OutputDebugString). As an except that you can go to the message box and event log, you can also go to all of the above positions. It is very simple to add BugsLayertracelistener to the Debug or Trace object.
Debug.Listeners.Remove ("default");
Bugslayertracelistener btl = new bugslayertracelistener ();
Debug.Listeners.Add (btl);
One thing you need to do is to delete the defaultTracelistener so that BugsLayertraceListener can control the output.
If you look at the code of the bugslayertracelistener itself, there is no exciting content. Interesting parts are located in BugsLayerwin32.cs (see Figure 5). I need to make sure that BugsLayertracelistener has to check if there is an interactive user before popping up the message box. This requires that I use special structures to transfer the Win32 API. Typically, calling the Win32 API from the hosted code will be confused. If you need to mention some of the code to .NET, I hope BugsLayerwin32.cs can provide some tips on how to do this.
Back to top
More new features
Non-managed Visual C compilers and linkers seem to have some interesting new features. After all discussions about .NET, new features in traditional Visual C will receive less attention. With significant improved debuggers and new compiler logos, Visual C .NET will be a mandatory upgrade for all users of the installed C / C code base. I understand these flags by reading the Visual C Compiler Reference in the .NET SDK documentation.
My favorite new logo is CL.EXE for / RTC for runtime error check. Some errors it examine include local memory overflow and underflow, uninited memory access and data truncation. It is necessary to remember that these checks occur at runtime. The new / GL (full programmatic) flag of CL.EXE and / LTCG (Link Time Code) provides an unprecedented program optimization level. The most interesting optimization is inline inline, that is, in the module in the module (even if the function is defined in another module). Another optimization of the x86 CPU is a custom call convention that allows compilers and linkers to transfer parameters between function calls. The cl.exe / GS (Generate Security Check) option will insert the code to check if there is a buffer overflow that causes the return address to rush out of the stack. After enable / GS, any virus or fraud code that is trying to take over your program will pop up a message box and terminate the process immediately. The last interesting new flag / pdbstripped is a link.exe flag that generates only the second PDB file using only the Public Symbol and Frame Pointer Optimization (FPO) data. This way, you can send the second PDB file to your customers so you can get the full call stack and information from the Dr. Watson log. In general, there is a very good new feature in Visual C .NET, so I can't wait to migrate the existing code in the past.
Back to top
summary
I hope this profile on .NET will make your development work easier. Specifically, BugsLayertracelistener should make your tracking and assertion easier. Don't forget to find new features at the old place when investigating .NET. And, you should always write better diagnostic code from the beginning to make your work easier.
Because everyone should frank their mistakes, I have to admit that there is a small error in the Smooth Working Set utility of my 2000 issue column. It is very embarrassed that I have a reassign problem in the cfilebase :: appendtodatabase :: appendtodatabase :: appendtodatabase :: AppendTodatabase. The updated code is shown in Figure 6. Thanks Eric Patey and Richard Cooper reported this issue.
Tips 41 (from TED YU): In your 2000 April 2000 column, you complain that the STL's BSearch function does not return value. Below is a BSearch function that returns an iterator corresponding to the value found. If this value is not found, the function will return end ().
Template inline _fi bsearch (_fi _f, _fi _l, const _ty & _v)
{
_Fi _i = lors_bound (_f, _l, _v);
IF (_i == _l || _v <* _i)
{
Return_L;
}
Return (_i);
}
Trip 42 (from Patrick Gautschi): Microsoft has released a set of interesting tools to help users track memory leaks named UMDH (user mode dumps). You can download these tools from how to use umdh.exe to find memory leaks. Make sure you read the entire knowledge base article on how to use them.
John Robbins is one of the founders of Wintellect, which is a software consultation, education and development company dedicated to Windows and COM programming. He is the author of Debugging Applications (Microsoft Press, 2000). To contact John, visit http://www.wintellect.com. This article is taken from MSDN Magazine 2001.