2424 */
2525package com .oracle .svm .core .jfr .sampler ;
2626
27- import jdk .graal .compiler .api .replacements .Fold ;
27+ import static com .oracle .svm .core .Uninterruptible .CALLED_FROM_UNINTERRUPTIBLE_CODE ;
28+
2829import org .graalvm .nativeimage .CurrentIsolate ;
2930import org .graalvm .nativeimage .ImageSingletons ;
3031import org .graalvm .nativeimage .IsolateThread ;
3334import org .graalvm .nativeimage .StackValue ;
3435import org .graalvm .nativeimage .c .function .CodePointer ;
3536import org .graalvm .word .Pointer ;
37+ import org .graalvm .word .UnsignedWord ;
3638import org .graalvm .word .WordFactory ;
3739
3840import com .oracle .svm .core .FrameAccess ;
41+ import com .oracle .svm .core .SubstrateOptions ;
3942import com .oracle .svm .core .Uninterruptible ;
4043import com .oracle .svm .core .code .CodeInfo ;
4144import com .oracle .svm .core .code .CodeInfoAccess ;
45+ import com .oracle .svm .core .code .CodeInfoQueryResult ;
4246import com .oracle .svm .core .code .CodeInfoTable ;
47+ import com .oracle .svm .core .code .SimpleCodeInfoQueryResult ;
48+ import com .oracle .svm .core .config .ConfigurationValues ;
4349import com .oracle .svm .core .heap .VMOperationInfos ;
4450import com .oracle .svm .core .jdk .UninterruptibleUtils ;
4551import com .oracle .svm .core .jfr .JfrEvent ;
5864import com .oracle .svm .core .thread .VMThreads ;
5965import com .oracle .svm .core .threadlocal .FastThreadLocalFactory ;
6066import com .oracle .svm .core .threadlocal .FastThreadLocalInt ;
67+ import com .oracle .svm .core .util .PointerUtils ;
68+
69+ import jdk .graal .compiler .api .replacements .Fold ;
6170
6271/*
6372 * Base class for different sampler implementations that emit JFR ExecutionSample events.
@@ -233,21 +242,12 @@ private static void doUninterruptibleStackWalk(CodePointer initialIp, Pointer in
233242 }
234243 }
235244
236- if (!isSPInsideStackBoundaries (ip , sp )) {
237- JfrThreadLocal .increaseUnparseableStacks ();
238- return ;
239- }
240-
241245 /* Try to do a stack walk. */
242246 SamplerSampleWriterData data = StackValue .get (SamplerSampleWriterData .class );
243247 if (SamplerSampleWriterDataAccess .initialize (data , 0 , false )) {
244248 JfrThreadLocal .setSamplerWriterData (data );
245249 try {
246- SamplerSampleWriter .begin (data );
247- SamplerStackWalkVisitor visitor = ImageSingletons .lookup (SamplerStackWalkVisitor .class );
248- if (JavaStackWalker .walkCurrentThread (sp , ip , visitor ) || data .getTruncated ()) {
249- SamplerSampleWriter .end (data , SamplerSampleWriter .EXECUTION_SAMPLE_END );
250- }
250+ doUninterruptibleStackWalk (data , sp , ip );
251251 } finally {
252252 JfrThreadLocal .setSamplerWriterData (WordFactory .nullPointer ());
253253 }
@@ -266,13 +266,107 @@ private static boolean isInAOTCompiledCode(CodePointer ip) {
266266 * might be positioned outside the stack's boundaries if a signal interrupted the execution at
267267 * the beginning of a method, before the SP was adjusted to its correct value.
268268 */
269- @ Uninterruptible (reason = "Called from uninterruptible code." , mayBeInlined = true )
270- private static boolean isSPInsideStackBoundaries (CodePointer ip , Pointer sp ) {
269+ @ Uninterruptible (reason = CALLED_FROM_UNINTERRUPTIBLE_CODE , mayBeInlined = true )
270+ private static boolean isSPOutsideStackBoundaries (Pointer sp ) {
271+ UnsignedWord stackBase = VMThreads .StackBase .get ();
272+ assert stackBase .notEqual (0 );
273+ Pointer returnAddressLocation = FrameAccess .singleton ().getReturnAddressLocation (sp ).add (FrameAccess .returnAddressSize ());
274+ return returnAddressLocation .aboveThan (stackBase ) || returnAddressLocation .belowOrEqual (VMThreads .StackEnd .get ());
275+ }
276+
277+ @ Uninterruptible (reason = "This method must be uninterruptible since it uses untethered code info." , callerMustBe = true )
278+ private static Pointer getCallerSP (CodeInfo codeInfo , Pointer sp , CodePointer ip ) {
279+ long relativeIP = CodeInfoAccess .relativeIP (codeInfo , ip );
280+ long totalFrameSize = CodeInfoAccess .lookupTotalFrameSize (codeInfo , relativeIP );
281+ return sp .add (WordFactory .unsigned (totalFrameSize ));
282+ }
283+
284+ @ Uninterruptible (reason = CALLED_FROM_UNINTERRUPTIBLE_CODE , mayBeInlined = true )
285+ private static boolean isSPAligned (Pointer sp ) {
286+ return PointerUtils .isAMultiple (sp , WordFactory .unsigned (ConfigurationValues .getTarget ().stackAlignment ));
287+ }
288+
289+ @ Uninterruptible (reason = CALLED_FROM_UNINTERRUPTIBLE_CODE , mayBeInlined = true )
290+ private static boolean isEntryPoint (CodeInfo codeInfo , CodePointer ip ) {
291+ long relativeIP = CodeInfoAccess .relativeIP (codeInfo , ip );
292+ SimpleCodeInfoQueryResult queryResult = StackValue .get (SimpleCodeInfoQueryResult .class );
293+ CodeInfoAccess .lookupCodeInfo (codeInfo , relativeIP , queryResult );
294+ return CodeInfoQueryResult .isEntryPoint (queryResult .getEncodedFrameSize ());
295+ }
296+
297+ @ Uninterruptible (reason = CALLED_FROM_UNINTERRUPTIBLE_CODE , mayBeInlined = true )
298+ private static JavaFrameAnchor findLastJavaFrameAnchor (Pointer callerSP ) {
299+ JavaFrameAnchor anchor = JavaFrameAnchors .getFrameAnchor ();
300+ while (anchor .isNonNull () && anchor .getLastJavaSP ().belowOrEqual (callerSP )) {
301+ /* Skip anchors that are in parts of the stack we are not traversing. */
302+ anchor = anchor .getPreviousAnchor ();
303+ }
304+ assert anchor .isNull () || anchor .getLastJavaSP ().aboveThan (callerSP );
305+ return anchor ;
306+ }
307+
308+ @ Uninterruptible (reason = "This method must be uninterruptible since it uses untethered code info." , callerMustBe = true )
309+ private static void doUninterruptibleStackWalk (SamplerSampleWriterData data , Pointer sp , CodePointer ip ) {
310+ SamplerSampleWriter .begin (data );
311+ /*
312+ * Visit the top frame.
313+ *
314+ * No matter where in the AOT-compiled code the signal has interrupted the execution, we
315+ * know how to decode it.
316+ */
317+ assert isInAOTCompiledCode (ip );
271318 CodeInfo codeInfo = CodeInfoTable .getImageCodeInfo ();
272- long totalFrameSize = CodeInfoAccess .lookupTotalFrameSize (codeInfo , CodeInfoAccess .relativeIP (codeInfo , ip ));
273- Pointer returnAddressAddress = FrameAccess .singleton ().getReturnAddressLocation (sp .add (WordFactory .unsigned (totalFrameSize )))
274- .add (FrameAccess .returnAddressSize ());
275- return returnAddressAddress .aboveThan (VMThreads .StackEnd .get ()) && returnAddressAddress .belowOrEqual (VMThreads .StackBase .get ());
319+ SamplerStackWalkVisitor visitor = ImageSingletons .lookup (SamplerStackWalkVisitor .class );
320+ if (!visitor .visitFrame (sp , ip , codeInfo , null , null )) {
321+ /* The top frame is also the last one. */
322+ SamplerSampleWriter .end (data , SamplerSampleWriter .EXECUTION_SAMPLE_END );
323+ return ;
324+ }
325+
326+ Pointer callerSP ;
327+ if (isSPAligned (sp )) {
328+ /* Stack is probably in a normal, walkable state. */
329+ callerSP = getCallerSP (codeInfo , sp , ip );
330+ if (SubstrateOptions .PreserveFramePointer .getValue () && (isSPOutsideStackBoundaries (callerSP ) || !isInAOTCompiledCode (FrameAccess .singleton ().readReturnAddress (callerSP )))) {
331+ /*
332+ * We are in the prologue or epilogue. Frame pointer and return address are on top
333+ * of the stack.
334+ */
335+ callerSP = sp .add (FrameAccess .wordSize ()).add (FrameAccess .singleton ().savedBasePointerSize ());
336+ }
337+ } else {
338+ /* We are in the prologue or epilogue. Return address is at the top of the stack. */
339+ callerSP = sp .add (FrameAccess .wordSize ());
340+ }
341+
342+ if (isSPOutsideStackBoundaries (callerSP )) {
343+ /* We made an incorrect assumption earlier, the stack is not walkable. */
344+ JfrThreadLocal .increaseUnparseableStacks ();
345+ return ;
346+ }
347+
348+ CodePointer returnAddressIP = FrameAccess .singleton ().readReturnAddress (callerSP );
349+ if (!isInAOTCompiledCode (returnAddressIP )) {
350+ if (isEntryPoint (codeInfo , ip )) {
351+ JavaFrameAnchor anchor = findLastJavaFrameAnchor (callerSP );
352+ if (anchor .isNonNull ()) {
353+ callerSP = anchor .getLastJavaSP ();
354+ returnAddressIP = anchor .getLastJavaIP ();
355+ } else {
356+ SamplerSampleWriter .end (data , SamplerSampleWriter .EXECUTION_SAMPLE_END );
357+ return ;
358+ }
359+ } else {
360+ /* We made an incorrect assumption earlier, the stack is not walkable. */
361+ JfrThreadLocal .increaseUnparseableStacks ();
362+ return ;
363+ }
364+ }
365+
366+ /* Start a stack walk but from the frame after the top one. */
367+ if (JavaStackWalker .walkCurrentThread (callerSP , returnAddressIP , visitor ) || data .getTruncated ()) {
368+ SamplerSampleWriter .end (data , SamplerSampleWriter .EXECUTION_SAMPLE_END );
369+ }
276370 }
277371
278372 /**
0 commit comments