Skip to content

Commit 3427e5f

Browse files
authored
Add AOT sampling profiler (#108083)
1 parent 1bcde97 commit 3427e5f

File tree

3 files changed

+72
-2
lines changed

3 files changed

+72
-2
lines changed

src/coreclr/nativeaot/Runtime/StackFrameIterator.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ class StackFrameIterator
5555
bool GetHijackedReturnValueLocation(PTR_OBJECTREF * pLocation, GCRefKind * pKind);
5656
#endif
5757
void SetControlPC(PTR_VOID controlPC);
58+
PTR_VOID GetControlPC() { return m_ControlPC; }
5859

5960
static bool IsValidReturnAddress(PTR_VOID pvAddress);
6061

src/coreclr/nativeaot/Runtime/eventpipe/ep-rt-aot.cpp

Lines changed: 70 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,29 @@ ep_rt_aot_walk_managed_stack_for_thread (
4646
ep_rt_thread_handle_t thread,
4747
EventPipeStackContents *stack_contents)
4848
{
49-
// NativeAOT does not support getting the call stack
50-
return false;
49+
STATIC_CONTRACT_NOTHROW;
50+
EP_ASSERT (thread != NULL);
51+
EP_ASSERT (stack_contents != NULL);
52+
53+
StackFrameIterator frameIterator(thread, thread->GetTransitionFrameForSampling());
54+
55+
while (frameIterator.IsValid())
56+
{
57+
frameIterator.CalculateCurrentMethodState();
58+
59+
// Get the IP.
60+
uintptr_t control_pc = (uintptr_t)frameIterator.GetControlPC();
61+
62+
if (control_pc != 0)
63+
{
64+
// Add the IP to the captured stack.
65+
ep_stack_contents_append (stack_contents, control_pc, NULL);
66+
}
67+
68+
frameIterator.Next();
69+
}
70+
71+
return true;
5172
}
5273

5374
bool
@@ -62,6 +83,53 @@ ep_rt_aot_sample_profiler_write_sampling_event_for_threads (
6283
ep_rt_thread_handle_t sampling_thread,
6384
EventPipeEvent *sampling_event)
6485
{
86+
STATIC_CONTRACT_NOTHROW;
87+
EP_ASSERT (sampling_thread != NULL);
88+
89+
ThreadStore *thread_store = GetThreadStore ();
90+
91+
// Check to see if we can suspend managed execution.
92+
if (thread_store->GetSuspendingThread () != NULL)
93+
return;
94+
95+
// Actually suspend managed execution.
96+
thread_store->LockThreadStore ();
97+
thread_store->SuspendAllThreads (false);
98+
99+
EventPipeStackContents stack_contents;
100+
EventPipeStackContents *current_stack_contents;
101+
current_stack_contents = ep_stack_contents_init (&stack_contents);
102+
103+
EP_ASSERT (current_stack_contents != NULL);
104+
105+
// Walk all managed threads and capture stacks.
106+
FOREACH_THREAD (target_thread)
107+
{
108+
ep_stack_contents_reset (current_stack_contents);
109+
110+
// Walk the stack and write it out as an event.
111+
if (ep_rt_aot_walk_managed_stack_for_thread (target_thread, current_stack_contents) && !ep_stack_contents_is_empty (current_stack_contents)) {
112+
// Set the payload.
113+
// TODO: We can actually detect whether we are in managed or external code but does it matter?!
114+
uint32_t payload_data = EP_SAMPLE_PROFILER_SAMPLE_TYPE_EXTERNAL;
115+
116+
// Write the sample.
117+
ep_write_sample_profile_event (
118+
sampling_thread,
119+
sampling_event,
120+
target_thread,
121+
current_stack_contents,
122+
(uint8_t *)&payload_data,
123+
sizeof (payload_data));
124+
}
125+
}
126+
END_FOREACH_THREAD
127+
128+
ep_stack_contents_fini (current_stack_contents);
129+
130+
// Resume managed execution.
131+
thread_store->ResumeAllThreads(false);
132+
thread_store->UnlockThreadStore();
65133
}
66134

67135
const ep_char8_t *

src/coreclr/nativeaot/Runtime/thread.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,7 @@ class Thread : private RuntimeThreadLocals
320320
bool IsCurrentThreadInCooperativeMode();
321321

322322
PInvokeTransitionFrame* GetTransitionFrameForStackTrace();
323+
PInvokeTransitionFrame* GetTransitionFrameForSampling() { return GetTransitionFrame(); }
323324
void * GetCurrentThreadPInvokeReturnAddress();
324325

325326
//

0 commit comments

Comments
 (0)