 Hi, everybody. My name is Leslie Richardson. I'm a program manager on the Visual Studio debugging team. And in this video, we're going to talk about debugging asynchronous managed code in Visual Studio. So ascent code can be really great. It can ultimately improve your program's throughput and having keywords and C sharp like async and await can help make writing that code much easier, but it can also add an extra layer of complexity that can make debugging it much harder. And on top of that, Visual Studio likes to hide a lot of information that could help you debug your async related issues by default, but on the bright side, it also provides several tools that can help make your debugging experience with asynchronous programs much easier. So let's check those out. All right, so I am using the latest version of Visual Studio 2019 and I've got a sample.net core console application that consists of several threads and tasks, which we're going to use in order to showcase the asynchronous debugging tools. Now if you want to follow along using this code or you just want to use it in order to play around with the tools that I'm about to talk about a little further, you are more than welcome to do so. I have posted the link to this code in the description below. It's actually from an older walkthrough that's available in the Visual Studio documentation that talks all about the different multi threaded and async tools that you can be using while debugging. So when you're debugging tasks for the first time or async code for the first time, one of your first spots might be how do you even view all of your tasks or where can you view them all at the same time? Because ultimately, unlike synchronous code, your code's execution may not be happening linearly, which can make trying to debug it a little bit harder. So one option that you have is the tasks window and the task window will give you a tabular view of all of your tasks, their current locations and their statuses at break time. So you can access this window either by going to debug and windows and then tasks. If you're a keyboard shortcut kind of person, you can also use control plus shift plus D comma K. It's kind of a mouthful, but there it is. And you can also search for it using the global search tool, of course. All right. So as mentioned, this is a tabular representation of all of your tasks. You can see their IDs. You can see whether your tasks are active blocked or a waiting. You can also see what their current location is as well as which thread they are assigned to if you have a multi-threaded application as well. So for example, I can use this table to see that task three is currently blocked and I can hover over blocked. Let's see. Here we go. Almost there we go. I can hover over blocked to see that task three is currently waiting on task four, which is owned by thread 15, 3, 2, 0. And if I look at that one, I can see that it's blocked and I can double click this because it's currently holding onto a lock that this thread is trying to access. So this can be really helpful if you just want to quickly navigate through all of your tasks at a given point in time in your code in order to better understand what's going on. So if you're like me and you prefer a more graphical representation of how your code is executing another alternative that you can use to the task window is something called the parallel stacks window, which I will open up by using global search. All right. And let's expand this window out. So this one is pretty cool because it provides you a graphical representation of the status of all of your tasks and task related call stack info. So each block here actually represents a portion of the call stack that shared across one or more tasks and that's defined by the async logical stack title up here. So for example, when I first started running this code, all four tasks in this program started in method A in this block down here and then they all ran through method B followed by C. So from there, each task branched out to a different method location as represented by the separate async logical stacks that branch from this original block here, such as task one, which went from C to E task to which went from C to H and tasks three and four, which go from C to K and C to J respectively. So we've actually made a lot of recent updates to the task view in the parallel stacks window since 16.4. So let's skip ahead to another breakpoint to show some of that off. One more. Right, so same view different information this time round, but this time you'll notice that not only can you view tasks that are currently active, but you can also see tasks that are just scheduled and haven't started running yet. So that's a new addition that we've made to this window because in the past you can only view active tasks. Also, if you want to see the call stack of a particular task in its usual format via the call stack window, then you can now double click on any frame in the parallel stacks window and it will show you that task corresponding call stack. So if I wanted to see what task one is up to over in this block here, double click s.m. And as you can see, I can get the standard tabular representation of the call stack for that given task. So if you've debug multi threaded applications before, you may already be familiar with the threads view of the parallel stacks window, which you can switch over to via this drop down here. And this gives you a very similar view to the task view except this is all thread related. The cool thing about both the threads view and the task view is that you can use the two of them together if you wanted to. So for instance, you might have noticed this waiting on async operation prompt that shows up in a couple of these thread blocks. So if you wanted to know what async operation is currently being weighted on, you can either double click this or select one of the frames within the block, right click it and select go to task and you'll be taken to the frame in the task view where that weight is occurring from. So that's extremely useful. And of course you can do the opposite. So if you wanted to see what thread task five is currently running on, I can right click a frame and then select go to thread and get the opposite result. So that was the parallel stacks window and the tasks window. Both are extremely useful tools. I actually like to use them at the same time personally. Sometimes I feel like having a tabular view other times I feel like having a more visual representation, but either way, they're both great for better understanding how your code is executing asynchronously. So from there, let's talk about another hot point of tension when it comes to debugging asynchronous tools. And that's exceptions exceptions can be a real pain in the butt sometimes when it comes to asynchronous code, especially because Visual Studio likes to hide away some of that info that you that would really help you debug those issues properly. So let's pivot over to another application that I have in order to examine how we can solve that problem. Alright, so with a little editing magic, I have now moved over to a different simpler async program. This one is taking a method called a which just calls B which calls C. So all of these are chained together. And ultimately I'm going to get a no reference error exception at this line here because I'm setting a string equal to null and then trying to set that string to lower case letters. So I'm going to run this program. And as expected, I get the exception helper telling me the no reference has occurred only. It's redirected me to this line where we're calling the a method that's not the most useful thing to have. Ideally, I would like to get or I would ideally like to find out the source of where that exception was thrown from not where the error ultimately becomes unhandled. So an additional feature that we added in post 16.5 is this call stack information that is new to this exception helper. So anytime there is an exception that is getting rethrown somewhere such as in this case. Visual Studio will actually provide the call stack from where that exception was originally thrown from. So this is really great and I can also just click the link here and it will redirect me to that exact line where this error was first thrown. Of course, it also tells me what method it was coming from like a traditional call stack would. But if you don't want to have to sift through all your methods in order to find that location, then you can use a link. So having rethrown exceptions is very useful for any kind of inner exception that you may have, but they're especially helpful when dealing with asynchronous exceptions which are typically caught and then rethrown by framework code. So of course, once you navigate to the source of where the exception is thrown from you can always set a break point for next time and then try to debug the issue from there and inspect anything that you need to inspect. Of course, the downside to simply using rethrown exceptions is that you don't get to use any watch window tools which would allow you to view all the variables and functions and all their values that occurred at that given point where the exception was first thrown from. So as an alternative in order to help you out with that element of debugging your exception, we can navigate over to the diagnostics tools window, which is that tool I find myself closing from time to time, but don't sleep on it because there's a lot of nifty things that you can check out such as this events tab here, which as you can see has documented every time an exception has occurred and at what point in time that it happened at. So specifically I want to look at this exception notification here that has a little camera icon on it and that is a sign that a snapshot of this state of this application state is available. So what that means is when this no reference exception was originally thrown, Visual Studio actually took a snapshot or saved the state of my application at that given point in time and what I can do now is double click and it will take me back to that exact moment in time where this exception was first thrown. So as you can see, I get a notification telling me that I'm now in historical debugging mode. So I'm in the past very mysterious from here. I can look at the autos window locals watch as if I were debugging regularly in the present. I can also check the call stack which can be very helpful and this is giving me the call stack of that given point in time where that exception was first thrown. So there's a lot of really cool debugging tools that I can still use even though I am currently looking in the past. So in order to use snapshots you may need to enable them if they're not turned on already and at the moment this is currently a tool that's only available for Visual Studio Enterprise. So if you're an enterprise user, definitely take advantage of it. So to do that, we can go to open Intellitrace settings and Intellitrace is a larger feature that we have in diagnostics land that includes snapshots. So make sure Intellitrace is enabled and then select Intellitrace snapshots in order to get the snapshot part and select okay and you should be good to go. So in summary, there's a lot of really nice debugging tools that can help you diagnose your asynchronous issues a lot smoother. We talked about the parallel stacks tool which provides a visual representation of what all of your tasks are doing and what their current statuses are at a given point in time. We also talked about the task window which provides a tabular representation of that same information. We discussed rethrown exceptions which will allow you to see the original location of where an exception was thrown from and we also discussed snapshots which will allow you to diagnose your asynchronous exceptions at the exact point in time where it was originally thrown. And that concludes this video. Thank you so much for watching. Hopefully you learned something new about how to better improve your asynchronous debugging experience. And as always, if you have any thoughts, comments or suggestions on how we can improve any of those asynchronous tools or just anything in the debugging space, don't forget to reach out on the dev community. And with that happy coding.