Virtual Developer Workshop: Containerized Development with Docker
Debugging is an important skill that every developer needs to acquire. .NET developers have a powerful debugger of Visual Studio at their disposal. Visual Studio offers many windows that can be used during the debugging session. Knowing these windows is essential for efficient debugging. To that end this article discusses some of the most commonly used debug windows of Visual Studio.
To work through the remainder of this article you need to create a new Console Application in Visual Studio. All the examples discussed below assume that you have such an application opened in Visual Studio.
Watch Window and QuickWatch Dialog
The above figure shows a Watch window with two variables and one expression. The simplest way to watch a variable is to right click on it during a debugging session and click on the "Add Watch" shortcut menu option.
You can also use the Watch window to change the value of a variable. To do so, just double click in the Value column in front of the variable under consideration, change its value and press the Enter key. To evaluate an expression enter it in an empty row under the Name column and hit Enter.
You can also open a Watch window using the DEBUG > Windows > Watch > Watch n menu option where n is 1-4. Thus you can open four Watch windows (Watch1, Watch 2, Watch3 and Watch 4) simultaneously. If a variable being watched is an object you can expand it to reveal its property values. An entry added to the Watch window remains there unless you delete it. To delete an entry simply select it and hit the Delete key.
Sometimes you don't need to add a watch but still wish to observe a variable or evaluate an expression. The QuickWatch dialog allows you to do that. To open the QuickWatch dialog you right click on a variable and then select QuickWatch from the shortcut menu. This is how a QuickWatch dialog looks:
You can watch and edit values and expressions in the QuickWatch dialog similar to the Watch window. Additionally, if you decide to monitor a variable or expression further you can click on the Add Watch button to add an entry for it in the Watch window.
Locals window lists variables in the current scope. Locals window can be opened during a debugging session using the DEBUG > Windows > Locals menu option. Consider the following figure:
As you can see the Locals window lists all the variables in the Main() method because that's the current debugging scope. However, variables inside SampleMethod() are not listed because that's out of the current scope. If you press F11 to jump inside SampleMethod() then Locals window will reflect the variables inside SampleMethod() because of the change in the current scope.
As in the case of Watch window, you can also edit the value of a variable displayed in the Locals window.
Autos window displays variables in the current and previous statements. You can open Autos window using the DEBUG > Windows > Autos menu option. Have a look at the following figure that shows the Autos window in action:
As you can see the current line of execution contains the variable j and the previous line contains variable i. Both of them are listed in the Autos window.
Immediate window allows you do arbitrary tasks without interfering with the current flow of execution. For example, you may want to evaluate some expression, read or change a variable and so on. Immediate window can be activated using the DEBUG > Windows > Immediate menu option. The following figure shows how the Immediate window looks:
As you can see the Immediate window executes the SampleMethod1() method of the Program class. The ? is used to print the output (you can also use Debug.Print). IntelliSense is also available to the code that you write inside the Immediate window. So, if you press a . after p2, a list of members is displayed. If the method being executed has a breakpoint then the execution halts at that breakpoint.
Call Stack Window
The Call Stack window lists a stack of method calls. You can open the Call Stack window during a debugging session using the DEBUG > Windows > Call Stack menu option. Consider that you have Main() method invoking SampleMethod1() of the Program class. The SampleMethod1() in turn is calling SampleMethod2() residing in the same class. So, if you open the Call Stack window inside SampleMethod2() it will look like this:
Call Stack Window
As you can see the stack consists of SimpleMethod2(), SimpleMethod1() and Main(). The external code in this case refers to the .NET framework code outside the boundary of your application. The line number is also listed. If you right click on the Call Stack window and select the "Show External Code" shortcut menu option the external code stack will also be revealed like this:
External Code Stack
You can navigate between the code belonging to various Call Stack window entries by right clicking on that entry and then selecting the "Switch To Frame" shortcut menu option.
Sometimes your applications spawn more than one thread. Debugging a multithreaded application can be challenging because multiple tasks are being carried out in the background at the same time. Luckily, the Threads window helps you to debug multithreaded applications. Before you see the Threads window and its use, let's quickly create a multithreaded application as shown below:
Create a Multithreaded Application
The above code consists of the Main() method that spawns two threads - t1 and t2 - using System.Threading.Thread class. The Thread class accepts a ThreadStart delegate as its parameter. The ThreadStart delegate in turn points to a method that is to be executed on the thread. In this case MethodOnThread() is such as a method and simply outputs current date and time on the console. Each thread is given a unique name so that you can monitor them easily in the Threads window. The Start() method of the Thread class starts the execution of a thread.
To see the Threads window in action, set a breakpoint inside MethodOnThread() method. When the execution halts there, click on the DEBUG > Windows > Threads menu option to open the Threads window.
The above figure shows the Threads window as well as the shortcut menu for a thread entry inside it. Notice that Thread 1 and Thread 2 are listed in the window along with the Main Thread. The current thread is shown with a yellow arrow. Right clicking on a thread will reveal a shortcut menu as shown. This menu allows you to stop a thread (Freeze), execute a thread (Thaw) and to set an active thread (Switch To Thread).
Tasks and Parallel Stack Window
The .NET framework 4.0 introduced parallel programming through the System.Threading.Tasks namespace. Just like Threads window is used to observe threads you can use Tasks window and Parallel Stack window to observe a Task execution. These windows can be opened during a debugging session using the DEBUG > Windows > Tasks and DEBUG > Windows > Parallel Stack menu options respectively. Before you see these windows let's create a simple application using Task class. Have a look at the following code:
Create a Simple Application using Task Class
The above application creates two tasks t1 and t2. The tasks are executed using StartNew() method of the Factory. The WaitAll() method is used to wait for all the tasks (t1 and t2 in this case) to complete. The MethodOnThread() is executed as a part of the tasks. The method simply outputs a message on the console. Just for the sake of understanding the windows the Thread.Sleep() has been called. To see how Parallel Task and Parallel Stack windows look, start debugging the application by setting a breakpoint at Task.WaitAll() line. The following figure shows both of these windows:
As you can see the Tasks window lists the two tasks that you started. The Parallel Stacks window displays visual representation of threads or tasks. It has two views - Thread view and Task view. Detailed discussion of debugging a parallel application is beyond the scope of this article. For a quick overview you may consider reading this article.
Being able to effectively debug your .NET application is an important skill. Visual Studio provides a powerful debugger and offers many windows that can simply enhance your debugging experience. This article presented an overview of some of the most commonly used debug windows. Using these windows effectively will help you debug your code in a better and quicker way.