Wednesday, February 4, 2026

Debugging Managed Async Code in Visual Studio 2019

hi everybody my name is Leslie
Richardson I'm a program manager on the
visual studio debugging team and in this
video we're gonna talk about debugging
asynchronous manage code in Visual
Studio so async code can be really great
it can ultimately improve your programs
throughput and having keywords and
c-sharp like async and weight 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 dotnet 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 thought one of your first
thoughts 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
codes execution may not be happening
linearly which can make trying to debug
it a little bit harder so one of one
option that you have is the tasks window
and the tasks window will give you a
tabular view of all of your tasks their
current locations and their status is a
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 ctrl +
Shift + 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 alright 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 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 3 is currently blocked and I
can hover over blocked let's see here we
go I can hover over block to see that
task 3 is currently waiting on task 4
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 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
here such as task 1 which went from C to
e task 2 which went from C to H and
tasks 3 and 4 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 break
point to show some of that off 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
could 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 tasks corresponding call stack
so if I wanted to see what task 1 is up
to over in this block here double click
S dot 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 debugged 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 a sink operation prompt that
shows up in a couple of these thread
blocks so if you wanted to know what
async operation is currently being
waited 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 5 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 tests 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 to bug those issues
properly so let's pivot over to another
application that I have in order to
examine how we can solve that problem
all right 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 these are chained
together and ultimately I'm gonna 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 lowercase 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 its 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 altom utley 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 read thrown
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 read thrown 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 read thrown by framework code
so of course once you navigate to the
source of where the exception is thrown
from you can always set a breakpoint 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 really thrown
exceptions is that you don't get to use
any watch window tools which would allow
you to view all the variables
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 had 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 president 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 and tell a trace 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
and tell Ettore snapshots and where 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 to bugging 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 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
wreath rone 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

No comments:

Post a Comment