Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1292,6 +1292,16 @@ static RuntimeMethodHandleInternal GetStubIfNeededWorker(RuntimeMethodHandleInte
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern RuntimeMethodHandleInternal GetMethodFromCanonical(RuntimeMethodHandleInternal method, RuntimeType declaringType);

[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "RuntimeMethodHandle_GetNativeCode")]
private static partial IntPtr GetNativeCode(RuntimeMethodHandleInternal method);

internal static IntPtr GetNativeCodeInternal(IRuntimeMethodInfo method)
{
IntPtr value = GetNativeCode(method.Value);
GC.KeepAlive(method);
return value;
}

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern bool IsGenericMethodDefinition(RuntimeMethodHandleInternal method);

Expand Down
1 change: 1 addition & 0 deletions src/coreclr/vm/qcallentrypoints.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ static const Entry s_QCall[] =
DllImportEntry(RuntimeMethodHandle_IsCAVisibleFromDecoratedType)
DllImportEntry(RuntimeMethodHandle_Destroy)
DllImportEntry(RuntimeMethodHandle_GetStubIfNeededSlow)
DllImportEntry(RuntimeMethodHandle_GetNativeCode)
DllImportEntry(RuntimeMethodHandle_GetMethodBody)
DllImportEntry(RuntimeModule_GetScopeName)
DllImportEntry(RuntimeModule_GetFullyQualifiedName)
Expand Down
27 changes: 27 additions & 0 deletions src/coreclr/vm/runtimehandles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2005,6 +2005,33 @@ FCIMPL2(MethodDesc*, RuntimeMethodHandle::GetMethodFromCanonical, MethodDesc *pM
}
FCIMPLEND

extern "C" PCODE QCALLTYPE RuntimeMethodHandle_GetNativeCode(MethodDesc* pMethod)
{
QCALL_CONTRACT;

PCODE result = (PCODE)NULL;

BEGIN_QCALL;

_ASSERTE(pMethod != NULL);

while (pMethod->IsWrapperStub())
{
MethodDesc* pWrapped = pMethod->GetWrappedMethodDesc();
if (pWrapped == NULL || pWrapped == pMethod)
{
break;
}
pMethod = pWrapped;
}

result = GetInterpreterCodeFromEntryPointIfPresent(pMethod->GetNativeCodeAnyVersion());

END_QCALL;

return result;
}

