Merge pull request #100 from Horusiath/fsharp-async-task-support

Demystify F# async/task stack traces
This commit is contained in:
Ben Adams 2021-01-03 16:59:27 +00:00 committed by GitHub
commit 1f10ebc836
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 134 additions and 3 deletions

View File

@ -26,6 +26,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ben.Demystifier.Benchmarks", "test\Ben.Demystifier.Benchmarks\Ben.Demystifier.Benchmarks.csproj", "{EF5557DF-C48E-4999-846C-D99A92E86373}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ben.Demystifier.Benchmarks", "test\Ben.Demystifier.Benchmarks\Ben.Demystifier.Benchmarks.csproj", "{EF5557DF-C48E-4999-846C-D99A92E86373}"
EndProject EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FSharpStackTrace", "sample\FSharpStackTrace\FSharpStackTrace.fsproj", "{D6B779D2-A678-47CC-A2F9-A312292EA7A2}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -48,6 +50,10 @@ Global
{EF5557DF-C48E-4999-846C-D99A92E86373}.Debug|Any CPU.Build.0 = Release|Any CPU {EF5557DF-C48E-4999-846C-D99A92E86373}.Debug|Any CPU.Build.0 = Release|Any CPU
{EF5557DF-C48E-4999-846C-D99A92E86373}.Release|Any CPU.ActiveCfg = Release|Any CPU {EF5557DF-C48E-4999-846C-D99A92E86373}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EF5557DF-C48E-4999-846C-D99A92E86373}.Release|Any CPU.Build.0 = Release|Any CPU {EF5557DF-C48E-4999-846C-D99A92E86373}.Release|Any CPU.Build.0 = Release|Any CPU
{D6B779D2-A678-47CC-A2F9-A312292EA7A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D6B779D2-A678-47CC-A2F9-A312292EA7A2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D6B779D2-A678-47CC-A2F9-A312292EA7A2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D6B779D2-A678-47CC-A2F9-A312292EA7A2}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@ -57,6 +63,7 @@ Global
{B9E150B0-AEEB-4D98-8BE1-92C1296699A2} = {59CA6310-4AA5-4093-95D4-472B94DC0CD4} {B9E150B0-AEEB-4D98-8BE1-92C1296699A2} = {59CA6310-4AA5-4093-95D4-472B94DC0CD4}
{E161FC12-53C2-47CD-A5FC-3684B86723A9} = {455921D3-DD54-4355-85CF-F4009DF2AB70} {E161FC12-53C2-47CD-A5FC-3684B86723A9} = {455921D3-DD54-4355-85CF-F4009DF2AB70}
{EF5557DF-C48E-4999-846C-D99A92E86373} = {59CA6310-4AA5-4093-95D4-472B94DC0CD4} {EF5557DF-C48E-4999-846C-D99A92E86373} = {59CA6310-4AA5-4093-95D4-472B94DC0CD4}
{D6B779D2-A678-47CC-A2F9-A312292EA7A2} = {455921D3-DD54-4355-85CF-F4009DF2AB70}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {841B7D5F-E810-4F94-A529-002C7E075216} SolutionGuid = {841B7D5F-E810-4F94-A529-002C7E075216}

View File

@ -1,5 +1,4 @@
<Project> <Project>
<PropertyGroup> <PropertyGroup>
<LangVersion>7.2</LangVersion>
</PropertyGroup> </PropertyGroup>
</Project> </Project>

View File

@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Compile Include="Program.fs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Ben.Demystifier\Ben.Demystifier.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Ply" Version="0.1.7" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,35 @@
// Learn more about F# at http://fsharp.org
open System
open System.Diagnostics
open System.Threading.Tasks
open FSharp.Control.Tasks.Builders
let call i = async {
do! Async.Sleep 1
if i = 10 then
failwith "BOOM!"
return i
}
let run count = async {
let calls = Array.init count call
for call in calls do
let! _ = call
()
return 0
}
let makeTheCall () = task {
let! x = run 20
return x
}
[<EntryPoint>]
let main argv =
try
let results = makeTheCall().GetAwaiter().GetResult()
printfn "%A" results
with e ->
printfn "%s" <| string (e.Demystify())
0 // return an integer exit code

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading.Tasks; using System.Threading.Tasks;
class Program class Program

