Allow demystifier to add strings to existing StringBuilder (second attempt) (#80)

* Allow demystifier to add strings to existing StringBuilder

* Add exception message
This commit is contained in:
Alexander Sher 2019-02-10 12:50:33 -08:00 committed by Ben Adams
parent a9830f38e3
commit fbd6ea49ab
7 changed files with 162 additions and 78 deletions

View File

@ -173,11 +173,10 @@ namespace System.Diagnostics
// ResolveStateMachineMethod may have set declaringType to null // ResolveStateMachineMethod may have set declaringType to null
if (type != null) if (type != null)
{ {
var declaringTypeName = TypeNameHelper.GetTypeDisplayName(type, fullName: true, includeGenericParameterNames: true); methodDisplayInfo.DeclaringType = type;
methodDisplayInfo.DeclaringTypeName = declaringTypeName;
} }
if (method is System.Reflection.MethodInfo mi) if (method is MethodInfo mi)
{ {
var returnParameter = mi.ReturnParameter; var returnParameter = mi.ReturnParameter;
if (returnParameter != null) if (returnParameter != null)
@ -190,7 +189,6 @@ namespace System.Diagnostics
{ {
Prefix = "", Prefix = "",
Name = "", Name = "",
Type = TypeNameHelper.GetTypeDisplayName(mi.ReturnType, fullName: false, includeGenericParameterNames: true).ToString(),
ResolvedType = mi.ReturnType, ResolvedType = mi.ReturnType,
}; };
} }
@ -542,7 +540,6 @@ namespace System.Diagnostics
{ {
var parameterType = parameter.ParameterType; var parameterType = parameter.ParameterType;
var prefix = GetPrefix(parameter, parameterType); var prefix = GetPrefix(parameter, parameterType);
var parameterTypeString = "?";
if (parameterType == null) if (parameterType == null)
{ {
@ -550,7 +547,6 @@ namespace System.Diagnostics
{ {
Prefix = prefix, Prefix = prefix,
Name = parameter.Name, Name = parameter.Name,
Type = parameterTypeString,
ResolvedType = parameterType, ResolvedType = parameterType,
}; };
} }
@ -574,40 +570,22 @@ namespace System.Diagnostics
parameterType = parameterType.GetElementType(); parameterType = parameterType.GetElementType();
} }
parameterTypeString = TypeNameHelper.GetTypeDisplayName(parameterType, fullName: false, includeGenericParameterNames: true);
return new ResolvedParameter return new ResolvedParameter
{ {
Prefix = prefix, Prefix = prefix,
Name = parameter.Name, Name = parameter.Name,
Type = parameterTypeString,
ResolvedType = parameterType, ResolvedType = parameterType,
}; };
} }
private static ResolvedParameter GetValueTupleParameter(IList<string> tupleNames, string prefix, string name, Type parameterType) private static ResolvedParameter GetValueTupleParameter(IList<string> tupleNames, string prefix, string name, Type parameterType)
{ {
string typeName; return new ValueTupleResolvedParameter
if (parameterType.IsValueTuple())
{
typeName = GetValueTupleParameterName(tupleNames, parameterType);
}
else
{
// Need to unwrap the first generic argument first.
var genericTypeName = TypeNameHelper.GetTypeNameForGenericType(parameterType);
var valueTupleFullName = GetValueTupleParameterName(tupleNames, parameterType.GetGenericArguments()[0]);
typeName = $"{genericTypeName}<{valueTupleFullName}>";
}
return new ResolvedParameter
{ {
TupleNames = tupleNames,
Prefix = prefix, Prefix = prefix,
Name = name, Name = name,
Type = typeName, ResolvedType = parameterType
ResolvedType = parameterType,
}; };
} }

View File

