Snapshot Debugging with Visual Studio 2017: Now Ready for Production

Visual Studio

December 6th, 2017

[Hello, we are looking to improve your experience on the Visual Studio Blog. It will be very helpful if you could share your feedback via this short survey that should take less than 2 minutes to fill out. Thanks!]

Earlier this year we previewed the Snapshot Debugger, a tool that enables you to debug web apps running in production in Azure. With the general availability of Visual Studio 2017 Enterprise 15.5 this week, Snapshot Debugger is now available for you to get started. Read more about how here.

Snapshot Debugging Overview

If an issue happens in production, you may find yourself digging through logs or attempting to repro the issue in a local environment. Often, the logs may be insufficient, or a local repro may be hard if not impossible to setup. The Snapshot Debugger enables a safe, non-invasive way for you to use the Visual Studio debugger you know and love directly against the production environment in Azure where the issue is happening.

The Snapshot Debugger works by taking a snapshot of the state of your app at specified lines of code where you set Snappoints. While traditional breakpoints would halt your live server when hit and stop it from serving requests; Snappoints quickly capture state, including locals, watches, and the call stack while your app continues to run. This means that you can debug the actual live, running app, without impacting the experience your customers have while using the app. You can read a further overview of how the Snapshot Debugger can be used effectively in production here.

You can capture snapshots at specified lines of code in by using Snappoints, as shown below. Additionally, you can capture snapshots automatically when exceptions happen in your app by setting up Application Insights.

Snapshot Debugging has almost no performance impact on the performance of your production service or the experience end users see while using your application. In the rest of this blog post, we’ll measure the performance impact of using the Snapshot Debugger against a live app with a quick case study using performance load testing.

Production Performance while Debugging

I ran a performance load test to measure the impact of Snapshot Debugging on a deployed app. In a 5-minute load test, I simulated 1,000 users continuously hitting an Azure App Service running the MusicStore ASP.NET Core app. Roughly two minutes into the test, I attached the Snapshot Debugger and set a Snappoint that is hit in the MusicStore app’s home page. I then opened the resulting Snapshot and spent the remaining three minutes inspecting variables and the state at the point of time the snapshot was captured.

During the load test, my Azure App Service plan was hovering at 80-90% CPU Usage. However, even when I started Snapshot Debugging, the performance and throughput did not degrade. Prior to attaching the Snapshot Debugger, the average response time for my server was between 2.0 and 2.4 seconds, and the throughput was between ~3,400 and ~4,000 requests per second. Attaching the Snapshot Debugger at the 2-minute mark caused no change or degradation to average response time, requests per second, or any other performance metric.

I was able to inspect the full state of my app at a snapshot, yet the performance of the app was unaffected while I was debugging. If I were to attach a live debugger and set a breakpoint, the requests per second would have dropped to zero, as the app would halt when the breakpoint was hit!

The Tech behind Snapshotting

The Snapshot Debugger achieves this minimal overhead by intelligently capturing state at the location you’ve set a Snappoint. When you place a Snappoint in your app, the Snapshot Debugger forks your app’s process and suspends the forked copy, creating a snapshot. You then debug against this snapshot, which sits in-memory on your server. The snapshot is not a copy of the full heap of the app – it’s only a copy of the page table with pages set to copy-on-write. The Snapshot Debugger only makes copies of pages in your app if the page gets modified, minimizing the memory impact on your server. In total, your app will only slow down by 10-30 milliseconds when creating snapshots. As snapshots are held in-memory on your server, they do cost ~100s of kilobytes while they are active, as well as an additional commit charge. The overhead of capturing snapshots is fixed and should therefore not affect the throughput of your app regardless of the scale of your app.

The Snapshot Debugger will only capture one snapshot per Snappoint placed in your code to further limit the performance impact. You can modify a Snappoint’s settings to add conditions to specify when the snapshot should be captured or increase the number of snapshots captured. Additionally, you can set several Snappoints in your app to capture Snapshots at different lines and switch between them. The Snapshot Debugger will ensure these snapshots come from the same end user session, even if there are thousands of requests hitting your app.

When you are finished using the Snapshot Debugger, you can hit the stop debugging button in Visual Studio. Hitting stop will detach the Snapshot Debugger and freeing all existing snapshots from memory on your server.

Try out the Snapshot Debugger

The Snapshot Debugger is available in Visual Studio 2017 Enterprise version 15.5 and greater. Currently, the Snapshot Debugger supports ASP.NET and ASP.NET Core apps running in Azure App Services. The first time you use the Snapshot Debugger you will be required to restart your Azure App Service, but no redeployment is necessary.