View File

@ -114,6 +114,13 @@ namespace System.Diagnostics
methodName = method.Name; methodName = method.Name;
} }
else if (IsFSharpAsync(method))
{
methodDisplayInfo.IsAsync = true;
methodDisplayInfo.SubMethodBase = null;
subMethodName = null;
methodName = null;
}
// Method name // Method name
methodDisplayInfo.MethodBase = method; methodDisplayInfo.MethodBase = method;
@ -241,6 +248,20 @@ namespace System.Diagnostics
return methodDisplayInfo; return methodDisplayInfo;
} }
private static bool IsFSharpAsync(MethodBase method)
{
if (method is MethodInfo minfo)
{
var returnType = minfo.ReturnType;
if (returnType.Namespace == "Microsoft.FSharp.Control" && returnType.Name == "FSharpAsync`1")
{
return true;
}
}
return false;
}
private static bool TryResolveGeneratedName(ref MethodBase method, out Type type, out string methodName, out string subMethodName, out GeneratedNameKind kind, out int? ordinal) private static bool TryResolveGeneratedName(ref MethodBase method, out Type type, out string methodName, out string subMethodName, out GeneratedNameKind kind, out int? ordinal)
{ {
kind = GeneratedNameKind.None; kind = GeneratedNameKind.None;
@ -699,6 +720,29 @@ namespace System.Diagnostics
} }
} }
if (type.Namespace == "Microsoft.FSharp.Control")
{
switch (type.Name)
{
case "AsyncPrimitives":
case "Trampoline":
return false;
case var typeName when type.IsGenericType:
{
if (typeName == "AsyncResult`1") return false;
else break;
}
}
}
if (type.Namespace == "Ply")
{
if (type.DeclaringType.Name == "TplPrimitives")
{
return false;
}
}
// Fallbacks for runtime pre-StackTraceHiddenAttribute // Fallbacks for runtime pre-StackTraceHiddenAttribute
if (type == typeof(ExceptionDispatchInfo) && method.Name == "Throw") if (type == typeof(ExceptionDispatchInfo) && method.Name == "Throw")
{ {

View File

@ -18,6 +18,9 @@ namespace System.Diagnostics
internal StringBuilder Append(StringBuilder sb) internal StringBuilder Append(StringBuilder sb)
{ {
if (ResolvedType.Assembly.ManifestModule.Name == "FSharp.Core.dll" && ResolvedType.Name == "Unit")
return sb;
if (!string.IsNullOrEmpty(Prefix)) if (!string.IsNullOrEmpty(Prefix))
{ {
sb.Append(Prefix) sb.Append(Prefix)

View File

@ -29,6 +29,15 @@ namespace System.Diagnostics
{ typeof(ushort), "ushort" } { typeof(ushort), "ushort" }
}; };
public static readonly Dictionary<string, string> FSharpTypeNames = new Dictionary<string, string>
{
{ "Unit", "void" },
{ "FSharpOption", "Option" },
{ "FSharpAsync", "Async" },
{ "FSharpOption`1", "Option" },
{ "FSharpAsync`1", "Async" }
};
/// <summary> /// <summary>
/// Pretty print a type name. /// Pretty print a type name.
/// </summary> /// </summary>
@ -92,6 +101,11 @@ namespace System.Diagnostics
{ {
builder.Append(type.Name); builder.Append(type.Name);
} }
else if (type.Assembly.ManifestModule.Name == "FSharp.Core.dll"
&& FSharpTypeNames.TryGetValue(type.Name, out builtInName))
{
builder.Append(builtInName);
}
else if (type.IsGenericParameter) else if (type.IsGenericParameter)
{ {
if (options.IncludeGenericParameterNames) if (options.IncludeGenericParameterNames)
@ -153,7 +167,15 @@ namespace System.Diagnostics
return; return;
} }
if (type.Assembly.ManifestModule.Name == "FSharp.Core.dll"
&& FSharpTypeNames.TryGetValue(type.Name, out var builtInName))
{
builder.Append(builtInName);
}
else
{
builder.Append(type.Name, 0, genericPartIndex); builder.Append(type.Name, 0, genericPartIndex);
}
builder.Append('<'); builder.Append('<');
for (var i = offset; i < length; i++) for (var i = offset; i < length; i++)