diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/CompatibilitySuppressions.xml b/src/coreclr/nativeaot/System.Private.CoreLib/src/CompatibilitySuppressions.xml
index 0e5e5abf785801..5919536924219a 100644
--- a/src/coreclr/nativeaot/System.Private.CoreLib/src/CompatibilitySuppressions.xml
+++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/CompatibilitySuppressions.xml
@@ -837,4 +837,10 @@
CP0002
M:System.Threading.Lock.#ctor(System.Boolean)
-
+
+ CP0002
+ M:System.Diagnostics.DiagnosticMethodInfo.#ctor(System.String,System.String,System.String)
+ ref/net9.0/System.Private.CoreLib.dll
+ lib/net9.0/System.Private.CoreLib.dll
+
+
\ No newline at end of file
diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/StackTraceMetadataCallbacks.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/StackTraceMetadataCallbacks.cs
index 655c460352de3e..ff3ab06b73c550 100644
--- a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/StackTraceMetadataCallbacks.cs
+++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/StackTraceMetadataCallbacks.cs
@@ -2,8 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System;
-
-using Internal.Runtime.CompilerServices;
+using System.Diagnostics;
namespace Internal.Runtime.Augments
{
@@ -26,5 +25,7 @@ public abstract class StackTraceMetadataCallbacks
/// Returns a value indicating whether the method should be hidden in stack traces
/// Formatted method name or null if metadata for the method is not available
public abstract string TryGetMethodNameFromStartAddress(IntPtr methodStartAddress, out bool isStackTraceHidden);
+
+ public abstract DiagnosticMethodInfo TryGetDiagnosticMethodInfoFromStartAddress(IntPtr methodStartAddress);
}
}
diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj
index 78a4e56c54976c..bea161cffbaa0c 100644
--- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj
+++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj
@@ -131,6 +131,7 @@
+
diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Delegate.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Delegate.cs
index bc8c517acb8ea9..05e3859bee90a1 100644
--- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Delegate.cs
+++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Delegate.cs
@@ -12,6 +12,7 @@
using Internal.Reflection.Augments;
using Internal.Runtime;
+using Internal.Runtime.Augments;
using Internal.Runtime.CompilerServices;
namespace System
@@ -254,6 +255,8 @@ private IntPtr GetActualTargetFunctionPointer(object thisObject)
protected virtual MethodInfo GetMethodImpl()
{
+ // NOTE: this implementation is mirrored in GetDiagnosticMethodInfo below
+
// Multi-cast delegates return the Method of the last delegate in the list
if (_helperObject is Wrapper[] invocationList)
{
@@ -270,6 +273,52 @@ protected virtual MethodInfo GetMethodImpl()
return ReflectionAugments.ReflectionCoreCallbacks.GetDelegateMethod(this);
}
+ internal DiagnosticMethodInfo GetDiagnosticMethodInfo()
+ {
+ // NOTE: this implementation is mirrored in GetMethodImpl above
+
+ // Multi-cast delegates return the diagnostic method info of the last delegate in the list
+ if (_helperObject is Wrapper[] invocationList)
+ {
+ int invocationCount = (int)_extraFunctionPointerOrData;
+ return invocationList[invocationCount - 1].Value.GetDiagnosticMethodInfo();
+ }
+
+ // Return the delegate Invoke method for marshalled function pointers and LINQ expressions
+ if ((_firstParameter is NativeFunctionPointerWrapper) || (_functionPointer == GetThunk(ObjectArrayThunk)))
+ {
+ Type t = GetType();
+ return new DiagnosticMethodInfo("Invoke", t.FullName, t.Module.Assembly.FullName);
+ }
+
+ IntPtr ldftnResult = GetDelegateLdFtnResult(out RuntimeTypeHandle _, out bool isOpenResolver);
+ if (isOpenResolver)
+ {
+ MethodInfo mi = ReflectionAugments.ReflectionCoreCallbacks.GetDelegateMethod(this);
+ Type? declaringType = mi.DeclaringType;
+ if (declaringType.IsConstructedGenericType)
+ declaringType = declaringType.GetGenericTypeDefinition();
+ return new DiagnosticMethodInfo(mi.Name, declaringType.FullName, mi.Module.Assembly.FullName);
+ }
+ else
+ {
+ IntPtr functionPointer;
+ if (FunctionPointerOps.IsGenericMethodPointer(ldftnResult))
+ {
+ unsafe
+ {
+ GenericMethodDescriptor* realTargetData = FunctionPointerOps.ConvertToGenericDescriptor(ldftnResult);
+ functionPointer = RuntimeAugments.GetCodeTarget(realTargetData->MethodFunctionPointer);
+ }
+ }
+ else
+ {
+ functionPointer = ldftnResult;
+ }
+ return RuntimeAugments.StackTraceCallbacksIfAvailable?.TryGetDiagnosticMethodInfoFromStartAddress(functionPointer);
+ }
+ }
+
public object? Target
{
get
diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Diagnostics/DiagnosticMethodInfo.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Diagnostics/DiagnosticMethodInfo.NativeAot.cs
new file mode 100644
index 00000000000000..b67fa05ba2f500
--- /dev/null
+++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Diagnostics/DiagnosticMethodInfo.NativeAot.cs
@@ -0,0 +1,34 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Internal.Runtime.Augments;
+
+namespace System.Diagnostics
+{
+ public sealed partial class DiagnosticMethodInfo
+ {
+ // Public for System.Private.StackTraceMetadata sake
+ public DiagnosticMethodInfo(string name, string declaringTypeName, string declaringAssemblyName)
+ => (Name, DeclaringTypeName, DeclaringAssemblyName) = (name, declaringTypeName, declaringAssemblyName);
+
+ public string Name { get; }
+
+ public string? DeclaringTypeName { get; }
+
+ public string? DeclaringAssemblyName { get; }
+
+ public static DiagnosticMethodInfo? Create(Delegate @delegate)
+ {
+ ArgumentNullException.ThrowIfNull(@delegate);
+ return @delegate.GetDiagnosticMethodInfo();
+ }
+
+ public static DiagnosticMethodInfo? Create(StackFrame frame)
+ {
+ ArgumentNullException.ThrowIfNull(frame);
+ return frame.TryGetMethodStartAddress(out IntPtr startAddress)
+ ? RuntimeAugments.StackTraceCallbacksIfAvailable?.TryGetDiagnosticMethodInfoFromStartAddress(startAddress)
+ : null;
+ }
+ }
+}
diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Diagnostics/StackFrame.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Diagnostics/StackFrame.NativeAot.cs
index f061cf1d461d71..ed17c7eb72d9ad 100644
--- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Diagnostics/StackFrame.NativeAot.cs
+++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Diagnostics/StackFrame.NativeAot.cs
@@ -46,6 +46,19 @@ public partial class StackFrame
return _method;
}
+ internal bool TryGetMethodStartAddress(out IntPtr startAddress)
+ {
+ if (_ipAddress == IntPtr.Zero || _ipAddress == Exception.EdiSeparator)
+ {
+ startAddress = IntPtr.Zero;
+ return false;
+ }
+
+ startAddress = _ipAddress - _nativeOffset;
+ Debug.Assert(RuntimeImports.RhFindMethodStartAddress(_ipAddress) == startAddress);
+ return true;
+ }
+
private bool TryInitializeMethodBase()
{
if (_noMethodBaseAvailable || _ipAddress == IntPtr.Zero || _ipAddress == Exception.EdiSeparator)
diff --git a/src/coreclr/nativeaot/System.Private.StackTraceMetadata/src/Internal/StackTraceMetadata/MethodNameFormatter.cs b/src/coreclr/nativeaot/System.Private.StackTraceMetadata/src/Internal/StackTraceMetadata/MethodNameFormatter.cs
index 5e84594fc62975..4b57c36fa4930a 100644
--- a/src/coreclr/nativeaot/System.Private.StackTraceMetadata/src/Internal/StackTraceMetadata/MethodNameFormatter.cs
+++ b/src/coreclr/nativeaot/System.Private.StackTraceMetadata/src/Internal/StackTraceMetadata/MethodNameFormatter.cs
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System;
using System.Diagnostics;
using System.Text;
@@ -10,6 +11,14 @@ namespace Internal.StackTraceMetadata
{
internal class MethodNameFormatter
{
+ [Flags]
+ private enum Flags
+ {
+ None = 0,
+ NamespaceQualify = 1,
+ ReflectionFormat = 2,
+ }
+
///
/// Metadata reader used for the purpose of method name formatting.
///
@@ -35,10 +44,17 @@ private MethodNameFormatter(MetadataReader metadataReader, SigTypeContext typeCo
_typeContext = typeContext;
}
+ public static string FormatReflectionNotationTypeName(MetadataReader metadataReader, Handle type)
+ {
+ MethodNameFormatter formatter = new MethodNameFormatter(metadataReader, default);
+ formatter.EmitTypeName(type, Flags.NamespaceQualify | Flags.ReflectionFormat);
+ return formatter._outputBuilder.ToString();
+ }
+
public static string FormatMethodName(MetadataReader metadataReader, Handle owningType, ConstantStringValueHandle name, MethodSignatureHandle signature, ConstantStringArrayHandle genericArguments)
{
MethodNameFormatter formatter = new MethodNameFormatter(metadataReader, SigTypeContext.FromMethod(metadataReader, owningType, genericArguments));
- formatter.EmitTypeName(owningType, namespaceQualified: true);
+ formatter.EmitTypeName(owningType, Flags.NamespaceQualify);
formatter._outputBuilder.Append('.');
formatter.EmitString(name);
@@ -75,7 +91,7 @@ public static string FormatMethodName(MetadataReader metadataReader, TypeDefinit
MethodNameFormatter formatter = new MethodNameFormatter(metadataReader, SigTypeContext.FromMethod(metadataReader, enclosingTypeHandle, methodHandle));
Method method = metadataReader.GetMethod(methodHandle);
- formatter.EmitTypeName(enclosingTypeHandle, namespaceQualified: true);
+ formatter.EmitTypeName(enclosingTypeHandle, Flags.NamespaceQualify);
formatter._outputBuilder.Append('.');
formatter.EmitString(method.Name);
@@ -91,7 +107,7 @@ public static string FormatMethodName(MetadataReader metadataReader, TypeDefinit
{
formatter._outputBuilder.Append(',');
}
- formatter.EmitTypeName(handle, namespaceQualified: false);
+ formatter.EmitTypeName(handle, Flags.None);
}
if (!first)
{
@@ -147,7 +163,7 @@ bool TryGetNextParameter(ref ParameterHandleCollection.Enumerator enumerator, ou
_outputBuilder.Append(", ");
}
- EmitTypeName(type, namespaceQualified: false);
+ EmitTypeName(type, Flags.None);
if (++typeIndex == parameter.Sequence && hasParameter)
{
@@ -182,37 +198,35 @@ private void EmitTypeVector(HandleCollection typeVector)
{
_outputBuilder.Append(", ");
}
- EmitTypeName(handle, namespaceQualified: false);
+ EmitTypeName(handle, Flags.None);
}
}
///
/// Emit the name of a given type to the output string builder.
///
- /// Type handle to format
- /// When set to true, include namespace information
- private void EmitTypeName(Handle typeHandle, bool namespaceQualified)
+ private void EmitTypeName(Handle typeHandle, Flags flags)
{
switch (typeHandle.HandleType)
{
case HandleType.TypeReference:
- EmitTypeReferenceName(typeHandle.ToTypeReferenceHandle(_metadataReader), namespaceQualified);
+ EmitTypeReferenceName(typeHandle.ToTypeReferenceHandle(_metadataReader), flags);
break;
case HandleType.TypeSpecification:
- EmitTypeSpecificationName(typeHandle.ToTypeSpecificationHandle(_metadataReader), namespaceQualified);
+ EmitTypeSpecificationName(typeHandle.ToTypeSpecificationHandle(_metadataReader), flags);
break;
case HandleType.TypeInstantiationSignature:
- EmitTypeInstantiationName(typeHandle.ToTypeInstantiationSignatureHandle(_metadataReader), namespaceQualified);
+ EmitTypeInstantiationName(typeHandle.ToTypeInstantiationSignatureHandle(_metadataReader), flags);
break;
case HandleType.SZArraySignature:
- EmitSZArrayTypeName(typeHandle.ToSZArraySignatureHandle(_metadataReader), namespaceQualified);
+ EmitSZArrayTypeName(typeHandle.ToSZArraySignatureHandle(_metadataReader), flags);
break;
case HandleType.ArraySignature:
- EmitArrayTypeName(typeHandle.ToArraySignatureHandle(_metadataReader), namespaceQualified);
+ EmitArrayTypeName(typeHandle.ToArraySignatureHandle(_metadataReader), flags);
break;
case HandleType.PointerSignature:
@@ -224,15 +238,15 @@ private void EmitTypeName(Handle typeHandle, bool namespaceQualified)
break;
case HandleType.TypeDefinition:
- EmitTypeDefinitionName(typeHandle.ToTypeDefinitionHandle(_metadataReader), namespaceQualified);
+ EmitTypeDefinitionName(typeHandle.ToTypeDefinitionHandle(_metadataReader), flags);
break;
case HandleType.TypeVariableSignature:
- EmitTypeName(_typeContext.GetTypeVariable(typeHandle.ToTypeVariableSignatureHandle(_metadataReader).GetTypeVariableSignature(_metadataReader).Number), namespaceQualified);
+ EmitTypeName(_typeContext.GetTypeVariable(typeHandle.ToTypeVariableSignatureHandle(_metadataReader).GetTypeVariableSignature(_metadataReader).Number), flags);
break;
case HandleType.MethodTypeVariableSignature:
- EmitTypeName(_typeContext.GetMethodVariable(typeHandle.ToMethodTypeVariableSignatureHandle(_metadataReader).GetMethodTypeVariableSignature(_metadataReader).Number), namespaceQualified);
+ EmitTypeName(_typeContext.GetMethodVariable(typeHandle.ToMethodTypeVariableSignatureHandle(_metadataReader).GetMethodTypeVariableSignature(_metadataReader).Number), flags);
break;
case HandleType.GenericParameter:
@@ -290,9 +304,7 @@ private void EmitNamespaceDefinitionName(NamespaceDefinitionHandle namespaceDefH
///
/// Emit type reference.
///
- /// Type reference handle
- /// When set to true, include namespace information
- private void EmitTypeReferenceName(TypeReferenceHandle typeRefHandle, bool namespaceQualified)
+ private void EmitTypeReferenceName(TypeReferenceHandle typeRefHandle, Flags flags)
{
TypeReference typeRef = _metadataReader.GetTypeReference(typeRefHandle);
if (!typeRef.ParentNamespaceOrType.IsNil)
@@ -300,10 +312,13 @@ private void EmitTypeReferenceName(TypeReferenceHandle typeRefHandle, bool names
if (typeRef.ParentNamespaceOrType.HandleType != HandleType.NamespaceReference)
{
// Nested type
- EmitTypeName(typeRef.ParentNamespaceOrType, namespaceQualified);
- _outputBuilder.Append('.');
+ EmitTypeName(typeRef.ParentNamespaceOrType, flags);
+ if ((flags & Flags.ReflectionFormat) != 0)
+ _outputBuilder.Append('+');
+ else
+ _outputBuilder.Append('.');
}
- else if (namespaceQualified)
+ else if ((flags & Flags.NamespaceQualify) != 0)
{
int charsWritten = _outputBuilder.Length;
EmitNamespaceReferenceName(typeRef.ParentNamespaceOrType.ToNamespaceReferenceHandle(_metadataReader));
@@ -314,16 +329,19 @@ private void EmitTypeReferenceName(TypeReferenceHandle typeRefHandle, bool names
EmitString(typeRef.TypeName);
}
- private void EmitTypeDefinitionName(TypeDefinitionHandle typeDefHandle, bool namespaceQualified)
+ private void EmitTypeDefinitionName(TypeDefinitionHandle typeDefHandle, Flags flags)
{
TypeDefinition typeDef = _metadataReader.GetTypeDefinition(typeDefHandle);
if (!typeDef.EnclosingType.IsNil)
{
// Nested type
- EmitTypeName(typeDef.EnclosingType, namespaceQualified);
- _outputBuilder.Append('.');
+ EmitTypeName(typeDef.EnclosingType, flags);
+ if ((flags & Flags.ReflectionFormat) != 0)
+ _outputBuilder.Append('+');
+ else
+ _outputBuilder.Append('.');
}
- else if (namespaceQualified)
+ else if ((flags & Flags.NamespaceQualify) != 0)
{
int charsWritten = _outputBuilder.Length;
EmitNamespaceDefinitionName(typeDef.NamespaceDefinition);
@@ -336,47 +354,39 @@ private void EmitTypeDefinitionName(TypeDefinitionHandle typeDefHandle, bool nam
///
/// Emit an arbitrary type specification.
///
- /// Type specification handle
- /// When set to true, include namespace information
- private void EmitTypeSpecificationName(TypeSpecificationHandle typeSpecHandle, bool namespaceQualified)
+ private void EmitTypeSpecificationName(TypeSpecificationHandle typeSpecHandle, Flags flags)
{
TypeSpecification typeSpec = _metadataReader.GetTypeSpecification(typeSpecHandle);
- EmitTypeName(typeSpec.Signature, namespaceQualified);
+ EmitTypeName(typeSpec.Signature, flags);
}
///
/// Emit generic instantiation type.
///
- /// Instantiated type specification signature handle
- /// When set to true, include namespace information
- private void EmitTypeInstantiationName(TypeInstantiationSignatureHandle typeInstHandle, bool namespaceQualified)
+ private void EmitTypeInstantiationName(TypeInstantiationSignatureHandle typeInstHandle, Flags flags)
{
// Stack trace metadata ignores the instantiation arguments of the type in the CLR
TypeInstantiationSignature typeInst = _metadataReader.GetTypeInstantiationSignature(typeInstHandle);
- EmitTypeName(typeInst.GenericType, namespaceQualified);
+ EmitTypeName(typeInst.GenericType, flags);
}
///
/// Emit SZArray (single-dimensional array with zero lower bound) type.
///
- /// SZArray type specification signature handle
- /// When set to true, include namespace information
- private void EmitSZArrayTypeName(SZArraySignatureHandle szArraySigHandle, bool namespaceQualified)
+ private void EmitSZArrayTypeName(SZArraySignatureHandle szArraySigHandle, Flags flags)
{
SZArraySignature szArraySig = _metadataReader.GetSZArraySignature(szArraySigHandle);
- EmitTypeName(szArraySig.ElementType, namespaceQualified);
+ EmitTypeName(szArraySig.ElementType, flags);
_outputBuilder.Append("[]");
}
///
/// Emit multi-dimensional array type.
///
- /// Multi-dimensional array type specification signature handle
- /// When set to true, include namespace information
- private void EmitArrayTypeName(ArraySignatureHandle arraySigHandle, bool namespaceQualified)
+ private void EmitArrayTypeName(ArraySignatureHandle arraySigHandle, Flags flags)
{
ArraySignature arraySig = _metadataReader.GetArraySignature(arraySigHandle);
- EmitTypeName(arraySig.ElementType, namespaceQualified);
+ EmitTypeName(arraySig.ElementType, flags);
_outputBuilder.Append('[');
if (arraySig.Rank > 1)
{
@@ -396,7 +406,7 @@ private void EmitArrayTypeName(ArraySignatureHandle arraySigHandle, bool namespa
private void EmitPointerTypeName(PointerSignatureHandle pointerSigHandle)
{
PointerSignature pointerSig = _metadataReader.GetPointerSignature(pointerSigHandle);
- EmitTypeName(pointerSig.Type, namespaceQualified: false);
+ EmitTypeName(pointerSig.Type, Flags.None);
_outputBuilder.Append('*');
}
@@ -415,7 +425,7 @@ private void EmitFunctionPointerTypeName()
private void EmitByRefTypeName(ByReferenceSignatureHandle byRefSigHandle)
{
ByReferenceSignature byRefSig = _metadataReader.GetByReferenceSignature(byRefSigHandle);
- EmitTypeName(byRefSig.Type, namespaceQualified: false);
+ EmitTypeName(byRefSig.Type, Flags.None);
_outputBuilder.Append('&');
}
diff --git a/src/coreclr/nativeaot/System.Private.StackTraceMetadata/src/Internal/StackTraceMetadata/StackTraceMetadata.cs b/src/coreclr/nativeaot/System.Private.StackTraceMetadata/src/Internal/StackTraceMetadata/StackTraceMetadata.cs
index bed368cb664a44..15c65d5e9dd63c 100644
--- a/src/coreclr/nativeaot/System.Private.StackTraceMetadata/src/Internal/StackTraceMetadata/StackTraceMetadata.cs
+++ b/src/coreclr/nativeaot/System.Private.StackTraceMetadata/src/Internal/StackTraceMetadata/StackTraceMetadata.cs
@@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics;
using System.Reflection.Runtime.General;
using Internal.Metadata.NativeFormat;
@@ -51,9 +52,18 @@ public static unsafe string GetMethodNameFromStartAddressIfAvailable(IntPtr meth
{
if (moduleInfo.Handle.OsModuleBase == moduleStartAddress)
{
- string name = _perModuleMethodNameResolverHashtable.GetOrCreateValue(moduleInfo.Handle.GetIntPtrUNSAFE()).GetMethodNameFromRvaIfAvailable(rva, out isStackTraceHidden);
- if (name != null || isStackTraceHidden)
- return name;
+ PerModuleMethodNameResolver resolver = _perModuleMethodNameResolverHashtable.GetOrCreateValue(moduleInfo.Handle.GetIntPtrUNSAFE());
+ if (resolver.TryGetStackTraceData(rva, out var data))
+ {
+ isStackTraceHidden = data.IsHidden;
+ if (data.OwningType.IsNil)
+ {
+ Debug.Assert(data.Name.IsNil && data.Signature.IsNil);
+ Debug.Assert(isStackTraceHidden);
+ return null;
+ }
+ return MethodNameFormatter.FormatMethodName(resolver.Reader, data.OwningType, data.Name, data.Signature, data.GenericArguments);
+ }
}
}
@@ -89,6 +99,93 @@ public static unsafe string GetMethodNameFromStartAddressIfAvailable(IntPtr meth
return null;
}
+ public static unsafe DiagnosticMethodInfo? GetDiagnosticMethodInfoFromStartAddressIfAvailable(IntPtr methodStartAddress)
+ {
+ IntPtr moduleStartAddress = RuntimeAugments.GetOSModuleFromPointer(methodStartAddress);
+ int rva = (int)((byte*)methodStartAddress - (byte*)moduleStartAddress);
+ foreach (NativeFormatModuleInfo moduleInfo in ModuleList.EnumerateModules())
+ {
+ if (moduleInfo.Handle.OsModuleBase == moduleStartAddress)
+ {
+ PerModuleMethodNameResolver resolver = _perModuleMethodNameResolverHashtable.GetOrCreateValue(moduleInfo.Handle.GetIntPtrUNSAFE());
+ if (resolver.TryGetStackTraceData(rva, out var data))
+ {
+ if (data.OwningType.IsNil)
+ {
+ Debug.Assert(data.Name.IsNil && data.Signature.IsNil);
+ return null;
+ }
+ return new DiagnosticMethodInfo(
+ resolver.Reader.GetString(data.Name),
+ MethodNameFormatter.FormatReflectionNotationTypeName(resolver.Reader, data.OwningType),
+ FormatAssemblyName(resolver.Reader, data.OwningType)
+ );
+ }
+ }
+ }
+
+ // We haven't found information in the stack trace metadata tables, but maybe reflection will have this
+ if (IsReflectionExecutionAvailable() && ReflectionExecution.TryGetMethodMetadataFromStartAddress(methodStartAddress,
+ out MetadataReader reader,
+ out TypeDefinitionHandle typeHandle,
+ out MethodHandle methodHandle))
+ {
+ return new DiagnosticMethodInfo(
+ reader.GetString(reader.GetMethod(methodHandle).Name),
+ MethodNameFormatter.FormatReflectionNotationTypeName(reader, typeHandle),
+ FormatAssemblyName(reader, typeHandle)
+ );
+ }
+
+ return null;
+ }
+
+ private static string FormatAssemblyName(MetadataReader reader, Handle handle)
+ {
+ switch (handle.HandleType)
+ {
+ case HandleType.TypeDefinition:
+ TypeDefinition typeDef = reader.GetTypeDefinition(handle.ToTypeDefinitionHandle(reader));
+ TypeDefinitionHandle enclosingTypeDef = typeDef.EnclosingType;
+ if (!enclosingTypeDef.IsNil)
+ return FormatAssemblyName(reader, enclosingTypeDef);
+ return FormatAssemblyName(reader, typeDef.NamespaceDefinition);
+ case HandleType.TypeReference:
+ TypeReference typeRef = reader.GetTypeReference(handle.ToTypeReferenceHandle(reader));
+ return FormatAssemblyName(reader, typeRef.ParentNamespaceOrType);
+ case HandleType.TypeSpecification:
+ TypeSpecification typeSpec = reader.GetTypeSpecification(handle.ToTypeSpecificationHandle(reader));
+ return FormatAssemblyName(reader, typeSpec.Signature);
+ case HandleType.TypeInstantiationSignature:
+ TypeInstantiationSignature typeInst = reader.GetTypeInstantiationSignature(handle.ToTypeInstantiationSignatureHandle(reader));
+ return FormatAssemblyName(reader, typeInst.GenericType);
+ case HandleType.NamespaceDefinition:
+ NamespaceDefinition nsDef = reader.GetNamespaceDefinition(handle.ToNamespaceDefinitionHandle(reader));
+ return FormatAssemblyName(reader, nsDef.ParentScopeOrNamespace);
+ case HandleType.NamespaceReference:
+ NamespaceReference nsRef = reader.GetNamespaceReference(handle.ToNamespaceReferenceHandle(reader));
+ return FormatAssemblyName(reader, nsRef.ParentScopeOrNamespace);
+ case HandleType.ScopeDefinition:
+ return FormatAssemblyName(reader, handle.ToScopeDefinitionHandle(reader));
+ case HandleType.ScopeReference:
+ return FormatAssemblyName(reader, handle.ToScopeReferenceHandle(reader));
+ default:
+ return "";
+ }
+ }
+
+ private static string FormatAssemblyName(MetadataReader reader, ScopeDefinitionHandle handle)
+ {
+ ScopeDefinition scopeDef = reader.GetScopeDefinition(handle);
+ return $"{reader.GetString(scopeDef.Name)}, Version={scopeDef.MajorVersion}.{scopeDef.MinorVersion}.{scopeDef.BuildNumber}.{scopeDef.RevisionNumber}";
+ }
+
+ private static string FormatAssemblyName(MetadataReader reader, ScopeReferenceHandle handle)
+ {
+ ScopeReference scopeRef = reader.GetScopeReference(handle);
+ return $"{reader.GetString(scopeRef.Name)}, Version={scopeRef.MajorVersion}.{scopeRef.MinorVersion}.{scopeRef.BuildNumber}.{scopeRef.RevisionNumber}";
+ }
+
// Can be rewritten to false through a feature switch.
private static bool IsReflectionExecutionAvailable() => true;
@@ -148,6 +245,11 @@ protected override PerModuleMethodNameResolver CreateValueFromKey(IntPtr key)
///
private sealed class StackTraceMetadataCallbacksImpl : StackTraceMetadataCallbacks
{
+ public override DiagnosticMethodInfo TryGetDiagnosticMethodInfoFromStartAddress(nint methodStartAddress)
+ {
+ return GetDiagnosticMethodInfoFromStartAddressIfAvailable(methodStartAddress);
+ }
+
public override string TryGetMethodNameFromStartAddress(IntPtr methodStartAddress, out bool isStackTraceHidden)
{
return GetMethodNameFromStartAddressIfAvailable(methodStartAddress, out isStackTraceHidden);
@@ -172,7 +274,7 @@ private sealed class PerModuleMethodNameResolver
///
/// Metadata reader for the stack trace metadata.
///
- private readonly MetadataReader _metadataReader;
+ public readonly MetadataReader Reader;
///
/// Publicly exposed module address property.
@@ -216,7 +318,7 @@ public unsafe PerModuleMethodNameResolver(IntPtr moduleAddress)
out rvaToTokenMapBlob,
out rvaToTokenMapBlobSize))
{
- _metadataReader = new MetadataReader(new IntPtr(metadataBlob), (int)metadataBlobSize);
+ Reader = new MetadataReader(new IntPtr(metadataBlob), (int)metadataBlobSize);
int entryCount = *(int*)rvaToTokenMapBlob;
_stacktraceDatas = new StackTraceData[entryCount];
@@ -253,19 +355,19 @@ private unsafe void PopulateRvaToTokenMap(TypeManagerHandle handle, byte* pMap,
if ((command & StackTraceDataCommand.UpdateName) != 0)
{
- currentName = new Handle(HandleType.ConstantStringValue, (int)NativePrimitiveDecoder.DecodeUnsigned(ref pCurrent)).ToConstantStringValueHandle(_metadataReader);
+ currentName = new Handle(HandleType.ConstantStringValue, (int)NativePrimitiveDecoder.DecodeUnsigned(ref pCurrent)).ToConstantStringValueHandle(Reader);
}
if ((command & StackTraceDataCommand.UpdateSignature) != 0)
{
- currentSignature = new Handle(HandleType.MethodSignature, (int)NativePrimitiveDecoder.DecodeUnsigned(ref pCurrent)).ToMethodSignatureHandle(_metadataReader);
+ currentSignature = new Handle(HandleType.MethodSignature, (int)NativePrimitiveDecoder.DecodeUnsigned(ref pCurrent)).ToMethodSignatureHandle(Reader);
currentMethodInst = default;
}
if ((command & StackTraceDataCommand.UpdateGenericSignature) != 0)
{
- currentSignature = new Handle(HandleType.MethodSignature, (int)NativePrimitiveDecoder.DecodeUnsigned(ref pCurrent)).ToMethodSignatureHandle(_metadataReader);
- currentMethodInst = new Handle(HandleType.ConstantStringArray, (int)NativePrimitiveDecoder.DecodeUnsigned(ref pCurrent)).ToConstantStringArrayHandle(_metadataReader);
+ currentSignature = new Handle(HandleType.MethodSignature, (int)NativePrimitiveDecoder.DecodeUnsigned(ref pCurrent)).ToMethodSignatureHandle(Reader);
+ currentMethodInst = new Handle(HandleType.ConstantStringArray, (int)NativePrimitiveDecoder.DecodeUnsigned(ref pCurrent)).ToConstantStringArrayHandle(Reader);
}
void* pMethod = ReadRelPtr32(pCurrent);
@@ -296,38 +398,28 @@ private unsafe void PopulateRvaToTokenMap(TypeManagerHandle handle, byte* pMap,
///
/// Try to resolve method name based on its address using the stack trace metadata
///
- public string GetMethodNameFromRvaIfAvailable(int rva, out bool isStackTraceHidden)
+ public bool TryGetStackTraceData(int rva, out StackTraceData data)
{
- isStackTraceHidden = false;
-
if (_stacktraceDatas == null)
{
// No stack trace metadata for this module
- return null;
+ data = default;
+ return false;
}
int index = Array.BinarySearch(_stacktraceDatas, new StackTraceData() { Rva = rva });
if (index < 0)
{
// Method RVA not found in the map
- return null;
- }
-
- StackTraceData data = _stacktraceDatas[index];
-
- isStackTraceHidden = data.IsHidden;
-
- if (data.OwningType.IsNil)
- {
- Debug.Assert(data.Name.IsNil && data.Signature.IsNil);
- Debug.Assert(isStackTraceHidden);
- return null;
+ data = default;
+ return false;
}
- return MethodNameFormatter.FormatMethodName(_metadataReader, data.OwningType, data.Name, data.Signature, data.GenericArguments);
+ data = _stacktraceDatas[index];
+ return true;
}
- private struct StackTraceData : IComparable
+ public struct StackTraceData : IComparable
{
private const int IsHiddenFlag = 0x2;
diff --git a/src/libraries/System.Diagnostics.StackTrace/ref/System.Diagnostics.StackTrace.cs b/src/libraries/System.Diagnostics.StackTrace/ref/System.Diagnostics.StackTrace.cs
index edfbd2534f4b04..c46c202e7ae146 100644
--- a/src/libraries/System.Diagnostics.StackTrace/ref/System.Diagnostics.StackTrace.cs
+++ b/src/libraries/System.Diagnostics.StackTrace/ref/System.Diagnostics.StackTrace.cs
@@ -159,6 +159,15 @@ public SymLanguageVendor() { }
#endif // !BUILDING_CORELIB_REFERENCE
namespace System.Diagnostics
{
+ public sealed partial class DiagnosticMethodInfo
+ {
+ private DiagnosticMethodInfo() { }
+ public string Name { get { throw null; } }
+ public string DeclaringTypeName { get { throw null; } }
+ public string DeclaringAssemblyName { get { throw null; } }
+ public static DiagnosticMethodInfo? Create(System.Delegate @delegate) { throw null; }
+ public static DiagnosticMethodInfo? Create(System.Diagnostics.StackFrame frame) { throw null; }
+ }
public partial class StackFrame
{
public const int OFFSET_UNKNOWN = -1;
diff --git a/src/libraries/System.Diagnostics.StackTrace/tests/DiagnosticMethodInfoTests.cs b/src/libraries/System.Diagnostics.StackTrace/tests/DiagnosticMethodInfoTests.cs
new file mode 100644
index 00000000000000..a25fcc0a8d6f9b
--- /dev/null
+++ b/src/libraries/System.Diagnostics.StackTrace/tests/DiagnosticMethodInfoTests.cs
@@ -0,0 +1,323 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Collections.Generic;
+using System.Reflection;
+using System.Reflection.Metadata;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using Microsoft.DotNet.RemoteExecutor;
+using Xunit;
+
+namespace System.Diagnostics.Tests
+{
+ public class DiagnosticMethodInfoTests
+ {
+ [Fact]
+ public void Create_Null()
+ {
+ Assert.Throws(() => DiagnosticMethodInfo.Create((Delegate)null));
+ Assert.Throws(() => DiagnosticMethodInfo.Create((StackFrame)null));
+ }
+
+ public static IEnumerable
public partial class StackTrace
{
+ internal static bool IsSupported { get; } = InitializeIsSupported();
+
+ private static bool InitializeIsSupported() =>
+ AppContext.TryGetSwitch("System.Diagnostics.StackTrace.IsSupported", out bool isSupported) ? isSupported : true;
+
public const int METHODS_TO_SKIP = 0;
private int _numOfFrames;
diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/FutureFactory.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/FutureFactory.cs
index 1f6fb5f62337ef..088f359877badb 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/FutureFactory.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/FutureFactory.cs
@@ -765,7 +765,7 @@ internal static Task FromAsyncImpl(Func promise = new Task(state, creationOptions);
if (TplEventSource.Log.IsEnabled())
- TplEventSource.Log.TraceOperationBegin(promise.Id, "TaskFactory.FromAsync: " + beginMethod.Method.Name, 0);
+ TplEventSource.Log.TraceOperationBegin(promise.Id, "TaskFactory.FromAsync: " + beginMethod.GetMethodName(), 0);
if (Task.s_asyncDebuggingEnabled)
Task.AddToActiveTasks(promise);
@@ -882,7 +882,7 @@ internal static Task FromAsyncImpl(Func promise = new Task(state, creationOptions);
if (TplEventSource.Log.IsEnabled())
- TplEventSource.Log.TraceOperationBegin(promise.Id, "TaskFactory.FromAsync: " + beginMethod.Method.Name, 0);
+ TplEventSource.Log.TraceOperationBegin(promise.Id, "TaskFactory.FromAsync: " + beginMethod.GetMethodName(), 0);
if (Task.s_asyncDebuggingEnabled)
Task.AddToActiveTasks(promise);
@@ -1007,7 +1007,7 @@ internal static Task FromAsyncImpl(Func promise = new Task(state, creationOptions);
if (TplEventSource.Log.IsEnabled())
- TplEventSource.Log.TraceOperationBegin(promise.Id, "TaskFactory.FromAsync: " + beginMethod.Method.Name, 0);
+ TplEventSource.Log.TraceOperationBegin(promise.Id, "TaskFactory.FromAsync: " + beginMethod.GetMethodName(), 0);
if (Task.s_asyncDebuggingEnabled)
Task.AddToActiveTasks(promise);
@@ -1140,7 +1140,7 @@ internal static Task FromAsyncImpl(Func promise = new Task(state, creationOptions);
if (TplEventSource.Log.IsEnabled())
- TplEventSource.Log.TraceOperationBegin(promise.Id, "TaskFactory.FromAsync: " + beginMethod.Method.Name, 0);
+ TplEventSource.Log.TraceOperationBegin(promise.Id, "TaskFactory.FromAsync: " + beginMethod.GetMethodName(), 0);
if (Task.s_asyncDebuggingEnabled)
Task.AddToActiveTasks(promise);
diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/LoggingExtensions.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/LoggingExtensions.cs
new file mode 100644
index 00000000000000..0e30ae67096c2c
--- /dev/null
+++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/LoggingExtensions.cs
@@ -0,0 +1,16 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Diagnostics;
+
+namespace System.Threading.Tasks
+{
+ internal static class LoggingExtensions
+ {
+ public static string GetMethodName(this Delegate @delegate)
+ {
+ DiagnosticMethodInfo? dmi = DiagnosticMethodInfo.Create(@delegate);
+ return dmi?.Name ?? "";
+ }
+ }
+}
diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs
index 4bf84b92a64d44..ccc2e3d021ffc4 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs
@@ -1681,7 +1681,7 @@ internal void ScheduleAndStart(bool needsProtection)
{
// For all other task than TaskContinuations we want to log. TaskContinuations log in their constructor
Debug.Assert(m_action != null, "Must have a delegate to be in ScheduleAndStart");
- TplEventSource.Log.TraceOperationBegin(this.Id, "Task: " + m_action.Method.Name, 0);
+ TplEventSource.Log.TraceOperationBegin(this.Id, "Task: " + m_action.GetMethodName(), 0);
}
try
diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TaskContinuation.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TaskContinuation.cs
index 277a6e90765eda..fac0bb9d4b25e6 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TaskContinuation.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TaskContinuation.cs
@@ -277,7 +277,7 @@ internal ContinueWithTaskContinuation(Task task, TaskContinuationOptions options
m_options = options;
m_taskScheduler = scheduler;
if (TplEventSource.Log.IsEnabled())
- TplEventSource.Log.TraceOperationBegin(m_task.Id, "Task.ContinueWith: " + task.m_action!.Method.Name, 0);
+ TplEventSource.Log.TraceOperationBegin(m_task.Id, "Task.ContinueWith: " + task.m_action!.GetMethodName(), 0);
if (Task.s_asyncDebuggingEnabled)
Task.AddToActiveTasks(m_task);
diff --git a/src/tests/nativeaot/SmokeTests/StackTraceMetadata/StackTraceMetadata.cs b/src/tests/nativeaot/SmokeTests/StackTraceMetadata/StackTraceMetadata.cs
index 19bff10ee7f489..cbbf114f4dd6fb 100644
--- a/src/tests/nativeaot/SmokeTests/StackTraceMetadata/StackTraceMetadata.cs
+++ b/src/tests/nativeaot/SmokeTests/StackTraceMetadata/StackTraceMetadata.cs
@@ -2,13 +2,15 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System;
+using System.Diagnostics;
using System.Runtime.CompilerServices;
class Program
{
- [MethodImpl(MethodImplOptions.NoInlining)]
static int Main()
{
+ DiagnosticMethodInfoTests.Run();
+
string stackTrace = Environment.StackTrace;
Console.WriteLine(stackTrace);
@@ -22,4 +24,62 @@ static int Main()
return expected == actual ? 100 : 1;
}
+ class DiagnosticMethodInfoTests
+ {
+ public static void Run()
+ {
+#if STRIPPED
+ DiagnosticMethodInfo dmi = DiagnosticMethodInfo.Create(new StackFrame());
+ if (dmi != null)
+ throw new Exception("Succeeded in creating DiagnosticMethodInfo despite no expectation");
+#else
+ DiagnosticMethodInfo dmi = DiagnosticMethodInfo.Create(new StackFrame());
+ if (dmi == null)
+ throw new Exception("No DiagnosticMethodInfo despite no expectation");
+ if (dmi.Name != nameof(Run))
+ throw new Exception($"Name is {dmi.Name} from {dmi.DeclaringTypeName}");
+
+ StackTrace tr = NonGenericStackTraceClass.TestNonGeneric();
+
+ Verify(tr.GetFrame(0), "Test", "GenericStackTraceClass`1+Nested");
+ Verify(tr.GetFrame(1), "TestGeneric", "GenericStackTraceClass`1");
+ Verify(tr.GetFrame(2), "TestNonGeneric", "GenericStackTraceClass`1");
+ Verify(tr.GetFrame(3), "TestGeneric", "NonGenericStackTraceClass");
+ Verify(tr.GetFrame(4), "TestNonGeneric", "NonGenericStackTraceClass");
+
+ static void Verify(StackFrame fr, string expectedName, string expectedDeclaringName)
+ {
+ DiagnosticMethodInfo dmi = DiagnosticMethodInfo.Create(fr);
+ if (expectedName != dmi.Name)
+ throw new Exception($"{expectedName} != {dmi.Name}");
+ if (!dmi.DeclaringTypeName.EndsWith(expectedDeclaringName))
+ throw new Exception($"!{dmi.DeclaringTypeName}.EndsWith({expectedDeclaringName})");
+ }
+#endif
+ }
+
+ class NonGenericStackTraceClass
+ {
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public static StackTrace TestNonGeneric() => TestGeneric();
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public static StackTrace TestGeneric() => GenericStackTraceClass.TestNonGeneric();
+ }
+
+ class GenericStackTraceClass
+ {
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public static StackTrace TestNonGeneric() => TestGeneric();
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public static StackTrace TestGeneric() => Nested.Test();
+
+ public class Nested
+ {
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public static StackTrace Test() => new StackTrace();
+ }
+ }
+ }
}