Professional Documents
Culture Documents
Many different types of applications can be debugged with the techniques we discuss
here:
WPF
ASP.NET
Silverlight
WCF
WinForms
MVC
Mobile Development
Workflow
C++
Language Agnostic
Advanced Breakpoints
You need both the Visual Studio projects and the PDB files
Setting a breakpoint
Now start debugging.
Notice the call stack is displayed. And as we would expect, we can see that we are resting
at the breakpoint in a deeply nested call stack.
You can see that our breakpoint is at Doh(). Notice the the buttonDeepStack_Click called
“Do()” and that “Do()” called “Re()” and so on.
The answer is to set a breakpoint within the “Call Stack” window. We can use the “Call
Stack” window to set breakpoints.
You can see that we stopped at “buttonDeepStack_Click”. This approach makes it
convenient for to break anywhere in the call stack depending on the needs of your
debugging experience.
It is possible to set breakpoionts in any of the 3 sections of the for loop below. As you know
section “1” executes once at the beginning of the loop. Section “2” (the condition) executes
once at the top of each for loop. Finally, section “3” executes once at the end of each loop.
So put your cursor any of the 3 sections and hit the “F9” key, the “Insert Breakpoint” command.
Using the BreakPoint Dialog Box – Just type in a name into the “New Breakpoint” Dialog Box
The beauty of it is that you can leverage the intellisense feature. Choose “Debug | New
Breakpoint | Break at Function.”
Be sure to have the “Use IntelliSense to verify the function name” selected. Notice in the
example below that Visual Studio is complaining that we mis-spelled Callevenmore. The
intellisense is protecting us.
Simple. Just type in that function name and a list will appear.
Notice that we can choose the one we wish to set a “BreakPoint” at. You could have narrowed
the list by writing “Overload(int).”
This setting determines how the breakpoint should behave when it is hit. You can choose to:
Break always, regardless of hit count. This is the default option. This option has the least
effect on program performance, since the debugger does not actually bother to compare the
values.
Break when the hit count is a multiple of the counter. Like “Modulo.”
Note that the “Breakpoints” window can tell you the current state.
If your computer is crashing…
You can set the “hit count” to a very large number and when your software crashes you can
see the loop count to get an idea where your software is crashing.
Conditional Breakpoints
In the Breakpoint Condition dialog box, enter a valid expression in the Condition box.
Choose is true if you want to break when the expression is satisfied or has changed if you
want to break when the value of the expression has changed.
Click OK.
“Has Changed” means it will break if i changes.
An "Is true" condition specifies that the expression in the textbox must be met in order
for execution to be paused. If, when the code reaches the breakpoint, the condition does
not evaluate as "true", the code will continue as normal.
A second type of condition is the "Has changed" expression.In this case, when the
breakpoint is hit, the value of the expression in the textbox is compared with the value of
the expression on the previous occasion that the breakpoint was encountered. Only if the
value has changed is execution paused.
Unselecting the “Condition Checkbox” allows you to return back to an regular,
“unconditional” breakpoint.
Note: You can combine a “Hit Count” and “Conditional” breakpoint at the same time.
Conditional breakpoints are for breaking at the expression level, when a particular condition is
true, like x=5. But what if you have multipl
multiple
e instances of the same app running? How do you
set to break the instance you want?
Filter breakpoints need to be enabled on the tools menu. Go to Tools – Options – Debugging –
General, and there you’ll see the option to enable breakpoint filters.
Lesson 4 – Debugging Threads
Threading Primer
Threading is a complex topic. Parallelism is another word used to describe the concept of
running code in parallel. Understanding threading is an important first step to following this
tutorial.
Unless very carefully designed, multithreaded programs tend to be unpredictable, hard and
to test, and hard to get right.
I encourage you to download the project below and run it to see exactly how it works. This
sample covers the Manual method of context switching between threads.
This example was also included in the previous download link, but here it is again for your
convenience. The best way to understand both the threading examples is to download and
run
un them. The two examples will show you the difference between a manual reset and auto
reset.
Before debugging threads, we need to tell Visual Studio to enable source code debugging.
This is easy to do. Just go to the Tools menu and select Options.. Navigate to the debugging
area and check the following boxes as seen below:
To get things started, we will say debug
debug,, start debugging. Next we will hit the button whose
caption reads create threads. At this point 5 threads have been created, each one is blocked or
in a wait state. Now we wish to break in to the debugging session by going to the debug menu
and selecting break all.
To start things off where we’ll start debugging the application as you can see in step one. Next
we will click the create threads button in step two. In step three select the break all menu
selection from the debug menu.
At this
his point, we would like to see which threads are operating inside of our application. That is
what the threads window is used for. So display the threads window.
All 5 threads can be seen above. All of them are started but are in a “wait” state. They are
blocked at this time.
The goal
With five threads running setting a breakpoint becomes challenging. It may become
necessary to want only one of the threads to hit the breakpoint. Notice in the debugger
threads window, each thread has an ID. It is with this ID that we can tell the debugger to
break for a specific thread.
Step 2: Run the application. The debugger output window will display the thread numbers.
Step 3:: Right click on breakpoint and set a “Filter” on one of the threadnumbers.
Step 4:: Go back to the main form of the application and choose “Close Threads”.
We will start by setting
ing a generic breakpoint inside of our code. Once that is done we can
apply the breakpoint filter.
At this point we can just choose any one of our worker threads. We will choose thread
number 1828 just for their heck of it.
By right mouse clicking on the breakpoint we can choose the filter menu item. Notice that
this dialog box allows us to specify a machine, the process, and a thread, by either ID or by
name, we will simply type in thre
thread ID equals 1828.
At this point we’re ready to continue. So go to the debug menu and select continue.
continue At this
stage the applications threads are in a wait state (meaning they are blocked on the
waitHandle.WaitOne()
Handle.WaitOne() code statement). But if we go to the applications in main form and
choose close threads, they will become unblocked and therefore the threads will be allowed to
continue to execute. However, only thread 1828 will hit the breakpoint becau
because
se that is what
we specified in the breakpoint filter modifier.
But that is the big take away and this lesson. Being able to hit a breakpoi
breakpoint
nt for a specific thread
is critical when debugging.
Notice that the dialogue box that comes up and allows us to perform two basic tasks. One
thing that we can do is to print a message. Another thing that we can do is to run a macro.
Continue execution simply means that youyourr code will continue to run after performing the
task that you specified. Notice that when you print a message there are a lot of valuable line
items that you can display. For example, you can display the callstack, the process ID, the
thread ID, etc.
We can also add our own variables to the “print a message” section of the “when breakpoint
is hit” dialog box. For example, the this keyword refers to the currently executing form, and
if we want to display the public properties of the form we will add tthe
he following code to our
dialogue box text: this={this}.
Simply open up the output window to see the result.
The output
Function: CSBreakPoints.MainForm.buttonAssertExample_Click(object, System.EventArgs),
Thread: 0x2014 Main Thread this={CSBreakPoints.MainForm, Text: C# Breakpoints Demo}
Demo
Pam
Notice that in addition to the function name, the thread ID, and the thread name, we
also displayed the results of our this object, which printed the text property for the
main form.
The Watch Window (AKA Expression Evaluator)
The most important thing is that in the Watch window and its cousins (Autos, Locals, Quick
Watch, etc) you can click on the value of a variable and change it
Pressing the CTRL key makes the Data Tip window see through
Demo
Open source code to MainForm.cs and navigate to XmlManipulation method and set a
breakpoint on the “XmlElement myElement = doc
doc.DocumentElement;” line.
Switch to the C# Breakpoints Demo window and click the Default Views button. Move the
mouse to the “doc” value on the doc.LoadXml line.
You can make direct modifications to variables through the data tip window. As you can see
here, I went to the InnerXML element and modified the title of the book.
Lesson 6 – Watch and Immediate Windows – Advanced Techniques
The watch window is incredibly powerful. You can actually call methods directly from this window.
But note that that method call must execute within 20 seconds, or visual studio will assume that the
method is hung and will cancel its execution
execution.
Switch to the C# Breakpoints Demo window and click the Call A Bunch button.
After setting a breakpoint, go ahead and start debugging. At this point, we are ready to bring up the
watch
ch window. The watch window is available from the debug menu. Notice that there are up to
four separate watch windows you can choose from.
Before we actually execute them that could from the watch window, let’s actually go and see what
the method looks like. Notice that the method simp
simply
ly prints out messages to the output window.
Depending on the result of the modulus operator we print different messages.
Notice that we’ve got full intellisense available. In this case we want to execute
CallABunchMethod(). So select ct the method from the intellisense and hit the enter key. You’ll need
to remember to add the parentheses however.
Notice that the output window is displaying the appropriate message –namely, that we have not hit in
the value of five in our static variable. One thing to remember is that if we want to call this method
more than once through the watch window, we need to erase it and re re-type it.
You can also execute methods from the immediate window. One thing to note is that break points
are disregarded in the watch windows while breakpoints are adhered to in the immediate window.
That means that if you set a breakpoint
akpoint within a function call the debugger will stop there if you
execute that method through the immediate window. The same is not true for the watch windows.
Another difference between the immediate windows and the watch windows is that method calls
from
rom the immediate windows can last as long as you wish them too. However, in the watch windows
you only have 20 seconds two finish executing the method before the debugger abandons the call.
call
Using the watch window to help you do find test case
cases
The watch window allows you to a modify data directly. In the window below you can see the
condition in the if statement. Currently it is a true statement. In order for me to make it a false
statement I have to step through the debugger some more. But what I can do instead is change the
static variable directly within the debugger to see the effect it has on the condition within the if
statement.
Now I can go to the value column in the watch window and type in the number “1”. This will cause
the condition in the if statement to become false. This gives me a quick and easy way to test my if
statements without having to do endless stepping through the debugger.
This is a real straightforward way to change the state of my application without having to constantly
re- run the application, testing various conditions during each run. This is a real big time saver. It
also allows for
or easy test case generation. Scripting languages have had this for a while, but now we
can take advantage of it for managed code as well
well.
How to create and replace a local object from the watch window or from the immediate window
windo
Similar to the way we callll the methods from the immediate and watch windows, we can also create
objects, replacing existing objects within the stack frame.
Notice that we are about to pass a null populated object to the method called CallEvenMore().
Hitting the F11 key (single step) in the debugger end
ends up passing the ae object to the method called
CallEvenMore().
Notice that in the function above, the AE object is in fact equal to null. Before single stepping one
more time, we want to inject a new object called the x, except that we will not put in a null value –
instead, we will populate this object with a real value
value.
This new object will appear in the locals window, preceded by the symb
symbol “$”.
Using standard c# syntax, this can be done from the immediate window as follows:
Now, we can assign this $x variable to ae, allowing us to directly use the ae variable within the
CallEvenMore() method. This is a useful technique which allows you to create new variables or
objects on the fly. As you can see, we were able to test the strin
stringg method called IsNullOrEmpty()
without having to run the application multiple times. We simply created a new variable on the fly
and assigned it to a local variable within the method, all from the immediate window.
Lesson 7 – Advanced Techniques – Using Object ID
Make object ID – allows you to track an object within the visual studio debugging system
When debugging native code, it's easy to keep track of objects by their addresses.
But, with managed code, you don't really have this option. Everything is a reference in managed
code. Address is simply are not relevant and managed code.
But, when debugging in Visual Studio (at least 2005/2008), you can right click an object and select
"Make Object ID".
I find this really useful in investigating a bug that involved some nasty recursion and reentrancy.
Note that in the code below we have set a breakpoint. If we hover the mouse over the doc variable,
data tips will popup. Next, you can right mouse click and a context menu will appear, with Make
object ID as one of the options. A few Daugherty
Essentially, object ID is making objects or variables visible throughout the execution of your
application, regardless of scope.
Next we will run the application and set a breakpoint where the doc variable is not in scope.
Notice that in the watch window the doc is grayed out while 1# is visible, even though the doc
variable is completely out of scope.
Furthermore, notice that the 1# retains its most previous runtime value. This can be an important
feature if the execution path changes based on the value of a variable or object.
The bottom line is that you can observe your variable or object as it moves through the system.
Imagine a scenario where you have an array of objects that all exercise the same methods. But
you want to break only when a specific object invokes a method.
Start the debugger and run the application until it hits the for each loop. Once you hit the breakpoint at
the fort each loop you can assign an object ID to one of the car objects.
Notice that I am right mouse clicking on the second array element of cars. I am making an object ID out
of the convertible car object.
Notice that the condition sets the this object to the newly generated object ID of 1#.
This means that the breakpoint will only be hit when the invoking object of the DescribeCar() method is
a convertible car.
Notice the + within the breakpoint indicates that this is a conditional breakpoint.
From the menu select debug| continue at which point the breakpoint will be hit.
This is a very powerful technique in situations where you have large numbers of objects and you want to
break on method calls for only one specific instance
Lesson 8 – The Garbage Collector
It is important to know how resources are allocated and managed, and how the garbage
collection algorithm works.
The garbage collector checks to see if there are any objects in the heap that are no longer
being used by the application.
If such objects exist, then the memory used by these objects can be reclaimed.
If no more memory is available for the heap, then the new operator throws an
OutOfMemoryException.
It is not trivial for the garbage collector know if an application is using an object or not.
Each time you use the new operator to create an object, the runtime allocates memory for
the object from the managed heap.
As long as address space is available in the managed heap, the runtime continues to allocate
space for new objects.
Eventually the garbage collector must perform a collection in order to free some memory.
The garbage collector's optimizing engine determines the best time to perform a collection,
based upon the allocations being made.
As stated previously, when the garbage collector performs a collection, it checks for objects
in the managed heap that are no longer being used by the application and performs the
necessary operations to reclaim their memory.
One feature of the garbage collector that exists purely to improve performance is called
generations.
A generational garbage collector takes into account two facts that have been empirically
observed in most programs in a variety of languages:
Generational collectors group objects by age and collect younger objects more often than
older objects.
All new objects added to the heap can be said to be in generation 0, until the heap gets filled
up which invokes garbage collection.
As most objects are short-lived, only a small percentage of young objects are likely to survive
their first collection.
Surviving collection and moving to the next generation – generation 1
Once an object survives the first garbage collection, it gets promoted to generation 1.Newer
objects after GC can then be said to be in generation 0.The garbage collector gets invoked
next only when the sub-heap of generation 0 gets filled up.
All objects in generation 1 that survive get compacted and promoted to generation 2.
Generation 0 then contains no objects, but all newer objects after GC go into generation 0.
What developers should care about
It is better to have your objects in lower generations, such as generation 0 or 1. That way there
is a higher likelihood that they will be collected and memory will be freed.
While all the background? Knowing which generation our objects reside in is a big help.
Knowing that our objects on not being garbage collected is critical to managing memory
efficiently.
Currently, there is no built in way to determine the generation of an object that needs to be
collected.
The .net framework contains a class called GC. This might be a good starting point to determine
which generation our object is in.
Notice below that we are building a string builder object. It is called SB. We would expect that
this object is in generation zero right after we step over its allocation in the debugger.
There is a method in the GC class called GetGeneration. This method takes as a parameter the
object whose generation we wish to determine.
Notice that it is now in generation one, meaning is survived the sweep by the garbage collector.
Too many objects in higher generations can lead to real performance problems.
Too many objects in generation two, for example, can cause the garbage collector to work too
hard during low memory situations.
Your application may experience lots of disk swapping and high CPU utilization in this situation.
This may cause your application to the CPU starved and it will perform poorly.
The goal of this blog was to teach you more about the garbage collector.
And despite the fact that the built in debugger doesn't provide useful generational information,
I have provided the means by which you can determine which generation your object resides
in.
If you are not familiar with the Set Next Statement feature, it allows you to specify from
which line of your application's code you wish to resume running.
It is easy to skip over critical code (ex: object creation, variable initialization, etc) and lead to
unexpected application behavior and/or crash your application.
There are some limitations on which lines you can specify as the next statement: You cannot
set the next statement outside of the current method You cannot set the next statement
into a catch or finally block If you cannot set the next statement to your desired line, Visual
Studio 2008 does an excellent job in letting you know why.
For example, attempting to set the next statement outside of the current method will cause
Visual Studio 2005 to display the following message.
In the next sample, we will force in a statement to evaluate to true. We can do this in one of
two ways. The first way is to simply drag the yellow arrow. The second way is to right
mouse click on the line of code that you wish to execute.
As stated previously, you can also set the next statement with a simple right mouse click.
This approach makes more sense if the function is quite large. Dragging the arrow on a very
large function may be impossible.
Underneath the hood it is actually quite simple what the set next statement is really going.
You may inadvertently skip over a key object initialization. This may cause a null reference
exception later in your code.
The answer is no. The reason is that the runtime may have not jitted the code for that other
function. Therefore, changing the instruction pointer is impossible.
Yes you can. This might make sense in a scenario where you wish to initialize an object to
manually by moving forward and backwards as needed.
Lesson 10 – Debugging Threads
This next section is fairly brief and will help you work with the debugging of threads more
effectively.
It is available from the toolbar so as a first step we need to bring up the debug toolbar.
The next step is to enable the option “show threads in source.” Once again that is available
from the debug toolbar. It is not enabled by default, therefore we must switch it on.
To test this option we will create our threads and then enter break mode. Select the “create
threads” button below.
Next, we will select “Break All” from the menu.
Because we have enabled source code debugging, we will end up in some .net system source
code.
We need to switch windows back to our own source code. We can do this with a simple control
tab. Notice the squiggly lines in the gutter. If we hover our mouse over the squiggly lines we
can see exactly which threads are awaiting to be signaled.
Notice we can see that five threads are awaiting on the method call WaitOne().
As the next step,let’sbringupthethreads debugger window. You can find this in the Visual
Studio menu Debug | Windows | Threads.
Notice that we can hover the mouse over one of the thread’s location columns and get the call
stack for that thread. For example, below we are looking at thread 3’s call stack.
You also have the option of renaming the thread the right from the debugger using a simple
right mouse click.
Big challenge for developers – single stepping through a thread’s execution
Because windows is a preemptive operating system, the context switch to another thread may
be forced upon you buy Windows. This can interfere greatly with your debugging session. The
solution to this problem is to suspend the other threads so that the thread you are debugging
does not get interrupted by the operating system.
That means you can control click and select all the threads that you wish to suspend, right
mouse click and choose the freeze option as you see above. Freeze is the same thing as
suspend.
This section of my blog is to illustrate how visual studio can be used to debug stored
procedures in SQL Server. Luckily, most of the commands are identical and the learning
curve is trivial. Let’s get started.
In this blog we will add a new stored procedure. We will execute the stored procedure. We
will step through the stored procedure and use traditional debugger commands that have
been discussed in the previous 10 blogs.
These are very powerful techniques and are dramatic time savers. You can also use these
techniques to help you discover how other store procedures were written by other
developers.
Begin by going to Visual Studio’s View menu and selecting Server Explorer.
Notice that our AdventureWorks database is already contained or hosted by SQL server
2008. This is the instance that we wish to connect to and debug.
Click on the change button and make sure you select Microsoft SQL server as the data
source type.
When I tried to connect directly to the SQL server database file, the .mdf file, I got an error
indicating versioning problem.
The second stored procedure will contain some business logic allowing us to test the
debugger more complex scenario
Name of stored procedure = uspGetList
Within server explorer right mouse click and stored procedures and add new stored
procedure.
Paste in the stored procedure code then select file save.
Notice the stored procedure appears in the server explorer to the left, highlighted in blue.
The second select statement chooses the maximum list price for a given product that is less
than the maximum price passed into the stored procedure.
Running [Production].[uspGetList] ( @Product = %Frame%, @MaxPrice = 500, @Co
@ComparePrice
mparePrice = 350,
@ListPrice = 400 ).
To start
tart the debugging session, right mouse click on the stored procedure and Select Step
Into Stored Procedure.
You will now be able to use the F10 and F11 keys to step through your stored procedure
code.
A dialog box will appear that allows you to provide parameters for your stored procedure.
You can hover the mouse over variable names to determine their value.
Notice that the locals window is there, the output window is there.
This concludes the tutorial on using Visual Studio 2008 to debug of SQL Server Stored
Procedures.
Lesson 12 – Debugging Javascript In Web Pages
Be sure to allow for script debugging. To do this, start Internet explorer and select from the tools
menu. Click on options. Navigate to the advanced tab and make sure the checkboxes in the red
box art deselected.
Within the Visual Studio Solution Explorer, make sure that your web project is the startup
project.
Once you have made your web project the startup project, now you will define which web page
is the start page once you start the debugger.
Start the debugging process by selecting step into, which is the F11 key.
You may be asked to modify the configuration file to enable debugging. You should obviously
select yes.
Notice that the instruction pointer has begun running the JavaScript. That this did you can easily
single step or step over the code.
The various windows that we discussed previously can be used.
You can see in the JavaScript code above the value of the mydate variable.
The next approach to debugging launches the debugger directly from within Internet explorer.
The way this is accomplished is by using the view menu once the web page is loaded. From the
View menu select View Script Debugger.
At this stage we are asked which debugger we wish to use. Since visual studio is the most
powerful debugger, we will select Visual Studio. We will select new instance of Visual Studio
2008, since we do not want to interfere with the existing instance of visual studio already open
which is running the solution called CSBreakPoints.
Notice that the bug are brought us appropriately to the onclick event.
Step into the code with F11 key. This technique will cover most of the debugging scenarios that
you encounter.
We hit the F5 or continue debugging command to finish running the JavaScript. You can see the
result below were appropriately.
1: dim filesys
2: dim step_number
3: dim trg, src
4:
5: step_number = "04"
6:
7: set filesys=CreateObject("Scripting.FileSystemObject")
8:
9:
10:
11: src = "c:\devprojects\azure\labs\BuildingWindowsAzureServices"
12: trg = "C:\livedemos\Azure\HelloWorld\step" & step_number
13: If filesys.FolderExists(trg) Then
14: filesys.DeleteFolder trg, true
15: end if
16: call Generatepath(trg)
17: wscript.echo "Copying " & src & "...."
18: 'filesys.CopyFolder src, filesys.GetParentFolderName(trg) & "\"
19: filesys.CopyFolder src, trg & "\"
20:
21:
22: Function GeneratePath(pFolderPath)
23: GeneratePath = False
24: If Not filesys.FolderExists(pFolderPath) Then
25: If GeneratePath(filesys.GetParentFolderName(pFolderPath)) Then
26: GeneratePath = True
27: Call filesys.CreateFolder(pFolderPath)
28: End If
29: Else
30: GeneratePath = True
31: End If
32: End Function
Debugging VBScript is incredibly easy. This will no doubt be the most straightforward and
simplest way to debug code of all the previous blogs.
I recommend by starting with an administrator level command prompt. If you are a developer
and not an end user, running as an administrator is recommended for most operations.
One of my favorite editors is VI. Below you can see a bunch of VBScript code that we wish to
debug one line at a time.
Notice my command prompt window below. Also notice my VBScript file waiting there to be
executed. Let’s go ahead and run that script in the debugger.
If you ever forget the syntax, you can just execute cscript /?.
Cid added the secret to debugging.cscr using our example simply type in cscript /x /d
MakeSteps.vbs
Here is the results of our command.