@ -4,6 +4,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Generic.Enumerable; using System.Collections.Generic.Enumerable;
using System.Reflection; using System.Reflection;
using System.Text;
namespace System.Diagnostics namespace System.Diagnostics
{ {
@ -12,27 +13,16 @@ namespace System.Diagnostics
{ {
private static readonly FieldInfo stackTraceString = typeof(Exception).GetField("_stackTraceString", BindingFlags.Instance | BindingFlags.NonPublic); private static readonly FieldInfo stackTraceString = typeof(Exception).GetField("_stackTraceString", BindingFlags.Instance | BindingFlags.NonPublic);
public static T Demystify<T>(this T exception) where T : Exception
=> Demystify(exception, originalStacksTracker: null);
private static string GetStackTracesString(this Exception exception)
=> (string)stackTraceString.GetValue(exception);
private static void SetStackTracesString(this Exception exception, string value) private static void SetStackTracesString(this Exception exception, string value)
=> stackTraceString.SetValue(exception, value); => stackTraceString.SetValue(exception, value);
/// <summary> /// <summary>
/// Demystifies the given <paramref name="exception"/> and tracks the original stack traces for the whole exception tree. /// Demystifies the given <paramref name="exception"/> and tracks the original stack traces for the whole exception tree.
/// </summary> /// </summary>
private static T Demystify<T>(this T exception, Dictionary<Exception, string> originalStacksTracker) where T : Exception public static T Demystify<T>(this T exception) where T : Exception
{ {
try try
{ {
if (originalStacksTracker?.ContainsKey(exception) == false)
{
originalStacksTracker[exception] = exception.GetStackTracesString();
}
var stackTrace = new EnhancedStackTrace(exception); var stackTrace = new EnhancedStackTrace(exception);
if (stackTrace.FrameCount > 0) if (stackTrace.FrameCount > 0)
@ -44,11 +34,11 @@ namespace System.Diagnostics
{ {
foreach (var ex in EnumerableIList.Create(aggEx.InnerExceptions)) foreach (var ex in EnumerableIList.Create(aggEx.InnerExceptions))
{ {
ex.Demystify(originalStacksTracker); ex.Demystify();
} }
} }
exception.InnerException?.Demystify(originalStacksTracker); exception.InnerException?.Demystify();
} }
catch catch
{ {
@ -69,27 +59,6 @@ namespace System.Diagnostics
/// </remarks> /// </remarks>
[Contracts.Pure] [Contracts.Pure]
public static string ToStringDemystified(this Exception exception) public static string ToStringDemystified(this Exception exception)
{ => new StringBuilder().AppendDemystified(exception).ToString();
try
{
var originalStacks = new Dictionary<Exception, string>();
exception.Demystify(originalStacks);
var result = exception.ToString();
foreach (var kvp in originalStacks)
{
kvp.Key.SetStackTracesString(kvp.Value);
}
return result;
}
catch
{
// Processing exceptions shouldn't throw exceptions; if it fails
}
return exception.ToString();
}
} }
} }

View File

@ -11,7 +11,7 @@ namespace System.Diagnostics
{ {
public MethodBase MethodBase { get; set; } public MethodBase MethodBase { get; set; }
public string DeclaringTypeName { get; set; } public Type DeclaringType { get; set; }
public bool IsAsync { get; set; } public bool IsAsync { get; set; }
@ -39,11 +39,9 @@ namespace System.Diagnostics
internal StringBuilder Append(StringBuilder builder) internal StringBuilder Append(StringBuilder builder)
{ {
if (IsAsync) if (IsAsync)
{ {
builder builder.Append("async ");
.Append("async ");
} }
if (ReturnParameter != null) if (ReturnParameter != null)
@ -52,7 +50,7 @@ namespace System.Diagnostics
builder.Append(" "); builder.Append(" ");
} }
if (!string.IsNullOrEmpty(DeclaringTypeName)) if (DeclaringType != null)
{ {
if (Name == ".ctor") if (Name == ".ctor")
@ -60,17 +58,16 @@ namespace System.Diagnostics
if (string.IsNullOrEmpty(SubMethod) && !IsLambda) if (string.IsNullOrEmpty(SubMethod) && !IsLambda)
builder.Append("new "); builder.Append("new ");
builder.Append(DeclaringTypeName); AppendDeclaringTypeName(builder);
} }
else if (Name == ".cctor") else if (Name == ".cctor")
{ {
builder.Append("static "); builder.Append("static ");
builder.Append(DeclaringTypeName); AppendDeclaringTypeName(builder);
} }
else else
{ {
builder AppendDeclaringTypeName(builder)
.Append(DeclaringTypeName)
.Append(".") .Append(".")
.Append(Name); .Append(Name);
} }
@ -145,5 +142,10 @@ namespace System.Diagnostics
return builder; return builder;
} }
private StringBuilder AppendDeclaringTypeName(StringBuilder builder)
{
return DeclaringType != null ? builder.AppendTypeDisplayName(DeclaringType, fullName: true, includeGenericParameterNames: true) : builder;
}
} }
} }

View File

