(How)
C^# You Are - 26 August 2007
VS2005 debugger is the topic this week. For most the Visual Studio 2005 IDE is
the gateway to .NET development. The debugger is critical to helping you
find, identify and resolve issues in your code. Without good debugging skills
you will waste valuable time trying to get your code working.
- A particular block of code is not working properly. What is the best way to
diagnose what is going on in VS2005? Answer
If you said put a trace statement into the code and run it then please stop right
now, take your keyboard and hit yourself in the head with it. This is really
the poor of the poor when it comes to debugging. You should only resort to
using trace statements when the problem can not be solved more easily.
The best solution is to place a breakpoint in the misbehaving code using the F9
key or the Debug\Toggle Breakpoint menu item. You can also click in the indicator
margin on the line of interest to toggle the breakpoin. A breakpoint is indicated
by a solid, red circle. Place the breakpoint as close to the point of where
you believe the problem is as possible.
Note that breakpoints can be applied to each statement on a line. Therefore
you should consider placing each statement on its own line. If you do not
then it is hard to put a breakpoint on the statement in question. For example
a for loop has three statements. You can set a breakpoint
on each one but not if they are on the same line.
- When you set a breakpoint on a line and go into debug mode you see the breakpoint
icon become hollow and an exclamation point appears. What does this mean? Answer
Breakpoints are tied to symbolic lines. If the code being debugged does not
match the source line that the breakpoint was tied to then the breakpoing will be
disabled. The hollow breakpoint symbol is used. In this case hovering
over the breakpoint will normally display a message indicating the symbols couldn't
be loaded.
In general the source code is built and debugged together. However there are
exceptions. If you attempt to debug a third-party assembly and you do not
have the source code that was used to build the assembly then you can run into this
problem. If the assembly itself is not loaded yet (remember that assemlies
are loaded on demand) then you will also see the hollow breakpoint. When the
assembly finally does load then the breakpoint will be enabled.
A small note here. There is actually an option to allow the breakpoint to
be tied to a line even if the original source does not match the actual assembly
code. You can set this through the Condition property for the breakpoint.
I don't recommend this unless you are comfortable with debugging.
- You want to break into your code to debug an issue. However the code is called
on many different threads and you only care about a particular thread. How
can you debug this? Answer
Set a breakpoint on the line in question. Run the program. Each time
you hit the breakpoint check to see if the current thread is the one you want.
If so then debug the issue otherwise hit F5 to continue. Now, if this was
your answer pleas take your keyboard and hit yourself in the head with it.
Breakpoints support a variety of features including the ability to only hit a breakpoint
after a certain number of iterations or only when a certain condition occurs (such
as a variable containing a certain value. One of the more interesting features
is the filter feature. BTW these are available by either going to the Breakpoints
window in the IDE or by right clicking on the breakpoint circle. The filter
allows you to specify that the breakpoing applies only to a certain machine, process
or thread. The process/thread can be identified by name (preferred) or by
identifier.
In the initial question you should give your thread a unique name. Set a breakpoint
on the line in question. Then right-click and add a filter such that it only
hits the breakpoint when the thread name matches the name you gave the thread you
are interested in. But how do you name a thread? Good question.
A thread can receive a name through the Thread
class. You should consider naming your threads both for breakpoints and to make it easier to
see which thread you are dealing with when doing performance analysis or when examining
an exception.
In the cases where you can't use a thread name, such as when using the thread pool,
then you have no choice but to either use the thread identifier, if available, or
revert to setting a breakpoint and enabling/disabling it when you start running
code in the thread pool. It is not pretty but it works.
- You
configure the IDE the way you want. You set up your toolbars and windows
exactly the way you want them. You then install an add-in and they get reset.
How can you prevent this from happening again? Answer
The default layout of VS2005 should be ideal for everybody. Adapt to the layout
MS has chosen. Yeah right! One of the nicest features of VS2005 is the
ability to save and load your IDE settings. This includes more than just the
window layouts. It includes all your preferences such as tabbing, help system,
project locations, etc. Once you have configured the IDE the way you want
go to Tools\Import & Export Settings and export your settings to the file system.
You should then store this file off somewhere safe.
Whenever the IDE gets mangled or you need to reinstall or you want to work on another machine import the settings file using the same menu. Unfortunately the external
tools that you might have configured do not import properly.
- What is a tracepoint? Answer
A tracepoint is a breakpoint with an action associated with it. Currently
the action can either be to print a message or to execute a macro. Through
a macro you can do just about anything. With a tracepoint you can actually
continue execution so even though it is a breakpoint you don't actually have to
stop the program (although it will technically stop for a second).
To set a tracepoint right-click the breakpoint circle and select When Hit to specify
the desired action.
- While executing your code an exception occurs deep within a class library you use.
The class library uses exception handling to recover. How can you debug what
is happening? Answer
The Exceptions dialog is your friend here. Within the Exceptions dialog you
can specify what the debugger should do when the exception occurs. When an
exception occurs (as soon as the throw statement executes) the
debugger kicks in. This is a first chance exception. After the debugger
returns control to the program the program can handle the exception. If the
exception is not handled then the debugger is called again. A second chance
exception will always cause the debugger to take control and prompt the user to
deal with it.
Within the Exceptions dialog there is a list of all the common C RTL, .NET and C++
exceptions. For each exception you can chose what to happen if the exception
is thrown (first chance). In most cases the default action is to simply let
the program handle it. However, as in the scenario given above, sometimes
you want to know the state of the program when the exception is thrown rather than
the state after it is handled by the application. By checking the Thrown box
the debugger will give control to the user as soon as the exception is thrown.
So, to debug the above scenario you must identify the exception that was originally
thrown (hopefully the class library didn't completely eat it). In the Exceptions
dialog check the Thrown box. Run the program. As soon as the exception
is thrown you will get control. Most likely you will be looking at the disassembly
window since you will not have the source code. However, at least for .NET
apps, you will have access to the locals, call stack and parameters so you can diagnose
what is going on.
- What is a managed debug assistant (MDA)? Answer
Added in VS2005 these are similar to exceptions, at least to us. They are
available only while running under the debugger. MDAs are designed to identify
common problems in code and alert the user. For example in WinForms you can
not modify the UI on a non-UI thread. An MDA exists that will detect this
case. When the MDA determines that the problem has occurred it will effectively thrown an exception that will cause the debugger to kick in. Unlike exceptions
MDAs should rarely be ignored as they identify potential problems in your code.
Also remember that MDAs, unlike exceptions, only occur while actually debugging
under VS2005.
Like exceptions, MDAs can be caught or not through the Exceptions dialog.
- While debugging your program you hit a breakpoint. You notice a variable does
not have the correct value. You identify the problem but want to continue
debugging. How can you modify the variable's value without restarting the
program? Answer
The Watch, Locals, Autos and This windows allow you to edit the variable values.
Double click in the Value column to edit the value. Of course the value must
be valid. Changed values are shown in red to let you know that they have changed.
- You are debugging a function several levels deep in the callstack. You identify
a potentially bad input value and you want to figure out how it got there.
How can you determine what parameter were passed to the first function in the call
chain? Answer
One of the important things to realize about function calls is that each function
call results in a new stack frame. The stack frame contains the parameters
and locals of the function. The collection of stack frames results in the
call stack. The debugger uses the stack frame to determine what to display.
The Locals window displays the local variables of the stack frame. The Call
Stack window controls what stack frame is currently active. By clicking on
the various functions in the Call Stack window you are changing the active stack
frame. As you change stack frames the Locals window will update.
To determine what the parameters where when the first function in the call chain
was called find the first function in the Call Stack window. Double click
it to change the stack frame. Then go to the Locals window to see the parameters
as they were at the point where the next function was called. For example
if FuncA calls FuncB which then calls FuncC then you can see the stack frame for
FuncA at the time it called FuncB by double clicking FuncA in the Call Stack window.
Same thing for FuncB at the point it called FuncC.
- How can you run arbitrary code while debugging your program? For example while
stopped at a breakpoint you want to call a couple of methods on an object that you
have. Answer
One of the most powerful features of the debugger is the ability to run arbitrary
expressions within the context of the currently running program. The Immediates
window can be used to view the contents of objects or run methods. For example
while sitting in any method you can go to the Immediates window and type Environment.CurrentDirectory
to get the current directory. Intellisense is even available. While
not always useful you can use the Immediates window to try new code without having
to stop debugging. For example you could add a new column to a DataTable variable
and then add a value for the column in each row of the table. All without
having to stop the debugger.
On a related note the Commands window can be used to run VS commands while debugging.