A Guide to Debugging .NET Software Applications

Introduction

This guide will grow over time and help developers understand debugging concepts, strategies, techniques and tools. It will start as a simple list of .NET application debugging tools. The content here will expand over time and also link to more in-depth pages on each topic.

Target Audience

This post intends to serve two audiences:

  1. Beginner .NET developers. This post will help beginners understand what tools are available to debug .NET software applications.
  2. Intermediate to Advanced Developers. This post can serve as a quick-reference for debugging tools.

I expect this post will serve as a personal quick-reference I will use myself in the future. When I need to review the available tools in the .NET developers debugging toolbox, I can refer back to this post for ideas. Especially when debugging complex, systemic problems.

A Debugging Anti-Pattern

A common application debugging anti-pattern that I see often when debugging code goes like this:

  1. I think code x is the problem.
  2. Make a change to code x.
  3. Bug still exists.
  4. Maybe it’s y.
  5. Make a change to code y.
  6. Bug still exists.
  7. Maybe it’s…

Don’t do this. It is usually a complete waste of time. I have found that the actual bug is often something completely unrelated to my best guess. Additionally, even in those cases where I was correct about the cause of the bug, running the debugger still often provides context that I hadn’t thought of regarding the bug and how to fix it. So stop guessing and just run the debugging tools right away. Even if you think you are certain what’s causing the bug. You can still make the assumption, but that assumption then needs to be validated with actual data from the available tools.

This is a habit I have had to break myself from as well. I still have the urge to make that small change to the code and run it again to see if my change fixed the bug. I resist this now and just go directly to the debugger, or profiler, or read the logs, or whatever other tools are available to me to use that match the debugging use case.

Summary of tools covered

In this post, I’ll briefly discuss each of these tools, what they are, what they can be used for, and high-level instructions on how to use them. I will link to more in-depth tutorials from this page.

  • Visual Studio Debugger
  • Visual Studio Profiler
  • Debugging in Visual Studio Code
  • Tracing
  • Logging (include popular frameworks)
  • Windows Event Viewer
  • SQL Server Activity Monitor
  • SQL Server Profiler
  • Estimated Execution Plan
  • Actual Execution Plan

Debugging Tools

Visual Studio Debugger

The Visual Studio debugger is the .NET developers primary tool for debugging issues in code. As the name implies, it is built into Visual Studio. Using it is simple: set breakpoints and start the application using Visual Studio.

Visual Studio Profiler

The Visual Studio profiler is a tool that examines performance metrics for various aspects of your code. It can often be an essential and valuable tool when debugging performance issues.

Debugging in Visual Studio Code

Visual Studio Code (VS Code) is slightly different than Visual Studio. VS Code primarily supports JavaScript, TypeScript, HTML, CSS and Node.js (requires installing the Node.js runtime environment separately.). Support for ASP.NET Core development has improved significantly, but legacy .NET Framework support is limited. For other languages, you need to install extensions specific to the environment you are working in. Python, for example, will have a different debugging extension than PHP. Not all language support is equal in VS Code either. Some languages work better than others. As of the writing of this article, .NET Framework development is still not very well supported in VS Code.

So, from a .NET development perspective, VS Code is best for working with front-end code when that code is written in JavaScript or TypeScript and supports a back-end .NET application.

Tracing

Tracing is when an application is configured to write output to the console when running in debugging mode. For .NET applications, it’s typically enabled in the web.config, app.config or application bootstrap code. Examples of what tracing is often used for include monitoring database queries generated by ORM frameworks like Entity Framework or HTTP requests/responses for things like SOAP web services or REST APIs.

Logging

Logging is adding points in your code where the application writes to a log. Typically a file or a database table. Logging is usually done with a logging framework. Most logging frameworks support what is known as “deferred writes”. Meaning the framework will queue the log entries and write them at specific times during application execution. This is to prevent the logging itself from creating a bottleneck in the application. Logging frameworks are also designed to fail quietly to keep issues with the logging itself from disrupting the application. This can make setting up logging in a new application challenging, but the results are worth the effort.

Some popular logging frameworks for .NET include:

  • Microsoft.Extensions.Logging
  • Serilog
  • NLog
  • Log4Net

Microsoft.Extensions.Logging is probably the easiest to start with, if you’re using a version of .NET that supports it. For third party libraries, I personally have used log4net and Serilog and found them both to be satisfactory. However, log4net is older and not as actively maintained, so between the two, I would recommend Serilog for new development.

Quick file writes

