diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/interpreter/OopMapCacheEntry.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/interpreter/OopMapCacheEntry.java index 75d9589a74f..fda97b4f337 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/interpreter/OopMapCacheEntry.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/interpreter/OopMapCacheEntry.java @@ -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); } diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/ConstantPool.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/ConstantPool.java index 3a4ea5546a1..9d19a1d7df1 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/ConstantPool.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/ConstantPool.java @@ -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: diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/InterpretedVFrame.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/InterpretedVFrame.java index 75750548ef5..ae9c048f9d7 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/InterpretedVFrame.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/InterpretedVFrame.java @@ -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 { diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/HeapHprofBinWriter.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/HeapHprofBinWriter.java index 72861d9c30d..ce048bf2d86 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/HeapHprofBinWriter.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/HeapHprofBinWriter.java @@ -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); diff --git a/test/hotspot/jtreg/serviceability/sa/ClhsdbDumpheap.java b/test/hotspot/jtreg/serviceability/sa/ClhsdbDumpheap.java index ee9872867ac..722c641f5b9 100644 --- a/test/hotspot/jtreg/serviceability/sa/ClhsdbDumpheap.java +++ b/test/hotspot/jtreg/serviceability/sa/ClhsdbDumpheap.java @@ -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 {