Book Image

Visual Studio 2013 and .NET 4.5 Expert Cookbook

Book Image

Visual Studio 2013 and .NET 4.5 Expert Cookbook

Overview of this book

Table of Contents (14 chapters)
Visual Studio 2013 and .NET 4.5 Expert Cookbook
Credits
About the Author
About the Reviewers
www.PacktPub.com
Preface
Index

Debugging a multithreaded program


Multithreaded programming is one of the primary needs in modern day programming. In this recipe, let's take a look at how to debug a multithreaded program.

Getting ready

To test a multithreaded program, let's consider the following code:

static void Main()
{
  Thread.CurrentThread.Name = "Main Thread";
  Thread t1 = new Thread(new ThreadStart(Run));
  t1.Name = "Thread 1";
  Thread t2 = new Thread(new ThreadStart(Run));
  t2.Name = "Thread 2";
  t1.Start();
  t2.Start();
  Run();
}
static void Run()
{
  Console.WriteLine("hello {0}!", Thread.CurrentThread.Name);
  Thread.Sleep(1000);
}

Tip

Downloading the example code

You can download the example code files for all Packt books you have purchased from your account at http://www.packtpub.com. If you purchased this book elsewhere, you can visit http://www.packtpub.com/support and register to have the files e-mailed directly to you.

Here, we created two threads in parallel, each of them calling the Run method. We have given each thread a name to identify it in the editor.

How to do it...

Now, let's try debugging the concurrent program using Visual Studio with the following simple steps:

  1. Place a breakpoint in the Run method and run the program by pressing F5.

  2. The program will pause during the first Run method.

  3. Open the Threads tool window by navigating to Debug | Windows | Threads or using Ctrl + Alt + H, as shown in the following screenshot:

    You can see in the preceding screenshot that the threads are listed in the window with their respective thread names and other information.

How it works...

A Threads window lists all the information about all the threads running in the current process. Other than a few default columns, you can add columns into the grid using Columns.

When we first run the program, the first call to the Run method is made. In the Threads window, it will show the current thread being executed using a yellow arrow sign. If you press F5 again, the previous thread goes into a sleep, wait, or join mode. This means that the thread execution is either finished or the thread is in the sleep mode.

In our case, since we did not invoke a join or wait statement, the thread must have been destroyed after the complete execution, but as the process is still running, the thread object will still show up in the window until it is disposed. Obviously, the thread running is a background thread (IsBackground = False).

Note that you can also expand the thread location for any thread to get the call stack that is currently being executed. The call stack will remain open automatically during debugging, and double-clicking on the Thread pane will change the context of the current stack.

There's more...

Now let's discuss some of the other options that are available in the IDE that can help in our multithreaded programs. Let's explore them one by one.

How to Flag Just My Code

When running the sample code, there are two types of threads that are created inside the process. We created three threads from the user code, but you can find a few other threads that run with the user threads as well, for example, the Finalizer thread, which is set at the highest priority, Jitter, and so on. These threads should always be set aside.

It is sometimes important to identify the threads created by the user.

In the previous screenshot, you can select Flag Just My Code to flag threads that are created by the user code.

Debugging parallel programs

Another important technique is to debug parallel programs. Parallel programs are concurrent programs that may or may not induce a thread inside it. So, you cannot always handle a parallel program simply by using threads. The Visual Studio IDE comes with a number of additional tools that help in debugging parallel programs.

Let's consider the following code:

class Program
{
  static void Main(string[] args)
  {
    Task task_a = Task.Factory. StartNew(() => DoSomeWork(10000));
    Task task_b = Task.Factory.StartNew(() => DoSomeWork(5000));
    Task task_c = Task.Factory.StartNew(() => DoSomeWork(1000));
    
    Task.WaitAll(task_a, task_b, task_c);
  }

  static void DoSomeWork(int time)
  {
    Task.Delay(time);
  }
}

This is similar to the previously used code but using parallel asynchrony.

Similar to the Thread window, the parallel execution programs have Parallel Stacks and Parallel Tasks windows. Open them by navigating to Debug | Windows. Place a break point on Task.Delay and run the program, as shown in the following screenshot:

The Parallel Tasks window shows the different tasks that have been created for the program and the current status of each of the tasks. In the same way, Parallel Stacks shows the graphical view of all the task creations and how they are related to each other.

What are PDB files and what do they contain?

A debugger needs additional information about the project to work internally. The metadata information needs to be stored separately outside the actual executable such that the debugger can preload certain extent of the information prior to the execution of code; for instance, in Visual Studio, when you initiate a method or start debugging, the debugger automatically opens the appropriate files from the filesystem. This information is not available to the debugger inside the executable itself, but in a new type of file that coexists with the executable called program database files (PDB files). The basic schema of PDB files is:

  • Addresses of all the methods that comprise the executable

  • Name of the global variables and their addresses

  • Source file names, checksum to determine the exact file content, line numbers, and so on

  • Parameters, names of local variables, and the offset of them on the stack

Each PDB file is identified by a globally unique identifier (GUID). This GUID is stored in the header information of the file and it identifies the actual executable.

In the preceding figure, it is clear that the executable has the information about the source code file path and a GUID to uniquely identify itself. The PDB file holds this information as well. When the debugger loads the executable, it gets the PDB file path and loads the symbols from the file, if they are found. If the PDB files are not found, the debugger does not have any connection with the line the executable executes and the debug info on the same in the source file. Hence, the debugger cannot give you the flexibility of a line-by-line execution of a program.

The PDB files are generally much more important to the debugger than the source code itself. The PDB file loads the symbols into the vshost debugging environment and links the source code blocks with symbols. You can also maintain symbol servers to store the PDB files so that it is available to you while debugging.

See also