extern "C" void QCALLTYPE RuntimeMethodHandle_GetMethodBody(MethodDesc* pMethod, QCall::TypeHandle pDeclaringType, QCall::ObjectHandleOnStack result)
{
QCALL_CONTRACT;
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/vm/runtimehandles.h
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ extern "C" void QCALLTYPE RuntimeMethodHandle_GetTypicalMethodDefinition(MethodD
extern "C" void QCALLTYPE RuntimeMethodHandle_StripMethodInstantiation(MethodDesc * pMethod, QCall::ObjectHandleOnStack refMethod);
extern "C" void QCALLTYPE RuntimeMethodHandle_Destroy(MethodDesc * pMethod);
extern "C" MethodDesc* QCALLTYPE RuntimeMethodHandle_GetStubIfNeededSlow(MethodDesc* pMethod, QCall::TypeHandle declaringTypeHandle, QCall::ObjectHandleOnStack methodInstantiation);
extern "C" PCODE QCALLTYPE RuntimeMethodHandle_GetNativeCode(MethodDesc* pMethod);
extern "C" void QCALLTYPE RuntimeMethodHandle_GetMethodBody(MethodDesc* pMethod, QCall::TypeHandle pDeclaringType, QCall::ObjectHandleOnStack result);

class RuntimeFieldHandle
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1230,7 +1230,7 @@ internal static partial class ContinuationWrapper
#if !RUNTIME_ASYNC_SUPPORTED
public static void InitInfo(ref Info info)
{
info.ContinuationTable = 0;
info.ContinuationTable = ref Unsafe.NullRef<nint>();
info.ContinuationIndex = 0;
}
#endif
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,14 @@ private static ulong ResolveMethodId()
MethodInfo? methodInfo = typeof(TStateMachine).GetMethod("MoveNext", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (methodInfo is not null)
{
return (ulong)methodInfo.MethodHandle.Value;
#if MONO
return (ulong)(nuint)RuntimeMethodHandle.GetNativeCodeInternal(methodInfo.MethodHandle.Value);
#else
if (methodInfo is IRuntimeMethodInfo runtimeMethodInfo)
{
return (ulong)(nuint)RuntimeMethodHandle.GetNativeCodeInternal(runtimeMethodInfo);
}
Comment thread
lateralusX marked this conversation as resolved.
#endif
}

return 0;
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -1270,7 +1270,7 @@ private static async Task StateMachineAsync_KeywordGatekeeping_Marker()
await StateMachineAsync_InnerThrows();
}
catch (InvalidOperationException) { }
await RuntimeAsync_SingleYield();
await StateMachineAsync_SingleYield();
await Task.Delay(50);
}

Expand Down Expand Up @@ -1508,6 +1508,15 @@ public void StateMachineAsync_CallstackOverflow_PreservesFullDepth()
const int MaxFrames = byte.MaxValue;
const int Iterations = 128;

// Warm up the recursive method so its state machine id is frozen at its tier-0 version, then
// snapshot that id before tracing. (Snapshot is a no-op on non-Mono, where ids resolve
// reflectively; the warmup itself runs on all runtimes.)
var warmupGate = new TaskCompletionSource();
Task warmup = StateMachineAsync_RecursiveChainGated(2, warmupGate.Task);
warmupGate.SetResult();
warmup.GetAwaiter().GetResult();
SnapshotStateMachineMethodIdFor(typeof(AsyncProfilerTests).GetMethod(nameof(StateMachineAsync_RecursiveChainGated), BindingFlags.NonPublic | BindingFlags.Static)!);

var events = CollectEvents(ResumeStateMachineAsyncCallstackKeyword, () =>
{
RunScenarioAndFlush(async () =>
Expand Down Expand Up @@ -1545,11 +1554,23 @@ public void StateMachineAsync_CallstackOverflow_PreservesFullDepth()
}
}

[RuntimeAsyncMethodGeneration(false)]
[MethodImpl(MethodImplOptions.NoInlining)]
private static async Task StateMachineAsync_CallstackStressWithVaryingDepths_Recurse(int depth)
{
if (depth <= 1)
{
await Task.Delay(100);
return;
}
await StateMachineAsync_CallstackStressWithVaryingDepths_Recurse(depth - 1);
}

[RuntimeAsyncMethodGeneration(false)]
[MethodImpl(MethodImplOptions.NoInlining)]
private static async Task StateMachineAsync_CallstackStressWithVaryingDepths_Marker(int depth)
{
await StateMachineAsync_RecursiveChain(depth);
await StateMachineAsync_CallstackStressWithVaryingDepths_Recurse(depth);
}

[ConditionalFact(typeof(AsyncProfilerTests), nameof(IsStateMachineAsyncAndThreadingSupported))]
Expand All @@ -1561,6 +1582,12 @@ public void StateMachineAsync_CallstackStressWithVaryingDepths()
for (int i = 0; i < iterations; i++)
depths[i] = rng.Next(1, 60);

// Warm up the recursive method so its state machine id is frozen at its tier-0 version, then
// snapshot that id before tracing. (Snapshot is a no-op on non-Mono, where ids resolve
// reflectively; the warmup itself runs on all runtimes.)
StateMachineAsync_CallstackStressWithVaryingDepths_Recurse(2).GetAwaiter().GetResult();
SnapshotStateMachineMethodIdFor(typeof(AsyncProfilerTests).GetMethod(nameof(StateMachineAsync_CallstackStressWithVaryingDepths_Recurse), BindingFlags.NonPublic | BindingFlags.Static)!);

var events = CollectEvents(ResumeStateMachineAsyncCallstackKeyword | StateMachineAsyncCoreKeywords, () =>
{
RunScenarioAndFlush(async () =>
Expand Down Expand Up @@ -1709,6 +1736,78 @@ public void StateMachineAsync_ConfigureAwaitFalse()
AssertExactlyOneCreateAndComplete(stream, markerCallstacks[0].DispatcherId, nameof(StateMachineAsync_ConfigureAwaitFalse_Marker));
}

// Generic async chain. Each method is generic over T and uses its parameter after the await, so the
// compiler emits a generic state machine (<Marker>d__N`1). Instantiated with a reference type the JIT
// reaches the async body through the shared (__Canon) generic code, whose per-instantiation MethodDesc
// is an instantiating (wrapper) stub. The V1 methodId is the native code start of MoveNext, so
// RuntimeMethodHandle_GetNativeCode must peel wrapper stubs to the shared body's code for the id to map
// back to a managed method; otherwise a frame would carry a stub thunk address that resolves to null.
[RuntimeAsyncMethodGeneration(false)]
[MethodImpl(MethodImplOptions.NoInlining)]
private static async Task<T> StateMachineAsync_GenericChain_Leaf_Marker<T>(T value)
{
await Task.Delay(100).ConfigureAwait(false);
return value;
}

[RuntimeAsyncMethodGeneration(false)]
[MethodImpl(MethodImplOptions.NoInlining)]
private static async Task<T> StateMachineAsync_GenericChain_FramesResolveToSharedBody_Mid_Marker<T>(T value)
{
T result = await StateMachineAsync_GenericChain_Leaf_Marker<T>(value).ConfigureAwait(false);
return result;
}

[RuntimeAsyncMethodGeneration(false)]
[MethodImpl(MethodImplOptions.NoInlining)]
private static async Task<T> StateMachineAsync_GenericChain_FramesResolveToSharedBody_Marker<T>(T value)
{
T result = await StateMachineAsync_GenericChain_FramesResolveToSharedBody_Mid_Marker<T>(value).ConfigureAwait(false);
return result;
}

// CoreCLR-only: the primary IP->name resolver (StackFrame.GetMethodFromNativeIP) exists only on CoreCLR,
// and the Mono reverse-map fallback (CollectStateMachineMoveNextMethods) deliberately skips generic state
// machines (ContainsGenericParameters), so a generic frame can't be named on Mono without proper rundown
// or JIT-map data.
//
// A reference type (string) reaches the shared __Canon body through an instantiating (wrapper) stub that
// RuntimeMethodHandle_GetNativeCode must peel; a value type (int) is fully specialized into its own code
// (no wrapper stub) and resolves directly. Both must symbolize to their managed names.
[ConditionalTheory(typeof(AsyncProfilerTests), nameof(IsStateMachineAsyncAndThreadingSupported), nameof(IsNotMonoRuntime))]
[InlineData(typeof(string))]
[InlineData(typeof(int))]
public void StateMachineAsync_GenericChain_FramesResolveToSharedBody(Type argType)
{
var events = CollectEvents(ResumeStateMachineAsyncCallstackKeyword | StateMachineAsyncCoreKeywords, () =>
{
if (argType == typeof(int))
{
RunScenarioAndFlush(() => StateMachineAsync_GenericChain_FramesResolveToSharedBody_Marker<int>(42));
}
else
{
RunScenarioAndFlush(() => StateMachineAsync_GenericChain_FramesResolveToSharedBody_Marker<string>("value"));
}
});

// DumpAllEvents(events);

var stream = ParseAllEvents(events);

var markerCallstacks = stream.CallstacksWithMarker(AsyncEventID.ResumeStateMachineAsyncCallstack, nameof(StateMachineAsync_GenericChain_FramesResolveToSharedBody_Marker));
AssertNotEmpty(stream, markerCallstacks);

var frameNames = markerCallstacks[0].Frames
.Select(f => GetMethodNameFromMethodId(markerCallstacks[0].CallstackType, f.MethodId))
.Where(n => n is not null)
.ToList();

// Every generic-chain frame must resolve to its managed name.
AssertContains(stream, nameof(StateMachineAsync_GenericChain_Leaf_Marker), frameNames);
AssertContains(stream, nameof(StateMachineAsync_GenericChain_FramesResolveToSharedBody_Mid_Marker), frameNames);
}

[RuntimeAsyncMethodGeneration(false)]
[MethodImpl(MethodImplOptions.NoInlining)]
private static async Task StateMachineAsync_FaultedTask_Inner_Marker()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,14 @@
<Compile Include="System.Runtime.CompilerServices\AsyncIteratorMethodBuilderTests.cs" />
<Compile Include="System.Runtime.CompilerServices\AsyncTaskMethodBuilderTests.cs" />
<Compile Include="System.Runtime.CompilerServices\RuntimeAsyncTests.cs" Condition="'$(RuntimeFlavor)' != 'mono'" />
<Compile Include="System.Runtime.CompilerServices\AsyncProfilerTests.cs" Condition="'$(RuntimeFlavor)' != 'mono'" />
<Compile Include="System.Runtime.CompilerServices\AsyncProfilerV1Tests.cs" Condition="'$(RuntimeFlavor)' != 'mono'" />
<Compile Include="System.Runtime.CompilerServices\AsyncProfilerTests.cs" />
<Compile Include="System.Runtime.CompilerServices\AsyncProfilerV1Tests.cs" />
<Compile Include="System.Runtime.CompilerServices\AsyncProfilerV2Tests.cs" Condition="'$(RuntimeFlavor)' != 'mono'" />
<Compile Include="System.Runtime.CompilerServices\ConfiguredAsyncDisposable.cs" />
<Compile Include="System.Runtime.CompilerServices\ConfiguredCancelableAsyncEnumerableTests.cs" />
<Compile Include="System.Runtime.CompilerServices\TaskAwaiterTests.cs" />
<Compile Include="System.Runtime.CompilerServices\YieldAwaitableTests.cs" />
<Compile Include="$(CommonTestPath)System\Runtime\CompilerServices\RuntimeAsyncMethodGenerationAttribute.cs" Condition="'$(RuntimeFlavor)' != 'mono'" />
<Compile Include="$(CommonTestPath)System\Runtime\CompilerServices\RuntimeAsyncMethodGenerationAttribute.cs" />
<Compile Include="$(CommonTestPath)System\Diagnostics\Tracing\TestEventListener.cs" Link="Common\System\Diagnostics\Tracing\TestEventListener.cs" />
<Compile Include="$(CommonTestPath)System\Threading\ThreadPoolHelpers.cs" Link="CommonTest\System\Threading\ThreadPoolHelpers.cs" />
<Compile Include="$(CommonTestPath)System\Threading\Tasks\TaskTimeoutExtensions.cs" Link="Common\System\Threading\Tasks\TaskTimeoutExtensions.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@ public IntPtr GetFunctionPointer()
return GetFunctionPointer(value);
}

[MethodImpl(MethodImplOptions.InternalCall)]
private static extern IntPtr GetNativeCode(IntPtr m);

internal static IntPtr GetNativeCodeInternal(IntPtr methodHandleValue)
{
return GetNativeCode(methodHandleValue);
}

public override bool Equals(object? obj)
{
if (obj == null || GetType() != obj.GetType())
Expand Down
1 change: 1 addition & 0 deletions src/mono/mono/metadata/icall-def.h
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,7 @@ HANDLES_REUSE_WRAPPER(RFH_4, "SetValueInternal", ves_icall_RuntimeFieldInfo_SetV

ICALL_TYPE(MHAN, "System.RuntimeMethodHandle", MHAN_1)
HANDLES(MHAN_1, "GetFunctionPointer", ves_icall_RuntimeMethodHandle_GetFunctionPointer, gpointer, 1, (MonoMethod_ptr))
HANDLES(MHAN_4, "GetNativeCode", ves_icall_RuntimeMethodHandle_GetNativeCode, gpointer, 1, (MonoMethod_ptr))
HANDLES(MAHN_3, "ReboxFromNullable", ves_icall_RuntimeMethodHandle_ReboxFromNullable, void, 2, (MonoObject, MonoObjectHandleOnStack))
HANDLES(MAHN_2, "ReboxToNullable", ves_icall_RuntimeMethodHandle_ReboxToNullable, void, 3, (MonoObject, MonoQCallTypeHandle, MonoObjectHandleOnStack))

Expand Down
7 changes: 7 additions & 0 deletions src/mono/mono/metadata/icall.c
Original file line number Diff line number Diff line change
Expand Up @@ -6250,6 +6250,13 @@ ves_icall_RuntimeMethodHandle_GetFunctionPointer (MonoMethod *method, MonoError
return mono_method_get_unmanaged_wrapper_ftnptr_internal (method, FALSE, error);
}

gpointer
ves_icall_RuntimeMethodHandle_GetNativeCode (MonoMethod *method, MonoError *error)
{
MonoRuntimeCallbacks *callbacks = mono_get_runtime_callbacks ();
return callbacks->get_method_code_start ? callbacks->get_method_code_start (method) : NULL;
}

void*
mono_method_get_unmanaged_wrapper_ftnptr_internal (MonoMethod *method, gboolean only_unmanaged_callers_only, MonoError *error)
{
Expand Down
3 changes: 3 additions & 0 deletions src/mono/mono/metadata/object-internals.h
Original file line number Diff line number Diff line change
Expand Up @@ -723,6 +723,9 @@ typedef struct {
void (*get_exception_stats)(guint32 *exception_count);
// Same as compile_method, but returns a MonoFtnDesc in llvmonly mode
gpointer (*get_ftnptr)(MonoMethod *method, gboolean need_unbox, MonoError *error);
// Returns the start address of the method's already-present native code (JIT, AOT or interpreter), or NULL if
// the method has not been compiled/prepared.
gpointer (*get_method_code_start)(MonoMethod *method);
void (*interp_jit_info_foreach)(InterpJitInfoFunc func, gpointer user_data);
gboolean (*interp_sufficient_stack)(gsize size);
void (*init_class) (MonoClass *klass);
Expand Down
9 changes: 9 additions & 0 deletions src/mono/mono/mini/mini-runtime.c
Original file line number Diff line number Diff line change
Expand Up @@ -3114,6 +3114,14 @@ mono_jit_search_all_backends_for_jit_info (MonoMethod *method, MonoJitInfo **out
return code;
}

static gpointer
get_method_code_start (MonoMethod *method)
{
MonoJitInfo *ji = NULL;
gpointer code = mono_jit_search_all_backends_for_jit_info (method, &ji);
return ji ? mono_jit_info_get_code_start (ji) : code;
}

gpointer
mono_jit_find_compiled_method_with_jit_info (MonoMethod *method, MonoJitInfo **ji)
{
Expand Down Expand Up @@ -4716,6 +4724,7 @@ mini_init (const char *filename)
callbacks.get_weak_field_indexes = mono_aot_get_weak_field_indexes;
#endif
callbacks.find_jit_info_in_aot = mono_aot_find_jit_info;
callbacks.get_method_code_start = get_method_code_start;
callbacks.metadata_update_published = mini_invalidate_transformed_interp_methods;
callbacks.interp_jit_info_foreach = mini_interp_jit_info_foreach;
callbacks.interp_sufficient_stack = mini_interp_sufficient_stack;
Expand Down
Loading