Remove Microsoft.Extensions.StackTrace.Sources
\n\nCommit migrated from 1713ada32d
This commit is contained in:
parent
01a4a273d7
commit
cb018faf53
|
|
@ -1,29 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.Extensions.StackTrace.Sources
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains details for individual exception messages.
|
||||
/// </summary>
|
||||
internal class ExceptionDetails
|
||||
{
|
||||
/// <summary>
|
||||
/// An individual exception
|
||||
/// </summary>
|
||||
public Exception Error { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The generated stack frames
|
||||
/// </summary>
|
||||
public IEnumerable<StackFrameSourceCodeInfo> StackFrames { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the summary message.
|
||||
/// </summary>
|
||||
public string ErrorMessage { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,170 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
|
||||
namespace Microsoft.Extensions.StackTrace.Sources
|
||||
{
|
||||
internal class ExceptionDetailsProvider
|
||||
{
|
||||
private readonly IFileProvider _fileProvider;
|
||||
private readonly int _sourceCodeLineCount;
|
||||
|
||||
public ExceptionDetailsProvider(IFileProvider fileProvider, int sourceCodeLineCount)
|
||||
{
|
||||
_fileProvider = fileProvider;
|
||||
_sourceCodeLineCount = sourceCodeLineCount;
|
||||
}
|
||||
|
||||
public IEnumerable<ExceptionDetails> GetDetails(Exception exception)
|
||||
{
|
||||
var exceptions = FlattenAndReverseExceptionTree(exception);
|
||||
|
||||
foreach (var ex in exceptions)
|
||||
{
|
||||
yield return new ExceptionDetails
|
||||
{
|
||||
Error = ex,
|
||||
StackFrames = StackTraceHelper.GetFrames(ex)
|
||||
.Select(frame => GetStackFrameSourceCodeInfo(
|
||||
frame.MethodDisplayInfo.ToString(),
|
||||
frame.FilePath,
|
||||
frame.LineNumber))
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<Exception> FlattenAndReverseExceptionTree(Exception ex)
|
||||
{
|
||||
// ReflectionTypeLoadException is special because the details are in
|
||||
// the LoaderExceptions property
|
||||
var typeLoadException = ex as ReflectionTypeLoadException;
|
||||
if (typeLoadException != null)
|
||||
{
|
||||
var typeLoadExceptions = new List<Exception>();
|
||||
foreach (var loadException in typeLoadException.LoaderExceptions)
|
||||
{
|
||||
typeLoadExceptions.AddRange(FlattenAndReverseExceptionTree(loadException));
|
||||
}
|
||||
|
||||
typeLoadExceptions.Add(ex);
|
||||
return typeLoadExceptions;
|
||||
}
|
||||
|
||||
var list = new List<Exception>();
|
||||
if (ex is AggregateException aggregateException)
|
||||
{
|
||||
list.Add(ex);
|
||||
foreach (var innerException in aggregateException.Flatten().InnerExceptions)
|
||||
{
|
||||
list.Add(innerException);
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
while (ex != null)
|
||||
{
|
||||
list.Add(ex);
|
||||
ex = ex.InnerException;
|
||||
}
|
||||
list.Reverse();
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
// make it internal to enable unit testing
|
||||
internal StackFrameSourceCodeInfo GetStackFrameSourceCodeInfo(string method, string filePath, int lineNumber)
|
||||
{
|
||||
var stackFrame = new StackFrameSourceCodeInfo
|
||||
{
|
||||
Function = method,
|
||||
File = filePath,
|
||||
Line = lineNumber
|
||||
};
|
||||
|
||||
if (string.IsNullOrEmpty(stackFrame.File))
|
||||
{
|
||||
return stackFrame;
|
||||
}
|
||||
|
||||
IEnumerable<string> lines = null;
|
||||
if (File.Exists(stackFrame.File))
|
||||
{
|
||||
lines = File.ReadLines(stackFrame.File);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Handle relative paths and embedded files
|
||||
var fileInfo = _fileProvider.GetFileInfo(stackFrame.File);
|
||||
if (fileInfo.Exists)
|
||||
{
|
||||
// ReadLines doesn't accept a stream. Use ReadLines as its more efficient
|
||||
// relative to reading lines via stream reader
|
||||
if (!string.IsNullOrEmpty(fileInfo.PhysicalPath))
|
||||
{
|
||||
lines = File.ReadLines(fileInfo.PhysicalPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
lines = ReadLines(fileInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (lines != null)
|
||||
{
|
||||
ReadFrameContent(stackFrame, lines, stackFrame.Line, stackFrame.Line);
|
||||
}
|
||||
|
||||
return stackFrame;
|
||||
}
|
||||
|
||||
// make it internal to enable unit testing
|
||||
internal void ReadFrameContent(
|
||||
StackFrameSourceCodeInfo frame,
|
||||
IEnumerable<string> allLines,
|
||||
int errorStartLineNumberInFile,
|
||||
int errorEndLineNumberInFile)
|
||||
{
|
||||
// Get the line boundaries in the file to be read and read all these lines at once into an array.
|
||||
var preErrorLineNumberInFile = Math.Max(errorStartLineNumberInFile - _sourceCodeLineCount, 1);
|
||||
var postErrorLineNumberInFile = errorEndLineNumberInFile + _sourceCodeLineCount;
|
||||
var codeBlock = allLines
|
||||
.Skip(preErrorLineNumberInFile - 1)
|
||||
.Take(postErrorLineNumberInFile - preErrorLineNumberInFile + 1)
|
||||
.ToArray();
|
||||
|
||||
var numOfErrorLines = (errorEndLineNumberInFile - errorStartLineNumberInFile) + 1;
|
||||
var errorStartLineNumberInArray = errorStartLineNumberInFile - preErrorLineNumberInFile;
|
||||
|
||||
frame.PreContextLine = preErrorLineNumberInFile;
|
||||
frame.PreContextCode = codeBlock.Take(errorStartLineNumberInArray).ToArray();
|
||||
frame.ContextCode = codeBlock
|
||||
.Skip(errorStartLineNumberInArray)
|
||||
.Take(numOfErrorLines)
|
||||
.ToArray();
|
||||
frame.PostContextCode = codeBlock
|
||||
.Skip(errorStartLineNumberInArray + numOfErrorLines)
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
private static IEnumerable<string> ReadLines(IFileInfo fileInfo)
|
||||
{
|
||||
using (var reader = new StreamReader(fileInfo.CreateReadStream()))
|
||||
{
|
||||
string line;
|
||||
while ((line = reader.ReadLine()) != null)
|
||||
{
|
||||
yield return line;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. 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.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Microsoft.Extensions.StackTrace.Sources
|
||||
{
|
||||
internal class MethodDisplayInfo
|
||||
{
|
||||
public string DeclaringTypeName { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public string GenericArguments { get; set; }
|
||||
|
||||
public string SubMethod { get; set; }
|
||||
|
||||
public IEnumerable<ParameterDisplayInfo> Parameters { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
if (!string.IsNullOrEmpty(DeclaringTypeName))
|
||||
{
|
||||
builder
|
||||
.Append(DeclaringTypeName)
|
||||
.Append(".");
|
||||
}
|
||||
|
||||
builder.Append(Name);
|
||||
builder.Append(GenericArguments);
|
||||
|
||||
builder.Append("(");
|
||||
builder.Append(string.Join(", ", Parameters.Select(p => p.ToString())));
|
||||
builder.Append(")");
|
||||
|
||||
if (!string.IsNullOrEmpty(SubMethod))
|
||||
{
|
||||
builder.Append("+");
|
||||
builder.Append(SubMethod);
|
||||
builder.Append("()");
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Text;
|
||||
|
||||
namespace Microsoft.Extensions.StackTrace.Sources
|
||||
{
|
||||
internal class ParameterDisplayInfo
|
||||
{
|
||||
public string Name { get; set; }
|
||||
|
||||
public string Type { get; set; }
|
||||
|
||||
public string Prefix { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
if (!string.IsNullOrEmpty(Prefix))
|
||||
{
|
||||
builder
|
||||
.Append(Prefix)
|
||||
.Append(" ");
|
||||
}
|
||||
|
||||
builder.Append(Type);
|
||||
builder.Append(" ");
|
||||
builder.Append(Name);
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,135 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Metadata;
|
||||
using System.Reflection.Metadata.Ecma335;
|
||||
using System.Reflection.PortableExecutable;
|
||||
|
||||
namespace Microsoft.Extensions.StackTrace.Sources
|
||||
{
|
||||
internal class PortablePdbReader : IDisposable
|
||||
{
|
||||
private readonly Dictionary<string, MetadataReaderProvider> _cache =
|
||||
new Dictionary<string, MetadataReaderProvider>(StringComparer.Ordinal);
|
||||
|
||||
public void PopulateStackFrame(StackFrameInfo frameInfo, MethodBase method, int IlOffset)
|
||||
{
|
||||
if (method.Module.Assembly.IsDynamic)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var metadataReader = GetMetadataReader(method.Module.Assembly.Location);
|
||||
|
||||
if (metadataReader == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var methodToken = MetadataTokens.Handle(method.MetadataToken);
|
||||
|
||||
Debug.Assert(methodToken.Kind == HandleKind.MethodDefinition);
|
||||
|
||||
var handle = ((MethodDefinitionHandle)methodToken).ToDebugInformationHandle();
|
||||
|
||||
if (!handle.IsNil)
|
||||
{
|
||||
var methodDebugInfo = metadataReader.GetMethodDebugInformation(handle);
|
||||
var sequencePoints = methodDebugInfo.GetSequencePoints();
|
||||
SequencePoint? bestPointSoFar = null;
|
||||
|
||||
foreach (var point in sequencePoints)
|
||||
{
|
||||
if (point.Offset > IlOffset)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (point.StartLine != SequencePoint.HiddenLine)
|
||||
{
|
||||
bestPointSoFar = point;
|
||||
}
|
||||
}
|
||||
|
||||
if (bestPointSoFar.HasValue)
|
||||
{
|
||||
frameInfo.LineNumber = bestPointSoFar.Value.StartLine;
|
||||
frameInfo.FilePath = metadataReader.GetString(metadataReader.GetDocument(bestPointSoFar.Value.Document).Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private MetadataReader GetMetadataReader(string assemblyPath)
|
||||
{
|
||||
MetadataReaderProvider provider = null;
|
||||
if (!_cache.TryGetValue(assemblyPath, out provider))
|
||||
{
|
||||
var pdbPath = GetPdbPath(assemblyPath);
|
||||
|
||||
if (!string.IsNullOrEmpty(pdbPath) && File.Exists(pdbPath) && IsPortable(pdbPath))
|
||||
{
|
||||
var pdbStream = File.OpenRead(pdbPath);
|
||||
provider = MetadataReaderProvider.FromPortablePdbStream(pdbStream);
|
||||
}
|
||||
|
||||
_cache[assemblyPath] = provider;
|
||||
}
|
||||
|
||||
return provider?.GetMetadataReader();
|
||||
}
|
||||
|
||||
private static string GetPdbPath(string assemblyPath)
|
||||
{
|
||||
if (string.IsNullOrEmpty(assemblyPath))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (File.Exists(assemblyPath))
|
||||
{
|
||||
var peStream = File.OpenRead(assemblyPath);
|
||||
|
||||
using (var peReader = new PEReader(peStream))
|
||||
{
|
||||
foreach (var entry in peReader.ReadDebugDirectory())
|
||||
{
|
||||
if (entry.Type == DebugDirectoryEntryType.CodeView)
|
||||
{
|
||||
var codeViewData = peReader.ReadCodeViewDebugDirectoryData(entry);
|
||||
var peDirectory = Path.GetDirectoryName(assemblyPath);
|
||||
return Path.Combine(peDirectory, Path.GetFileName(codeViewData.Path));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static bool IsPortable(string pdbPath)
|
||||
{
|
||||
using (var pdbStream = File.OpenRead(pdbPath))
|
||||
{
|
||||
return pdbStream.ReadByte() == 'B' &&
|
||||
pdbStream.ReadByte() == 'S' &&
|
||||
pdbStream.ReadByte() == 'J' &&
|
||||
pdbStream.ReadByte() == 'B';
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (var entry in _cache)
|
||||
{
|
||||
entry.Value?.Dispose();
|
||||
}
|
||||
|
||||
_cache.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Microsoft.Extensions.StackTrace.Sources
|
||||
{
|
||||
internal class StackFrameInfo
|
||||
{
|
||||
public int LineNumber { get; set; }
|
||||
|
||||
public string FilePath { get; set; }
|
||||
|
||||
public StackFrame StackFrame { get; set; }
|
||||
|
||||
public MethodDisplayInfo MethodDisplayInfo { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. 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.Linq;
|
||||
|
||||
namespace Microsoft.Extensions.StackTrace.Sources
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains the source code where the exception occurred.
|
||||
/// </summary>
|
||||
internal class StackFrameSourceCodeInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Function containing instruction
|
||||
/// </summary>
|
||||
public string Function { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// File containing the instruction
|
||||
/// </summary>
|
||||
public string File { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The line number of the instruction
|
||||
/// </summary>
|
||||
public int Line { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The line preceding the frame line
|
||||
/// </summary>
|
||||
public int PreContextLine { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Lines of code before the actual error line(s).
|
||||
/// </summary>
|
||||
public IEnumerable<string> PreContextCode { get; set; } = Enumerable.Empty<string>();
|
||||
|
||||
/// <summary>
|
||||
/// Line(s) of code responsible for the error.
|
||||
/// </summary>
|
||||
public IEnumerable<string> ContextCode { get; set; } = Enumerable.Empty<string>();
|
||||
|
||||
/// <summary>
|
||||
/// Lines of code after the actual error line(s).
|
||||
/// </summary>
|
||||
public IEnumerable<string> PostContextCode { get; set; } = Enumerable.Empty<string>();
|
||||
|
||||
/// <summary>
|
||||
/// Specific error details for this stack frame.
|
||||
/// </summary>
|
||||
public string ErrorDetails { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,261 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.ExceptionServices;
|
||||
using Microsoft.Extensions.Internal;
|
||||
|
||||
namespace Microsoft.Extensions.StackTrace.Sources
|
||||
{
|
||||
internal class StackTraceHelper
|
||||
{
|
||||
public static IList<StackFrameInfo> GetFrames(Exception exception)
|
||||
{
|
||||
var frames = new List<StackFrameInfo>();
|
||||
|
||||
if (exception == null)
|
||||
{
|
||||
return frames;
|
||||
}
|
||||
|
||||
using (var portablePdbReader = new PortablePdbReader())
|
||||
{
|
||||
var needFileInfo = true;
|
||||
var stackTrace = new System.Diagnostics.StackTrace(exception, needFileInfo);
|
||||
var stackFrames = stackTrace.GetFrames();
|
||||
|
||||
if (stackFrames == null)
|
||||
{
|
||||
return frames;
|
||||
}
|
||||
|
||||
for (var i = 0; i < stackFrames.Length; i++)
|
||||
{
|
||||
var frame = stackFrames[i];
|
||||
var method = frame.GetMethod();
|
||||
|
||||
// Always show last stackFrame
|
||||
if (!ShowInStackTrace(method) && i < stackFrames.Length - 1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var stackFrame = new StackFrameInfo
|
||||
{
|
||||
StackFrame = frame,
|
||||
FilePath = frame.GetFileName(),
|
||||
LineNumber = frame.GetFileLineNumber(),
|
||||
MethodDisplayInfo = GetMethodDisplayString(frame.GetMethod()),
|
||||
};
|
||||
|
||||
if (string.IsNullOrEmpty(stackFrame.FilePath))
|
||||
{
|
||||
// .NET Framework and older versions of mono don't support portable PDBs
|
||||
// so we read it manually to get file name and line information
|
||||
portablePdbReader.PopulateStackFrame(stackFrame, method, frame.GetILOffset());
|
||||
}
|
||||
|
||||
frames.Add(stackFrame);
|
||||
}
|
||||
|
||||
return frames;
|
||||
}
|
||||
}
|
||||
|
||||
internal static MethodDisplayInfo GetMethodDisplayString(MethodBase method)
|
||||
{
|
||||
// Special case: no method available
|
||||
if (method == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var methodDisplayInfo = new MethodDisplayInfo();
|
||||
|
||||
// Type name
|
||||
var type = method.DeclaringType;
|
||||
|
||||
var methodName = method.Name;
|
||||
|
||||
if (type != null && type.IsDefined(typeof(CompilerGeneratedAttribute)) &&
|
||||
(typeof(IAsyncStateMachine).IsAssignableFrom(type) || typeof(IEnumerator).IsAssignableFrom(type)))
|
||||
{
|
||||
// Convert StateMachine methods to correct overload +MoveNext()
|
||||
if (TryResolveStateMachineMethod(ref method, out type))
|
||||
{
|
||||
methodDisplayInfo.SubMethod = methodName;
|
||||
}
|
||||
}
|
||||
// ResolveStateMachineMethod may have set declaringType to null
|
||||
if (type != null)
|
||||
{
|
||||
methodDisplayInfo.DeclaringTypeName = TypeNameHelper.GetTypeDisplayName(type, includeGenericParameterNames: true);
|
||||
}
|
||||
|
||||
// Method name
|
||||
methodDisplayInfo.Name = method.Name;
|
||||
if (method.IsGenericMethod)
|
||||
{
|
||||
var genericArguments = string.Join(", ", method.GetGenericArguments()
|
||||
.Select(arg => TypeNameHelper.GetTypeDisplayName(arg, fullName: false, includeGenericParameterNames: true)));
|
||||
methodDisplayInfo.GenericArguments += "<" + genericArguments + ">";
|
||||
}
|
||||
|
||||
// Method parameters
|
||||
methodDisplayInfo.Parameters = method.GetParameters().Select(parameter =>
|
||||
{
|
||||
var parameterType = parameter.ParameterType;
|
||||
|
||||
var prefix = string.Empty;
|
||||
if (parameter.IsOut)
|
||||
{
|
||||
prefix = "out";
|
||||
}
|
||||
else if (parameterType != null && parameterType.IsByRef)
|
||||
{
|
||||
prefix = "ref";
|
||||
}
|
||||
|
||||
var parameterTypeString = "?";
|
||||
if (parameterType != null)
|
||||
{
|
||||
if (parameterType.IsByRef)
|
||||
{
|
||||
parameterType = parameterType.GetElementType();
|
||||
}
|
||||
|
||||
parameterTypeString = TypeNameHelper.GetTypeDisplayName(parameterType, fullName: false, includeGenericParameterNames: true);
|
||||
}
|
||||
|
||||
return new ParameterDisplayInfo
|
||||
{
|
||||
Prefix = prefix,
|
||||
Name = parameter.Name,
|
||||
Type = parameterTypeString,
|
||||
};
|
||||
});
|
||||
|
||||
return methodDisplayInfo;
|
||||
}
|
||||
|
||||
private static bool ShowInStackTrace(MethodBase method)
|
||||
{
|
||||
Debug.Assert(method != null);
|
||||
|
||||
// Don't show any methods marked with the StackTraceHiddenAttribute
|
||||
// https://github.com/dotnet/coreclr/pull/14652
|
||||
if (HasStackTraceHiddenAttribute(method))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
var type = method.DeclaringType;
|
||||
if (type == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (HasStackTraceHiddenAttribute(type))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Fallbacks for runtime pre-StackTraceHiddenAttribute
|
||||
if (type == typeof(ExceptionDispatchInfo) && method.Name == "Throw")
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (type == typeof(TaskAwaiter) ||
|
||||
type == typeof(TaskAwaiter<>) ||
|
||||
type == typeof(ConfiguredTaskAwaitable.ConfiguredTaskAwaiter) ||
|
||||
type == typeof(ConfiguredTaskAwaitable<>.ConfiguredTaskAwaiter))
|
||||
{
|
||||
switch (method.Name)
|
||||
{
|
||||
case "HandleNonSuccessAndDebuggerNotification":
|
||||
case "ThrowForNonSuccess":
|
||||
case "ValidateEnd":
|
||||
case "GetResult":
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryResolveStateMachineMethod(ref MethodBase method, out Type declaringType)
|
||||
{
|
||||
Debug.Assert(method != null);
|
||||
Debug.Assert(method.DeclaringType != null);
|
||||
|
||||
declaringType = method.DeclaringType;
|
||||
|
||||
var parentType = declaringType.DeclaringType;
|
||||
if (parentType == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var methods = parentType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance | BindingFlags.DeclaredOnly);
|
||||
if (methods == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (var candidateMethod in methods)
|
||||
{
|
||||
var attributes = candidateMethod.GetCustomAttributes<StateMachineAttribute>();
|
||||
if (attributes == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var asma in attributes)
|
||||
{
|
||||
if (asma.StateMachineType == declaringType)
|
||||
{
|
||||
method = candidateMethod;
|
||||
declaringType = candidateMethod.DeclaringType;
|
||||
// Mark the iterator as changed; so it gets the + annotation of the original method
|
||||
// async statemachines resolve directly to their builder methods so aren't marked as changed
|
||||
return asma is IteratorStateMachineAttribute;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool HasStackTraceHiddenAttribute(MemberInfo memberInfo)
|
||||
{
|
||||
IList<CustomAttributeData> attributes;
|
||||
try
|
||||
{
|
||||
// Accessing MembmerInfo.GetCustomAttributesData throws for some types (such as types in dynamically generated assemblies).
|
||||
// We'll skip looking up StackTraceHiddenAttributes on such types.
|
||||
attributes = memberInfo.GetCustomAttributesData();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var i = 0; i < attributes.Count; i++)
|
||||
{
|
||||
if (attributes[i].AttributeType.Name == "StackTraceHiddenAttribute")
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>$(StandardTestTfms)</TargetFrameworks>
|
||||
<DebugType>portable</DebugType>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
|
|
@ -12,15 +11,9 @@
|
|||
..\src\BenchmarkRunner\**\*.cs;
|
||||
..\src\StackTrace\ExceptionDetails\**\*.cs;
|
||||
" />
|
||||
<EmbeddedResource Include="..\src\**\*.resx" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\testassets\ThrowingLibrary\ThrowingLibrary.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="System.Reflection.Metadata" />
|
||||
<Reference Include="System.Threading.Tasks.Extensions" />
|
||||
<Reference Include="System.Security.Cryptography.Cng" />
|
||||
<Reference Include="System.Runtime.CompilerServices.Unsafe" />
|
||||
|
|
|
|||
|
|
@ -1,4 +0,0 @@
|
|||
NOTE:
|
||||
1. The tests for 'ExceptionDetailProvider' and 'StackTraceHelper' in project 'Microsoft.Extensions.StackTrace.Sources' are located in Diagnostics
|
||||
repo. This is because they refer to some packages from FileSystem repo which causes a circular reference and breaks the
|
||||
build.
|
||||
|
|
@ -1,345 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.StackTrace.Sources;
|
||||
using ThrowingLibrary;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.Extensions.Internal
|
||||
{
|
||||
public class StackTraceHelperTest
|
||||
{
|
||||
[Fact]
|
||||
public void StackTraceHelper_IncludesLineNumbersForFiles()
|
||||
{
|
||||
// Arrange
|
||||
Exception exception = null;
|
||||
try
|
||||
{
|
||||
// Throwing an exception in the current assembly always seems to populate the full stack
|
||||
// trace regardless of symbol type. Crossing assembly boundaries ensures PortablePdbReader gets used
|
||||
// on desktop.
|
||||
Thrower.Throw();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
exception = ex;
|
||||
}
|
||||
|
||||
// Act
|
||||
var stackFrames = StackTraceHelper.GetFrames(exception);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(stackFrames,
|
||||
frame =>
|
||||
{
|
||||
Assert.Contains("Thrower.cs", frame.FilePath);
|
||||
Assert.Equal(17, frame.LineNumber);
|
||||
},
|
||||
frame =>
|
||||
{
|
||||
Assert.Contains("StackTraceHelperTest.cs", frame.FilePath);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StackTraceHelper_PrettyPrintsStackTraceForGenericMethods()
|
||||
{
|
||||
// Arrange
|
||||
var exception = Record.Exception(() => GenericMethod<string>(null));
|
||||
|
||||
// Act
|
||||
var stackFrames = StackTraceHelper.GetFrames(exception);
|
||||
|
||||
// Assert
|
||||
var methods = stackFrames.Select(frame => frame.MethodDisplayInfo.ToString()).ToArray();
|
||||
Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest.GenericMethod<T>(T val)", methods[0]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StackTraceHelper_PrettyPrintsStackTraceForMethodsWithOutParameters()
|
||||
{
|
||||
// Arrange
|
||||
var exception = Record.Exception(() => MethodWithOutParameter(out var value));
|
||||
|
||||
// Act
|
||||
var stackFrames = StackTraceHelper.GetFrames(exception);
|
||||
|
||||
// Assert
|
||||
var methods = stackFrames.Select(frame => frame.MethodDisplayInfo.ToString()).ToArray();
|
||||
Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest.MethodWithOutParameter(out int value)", methods[0]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StackTraceHelper_PrettyPrintsStackTraceForMethodsWithGenericOutParameters()
|
||||
{
|
||||
// Arrange
|
||||
var exception = Record.Exception(() => MethodWithGenericOutParameter("Test", out int value));
|
||||
|
||||
// Act
|
||||
var stackFrames = StackTraceHelper.GetFrames(exception);
|
||||
|
||||
// Assert
|
||||
var methods = stackFrames.Select(frame => frame.MethodDisplayInfo.ToString()).ToArray();
|
||||
Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest.MethodWithGenericOutParameter<TVal>(string a, out TVal value)", methods[0]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StackTraceHelper_PrettyPrintsStackTraceForMethodsWithRefParameters()
|
||||
{
|
||||
// Arrange
|
||||
var value = 0;
|
||||
var exception = Record.Exception(() => MethodWithRefParameter(ref value));
|
||||
|
||||
// Act
|
||||
var stackFrames = StackTraceHelper.GetFrames(exception);
|
||||
|
||||
// Assert
|
||||
var methods = stackFrames.Select(frame => frame.MethodDisplayInfo.ToString()).ToArray();
|
||||
Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest.MethodWithRefParameter(ref int value)", methods[0]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StackTraceHelper_PrettyPrintsStackTraceForMethodsWithGenericRefParameters()
|
||||
{
|
||||
// Arrange
|
||||
var value = 0;
|
||||
var exception = Record.Exception(() => MethodWithGenericRefParameter(ref value));
|
||||
|
||||
// Act
|
||||
var stackFrames = StackTraceHelper.GetFrames(exception);
|
||||
|
||||
// Assert
|
||||
var methods = stackFrames.Select(frame => frame.MethodDisplayInfo.ToString()).ToArray();
|
||||
Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest.MethodWithGenericRefParameter<TVal>(ref TVal value)", methods[0]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StackTraceHelper_PrettyPrintsStackTraceForMethodsWithNullableParameters()
|
||||
{
|
||||
// Arrange
|
||||
var value = 0;
|
||||
var exception = Record.Exception(() => MethodWithNullableParameter(value));
|
||||
|
||||
// Act
|
||||
var stackFrames = StackTraceHelper.GetFrames(exception);
|
||||
|
||||
// Assert
|
||||
var methods = stackFrames.Select(frame => frame.MethodDisplayInfo.ToString()).ToArray();
|
||||
Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest.MethodWithNullableParameter(Nullable<int> value)", methods[0]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StackTraceHelper_PrettyPrintsStackTraceForMethodsOnGenericTypes()
|
||||
{
|
||||
// Arrange
|
||||
var exception = Record.Exception(() => new GenericClass<int>().Throw(0));
|
||||
|
||||
// Act
|
||||
var stackFrames = StackTraceHelper.GetFrames(exception);
|
||||
|
||||
// Assert
|
||||
var methods = stackFrames.Select(frame => frame.MethodDisplayInfo.ToString()).ToArray();
|
||||
Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest+GenericClass<T>.Throw(T parameter)", methods[0]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StackTraceHelper_ProducesReadableOutput()
|
||||
{
|
||||
// Arrange
|
||||
var expectedCallStack = new List<string>()
|
||||
{
|
||||
"Microsoft.Extensions.Internal.StackTraceHelperTest.Iterator()+MoveNext()",
|
||||
"string.Join(string separator, IEnumerable<string> values)",
|
||||
"Microsoft.Extensions.Internal.StackTraceHelperTest+GenericClass<T>.GenericMethod<V>(ref V value)",
|
||||
"Microsoft.Extensions.Internal.StackTraceHelperTest.MethodAsync(int value)",
|
||||
"Microsoft.Extensions.Internal.StackTraceHelperTest.MethodAsync<TValue>(TValue value)",
|
||||
"Microsoft.Extensions.Internal.StackTraceHelperTest.Method(string value)",
|
||||
"Microsoft.Extensions.Internal.StackTraceHelperTest.StackTraceHelper_ProducesReadableOutput()",
|
||||
};
|
||||
|
||||
Exception exception = null;
|
||||
try
|
||||
{
|
||||
Method("test");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
exception = ex;
|
||||
}
|
||||
|
||||
// Act
|
||||
var stackFrames = StackTraceHelper.GetFrames(exception);
|
||||
var methodNames = stackFrames.Select(stackFrame => stackFrame.MethodDisplayInfo.ToString()).ToArray();
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedCallStack, methodNames);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StackTraceHelper_DoesNotIncludeInstanceMethodsOnTypesWithStackTraceHiddenAttribute()
|
||||
{
|
||||
// Arrange
|
||||
var exception = Record.Exception(() => InvokeMethodOnTypeWithStackTraceHiddenAttribute());
|
||||
|
||||
// Act
|
||||
var stackFrames = StackTraceHelper.GetFrames(exception);
|
||||
|
||||
// Assert
|
||||
var methods = stackFrames.Select(frame => frame.MethodDisplayInfo.ToString()).ToArray();
|
||||
Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest.ThrowCore()", methods[0]);
|
||||
Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest.InvokeMethodOnTypeWithStackTraceHiddenAttribute()", methods[1]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StackTraceHelper_DoesNotIncludeStaticMethodsOnTypesWithStackTraceHiddenAttribute()
|
||||
{
|
||||
// Arrange
|
||||
var exception = Record.Exception(() => InvokeStaticMethodOnTypeWithStackTraceHiddenAttribute());
|
||||
|
||||
// Act
|
||||
var stackFrames = StackTraceHelper.GetFrames(exception);
|
||||
|
||||
// Assert
|
||||
var methods = stackFrames.Select(frame => frame.MethodDisplayInfo.ToString()).ToArray();
|
||||
Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest.ThrowCore()", methods[0]);
|
||||
Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest.InvokeStaticMethodOnTypeWithStackTraceHiddenAttribute()", methods[1]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StackTraceHelper_DoesNotIncludeMethodsWithStackTraceHiddenAttribute()
|
||||
{
|
||||
// Arrange
|
||||
var exception = Record.Exception(() => new TypeWithMethodWithStackTraceHiddenAttribute().Throw());
|
||||
|
||||
// Act
|
||||
var stackFrames = StackTraceHelper.GetFrames(exception);
|
||||
|
||||
// Assert
|
||||
var methods = stackFrames.Select(frame => frame.MethodDisplayInfo.ToString()).ToArray();
|
||||
Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest.ThrowCore()", methods[0]);
|
||||
Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest+TypeWithMethodWithStackTraceHiddenAttribute.Throw()", methods[1]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetFrames_DoesNotFailForDynamicallyGeneratedAssemblies()
|
||||
{
|
||||
// Arrange
|
||||
var action = (Action)Expression.Lambda(
|
||||
Expression.Throw(
|
||||
Expression.New(typeof(Exception)))).Compile();
|
||||
var exception = Record.Exception(action);
|
||||
|
||||
// Act
|
||||
var frames = StackTraceHelper.GetFrames(exception).ToArray();
|
||||
|
||||
// Assert
|
||||
var frame = frames[0];
|
||||
Assert.Null(frame.FilePath);
|
||||
Assert.Equal($"lambda_method(Closure )", frame.MethodDisplayInfo.ToString());
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
|
||||
async Task<string> MethodAsync(int value)
|
||||
{
|
||||
await Task.Delay(0);
|
||||
return GenericClass<byte>.GenericMethod(ref value);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
|
||||
async Task<string> MethodAsync<TValue>(TValue value)
|
||||
{
|
||||
return await MethodAsync(1);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
|
||||
string Method(string value)
|
||||
{
|
||||
return MethodAsync(value).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
|
||||
static IEnumerable<string> Iterator()
|
||||
{
|
||||
yield return "Success";
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
|
||||
void MethodWithOutParameter(out int value) => throw new Exception();
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
|
||||
void MethodWithGenericOutParameter<TVal>(string a, out TVal value) => throw new Exception();
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
|
||||
void MethodWithRefParameter(ref int value) => throw new Exception();
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
|
||||
void MethodWithGenericRefParameter<TVal>(ref TVal value) => throw new Exception();
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
|
||||
void MethodWithNullableParameter(int? value) => throw new Exception();
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
|
||||
void InvokeMethodOnTypeWithStackTraceHiddenAttribute() => new TypeWithStackTraceHiddenAttribute().Throw();
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
|
||||
void InvokeStaticMethodOnTypeWithStackTraceHiddenAttribute() => TypeWithStackTraceHiddenAttribute.ThrowStatic();
|
||||
|
||||
class GenericClass<T>
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
|
||||
public static string GenericMethod<V>(ref V value)
|
||||
{
|
||||
var returnVal = "";
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
returnVal += string.Join(", ", Iterator());
|
||||
}
|
||||
return returnVal;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
|
||||
public void Throw(T parameter) => throw new Exception();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
|
||||
private void GenericMethod<T>(T val) where T : class => throw new Exception();
|
||||
|
||||
private class StackTraceHiddenAttribute : Attribute
|
||||
{
|
||||
}
|
||||
|
||||
[StackTraceHidden]
|
||||
private class TypeWithStackTraceHiddenAttribute
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
|
||||
public void Throw() => ThrowCore();
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
|
||||
public static void ThrowStatic() => ThrowCore();
|
||||
}
|
||||
|
||||
private class TypeWithMethodWithStackTraceHiddenAttribute
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
|
||||
[StackTraceHidden]
|
||||
public void MethodWithStackTraceHiddenAttribute()
|
||||
{
|
||||
ThrowCore();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
|
||||
public void Throw() => MethodWithStackTraceHiddenAttribute();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
|
||||
private static void ThrowCore() => throw new Exception();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace ThrowingLibrary
|
||||
{
|
||||
// Throwing an exception in the current assembly always seems to populate the full stack
|
||||
// trace regardless of symbol type. This type exists to simulate an exception thrown
|
||||
// across assemblies which is the typical use case for StackTraceHelper.
|
||||
public static class Thrower
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static void Throw()
|
||||
{
|
||||
throw new DivideByZeroException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<DebugType>portable</DebugType>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
Loading…
Reference in New Issue