Skip to content

Commit 7237cb5

Browse files
Make it possible to construct StackFrame without MethodBase (#80681)
Contributes to #80165. Resolving a `MethodBase` is a pretty expensive operation and currently even a hello world needs it because of `Exception.ToString()`. This pull request is trying to get `MethodBase` out of the picture when constructing the exception string. We currently only need `_method` for two things - the public `GetMethod` API, and `StackFrame.ToString`. The `StackFrame.ToString` can already deal with `_method` being null (and that codepath exists solely for NativeAOT). This is the minimal change. We could potentially explore other approaches - leave `MethodBase` always at null, or unshare the `StackFrame` class.
1 parent e8dd078 commit 7237cb5

File tree

2 files changed

+39
-2
lines changed

2 files changed

+39
-2
lines changed

src/coreclr/nativeaot/System.Private.CoreLib/src/System/Diagnostics/StackFrame.NativeAot.cs

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System.Diagnostics.CodeAnalysis;
5+
using System.Reflection;
46
using System.Runtime;
57
using System.Runtime.CompilerServices;
68
using System.Text;
@@ -28,6 +30,40 @@ public partial class StackFrame
2830
/// </summary>
2931
private bool _needFileInfo;
3032

33+
/// <summary>
34+
/// Will be true if we attempted to retrieve the associated MethodBase but couldn't.
35+
/// </summary>
36+
private bool _noMethodBaseAvailable;
37+
38+
/// <summary>
39+
/// Returns the method the frame is executing
40+
/// </summary>
41+
[RequiresUnreferencedCode("Metadata for the method might be incomplete or removed")]
42+
public virtual MethodBase? GetMethod()
43+
{
44+
TryInitializeMethodBase();
45+
return _method;
46+
}
47+
48+
private bool TryInitializeMethodBase()
49+
{
50+
if (_noMethodBaseAvailable || _ipAddress == IntPtr.Zero || _ipAddress == Exception.EdiSeparator)
51+
return false;
52+
53+
if (_method != null)
54+
return true;
55+
56+
IntPtr methodStartAddress = _ipAddress - _nativeOffset;
57+
Debug.Assert(RuntimeImports.RhFindMethodStartAddress(_ipAddress) == methodStartAddress);
58+
DeveloperExperience.Default.TryGetMethodBase(methodStartAddress, out _method);
59+
if (_method == null)
60+
{
61+
_noMethodBaseAvailable = true;
62+
return false;
63+
}
64+
return true;
65+
}
66+
3167
/// <summary>
3268
/// Constructs a StackFrame corresponding to a given IP address.
3369
/// </summary>
@@ -55,7 +91,6 @@ private void InitializeForIpAddress(IntPtr ipAddress, bool needFileInfo)
5591
_nativeOffset = (int)((nint)_ipAddress - (nint)methodStartAddress);
5692

5793
DeveloperExperience.Default.TryGetILOffsetWithinMethod(_ipAddress, out _ilOffset);
58-
DeveloperExperience.Default.TryGetMethodBase(methodStartAddress, out _method);
5994

6095
if (needFileInfo)
6196
{
@@ -98,7 +133,7 @@ internal IntPtr GetNativeIPAddress()
98133
/// </summary>
99134
internal bool HasMethod()
100135
{
101-
return _method != null;
136+
return TryInitializeMethodBase();
102137
}
103138

104139
/// <summary>

src/libraries/System.Private.CoreLib/src/System/Diagnostics/StackFrame.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ public StackFrame(string? fileName, int lineNumber, int colNumber)
134134

135135
internal bool IsLastFrameFromForeignExceptionStackTrace => _isLastFrameFromForeignExceptionStackTrace;
136136

137+
#if !NATIVEAOT
137138
/// <summary>
138139
/// Returns the method the frame is executing
139140
/// </summary>
@@ -142,6 +143,7 @@ public StackFrame(string? fileName, int lineNumber, int colNumber)
142143
{
143144
return _method;
144145
}
146+
#endif
145147

146148
/// <summary>
147149
/// Returns the offset from the start of the native (jitted) code for the

0 commit comments

Comments
 (0)