@ -9,8 +9,6 @@ namespace System.Diagnostics
{ {
public string Name { get; set; } public string Name { get; set; }
public string Type { get; set; }
public Type ResolvedType { get; set; } public Type ResolvedType { get; set; }
public string Prefix { get; set; } public string Prefix { get; set; }
@ -25,7 +23,15 @@ namespace System.Diagnostics
.Append(" "); .Append(" ");
} }
sb.Append(Type); if (ResolvedType != null)
{
AppendTypeName(sb);
}
else
{
sb.Append("?");
}
if (!string.IsNullOrEmpty(Name)) if (!string.IsNullOrEmpty(Name))
{ {
sb.Append(" ") sb.Append(" ")
@ -34,5 +40,10 @@ namespace System.Diagnostics
return sb; return sb;
} }
protected virtual void AppendTypeName(StringBuilder sb)
{
sb.AppendTypeDisplayName(ResolvedType, fullName: false, includeGenericParameterNames: true);
}
} }
} }

View File

@ -0,0 +1,56 @@
// Copyright (c) Ben A Adams. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic.Enumerable;
using System.Text;
namespace System.Diagnostics
{
public static class StringBuilderExtentions
{
public static StringBuilder AppendDemystified(this StringBuilder builder, Exception exception)
{
try
{
var stackTrace = new EnhancedStackTrace(exception);
builder.Append(exception.GetType());
if (!string.IsNullOrEmpty(exception.Message))
{
builder.Append(": ").Append(exception.Message);
}
builder.Append(Environment.NewLine);
if (stackTrace.FrameCount > 0)
{
stackTrace.Append(builder);
}
if (exception is AggregateException aggEx)
{
foreach (var ex in EnumerableIList.Create(aggEx.InnerExceptions))
{
builder.AppendInnerException(ex);
}
}
if (exception.InnerException != null)
{
builder.AppendInnerException(exception.InnerException);
}
}
catch
{
// Processing exceptions shouldn't throw exceptions; if it fails
}
return builder;
}
private static void AppendInnerException(this StringBuilder builder, Exception exception)
=> builder.Append(" ---> ")
.AppendDemystified(exception)
.AppendLine()
.Append(" --- End of inner exception stack trace ---");
}
}

View File

@ -7,7 +7,7 @@ using System.Text;
namespace System.Diagnostics namespace System.Diagnostics
{ {
// Adapted from https://github.com/aspnet/Common/blob/dev/shared/Microsoft.Extensions.TypeNameHelper.Sources/TypeNameHelper.cs // Adapted from https://github.com/aspnet/Common/blob/dev/shared/Microsoft.Extensions.TypeNameHelper.Sources/TypeNameHelper.cs
public class TypeNameHelper public static class TypeNameHelper
{ {
public static readonly Dictionary<Type, string> BuiltInTypeNames = new Dictionary<Type, string> public static readonly Dictionary<Type, string> BuiltInTypeNames = new Dictionary<Type, string>
{ {
@ -43,6 +43,12 @@ namespace System.Diagnostics
return builder.ToString(); return builder.ToString();
} }
public static StringBuilder AppendTypeDisplayName(this StringBuilder builder, Type type, bool fullName = true, bool includeGenericParameterNames = false)
{
ProcessType(builder, type, new DisplayNameOptions(fullName, includeGenericParameterNames));
return builder;
}
/// <summary> /// <summary>
/// Returns a name of given generic type without '`'. /// Returns a name of given generic type without '`'.
/// </summary> /// </summary>

View File

@ -0,0 +1,62 @@
// Copyright (c) Ben A Adams. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Diagnostics.Internal;
using System.Text;
namespace System.Diagnostics
{
public class ValueTupleResolvedParameter : ResolvedParameter
{
public IList<string> TupleNames { get; set; }
protected override void AppendTypeName(StringBuilder sb)
{
if (ResolvedType.IsValueTuple())
{
AppendValueTupleParameterName(sb, ResolvedType);
}
else
{
// Need to unwrap the first generic argument first.
sb.Append(TypeNameHelper.GetTypeNameForGenericType(ResolvedType));
sb.Append("<");
AppendValueTupleParameterName(sb, ResolvedType.GetGenericArguments()[0]);
sb.Append(">");
}
}
private void AppendValueTupleParameterName(StringBuilder sb, Type parameterType)
{
sb.Append("(");
var args = parameterType.GetGenericArguments();
for (var i = 0; i < args.Length; i++)
{
if (i > 0)
{
sb.Append(", ");
}
sb.AppendTypeDisplayName(args[i], fullName: false, includeGenericParameterNames: true);
if (i >= TupleNames.Count)
{
continue;
}
var argName = TupleNames[i];
if (argName == null)
{
continue;
}
sb.Append(" ");
sb.Append(argName);
}
sb.Append(")");
}
}
}