Debugging in Visual Studio

October 14, 2014

Blog | Technology | Debugging in Visual Studio
Debugging in Visual Studio

At Geekhive, we’re always looking for ways to debug complex apps quickly and efficiently so our clients and their users can have the best experience possible when interacting with their product. There are many tried and true programs available to support this that developers often overlook when facing an issue. We like Visual Studio because it has a few windows that garner the lion’s share of attention, but it also comes packed with debugging tools that can help you and your duck crack even the toughest nuts. Today, I’ll highlight some of the awesome that exists in three debugging-oriented windows: Immediate Window, Threads Window, and Modules.

 


Visual Studio Immediate Window


The Immediate Window can be found in the Debug -> Windows -> Immediate menu item, or  through the CTRL+ALT+I shortcut.


This window is primarily used when attached to a running process, but a little known fact is that you can instantiate any class and execute methods in it, by-passing the Main method of the application altogether. In some cases, there is prerequisite code defined during the application’s initialization step that, if by-passed, may result in a fairly unstable runtime. (Or as my ‘tech-savvy’ co-worker put it, “If you by-pass this defined code, it will muck things up later. Ahem.) For instance, the typical ASP.Net life cycle will be skipped entirely, so any dependencies on the Request or Response object, User information, etc. will remain uninitialized.





At runtime, you can use the Immediate window to execute simple commands, like you would in the Watch window. For example, given a variable firstVar, the value can be examined or re-assigned. 


Note: The % prefix indicates the command entered. 


% firstVar
1

% firstVar = 5
5




Similar to primitive types, arrays can be accessed as well.

% myArray
{Program.Entry[3]}
[0]: {Program.Entry}
[1]: {Program.Entry}
[2]: {Program.Entry}
% int i = 0;
0
% myArray[i++].Prop
(output: 5)
% myArray[i++].Prop
(output: 6)
% myArray[i++].Prop
(output: 7)
% myArray[i++].Prop
Out of bounds array index




Likewise, analyzing existing variables is small potatoes. Creating variables in the current running context is also easily possible. Once defined, the variable exists within the scope of whatever is executing, and cannot be defined a second time without an exception being reported by the window.


% var myNewObject = 5;
5

% var myNewObject = 5;
A local variable named 'myNewObject' is already defined in this scope




Using these snippets, we can manually iterate through an array without having to navigate each element. This is particularly useful when  you want an element’s value to be inspected, but the type does not override the .ToString() method.  Note: the Up Arrow key populates the command line with the previous entry.

% myArray
{Program.Entry[3]}
    [0]: {Program.Entry}
    [1]: {Program.Entry}
    [2]: {Program.Entry}

% int i = 0;
0
% myArray[i++].Prop
(output: 5)
% myArray[i++].Prop
(output: 6)
% myArray[i++].Prop
(output: 7)
% myArray[i++].Prop
Out of bounds array index

Unfortunately, a traditional For, ForEach, or While loop are not supported in the immediate window.




Visual Studio Threads Window


The Threads Window can be found in the Debug -> Windows -> Threads menu item.


This window gives the developer a look at the multiple threads running within the application. In most applications, a single threaded application is all you need. Bringing up the window when attached to Visual Studio will almost surely report more threads than you may expect, simply because of how the framework executes your application.


However, when developing a multi-threaded application this window is critical to debugging your components with accuracy.


Threads 



Looking at the above image, we can see all our named threads, as well as the framework threads. For example, suppose we have the following application code:


1 private bool isRunning = true;
2 
3 private void Main()
4 {
5     Task.Run((Action)OtherThreadMethod1);
6     Task.Run((Action)OtherThreadMethod2);
7     MainThreadMethod();
8 }
9 private void MainThreadMethod()
10 {
11    do
12    {
13        Console.WriteLine(“Hello from the MainThread. Type ‘exit’ to quit.”);
14        if (Console.ReadLine() == “exit”)
15            isRunning = false;
16    } while (isRunning);  
17    Console.WriteLine(“MainThread is exiting. Press any key.”);
18    Console.ReadKey();     
19 }
20 private void OtherThreadMethod1()
21 {
22     do
23     {
24         Thread.Sleep(1000);
25         Console.WriteLine(“Hello from the OtherThread1”);
26     } while (isRunning);
27     Console.WriteLine(“OtherThread1 is exiting.”);
28 }
29 private void OtherThreadMethod2()
30 {
31     do
32     {
33         Thread.Sleep(1000);
34         Console.WriteLine(“Hello from the OtherThread2”);
35     } while (isRunning);
36     Console.WriteLine(“OtherThread2 is exiting.”);
37 }




On launch, our main thread will spin up a second thread using the Task libraries* which will simply output a distinct string every second until the main thread is ready to exit. If we set a breakpoint on line 25, in the middle of our OtherThread1’s execution lifecycle, the Threads window will look like this:


Threads 2 - http://www.geekhive.com


We can clearly see where in the execution our threads are and we can switch between the different execution contexts to analyze variables (as well as use all the benefits of the Immediate Window has the offer) from the context menu. We can even freeze a thread from continuing to execute altogether.







Note: Freezing a thread in an application can have undesired consequences, particularly when the threads have dependencies on each other. If a deadlock occurs, the only solution will likely be to terminate and re-launch the application.




If you attach to a faulting 3rd party process, additional information (subject to interpretation) may be revealed via this window that wouldn’t otherwise be known from the exception itself (such as whether a known problematic assembly version is being used.)


By leveraging Visual Studio’s powerful debugging capabilities through these three windows, you can gain a  better look inside your running application. Do you often make use of one or all of these windows, or the more recent IntelliTrace feature available in Visual Studio 2013 Ultimate? Let us know @GeekHive.



Phil Azzi, Developer, GeekHive

Phil Azzi

Technical Lead
Tags
  • .NET
  • Tutorial

Recent Work

Check out what else we've been working on