Sometimes, you may find yourself debugging applications that make it difficult to debug, such as multithreaded applications, and have no logging infrastructure due to being in the early stages of development or having been rushed due to business priorities and released without logging. While adding a logging framework is the recommend course of action, a developer can add System.File.IO.AppendAllText calls as an interim step. This mostly works best as a “quick and dirty” tool when debugging in local development environments, as SDLC workflow policies often restrict releasing ad-hoc debugging changes like this to production systems.

Temporary Logging

When working in a more restricted environment, it can sometimes be helpful to add temporary logging for debugging things like bugs that only occur in production. This can be done through adding the logging through the companies established SDLC process and then removing it when done, or using the capabilities of the logging framework to only write to the log if the application configuration is defined to allow the minimum level where those logs will be written.

Visualization Tools

For highly complex and distributed environments, visualization tools like Kibana can be used to store logs in a central location. This is typically done by setting up log forwarding jobs that take application logs and periodically ingest them into Kibana. This can be extremely helpful for seeing the “big picture” for complex systems and diagnosing complicated bugs.

Windows Event Viewer

Windows Event Viewer is a built in application where the Windows operating system logs system events. I have found that the Application log in Event Viewer is typically where I find the most useful information for application debugging. The Event Viewer is helpful for these types of problems:

  • An application is thrown an exception that is not being caught or logged by the application. For example, a web site or API is throwing a 500 error on startup, before the application logging has been initialized.

  • Issues that occur due to system related issues. Things like the correct version of .NET Framework not being installed, a DLL the application depends on not being found, etc.

SQL Server Debugging

If you’re developing .NET applications, especially in an enterprise environment, the chances are very high that you are using a SQL Server database. So, I’ve included some SQL Server debugging tools on this list as well.

SQL Server Management Studio (SSMS)

SQL Server Management Studio (SSMS) is a tool that can be used for things like database administration, create database projects, run ad-hoc queries, view database execution plans, etc.

SQL Server Activity Monitor

SQL Server Activity Monitor is a tool found in SSMS. It can be used to view high-level statistics about current database activity and to monitor recent and active expensive queries. It can also be used to get execution plans for currently running queries.

While SQL Server Activity Monitor is a good tool for general information, most DBAs tend to favor other products. However, it’s useful when SSMS is the only tool you have at your disposal.

SQL Server Profiler

SQL Server Profiler is a GUI tool for examining detailed information about SQL Server activity. Traces can be paused, started and stopped, saved to the file system, etc. It is typically installed with SSMS. I am most used to accessing it through SSMS using the Tools > SQL Server Profiler menu option.

Estimated Execution Plan

SSMS has the ability to show the estimated execution plan for a query. This is helpful when debugging long-running, performance intensive queries, as it generates the estimated plan without running the query. The estimated plan can be accessed by clicking the “Display Estimated Execution Plan” button and running the query, or clicking Ctrl + L, which performs both steps in one action.

Actual Execution Plan

While SQL Server does a pretty good job estimating the execution plan, viewing the actual execution plan is often more helpful. This is the plan that SQL actually uses when the query runs to retrieve data. To view the actual execution plan, click the “Include Actual Execution Plan” button and run the query or press Ctrl + M to toggle the feature on and off. Unlike the estimated execution plan, Ctrl + M does not automatically execute the query to return the plan. This is likely by design, as the actual execution plan requires that the query run, retrieve actual data and complete.

Include Client Statistics

I don’t use this as much as other items on the list, but Include Client Statistics can sometimes be helpful in diagnosing problems with database performance that may be occurring due to factors other than the database itself, such as network latency, processing time on the client side, etc.

Final Thoughts

I hope this list of tools helps modern .NET developers understand what is available for debugging .NET applications. You can refer to this list to help you find the right tool for your specific debugging scenario. I plan to refer to it when I debug problems, simply as a tool to remember things that I may not have thought to try yet, such as looking at the Windows Event Viewer on a production server, or running traces in the SQL Server Profiler. As I expand and link to more in-depth posts, this will create the hub page for more in-depth .NET debugging content.


The postings on this site are my own and do not necessarily reflect the views of my employer.

The content on this blog is for informational and educational purposes only and represents my personal opinions and experience. While I strive to provide accurate and up-to-date information, I make no guarantees regarding the completeness, reliability, or accuracy of the information provided.

By using this website, you acknowledge that any actions you take based on the information provided here are at your own risk. I am not liable for any losses, damages, or issues arising from the use or misuse of the content on this blog.

Please consult a qualified professional or conduct your own research before implementing any solutions or advice mentioned here.