Bug 51684 - Unlike the Visual Studio debugger, the Mono soft debugger does not break directly on unhandled exceptions in `async` methods even when "Debug project code only" is enabled
Summary: Unlike the Visual Studio debugger, the Mono soft debugger does not break dire...
Status: CONFIRMED
Alias: None
Product: Runtime
Classification: Mono
Component: Debugger (show other bugs)
Version: 4.8.0 (C9)
Hardware: PC Mac OS
: --- normal
Target Milestone: ---
Assignee: Zoltan Varga
URL:
: 45540 (view as bug list)
Depends on: 39371
Blocks:
  Show dependency tree
 
Reported: 2017-01-21 04:19 UTC by Brendan Zagaeski (Xamarin Team, assistant)
Modified: 2017-01-22 23:49 UTC (History)
4 users (show)

Tags: papercut
Is this bug a regression?: No
Last known good build:

Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.


Please create a new report for Bug 51684 on GitHub or Developer Community if you have new information to add and do not yet see a matching new report.

If the latest results still closely match this report, you can use the original description:

  • Export the original title and description: GitHub Markdown or Developer Community HTML
  • Copy the title and description into the new report. Adjust them to be up-to-date if needed.
  • Add your new information.

In special cases on GitHub you might also want the comments: GitHub Markdown with public comments

Related Links:
Status:
CONFIRMED

Description Brendan Zagaeski (Xamarin Team, assistant) 2017-01-21 04:19:25 UTC
Unlike the Visual Studio debugger, the Mono soft debugger does not break directly on unhandled exceptions in `async` methods even when "Debug project code only" is enabled




## Motivation and background

The original observation from Xamarin users that prompted this investigation is that unhandled exceptions in async methods in Xamarin.iOS and Xamarin.Android apps don't break where expected in Xamarin Studio or Visual Studio.




## Note that "Enable Just My Code" is required to see the "good" results in Visual Studio

Visual Studio shows the desired behavior for this console C# test case, but _only_ when "Tools > Options > Debugging > General > Enable Just My Code" is enabled.

Based on that observation, it might be sensible to take care of Bug 39371 and Bug 39345 first and then re-test this bug to see if those fixes also resolve this issue.




## Partial workaround

Set the debugger to break on all thrown `System.Exception` exceptions via "Debug > Exceptions" in VS 2013, "Debug > Windows > Exception Settings" in VS 2015, or "Run > New Exception Catchpoint" in Xamarin Studio.

The Mono debugger will then break at the desired location: Program.cs, line 22.

This is only a partial workaround because the debugger will also break on the _handled_ exception in the sample code.  The ideal behavior would be for the debugger to ignore that exception in this particular case.




## Steps to replicate


1. Compile the following program, for example by running `mcs /debug Program.cs`.

```
using System;

namespace Program
{
    class MainClass
    {
        public static void Main(string[] args)
        {
            try
            {
                throw new Exception("Ignore this handled exception");
            }
            catch
            {
            }
            ThrowAnExceptionAsync();
            Console.ReadKey();
        }
        
        public static async void ThrowAnExceptionAsync()
        {
            throw new Exception("An Exception");
        }
    }
}
```


2. Open Xamarin Studio and ensure that "Xamarin Studio > Preferences > Debugger > Debug project code only; do not step into framework code." is enabled.


3. Debug the program.  For example, you can launch the .exe file via "Run > Debug Application", or you can paste the source code into a ".NET > Console Project" and then use "Run > Start Debugging".




## Results

The debugger breaks at `System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()` rather than at the line in the source code where the exception was originally thrown.

In contrast, the `StackTrace` property of the `$exception` variable _does_ reference the desired line of code:

>  at Program.MainClass+<ThrowAnExceptionAsync>c__async0.MoveNext () [0x00018] in /Users/macuser/Desktop/Program.cs:22 


### Example of the full "bad" Call Stack from Xamarin Studio

> System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() in /private/tmp/source-mono-4.8.0/bockbuild-mono-4.8.0-branch/profiles/mono-mac-xamarin/build-root/mono-x86/mcs/class/referencesource/mscorlib/system/runtime/exceptionservices/exceptionservicescommon.cs:143
> System.Runtime.CompilerServices.AsyncMethodBuilderCore.AnonymousMethod__1(System.Runtime.ExceptionServices.ExceptionDispatchInfo state) in /private/tmp/source-mono-4.8.0/bockbuild-mono-4.8.0-branch/profiles/mono-mac-xamarin/build-root/mono-x86/mcs/class/referencesource/mscorlib/system/runtime/compilerservices/AsyncMethodBuilder.cs:1034
> System.Threading.QueueUserWorkItemCallback.WaitCallback_Context(System.Threading.QueueUserWorkItemCallback state) in /private/tmp/source-mono-4.8.0/bockbuild-mono-4.8.0-branch/profiles/mono-mac-xamarin/build-root/mono-x86/mcs/class/referencesource/mscorlib/system/threading/threadpool.cs:1304
> System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, System.Threading.QueueUserWorkItemCallback state, bool preserveSyncCtx) in /private/tmp/source-mono-4.8.0/bockbuild-mono-4.8.0-branch/profiles/mono-mac-xamarin/build-root/mono-x86/mcs/class/referencesource/mscorlib/system/threading/executioncontext.cs:957
> System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, System.Threading.QueueUserWorkItemCallback state, bool preserveSyncCtx) in /private/tmp/source-mono-4.8.0/bockbuild-mono-4.8.0-branch/profiles/mono-mac-xamarin/build-root/mono-x86/mcs/class/referencesource/mscorlib/system/threading/executioncontext.cs:904
> System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem() in /private/tmp/source-mono-4.8.0/bockbuild-mono-4.8.0-branch/profiles/mono-mac-xamarin/build-root/mono-x86/mcs/class/referencesource/mscorlib/system/threading/threadpool.cs:1281
> System.Threading.ThreadPoolWorkQueue.Dispatch() in /private/tmp/source-mono-4.8.0/bockbuild-mono-4.8.0-branch/profiles/mono-mac-xamarin/build-root/mono-x86/mcs/class/referencesource/mscorlib/system/threading/threadpool.cs:854
> System.Threading._ThreadPoolWaitCallback.PerformWaitCallback() in /private/tmp/source-mono-4.8.0/bockbuild-mono-4.8.0-branch/profiles/mono-mac-xamarin/build-root/mono-x86/mcs/class/referencesource/mscorlib/system/threading/threadpool.cs:1209



