Skip to content

Commit

Permalink
[GR-43134] Add support for profiling of topmost frame.
Browse files Browse the repository at this point in the history
PullRequest: graal/14395
  • Loading branch information
jovanstevanovic committed Jul 16, 2023
2 parents 25ce8d1 + 441cef4 commit e738302
Show file tree
Hide file tree
Showing 9 changed files with 117 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1035,7 +1035,7 @@ public boolean printLocationInfo(Log log, UnsignedWord value) {

private FrameInfoQueryResult getCompilationRoot(CodeInfo imageCodeInfo, CodePointer ip) {
FrameInfoQueryResult rootInfo = null;
frameInfoCursor.initialize(imageCodeInfo, ip);
frameInfoCursor.initialize(imageCodeInfo, ip, false);
while (frameInfoCursor.advance()) {
rootInfo = frameInfoCursor.get();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import com.oracle.svm.core.Uninterruptible;
import com.oracle.svm.core.c.NonmovableObjectArray;
import com.oracle.svm.core.heap.ReferenceMapIndex;
import com.oracle.svm.core.jdk.UninterruptibleUtils;
import com.oracle.svm.core.option.HostedOptionKey;
import com.oracle.svm.core.util.Counter;
import com.oracle.svm.core.util.NonmovableByteArrayReader;
Expand Down Expand Up @@ -90,7 +91,45 @@ private static long lookupCodeInfoEntryOffset(CodeInfo info, long ip) {
entryOffset = advanceOffset(entryOffset, entryFlags);
} while (entryIP <= ip);

return -1;
return INVALID_FRAME_INFO_ENTRY_OFFSET;
}

@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
private static long lookupCodeInfoEntryOffsetOrDefault(CodeInfo info, long ip) {
int chunksToSearch = 0;
while (true) {
long defaultFIEntryOffset = INVALID_FRAME_INFO_ENTRY_OFFSET;
long entryIP = UninterruptibleUtils.Math.max(lookupEntryIP(ip) - chunksToSearch * CodeInfoDecoder.indexGranularity(), 0);
long entryOffset = loadEntryOffset(info, entryIP);
do {
int entryFlags = loadEntryFlags(info, entryOffset);
int frameInfoFlag = extractFI(entryFlags);
defaultFIEntryOffset = frameInfoFlag == FI_DEFAULT_INFO_INDEX_S4 ? entryOffset : defaultFIEntryOffset;
if (entryIP == ip) {
if (frameInfoFlag == FI_NO_DEOPT) {
/* There is no frame info. Try to find a default one. */
break;
} else {
return entryOffset;
}
}

entryIP = advanceIP(info, entryOffset, entryIP);
entryOffset = advanceOffset(entryOffset, entryFlags);
} while (entryIP <= ip);

if (defaultFIEntryOffset != INVALID_FRAME_INFO_ENTRY_OFFSET) {
return defaultFIEntryOffset;
} else {
/*
* We should re-try lookup only in case when nearest chunk to a given IP is a call
* instruction i.e. chunk beginning and call have the same IP value. Continue
* searching until you find a chunk that is not a call or method start.
*/
chunksToSearch++;
assert entryIP != 0;
}
}
}

static void lookupCodeInfo(CodeInfo info, long ip, CodeInfoQueryResult codeInfoQueryResult) {
Expand Down Expand Up @@ -231,6 +270,7 @@ static int loadEntryFlags(CodeInfo info, long curOffset) {
}

private static final int INVALID_SIZE_ENCODING = 0;
private static final int INVALID_FRAME_INFO_ENTRY_OFFSET = -1;

@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
private static int initialSizeEncoding() {
Expand Down Expand Up @@ -338,6 +378,11 @@ private static boolean isDeoptEntryPoint(CodeInfo info, long entryOffset, int en
* We have frame information, but only for debugging purposes. This is not a
* deoptimization entry point.
*/
case FI_DEFAULT_INFO_INDEX_S4:
/*
* We have frame information, but without bci and line number. This is not a
* deoptimization entry point.
*/
return false;
default:
throw shouldNotReachHereUnexpectedInput(entryFlags); // ExcludeFromJacocoGeneratedReport
Expand All @@ -353,6 +398,7 @@ private static FrameInfoQueryResult loadFrameInfo(CodeInfo info, long entryOffse
isDeoptEntry = true;
break;
case FI_INFO_ONLY_INDEX_S4:
case FI_DEFAULT_INFO_INDEX_S4:
isDeoptEntry = false;
break;
default:
Expand Down Expand Up @@ -414,10 +460,21 @@ private static boolean endOfTable(long entryIP) {
static final int FI_BITS = 2;
static final int FI_SHIFT = RM_SHIFT + RM_BITS;
static final int FI_MASK_IN_PLACE = ((1 << FI_BITS) - 1) << FI_SHIFT;
/*
* Filler frame value. It is needed since we store the nextIP offset in a one-byte field,
* regardless of the CodeInfo index granularity. See CodeInfoEncoder#encodeIPData for more
* information.
*/
static final int FI_NO_DEOPT = 0;
static final int FI_DEOPT_ENTRY_INDEX_S4 = 1;
static final int FI_INFO_ONLY_INDEX_S4 = 2;
static final int[] FI_MEM_SIZE = {0, Integer.BYTES, Integer.BYTES, /* unused */ 0};
/*
* Frame value for default frame info. A default frame info contains a method name and a class
* but without BCI and line number. It is present on each chunk beginning and method starts. See
* FrameInfoEncoder#addDefaultDebugInfo for more information.
*/
static final int FI_DEFAULT_INFO_INDEX_S4 = 3;
static final int[] FI_MEM_SIZE = {0, Integer.BYTES, Integer.BYTES, Integer.BYTES};

private static final int TOTAL_BITS = FI_SHIFT + FI_BITS;

Expand Down Expand Up @@ -532,12 +589,12 @@ public FrameInfoCursor() {

@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
@SuppressWarnings("hiding")
public void initialize(CodeInfo info, CodePointer ip) {
public void initialize(CodeInfo info, CodePointer ip, boolean exactIPMatch) {
this.info = info;
result = null;
frameInfoReader.reset();
state.reset();
canDecode = initFrameInfoReader(ip);
canDecode = initFrameInfoReader(ip, exactIPMatch);
}

/**
Expand Down Expand Up @@ -591,19 +648,21 @@ private void decodeNextEntry() {
}

@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
private boolean initFrameInfoReader(CodePointer ip) {
long entryOffset = lookupCodeInfoEntryOffset(info, CodeInfoAccess.relativeIP(info, ip));
private boolean initFrameInfoReader(CodePointer ip, boolean exactIPMatch) {
long relativeIP = CodeInfoAccess.relativeIP(info, ip);
long entryOffset = exactIPMatch ? lookupCodeInfoEntryOffset(info, relativeIP) : lookupCodeInfoEntryOffsetOrDefault(info, relativeIP);
if (entryOffset >= 0) {
int entryFlags = loadEntryFlags(info, entryOffset);
if (extractFI(entryFlags) == FI_NO_DEOPT) {
entryOffset = -1;
entryOffset = INVALID_FRAME_INFO_ENTRY_OFFSET;
} else {
int frameInfoIndex = NonmovableByteArrayReader.getS4(CodeInfoAccess.getCodeInfoEncodings(info), offsetFI(entryOffset, entryFlags));
frameInfoReader.setByteIndex(frameInfoIndex);
frameInfoReader.setData(CodeInfoAccess.getFrameInfoEncodings(info));
}
}
state.entryOffset = entryOffset;
assert exactIPMatch || entryOffset >= 0;
return entryOffset >= 0;
}
}
Expand All @@ -624,7 +683,7 @@ public FrameInfoState() {

@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public FrameInfoState reset() {
entryOffset = -1;
entryOffset = INVALID_FRAME_INFO_ENTRY_OFFSET;
isFirstFrame = true;
isDone = false;
firstValue = -1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,12 +186,15 @@ public void addMethod(SharedMethod method, CompilationResult compilation, int co

/* Mark the method start and register the frame size. */
IPData startEntry = makeEntry(compilationOffset);
FrameInfoEncoder.FrameData defaultFrameData = frameInfoEncoder.addDefaultDebugInfo(method, totalFrameSize);
startEntry.frameData = defaultFrameData;
startEntry.frameSizeEncoding = encodeFrameSize(totalFrameSize, true, isEntryPoint, hasCalleeSavedRegisters);

/* Register the frame size for all entries that are starting points for the index. */
long entryIP = CodeInfoDecoder.lookupEntryIP(CodeInfoDecoder.indexGranularity() + compilationOffset);
while (entryIP <= CodeInfoDecoder.lookupEntryIP(compilationSize + compilationOffset - 1)) {
IPData entry = makeEntry(entryIP);
entry.frameData = defaultFrameData;
entry.frameSizeEncoding = encodeFrameSize(totalFrameSize, false, isEntryPoint, hasCalleeSavedRegisters);
entryIP += CodeInfoDecoder.indexGranularity();
}
Expand All @@ -209,7 +212,7 @@ public void addMethod(SharedMethod method, CompilationResult compilation, int co
throw VMError.shouldNotReachHere("Encoding two infopoints at same offset. Conflicting infopoint: " + infopoint);
}
IPData entry = makeEntry(offset + compilationOffset);
assert entry.referenceMap == null && entry.frameData == null;
assert entry.referenceMap == null && (entry.frameData == null || entry.frameData.isDefaultFrameData);
entry.referenceMap = (ReferenceMapEncoder.Input) debugInfo.getReferenceMap();
entry.frameData = frameInfoEncoder.addDebugInfo(method, compilation, infopoint, totalFrameSize);
if (entry.frameData != null && entry.frameData.frame.isDeoptEntry) {
Expand Down Expand Up @@ -433,7 +436,11 @@ private static int flagsForDeoptFrameInfo(IPData data) {
if (data.frameData.frame.isDeoptEntry) {
return CodeInfoDecoder.FI_DEOPT_ENTRY_INDEX_S4;
} else {
return CodeInfoDecoder.FI_INFO_ONLY_INDEX_S4;
if (data.frameData.isDefaultFrameData) {
return CodeInfoDecoder.FI_DEFAULT_INFO_INDEX_S4;
} else {
return CodeInfoDecoder.FI_INFO_ONLY_INDEX_S4;
}
}
} else {
throw new IllegalArgumentException();
Expand All @@ -442,6 +449,7 @@ private static int flagsForDeoptFrameInfo(IPData data) {

private static void writeEncodedFrameInfo(UnsafeArrayTypeWriter writeBuffer, IPData data, int entryFlags) {
switch (CodeInfoDecoder.extractFI(entryFlags)) {
case CodeInfoDecoder.FI_DEFAULT_INFO_INDEX_S4:
case CodeInfoDecoder.FI_DEOPT_ENTRY_INDEX_S4:
case CodeInfoDecoder.FI_INFO_ONLY_INDEX_S4:
writeBuffer.putS4(data.frameData.encodedFrameInfoIndex);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -530,19 +530,19 @@ private static ValueInfo newValueInfo(ValueInfoAllocator valueInfoAllocator) {

@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
protected static int decodeBci(long encodedBci) {
assert encodedBci > 0 && encodedBci != FrameInfoDecoder.ENCODED_BCI_NO_CALLER;
assert encodedBci >= 0 && encodedBci != FrameInfoDecoder.ENCODED_BCI_NO_CALLER;
long result = (encodedBci >> ENCODED_BCI_SHIFT) - ENCODED_BCI_ADDEND;
assert result >= Integer.MIN_VALUE && result <= Integer.MAX_VALUE;
return (int) result;
}

protected static boolean decodeDuringCall(long encodedBci) {
assert encodedBci > 0 && encodedBci != FrameInfoDecoder.ENCODED_BCI_NO_CALLER;
assert encodedBci >= 0 && encodedBci != FrameInfoDecoder.ENCODED_BCI_NO_CALLER;
return (encodedBci & ENCODED_BCI_DURING_CALL_MASK) != 0;
}

protected static boolean decodeRethrowException(long encodedBci) {
assert encodedBci > 0 && encodedBci != FrameInfoDecoder.ENCODED_BCI_NO_CALLER;
assert encodedBci >= 0 && encodedBci != FrameInfoDecoder.ENCODED_BCI_NO_CALLER;
return (encodedBci & ENCODED_BCI_RETHROW_EXCEPTION_MASK) != 0;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,17 +123,15 @@ protected void recordFrame(ResolvedJavaMethod method, Infopoint infopoint, boole
/**
* Fills the FrameInfoQueryResult.source* fields.
*/
protected abstract void fillSourceFields(BytecodeFrame bytecodeFrame, FrameInfoQueryResult resultFrameInfo);
protected abstract void fillSourceFields(ResolvedJavaMethod method, FrameInfoQueryResult resultFrameInfo);
}

public abstract static class SourceFieldsFromMethod extends Customization {
private final HostedStringDeduplication stringTable = HostedStringDeduplication.singleton();

@Override
protected void fillSourceFields(BytecodeFrame bytecodeFrame, FrameInfoQueryResult resultFrameInfo) {
final ResolvedJavaMethod method = bytecodeFrame.getMethod();

final StackTraceElement source = method.asStackTraceElement(bytecodeFrame.getBCI());
protected void fillSourceFields(ResolvedJavaMethod method, FrameInfoQueryResult resultFrameInfo) {
final StackTraceElement source = method.asStackTraceElement(resultFrameInfo.getBci());
resultFrameInfo.sourceClass = getDeclaringJavaClass(method);
/*
* There is no need to have method names as interned strings. But at least sometimes the
Expand All @@ -150,8 +148,8 @@ protected void fillSourceFields(BytecodeFrame bytecodeFrame, FrameInfoQueryResul

public abstract static class SourceFieldsFromImage extends Customization {
@Override
protected void fillSourceFields(BytecodeFrame bytecodeFrame, FrameInfoQueryResult resultFrameInfo) {
final int deoptOffsetInImage = ((SharedMethod) bytecodeFrame.getMethod()).getDeoptOffsetInImage();
protected void fillSourceFields(ResolvedJavaMethod method, FrameInfoQueryResult resultFrameInfo) {
final int deoptOffsetInImage = ((SharedMethod) method).getDeoptOffsetInImage();
if (deoptOffsetInImage != 0) {
CodeInfoQueryResult targetCodeInfo = CodeInfoTable.lookupDeoptimizationEntrypoint(deoptOffsetInImage, resultFrameInfo.encodedBci);
if (targetCodeInfo != null) {
Expand Down Expand Up @@ -180,14 +178,16 @@ static class FrameData {
protected final ValueInfo[][] virtualObjects;
protected final FrameInfoQueryResult frame;
protected long encodedFrameInfoIndex;
protected boolean isDefaultFrameData;
protected int frameSliceIndex = UNCOMPRESSED_FRAME_SLICE_INDEX;

FrameData(DebugInfo debugInfo, int totalFrameSize, ValueInfo[][] virtualObjects, FrameInfoQueryResult frame) {
assert debugInfo != null;
FrameData(DebugInfo debugInfo, int totalFrameSize, ValueInfo[][] virtualObjects, boolean isDefaultFrameData) {
assert (virtualObjects != null && debugInfo != null) || (virtualObjects == null && debugInfo == null);
this.debugInfo = debugInfo;
this.totalFrameSize = totalFrameSize;
this.virtualObjects = virtualObjects;
this.frame = frame;
this.isDefaultFrameData = isDefaultFrameData;
this.frame = new FrameInfoQueryResult();
}
}

Expand Down Expand Up @@ -465,14 +465,14 @@ protected FrameData addDebugInfo(ResolvedJavaMethod method, CompilationResult co
boolean includeLocalValues = customization.includeLocalValues(method, infopoint, isDeoptEntry);

DebugInfo debugInfo = infopoint.debugInfo;
FrameData data = new FrameData(debugInfo, totalFrameSize, new ValueInfo[countVirtualObjects(debugInfo)][], new FrameInfoQueryResult());
FrameData data = new FrameData(debugInfo, totalFrameSize, new ValueInfo[countVirtualObjects(debugInfo)][], false);
initializeFrameInfo(data.frame, data, debugInfo.frame(), isDeoptEntry, includeLocalValues);

List<CompressedFrameData> frameSlice = includeLocalValues ? null : new ArrayList<>();
BytecodeFrame bytecodeFrame = data.debugInfo.frame();
for (FrameInfoQueryResult resultFrame = data.frame; resultFrame != null; resultFrame = resultFrame.caller) {
assert bytecodeFrame != null;
customization.fillSourceFields(bytecodeFrame, resultFrame);
customization.fillSourceFields(bytecodeFrame.getMethod(), resultFrame);

// save source class and method name
final Class<?> sourceClass = resultFrame.sourceClass;
Expand All @@ -498,6 +498,26 @@ protected FrameData addDebugInfo(ResolvedJavaMethod method, CompilationResult co
return data;
}

protected FrameData addDefaultDebugInfo(ResolvedJavaMethod method, int totalFrameSize) {
FrameData data = new FrameData(null, totalFrameSize, null, true);
data.frame.encodedBci = FrameInfoEncoder.encodeBci(0, false, false);
customization.fillSourceFields(method, data.frame);
// invalidate source line number
data.frame.sourceLineNumber = -1;
// save source class and method name
Class<?> sourceClass = data.frame.sourceClass;
String sourceMethodName = data.frame.sourceMethodName;
encoders.sourceClasses.addObject(sourceClass);
encoders.sourceMethodNames.addObject(sourceMethodName);

// save encoding metadata
CompressedFrameData frame = new CompressedFrameData(sourceClass, sourceMethodName, data.frame.sourceLineNumber, data.frame.encodedBci, data.frame.methodId, true);
frameMetadata.addFrameSlice(data, List.of(frame));

allDebugInfos.add(data);
return data;
}

private static int countVirtualObjects(DebugInfo debugInfo) {
/*
* We want to know the highest virtual object id in use in this DebugInfo. For that, we have
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1212,7 +1212,7 @@ protected boolean visitFrame(Pointer sp, CodePointer ip, CodeInfo codeInfo, Deop
*/
markStackValuesAsGCRoots(sp, ip, codeInfo);

frameInfoCursor.initialize(codeInfo, ip);
frameInfoCursor.initialize(codeInfo, ip, true);
while (frameInfoCursor.advance()) {
FrameInfoQueryResult frame = frameInfoCursor.get();
visitFrame(frame);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ private int visitCodePointer(CodePointer ip, int oldFramesDecoded, int maxFrames
@Uninterruptible(reason = "Wraps the now safe call to the possibly interruptible visitor.", callerMustBe = true, calleeMustBe = false)
private int visitFrame(CodePointer ip, CodeInfo tetheredCodeInfo, int oldFramesDecoded, int maxFramesProcessed, int maxFramesDecode) {
int framesDecoded = oldFramesDecoded;
frameInfoCursor.initialize(tetheredCodeInfo, ip);
frameInfoCursor.initialize(tetheredCodeInfo, ip, true);
while (frameInfoCursor.advance()) {
FrameInfoQueryResult frameInfo = frameInfoCursor.get();
if (!StackTraceUtils.shouldShowFrame(frameInfo, false, true, false)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ private static int visitFrame(JfrNativeEventWriterData data, long address) {
@Uninterruptible(reason = "Prevent JFR recording and epoch change.")
private static int visitFrame(JfrNativeEventWriterData data, CodeInfo codeInfo, CodePointer ip) {
int numStackTraceElements = 0;
FRAME_INFO_CURSOR.initialize(codeInfo, ip);
FRAME_INFO_CURSOR.initialize(codeInfo, ip, false);
while (FRAME_INFO_CURSOR.advance()) {
if (data.isNonNull()) {
FrameInfoQueryResult frame = FRAME_INFO_CURSOR.get();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ protected void logFrame(Log log, Pointer sp, CodePointer ip, CodeInfo codeInfo,
}

boolean isFirst = true;
frameInfoCursor.initialize(codeInfo, ip);
frameInfoCursor.initialize(codeInfo, ip, false);
while (frameInfoCursor.advance()) {
if (printedFrames >= MAX_STACK_FRAMES_PER_THREAD_TO_PRINT) {
log.string("... (truncated)").newline();
Expand Down

0 comments on commit e738302

Please sign in to comment.