diff --git a/src/coreclr/nativeaot/Runtime/StackFrameIterator.h b/src/coreclr/nativeaot/Runtime/StackFrameIterator.h index 9c1739c3951fb4..4e9ab30ae41131 100644 --- a/src/coreclr/nativeaot/Runtime/StackFrameIterator.h +++ b/src/coreclr/nativeaot/Runtime/StackFrameIterator.h @@ -55,6 +55,7 @@ class StackFrameIterator bool GetHijackedReturnValueLocation(PTR_OBJECTREF * pLocation, GCRefKind * pKind); #endif void SetControlPC(PTR_VOID controlPC); + PTR_VOID GetControlPC() { return m_ControlPC; } static bool IsValidReturnAddress(PTR_VOID pvAddress); diff --git a/src/coreclr/nativeaot/Runtime/eventpipe/ep-rt-aot.cpp b/src/coreclr/nativeaot/Runtime/eventpipe/ep-rt-aot.cpp index f79ca41ce654a3..8d2c4b0e7c68d0 100644 --- a/src/coreclr/nativeaot/Runtime/eventpipe/ep-rt-aot.cpp +++ b/src/coreclr/nativeaot/Runtime/eventpipe/ep-rt-aot.cpp @@ -46,8 +46,29 @@ ep_rt_aot_walk_managed_stack_for_thread ( ep_rt_thread_handle_t thread, EventPipeStackContents *stack_contents) { - // NativeAOT does not support getting the call stack - return false; + STATIC_CONTRACT_NOTHROW; + EP_ASSERT (thread != NULL); + EP_ASSERT (stack_contents != NULL); + + StackFrameIterator frameIterator(thread, thread->GetTransitionFrameForSampling()); + + while (frameIterator.IsValid()) + { + frameIterator.CalculateCurrentMethodState(); + + // Get the IP. + uintptr_t control_pc = (uintptr_t)frameIterator.GetControlPC(); + + if (control_pc != 0) + { + // Add the IP to the captured stack. + ep_stack_contents_append (stack_contents, control_pc, NULL); + } + + frameIterator.Next(); + } + + return true; } bool @@ -62,6 +83,53 @@ ep_rt_aot_sample_profiler_write_sampling_event_for_threads ( ep_rt_thread_handle_t sampling_thread, EventPipeEvent *sampling_event) { + STATIC_CONTRACT_NOTHROW; + EP_ASSERT (sampling_thread != NULL); + + ThreadStore *thread_store = GetThreadStore (); + + // Check to see if we can suspend managed execution. + if (thread_store->GetSuspendingThread () != NULL) + return; + + // Actually suspend managed execution. + thread_store->LockThreadStore (); + thread_store->SuspendAllThreads (false); + + EventPipeStackContents stack_contents; + EventPipeStackContents *current_stack_contents; + current_stack_contents = ep_stack_contents_init (&stack_contents); + + EP_ASSERT (current_stack_contents != NULL); + + // Walk all managed threads and capture stacks. + FOREACH_THREAD (target_thread) + { + ep_stack_contents_reset (current_stack_contents); + + // Walk the stack and write it out as an event. + if (ep_rt_aot_walk_managed_stack_for_thread (target_thread, current_stack_contents) && !ep_stack_contents_is_empty (current_stack_contents)) { + // Set the payload. + // TODO: We can actually detect whether we are in managed or external code but does it matter?! + uint32_t payload_data = EP_SAMPLE_PROFILER_SAMPLE_TYPE_EXTERNAL; + + // Write the sample. + ep_write_sample_profile_event ( + sampling_thread, + sampling_event, + target_thread, + current_stack_contents, + (uint8_t *)&payload_data, + sizeof (payload_data)); + } + } + END_FOREACH_THREAD + + ep_stack_contents_fini (current_stack_contents); + + // Resume managed execution. + thread_store->ResumeAllThreads(false); + thread_store->UnlockThreadStore(); } const ep_char8_t * diff --git a/src/coreclr/nativeaot/Runtime/thread.h b/src/coreclr/nativeaot/Runtime/thread.h index a431cd1434314b..3a251ee9afa932 100644 --- a/src/coreclr/nativeaot/Runtime/thread.h +++ b/src/coreclr/nativeaot/Runtime/thread.h @@ -320,6 +320,7 @@ class Thread : private RuntimeThreadLocals bool IsCurrentThreadInCooperativeMode(); PInvokeTransitionFrame* GetTransitionFrameForStackTrace(); + PInvokeTransitionFrame* GetTransitionFrameForSampling() { return GetTransitionFrame(); } void * GetCurrentThreadPInvokeReturnAddress(); //