8374577: Heap dump from core does not contain HPROF_GC_ROOT_JAVA_FRAME

Reviewed-by: cjplummer, amenkov, kevinw, aturbanov
This commit is contained in:
Yasumasa Suenaga 2026-02-12 01:58:22 +00:00
parent 370929f826
commit 24f67917c2
5 changed files with 75 additions and 31 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2001, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2001, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -96,7 +96,7 @@ public class OopMapCacheEntry {
Method method() { return method; }
int bci() { return bci; }
int numberOfEntries() { return maskSize; }
public int numberOfEntries() { return maskSize; }
boolean entryAt(int offset) {
return mask.at(offset);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -253,13 +253,16 @@ public class ConstantPool extends Metadata implements ClassConstants {
return res;
}
private int invokedynamicBootstrapRefIndexAt(int indyIndex) {
return getCache().getIndyEntryAt(indyIndex).getConstantPoolIndex();
}
// Translate index, which could be CPCache index or Indy index, to a constant pool index
public int to_cp_index(int index, int code) {
Assert.that(getCache() != null, "'index' is a rewritten index so this class must have been rewritten");
switch(code) {
case Bytecodes._invokedynamic:
int poolIndex = getCache().getIndyEntryAt(index).getConstantPoolIndex();
return invokeDynamicNameAndTypeRefIndexAt(poolIndex);
return invokedynamicBootstrapRefIndexAt(index);
case Bytecodes._getfield:
case Bytecodes._getstatic:
case Bytecodes._putfield:

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -37,21 +37,18 @@ public class InterpretedVFrame extends JavaVFrame {
}
public StackValueCollection getLocals() {
Method m = getMethod();
int length = (int) m.getMaxLocals();
if (m.isNative()) {
// If the method is native, getMaxLocals is not telling the truth.
// maxlocals then equals the size of parameters
length = (int) m.getSizeOfParameters();
}
StackValueCollection result = new StackValueCollection(length);
// Get oopmap describing oops and int for current bci
OopMapCacheEntry oopMask = getMethod().getMaskFor(getBCI());
// If the method is native, method()->max_locals() is not telling the truth.
// For our purposes, max locals instead equals the size of parameters.
Method method = getMethod();
int maxLocals = method.isNative() ? (int)method.getSizeOfParameters() : (int)method.getMaxLocals();
int length = maxLocals;
StackValueCollection result = new StackValueCollection(length);
// handle locals
for(int i = 0; i < length; i++) {
// Find stack location
@ -74,26 +71,27 @@ public class InterpretedVFrame extends JavaVFrame {
}
public StackValueCollection getExpressions() {
int length = getFrame().getInterpreterFrameExpressionStackSize();
if (getMethod().isNative()) {
// If the method is native, there is no expression stack
length = 0;
}
int nofLocals = (int) getMethod().getMaxLocals();
StackValueCollection result = new StackValueCollection(length);
// Get oopmap describing oops and int for current bci
OopMapCacheEntry oopMask = getMethod().getMaskFor(getBCI());
int maskLen = oopMask.numberOfEntries();
// If the method is native, method()->max_locals() is not telling the truth.
// For our purposes, max locals instead equals the size of parameters.
Method method = getMethod();
int maxLocals = method.isNative() ? (int)method.getSizeOfParameters() : (int)method.getMaxLocals();
int length = maskLen - maxLocals;
StackValueCollection result = new StackValueCollection(length);
for(int i = 0; i < length; i++) {
// Find stack location
Address addr = addressOfExpressionStackAt(i);
// Depending on oop/int put it in the right package
StackValue sv;
if (oopMask.isOop(i + nofLocals)) {
if (oopMask.isOop(i + maxLocals)) {
// oop value
sv = new StackValue(addr.getOopHandleAt(0), 0);
} else {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2004, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2004, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -888,6 +888,16 @@ public class HeapHprofBinWriter extends AbstractHeapGraphWriter {
out.writeInt(index);
out.writeInt(DUMMY_STACK_TRACE_ID);
writeLocalJNIHandles(jt, index);
int depth = 0;
var jvf = jt.getLastJavaVFrameDbg();
while (jvf != null) {
writeStackRefs(index, depth, jvf.getLocals());
writeStackRefs(index, depth, jvf.getExpressions());
depth++;
jvf = jvf.javaSender();
}
}
protected void writeLocalJNIHandles(JavaThread jt, int index) throws IOException {
@ -926,6 +936,23 @@ public class HeapHprofBinWriter extends AbstractHeapGraphWriter {
}
}
protected void writeStackRefs(int threadIndex, int frameIndex, StackValueCollection values) throws IOException {
for (int index = 0; index < values.size(); index++) {
if (values.get(index).getType() == BasicType.getTObject()) {
OopHandle oopHandle = values.oopHandleAt(index);
Oop oop = objectHeap.newOop(oopHandle);
if (oop != null) {
int size = BYTE_SIZE + OBJ_ID_SIZE + INT_SIZE * 2;
writeHeapRecordPrologue(size);
out.writeByte((byte) HPROF_GC_ROOT_JAVA_FRAME);
writeObjectID(oop);
out.writeInt(threadIndex);
out.writeInt(frameIndex);
}
}
}
}
protected void writeGlobalJNIHandle(Address handleAddr) throws IOException {
OopHandle oopHandle = handleAddr.getOopHandleAt(0);
Oop oop = objectHeap.newOop(oopHandle);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -27,11 +27,14 @@ import java.util.Map;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import static jdk.test.lib.Asserts.assertTrue;
import static jdk.test.lib.Asserts.assertFalse;
import static jdk.test.lib.Asserts.fail;
import jdk.test.lib.hprof.HprofParser;
import jdk.test.lib.apps.LingeredApp;
import jdk.test.lib.hprof.model.Root;
import jdk.test.lib.hprof.parser.HprofReader;
import jtreg.SkippedException;
@ -66,9 +69,22 @@ public class ClhsdbDumpheap {
}
}
private static void verifyLocalRefs(String file) throws IOException {
try (var snapshot = HprofReader.readFile(file, false, 0)) {
for (var root = snapshot.getRoots(); root.hasMoreElements();) {
if (root.nextElement().getType() == Root.JAVA_LOCAL) {
// expected
return;
}
}
}
fail("HPROF_GC_ROOT_JAVA_FRAME not found");
}
private static void verifyDumpFile(File dump) throws Exception {
assertTrue(dump.exists() && dump.isFile(), "Could not create dump file " + dump.getAbsolutePath());
printStackTraces(dump.getAbsolutePath());
verifyLocalRefs(dump.getAbsolutePath());
}
private static class SubTest {