Skip to content

Commit 0bcc583

Browse files
[GR-49305] Allow sampling in method prologue and epilogue.
PullRequest: graal/15830
2 parents 921cfea + dc864ae commit 0bcc583

File tree

2 files changed

+112
-18
lines changed

2 files changed

+112
-18
lines changed

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/sampler/AbstractJfrExecutionSampler.java

Lines changed: 111 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@
2424
*/
2525
package 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+
2829
import org.graalvm.nativeimage.CurrentIsolate;
2930
import org.graalvm.nativeimage.ImageSingletons;
3031
import org.graalvm.nativeimage.IsolateThread;
@@ -33,13 +34,18 @@
3334
import org.graalvm.nativeimage.StackValue;
3435
import org.graalvm.nativeimage.c.function.CodePointer;
3536
import org.graalvm.word.Pointer;
37+
import org.graalvm.word.UnsignedWord;
3638
import org.graalvm.word.WordFactory;
3739

3840
import com.oracle.svm.core.FrameAccess;
41+
import com.oracle.svm.core.SubstrateOptions;
3942
import com.oracle.svm.core.Uninterruptible;
4043
import com.oracle.svm.core.code.CodeInfo;
4144
import com.oracle.svm.core.code.CodeInfoAccess;
45+
import com.oracle.svm.core.code.CodeInfoQueryResult;
4246
import com.oracle.svm.core.code.CodeInfoTable;
47+
import com.oracle.svm.core.code.SimpleCodeInfoQueryResult;
48+
import com.oracle.svm.core.config.ConfigurationValues;
4349
import com.oracle.svm.core.heap.VMOperationInfos;
4450
import com.oracle.svm.core.jdk.UninterruptibleUtils;
4551
import com.oracle.svm.core.jfr.JfrEvent;
@@ -58,6 +64,9 @@
5864
import com.oracle.svm.core.thread.VMThreads;
5965
import com.oracle.svm.core.threadlocal.FastThreadLocalFactory;
6066
import 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
/**

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/sampler/SamplerStackWalkVisitor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public SamplerStackWalkVisitor() {
4444

4545
@Override
4646
@Uninterruptible(reason = "The method executes during signal handling.", callerMustBe = true)
47-
protected boolean visitFrame(Pointer sp, CodePointer ip, CodeInfo codeInfo, DeoptimizedFrame deoptimizedFrame, Object data) {
47+
public boolean visitFrame(Pointer sp, CodePointer ip, CodeInfo codeInfo, DeoptimizedFrame deoptimizedFrame, Object data) {
4848
return recordIp(ip);
4949
}
5050

0 commit comments

Comments
 (0)