## Expected results (in Visual Studio)



### Steps followed to test

1. Create a new "Visual C# > Console Application" project in Visual Studio.

2. Paste in the program.  (If you prefer, you could instead compile by hand with `csc /debug Program.cs` and then set "Project Properties > Debug > Start Action" to "Start external program".  The results are the same either way.)

3. Ensure that "Tools > Options > Debugging > General > Enable Just My Code" is enabled.

4. Select "Debug > Start Debugging" in the "Debug|Any CPU" configuration.



### Example of the "good" Call Stack from Visual Studio with "Enable Just My Code" enabled

> Program.exe!Program.MainClass.ThrowAnExceptionAsync() Line 22	C#
> Program.exe!Program.MainClass.Main(string[] args) Line 16	C#
> [Native to Managed Transition]	
> [Managed to Native Transition]	
> mscorlib.dll!System.AppDomain.ExecuteAssembly(string assemblyFile, System.Security.Policy.Evidence assemblySecurity, string[] args)	Unknown
> Microsoft.VisualStudio.HostingProcess.Utilities.dll!Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()	Unknown
> mscorlib.dll!System.Threading.ThreadHelper.ThreadStart_Context(object state)	Unknown
> mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx)	Unknown
> mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx)	Unknown
> mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state)	Unknown
> mscorlib.dll!System.Threading.ThreadHelper.ThreadStart()	Unknown


### Example of the "bad" Call Stack from Visual Studio with "Enable Just My Code" _disabled_

(Note the close similarity to the Xamarin Studio call stack.)

> mscorlib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.ThrowAsync.AnonymousMethod__6_1(object state)	Unknown
> mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx)	Unknown
> mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx)	Unknown
> mscorlib.dll!System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()	Unknown
> mscorlib.dll!System.Threading.ThreadPoolWorkQueue.Dispatch()	Unknown


### Example of the "bad" Call Stack from Visual Studio when there is no .pdb file at all

If you set "Project Properties > Build > Advanced > Debug Info" to "None" for the Debug configuration, the call stack looks even more similar to Xamarin Studio's:

> mscorlib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.ThrowAsync.AnonymousMethod__6_1(object state)	Unknown
> mscorlib.dll!System.Threading.QueueUserWorkItemCallback.WaitCallback_Context(object state)	Unknown
> mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx)	Unknown
> mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx)	Unknown
> mscorlib.dll!System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()	Unknown
> mscorlib.dll!System.Threading.ThreadPoolWorkQueue.Dispatch()	Unknown
> mscorlib.dll!System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()	Unknown


### If you remove the `async` keyword, then the call stack is _always_ good

Good even when "Enable Just My Code" is disabled:
> Program.exe!Program.MainClass.ThrowAnExceptionAsync() Line 22	C#
> Program.exe!Program.MainClass.Main(string[] args) Line 16	C#

Good even when "Debug Info" set to "None":
> Program.exe!Program.MainClass.ThrowAnExceptionAsync()	Unknown
> Program.exe!Program.MainClass.Main(string[] args)	Unknown



## Results summary

It would seem that the "Enable Just My Code" feature in Visual Studio provides a special case for unhandled exceptions from `async` methods so that the debugger can break at the original location of the exception rather than at the re-thrown location.




## Testing environment info



### Mac

Xamarin Studio 6.2 (build 1757) (d769a24)
Mono 4.8.0 (mono-4.8.0-branch/cd26828)
sdb 1.5 (master/b8b3296)

Mac OS 10.11.6



### Windows

Microsoft Visual Studio Enterprise 2015
Version 14.0.25431.01 Update 3
Microsoft .NET Framework
Version 4.6.01586

Visual C# Compiler version 1.3.1.60616

Windows 10 (64-bit) Version 1607 (OS Build 14393.694)
US English locale, US Eastern time zone
Comment 1 Brendan Zagaeski (Xamarin Team, assistant) 2017-01-21 04:24:56 UTC
## Supplemental test with .NET Core in Visual Studio Code on Mac

1. Create a directory named "Program" and `cd` into it.

2. Run `dotnet new`.

3. Paste the program into the Program.cs file.

4. Open Visual Studio Code and open the "Program" directory.

5. Let Visual Studio Code add the required assets and restore the unresolved dependencies.

6. Install the ms-vscode.csharp VS Code extension (if not already installed).

7. Launch the app for debugging via the ".NET Core Launch (console)" button in the "Debug" sidebar.




## Example of "good" Call Stack in Visual Studio Code

> Program.MainClass.ThrowAnExceptionAsync() Program.cs 22
> Program.MainClass.Main(string[] args) Program.cs 16
> [External Code] Unknown Source 0



## Testing environment info

.NET Core 1.0.0-preview2-1-003177
VS Code 1.8.1 (ee428b0)
ms-vscode.csharp extension 1.6.2

Mac OS 10.11.6
Comment 2 Brendan Zagaeski (Xamarin Team, assistant) 2017-01-21 04:49:50 UTC
*** Bug 45540 has been marked as a duplicate of this bug. ***