{"id":30824,"date":"2020-12-02T09:00:02","date_gmt":"2020-12-02T16:00:02","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/dotnet\/?p=30824"},"modified":"2020-12-03T09:59:36","modified_gmt":"2020-12-03T16:59:36","slug":"improving-debug-time-productivity-with-source-link","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/dotnet\/improving-debug-time-productivity-with-source-link\/","title":{"rendered":"Improving Debug-time Productivity with Source Link"},"content":{"rendered":"<p>How many times have you been in the debugger tracking down a bug, stepping through code, looking at what local variable values changed, when you hit a wall &#8212; the value isn&#8217;t what you expected, and you can&#8217;t step into the method that produced it because it&#8217;s from a library or .NET framework itself? Or you set a conditional breakpoint waiting to examine how some value got set, then noticing a call stack that&#8217;s mostly greyed out, not letting you see what happened earlier in the call stack? Wouldn&#8217;t it be great if you could easily step into, set breakpoints, and use all the debugger&#8217;s features on NuGet dependencies or the framework itself?<\/p>\n<p>.NET development practices in 2020 are a lot different and better in many ways than they were ten years ago. The biggest change is that the .NET platform is open source and maintained on GitHub. Many of the NuGet libraries that we all use on a daily basis are also maintained on GitHub. That means that the source I&#8217;d really like to see in my debugger is just one HTTPS GET away. We could have this wonderfully productive ecosystem where we could all debug with source, for all our dependencies, all the time. That would be nice! In fact, the Source Link project, which was started by Cameron Taggart, realized this, and built an experience that did just that. Let me tell you about it.<\/p>\n<p>With Source Link-enabled libraries, the debugger can download the underlying source files as you step in, and you can set breakpoints\/tracepoints like you would with any other source. Source Link-enabled debugging makes it easier to understand the full flow of your code from your code down to the runtime. Source Link is language-agnostic, so you can benefit from it for any .NET language and for some native libraries.<\/p>\n<h3>Debugging the framework<\/h3>\n<p>Let&#8217;s look at an example. Sometimes you want to step into the framework to see what&#8217;s going on, especially if something is happening that you didn&#8217;t expect. With Source Link, you can step into framework methods just like you can with your own code, inspect all variables, and set breakpoints.<\/p>\n<p>If you tried it without Source Link, here&#8217;s what you&#8217;d see, before and after hitting F11 to step in.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/11\/debug-framework-before.png\" alt=\"At breakpoint with Console.WriteLine\" \/>\n<img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/11\/debug-framework-nosourcelink-after.png\" alt=\"Debugger at brace after Console.Writeline\" \/><\/p>\n<p>The debugger does not step into <code>Console.WriteLine<\/code> because there are no symbols or source for it. Once we configure Source Link, when we step in, we get a different result:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/11\/debug-framework-sourcelink-after.png\" alt=\"Debugger in Console.Writeline method\" \/><\/p>\n<p>You can see that Visual Studio has downloaded the matching source and stepped into the method. If you look at the <code>Autos<\/code> window, it shows the local variables passed in. You can step into, over, and out of the framework code as much as you&#8217;d like.<\/p>\n<h3>Debugging a dependency<\/h3>\n<p>Often, the issue you&#8217;re trying to solve is in a dependency. Wouldn&#8217;t it be great if you could step into the source for your dependencies too? If the dependency added Source Link information during its build, you can! Here&#8217;s an example with <code>Newtonsoft.Json<\/code>. Because <code>Newtonsoft.Json<\/code> was built with Source Link information, you can step into its code:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/11\/newtonsoft-json-before.png\" alt=\"Debugger outside of JsonConvert.SerializeObject method\" \/>\n<img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/11\/newtonsoft-json-download-prompt.png\" alt=\"Prompt to download source from the internet\" \/>\n<img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/11\/newtonsoft-json-after.png\" alt=\"Debugger inside CreateDefault method\" \/><\/p>\n<p>When I stepped in, the debugger skipped a couple of methods that were marked with <code>DebuggerStepThrough<\/code> and stopped on the next statement in the <code>CreateDefault<\/code> method. Since the source comes from the internet (GitHub, in this case), you&#8217;re prompted to allow it, either for just a single file or for all files.<\/p>\n<h3>Exceptions<\/h3>\n<p>Source Link helps you with exceptions that come from the framework or dependencies. How many times have you seen this message and what you really want is to examine the variables?<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/11\/unhandled-exception-before.png\" alt=\"Debugger stopped with unhandled exception\" \/>\n<img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/11\/unhandled-exception-after-thrown.png\" alt=\"Debugger stopped at exception site\" \/>\n<img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/11\/unhandled-exception-after.png\" alt=\"Debugger looking at call stack from exception\" \/><\/p>\n<p>With Source Link, the debugger will take you to the spot where the exception is thrown where you can then navigate the call stack and investigate.<\/p>\n<h2>Enabling Source Link<\/h2>\n<p>As Source Link downloads source files from the internet, it&#8217;s not enabled by default. Here&#8217;s how to enable it:<\/p>\n<h3>Visual Studio<\/h3>\n<p>There are a couple steps to enable it:<\/p>\n<ol>\n<li>Go to <strong>Tools<\/strong> &gt; <strong>Options<\/strong> &gt; <strong>Debugging<\/strong> &gt; <strong>Symbols<\/strong> and ensure that the &#8216;NuGet.org Symbol Server&#8217; option is checked. Specifying a directory for the symbol cache is a good idea to avoid downloading the same symbols again.\n<img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/11\/visual-studio-step-1.png\" alt=\"Dialog showing options for symbol file locations and local cache\" \/>\nIf you&#8217;d like to step into the .NET framework code, you&#8217;ll also need check the &#8216;Microsoft Symbol Servers&#8217; option.<\/li>\n<li>Disable <code>Just My Code<\/code> in <strong>Tools<\/strong> &gt; <strong>Options<\/strong> &gt; <strong>Debugging<\/strong> &gt; <strong>General<\/strong> since we want the debugger to attempt to locate symbols for code outside your solution.\n<img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/11\/visual-studio-step-2.png\" alt=\"Dialog showing 'Enable Just My Code' unchecked\" \/>\nVerify that <code>Enable Source Link support<\/code> is checked (it is by default). If you&#8217;d like to step into .NET Framework code, you&#8217;ll also need to check <code>Enable .NET Framework source stepping<\/code>. This is not required for .NET Core.<\/li>\n<\/ol>\n<h3>Visual Studio Code<\/h3>\n<p>Visual Studio Code has debugger settings configured per project in the <code>launch.json<\/code>:<\/p>\n<pre><code class=\"json\">\"justMyCode\": false,\r\n\"symbolOptions\": {\r\n    \"searchMicrosoftSymbolServer\": true,\r\n    \"searchNuGetOrgSymbolServer\": true\r\n},\r\n\"suppressJITOptimizations\": true,\r\n\"env\": {\r\n    \"COMPlus_ZapDisable\": \"1\",\r\n    \"COMPlus_ReadyToRun\": \"0\"\r\n}\r\n<\/code><\/pre>\n<h3>Visual Studio for Mac<\/h3>\n<p>To enable Source Link in Visual Studio for Mac, go to <strong>Visual Studio &gt; Preferences\u2026 &gt; Projects &gt; Debugger<\/strong> and ensure that the <code>Step into external code<\/code> option is checked. Click OK to save your changes.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/12\/VSfM-symbols.png\"><img decoding=\"async\" class=\"alignnone wp-image-31094 size-full\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/12\/VSfM-symbols.png\" alt=\"Image VSfM symbols\" width=\"1004\" height=\"768\" srcset=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/12\/VSfM-symbols.png 1004w, https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/12\/VSfM-symbols-300x229.png 300w, https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/12\/VSfM-symbols-768x587.png 768w\" sizes=\"(max-width: 1004px) 100vw, 1004px\" \/><\/a><\/p>\n<p>In Visual Studio for Mac, support for symbol servers doesn&#8217;t exist yet, so Source Link only works with NuGet packages that contain their own debug symbols. If you&#8217;re interested in symbol server support, please add your vote to the <a href=\"https:\/\/nam06.safelinks.protection.outlook.com\/?url=https%3A%2F%2Fdevelopercommunity.visualstudio.com%2Fidea%2F1267784%2Fdebugging-net-core-net-5-apps-with-a-symbol-server.html&amp;data=04%7C01%7CClaire.Novotny%40microsoft.com%7Cc44581a1f1e84e05cb0608d890c80446%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C637418536065216750%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&amp;sdata=wsrnetyZoz2fjGXPq35ZJPLkmKZwIrPzVMOvm8iJLcM%3D&amp;reserved=0\">symbol server suggestion<\/a> on the Visual Studio Developer Community site.<\/p>\n<h2>Notes<\/h2>\n<p>A few notes:<\/p>\n<ol>\n<li>Not every library on nuget.org will have their .pdb files indexed. If you find that the debugger cannot find a PDB file for an open-source library you are using, please encourage the open-source library to upload their PDBs (<a href=\"https:\/\/docs.microsoft.com\/en-us\/nuget\/create-packages\/symbol-packages-snupkg\">see here for instructions<\/a>).<\/li>\n<li>Most libraries on nuget.org are <strong>not<\/strong> ahead-of-time compiled, so if you are only trying to debug into this library and not the .NET Framework itself, you can likely omit the env section from above. Using an optimized .NET Framework will significantly improve performance in some cases.<\/li>\n<li>Only Microsoft provided libraries will have their .pdb files on the Microsoft symbol server, so you can disable that option if you are only interested in an OSS library.<\/li>\n<\/ol>\n<p>In a future post we&#8217;ll show you how to create libraries and applications with Source Link enabled so your users can benefit.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Improve your debug-time productivity by using Source Link to step into framework and dependency code.<\/p>\n","protected":false},"author":42867,"featured_media":31153,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[685,196,7196],"tags":[4,9,7201,322,7202],"class_list":["post-30824","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-dotnet","category-dotnet-core","category-debugging","tag-net","tag-net-core","tag-debug-time","tag-debugging","tag-source-link"],"acf":[],"blog_post_summary":"<p>Improve your debug-time productivity by using Source Link to step into framework and dependency code.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/30824","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/users\/42867"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/comments?post=30824"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/30824\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media\/31153"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media?parent=30824"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/categories?post=30824"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/tags?post=30824"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}