diff --git a/src/hotspot/cpu/x86/downcallLinker_x86_64.cpp b/src/hotspot/cpu/x86/downcallLinker_x86_64.cpp index 41039e0cfd835..e4b25ce00d64c 100644 --- a/src/hotspot/cpu/x86/downcallLinker_x86_64.cpp +++ b/src/hotspot/cpu/x86/downcallLinker_x86_64.cpp @@ -34,8 +34,8 @@ #define __ _masm-> -static const int native_invoker_code_base_size = 512; -static const int native_invoker_size_per_arg = 8; +static const int native_invoker_code_base_size = 256; +static const int native_invoker_size_per_arg = 16; RuntimeStub* DowncallLinker::make_downcall_stub(BasicType* signature, int num_args, diff --git a/src/hotspot/share/code/codeBlob.hpp b/src/hotspot/share/code/codeBlob.hpp index 26ae5f7df5941..8ecd9e21537df 100644 --- a/src/hotspot/share/code/codeBlob.hpp +++ b/src/hotspot/share/code/codeBlob.hpp @@ -595,6 +595,7 @@ class UpcallLinker; // A (Panama) upcall stub. Not used by JNI. class UpcallStub: public RuntimeBlob { + friend class VMStructs; friend class UpcallLinker; private: jobject _receiver; diff --git a/src/hotspot/share/runtime/vmStructs.cpp b/src/hotspot/share/runtime/vmStructs.cpp index 40c15e10c5a91..483b96fccf382 100644 --- a/src/hotspot/share/runtime/vmStructs.cpp +++ b/src/hotspot/share/runtime/vmStructs.cpp @@ -563,6 +563,12 @@ \ nonstatic_field(DeoptimizationBlob, _unpack_offset, int) \ \ + /*****************************************************/ \ + /* UpcallStubs (NOTE: incomplete, but only a little) */ \ + /*****************************************************/ \ + \ + nonstatic_field(UpcallStub, _frame_data_offset, ByteSize) \ + \ /**************************************************/ \ /* NMethods (NOTE: incomplete, but only a little) */ \ /**************************************************/ \ @@ -1012,7 +1018,9 @@ nonstatic_field(AccessFlags, _flags, jint) \ nonstatic_field(elapsedTimer, _counter, jlong) \ nonstatic_field(elapsedTimer, _active, bool) \ - nonstatic_field(InvocationCounter, _counter, unsigned int) + nonstatic_field(InvocationCounter, _counter, unsigned int) \ + \ + nonstatic_field(UpcallStub::FrameData, jfa, JavaFrameAnchor) //-------------------------------------------------------------------------------- // VM_TYPES @@ -1306,6 +1314,7 @@ declare_type(nmethod, CodeBlob) \ declare_type(RuntimeStub, RuntimeBlob) \ declare_type(SingletonBlob, RuntimeBlob) \ + declare_type(UpcallStub, RuntimeBlob) \ declare_type(SafepointBlob, SingletonBlob) \ declare_type(DeoptimizationBlob, SingletonBlob) \ declare_c2_type(ExceptionBlob, SingletonBlob) \ @@ -1900,6 +1909,7 @@ declare_integer_type(BasicType) /* FIXME: wrong type (not integer) */ \ \ declare_integer_type(CompLevel) \ + declare_integer_type(ByteSize) \ JVMTI_ONLY(declare_toplevel_type(BreakpointInfo)) \ JVMTI_ONLY(declare_toplevel_type(BreakpointInfo*)) \ declare_toplevel_type(CodeBlob*) \ @@ -1948,6 +1958,7 @@ declare_type(FileMapInfo, CHeapObj) \ declare_toplevel_type(FileMapHeader) \ declare_toplevel_type(CDSFileMapRegion) \ + declare_toplevel_type(UpcallStub::FrameData) \ \ /************/ \ /* GC types */ \ diff --git a/src/java.base/share/classes/java/lang/System.java b/src/java.base/share/classes/java/lang/System.java index 368db1928f113..2521eb0149398 100644 --- a/src/java.base/share/classes/java/lang/System.java +++ b/src/java.base/share/classes/java/lang/System.java @@ -2642,6 +2642,10 @@ public Object stringConcat1(String[] constants) { return new StringConcatHelper.Concat1(constants); } + public byte stringInitCoder() { + return String.COMPACT_STRINGS ? String.LATIN1 : String.UTF16; + } + public int getCharsLatin1(long i, int index, byte[] buf) { return StringLatin1.getChars(i, index, buf); } diff --git a/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java b/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java index 8486ede3a272c..6c8d72cec4e85 100644 --- a/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java +++ b/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java @@ -1196,9 +1196,14 @@ private static MethodTypeDesc prependArgs(MethodType concatArgs) { /** * Construct the MethodType of the coder method. The first parameter is the initialized coder. - * Only parameter types which can be UTF16 are added. Returns null if no such parameter exists. + * Only parameter types which can be UTF16 are added. + * Returns null if no such parameter exists or CompactStrings is off. */ private static MethodTypeDesc coderArgsIfMaybeUTF16(MethodType concatArgs) { + if (JLA.stringInitCoder() != 0) { + return null; + } + int parameterCount = concatArgs.parameterCount(); int maybeUTF16Count = 0; diff --git a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java index 98238af383174..dbe116599e099 100644 --- a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java +++ b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java @@ -459,6 +459,11 @@ public interface JavaLangAccess { Object stringConcat1(String[] constants); + /** + * Get the string initial coder, When COMPACT_STRINGS is on, it returns 0, and when it is off, it returns 1. + */ + byte stringInitCoder(); + /** * Join strings */ diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractInstruction.java b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractInstruction.java index 10c9cf0a5e15b..48c0cb7685702 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractInstruction.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractInstruction.java @@ -94,7 +94,6 @@ public abstract sealed class AbstractInstruction FMT_Discontinued = "Discontinued[OP=%s]"; final Opcode op; - final int size; @Override public Opcode opcode() { @@ -103,12 +102,12 @@ public Opcode opcode() { @Override public int sizeInBytes() { - return size; + // Note: only lookupswitch and tableswitch have variable sizes + return op.sizeIfFixed(); } - public AbstractInstruction(Opcode op, int size) { + AbstractInstruction(Opcode op) { this.op = op; - this.size = size; } @Override @@ -118,8 +117,8 @@ public abstract static sealed class BoundInstruction extends AbstractInstruction final CodeImpl code; final int pos; - protected BoundInstruction(Opcode op, int size, CodeImpl code, int pos) { - super(op, size); + protected BoundInstruction(Opcode op, CodeImpl code, int pos) { + super(op); this.code = code; this.pos = pos; } @@ -131,7 +130,7 @@ protected Label offsetToLabel(int offset) { @Override public void writeTo(DirectCodeBuilder writer) { // Override this if the instruction has any CP references or labels! - code.classReader.copyBytesTo(writer.bytecodesBufWriter, pos, size); + code.classReader.copyBytesTo(writer.bytecodesBufWriter, pos, op.sizeIfFixed()); } } @@ -139,7 +138,7 @@ public static final class BoundLoadInstruction extends BoundInstruction implements LoadInstruction { public BoundLoadInstruction(Opcode op, CodeImpl code, int pos) { - super(op, op.sizeIfFixed(), code, pos); + super(op, code, pos); } @@ -155,7 +154,7 @@ public String toString() { @Override public int slot() { - return switch (size) { + return switch (sizeInBytes()) { case 2 -> code.classReader.readU1(pos + 1); case 4 -> code.classReader.readU2(pos + 2); default -> throw new IllegalArgumentException("Unexpected op size: " + op.sizeIfFixed() + " -- " + op); @@ -168,7 +167,7 @@ public static final class BoundStoreInstruction extends BoundInstruction implements StoreInstruction { public BoundStoreInstruction(Opcode op, CodeImpl code, int pos) { - super(op, op.sizeIfFixed(), code, pos); + super(op, code, pos); } @Override @@ -183,10 +182,10 @@ public String toString() { @Override public int slot() { - return switch (size) { + return switch (sizeInBytes()) { case 2 -> code.classReader.readU1(pos + 1); case 4 -> code.classReader.readU2(pos + 2); - default -> throw new IllegalArgumentException("Unexpected op size: " + size + " -- " + op); + default -> throw new IllegalArgumentException("Unexpected op size: " + sizeInBytes() + " -- " + op); }; } @@ -196,17 +195,17 @@ public static final class BoundIncrementInstruction extends BoundInstruction implements IncrementInstruction { public BoundIncrementInstruction(Opcode op, CodeImpl code, int pos) { - super(op, op.sizeIfFixed(), code, pos); + super(op, code, pos); } @Override public int slot() { - return size == 6 ? code.classReader.readU2(pos + 2) : code.classReader.readU1(pos + 1); + return sizeInBytes() == 6 ? code.classReader.readU2(pos + 2) : code.classReader.readU1(pos + 1); } @Override public int constant() { - return size == 6 ? code.classReader.readS2(pos + 4) : (byte) code.classReader.readS1(pos + 2); + return sizeInBytes() == 6 ? code.classReader.readS2(pos + 4) : code.classReader.readS1(pos + 2); } @Override @@ -220,7 +219,7 @@ public static final class BoundBranchInstruction extends BoundInstruction implements BranchInstruction { public BoundBranchInstruction(Opcode op, CodeImpl code, int pos) { - super(op, op.sizeIfFixed(), code, pos); + super(op, code, pos); } @Override @@ -229,8 +228,8 @@ public Label target() { } public int branchByteOffset() { - return size == 3 - ? (int) (short) code.classReader.readU2(pos + 1) + return sizeInBytes() == 3 + ? code.classReader.readS2(pos + 1) : code.classReader.readInt(pos + 1); } @@ -256,33 +255,31 @@ public static final class BoundLookupSwitchInstruction // will always need size, cache everything to there private final int afterPad; private final int npairs; + private final int size; BoundLookupSwitchInstruction(Opcode op, CodeImpl code, int pos) { - super(op, size(code, code.codeStart, pos), code, pos); - - this.afterPad = pos + 1 + ((4 - ((pos + 1 - code.codeStart) & 3)) & 3); + super(op, code, pos); + this.afterPad = code.codeStart + RawBytecodeHelper.align(pos + 1 - code.codeStart); this.npairs = code.classReader.readInt(afterPad + 4); if (npairs < 0 || npairs > code.codeLength >> 3) { throw new IllegalArgumentException("Invalid lookupswitch npairs value: " + npairs); } - } - - static int size(CodeImpl code, int codeStart, int pos) { - int afterPad = pos + 1 + ((4 - ((pos + 1 - codeStart) & 3)) & 3); - int pad = afterPad - (pos + 1); - int npairs = code.classReader.readInt(afterPad + 4); - return 1 + pad + 8 + npairs * 8; + this.size = afterPad + 8 + npairs * 8 - pos; } private int defaultOffset() { return code.classReader.readInt(afterPad); } + @Override + public int sizeInBytes() { + return size; + } + @Override public List cases() { var cases = new SwitchCase[npairs]; - for (int i = 0; i < npairs; ++i) { - int z = afterPad + 8 + 8 * i; + for (int i = 0, z = afterPad + 8; i < npairs; ++i, z += 8) { cases[i] = SwitchCase.of(code.classReader.readInt(z), offsetToLabel(code.classReader.readInt(z + 4))); } return List.of(cases); @@ -308,25 +305,26 @@ public String toString() { public static final class BoundTableSwitchInstruction extends BoundInstruction implements TableSwitchInstruction { - BoundTableSwitchInstruction(Opcode op, CodeImpl code, int pos) { - super(op, size(code, code.codeStart, pos), code, pos); - } + private final int afterPad; + private final int low; + private final int high; + private final int size; - static int size(CodeImpl code, int codeStart, int pos) { - int ap = pos + 1 + ((4 - ((pos + 1 - codeStart) & 3)) & 3); - int pad = ap - (pos + 1); - int low = code.classReader.readInt(ap + 4); - int high = code.classReader.readInt(ap + 8); + BoundTableSwitchInstruction(Opcode op, CodeImpl code, int pos) { + super(op, code, pos); + afterPad = code.codeStart + RawBytecodeHelper.align(pos + 1 - code.codeStart); + low = code.classReader.readInt(afterPad + 4); + high = code.classReader.readInt(afterPad + 8); if (high < low || (long)high - low > code.codeLength >> 2) { throw new IllegalArgumentException("Invalid tableswitch values low: " + low + " high: " + high); } int cnt = high - low + 1; - return 1 + pad + 12 + cnt * 4; + size = afterPad + 12 + cnt * 4 - pos; } - private int afterPadding() { - int p = pos; - return p + 1 + ((4 - ((p + 1 - code.codeStart) & 3)) & 3); + @Override + public int sizeInBytes() { + return size; } @Override @@ -336,12 +334,12 @@ public Label defaultTarget() { @Override public int lowValue() { - return code.classReader.readInt(afterPadding() + 4); + return low; } @Override public int highValue() { - return code.classReader.readInt(afterPadding() + 8); + return high; } @Override @@ -350,19 +348,17 @@ public List cases() { int high = highValue(); int defOff = defaultOffset(); var cases = new ArrayList(high - low + 1); - int z = afterPadding() + 12; - for (int i = lowValue(); i <= high; ++i) { + for (int i = low, z = afterPad + 12; i <= high; ++i, z += 4) { int off = code.classReader.readInt(z); if (defOff != off) { cases.add(SwitchCase.of(i, offsetToLabel(off))); } - z += 4; } return Collections.unmodifiableList(cases); } private int defaultOffset() { - return code.classReader.readInt(afterPadding()); + return code.classReader.readInt(afterPad); } @Override @@ -383,7 +379,7 @@ public static final class BoundFieldInstruction private FieldRefEntry fieldEntry; public BoundFieldInstruction(Opcode op, CodeImpl code, int pos) { - super(op, op.sizeIfFixed(), code, pos); + super(op, code, pos); } @Override @@ -413,7 +409,7 @@ public static final class BoundInvokeInstruction MemberRefEntry methodEntry; public BoundInvokeInstruction(Opcode op, CodeImpl code, int pos) { - super(op, op.sizeIfFixed(), code, pos); + super(op, code, pos); } @Override @@ -453,7 +449,7 @@ public static final class BoundInvokeInterfaceInstruction InterfaceMethodRefEntry methodEntry; public BoundInvokeInterfaceInstruction(Opcode op, CodeImpl code, int pos) { - super(op, op.sizeIfFixed(), code, pos); + super(op, code, pos); } @Override @@ -493,7 +489,7 @@ public static final class BoundInvokeDynamicInstruction InvokeDynamicEntry indyEntry; BoundInvokeDynamicInstruction(Opcode op, CodeImpl code, int pos) { - super(op, op.sizeIfFixed(), code, pos); + super(op, code, pos); } @Override @@ -523,7 +519,7 @@ public static final class BoundNewObjectInstruction ClassEntry classEntry; BoundNewObjectInstruction(CodeImpl code, int pos) { - super(Opcode.NEW, Opcode.NEW.sizeIfFixed(), code, pos); + super(Opcode.NEW, code, pos); } @Override @@ -552,7 +548,7 @@ public static final class BoundNewPrimitiveArrayInstruction extends BoundInstruction implements NewPrimitiveArrayInstruction { public BoundNewPrimitiveArrayInstruction(Opcode op, CodeImpl code, int pos) { - super(op, op.sizeIfFixed(), code, pos); + super(op, code, pos); } @Override @@ -571,7 +567,7 @@ public static final class BoundNewReferenceArrayInstruction extends BoundInstruction implements NewReferenceArrayInstruction { public BoundNewReferenceArrayInstruction(Opcode op, CodeImpl code, int pos) { - super(op, op.sizeIfFixed(), code, pos); + super(op, code, pos); } @Override @@ -597,7 +593,7 @@ public static final class BoundNewMultidimensionalArrayInstruction extends BoundInstruction implements NewMultiArrayInstruction { public BoundNewMultidimensionalArrayInstruction(Opcode op, CodeImpl code, int pos) { - super(op, op.sizeIfFixed(), code, pos); + super(op, code, pos); } @Override @@ -630,7 +626,7 @@ public static final class BoundTypeCheckInstruction ClassEntry typeEntry; public BoundTypeCheckInstruction(Opcode op, CodeImpl code, int pos) { - super(op, op.sizeIfFixed(), code, pos); + super(op, code, pos); } @Override @@ -659,7 +655,7 @@ public static final class BoundArgumentConstantInstruction extends BoundInstruction implements ConstantInstruction.ArgumentConstantInstruction { public BoundArgumentConstantInstruction(Opcode op, CodeImpl code, int pos) { - super(op, op.sizeIfFixed(), code, pos); + super(op, code, pos); } @Override @@ -668,7 +664,7 @@ public Integer constantValue() { } public int constantInt() { - return size == 3 ? code.classReader.readS2(pos + 1) : code.classReader.readS1(pos + 1); + return sizeInBytes() == 3 ? code.classReader.readS2(pos + 1) : code.classReader.readS1(pos + 1); } @Override @@ -682,7 +678,7 @@ public static final class BoundLoadConstantInstruction extends BoundInstruction implements ConstantInstruction.LoadConstantInstruction { public BoundLoadConstantInstruction(Opcode op, CodeImpl code, int pos) { - super(op, op.sizeIfFixed(), code, pos); + super(op, code, pos); } @Override @@ -717,7 +713,7 @@ public static final class BoundJsrInstruction extends BoundInstruction implements DiscontinuedInstruction.JsrInstruction { public BoundJsrInstruction(Opcode op, CodeImpl code, int pos) { - super(op, op.sizeIfFixed(), code, pos); + super(op, code, pos); } @Override @@ -726,7 +722,7 @@ public Label target() { } public int branchByteOffset() { - return size == 3 + return sizeInBytes() == 3 ? code.classReader.readS2(pos + 1) : code.classReader.readInt(pos + 1); } @@ -747,7 +743,7 @@ public static final class BoundRetInstruction extends BoundInstruction implements DiscontinuedInstruction.RetInstruction { public BoundRetInstruction(Opcode op, CodeImpl code, int pos) { - super(op, op.sizeIfFixed(), code, pos); + super(op, code, pos); } @Override @@ -757,7 +753,7 @@ public String toString() { @Override public int slot() { - return switch (size) { + return switch (sizeInBytes()) { case 2 -> code.classReader.readU1(pos + 1); case 4 -> code.classReader.readU2(pos + 2); default -> throw new IllegalArgumentException("Unexpected op size: " + op.sizeIfFixed() + " -- " + op); @@ -769,7 +765,7 @@ public int slot() { public abstract static sealed class UnboundInstruction extends AbstractInstruction { UnboundInstruction(Opcode op) { - super(op, op.sizeIfFixed()); + super(op); } @Override diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/code/CodeBlob.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/code/CodeBlob.java index 67e799b271226..90e4b5b98b442 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/code/CodeBlob.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/code/CodeBlob.java @@ -143,6 +143,8 @@ public ImmutableOopMapSet getOopMaps() { public boolean isRuntimeStub() { return false; } + public boolean isUpcallStub() { return false; } + public boolean isDeoptimizationStub() { return false; } public boolean isUncommonTrapStub() { return false; } diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/code/CodeCache.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/code/CodeCache.java index 448372e2a36f7..e2eac930e02a5 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/code/CodeCache.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/code/CodeCache.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2024, 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 @@ -60,6 +60,7 @@ private static synchronized void initialize(TypeDataBase db) { virtualConstructor.addMapping("AdapterBlob", AdapterBlob.class); virtualConstructor.addMapping("MethodHandlesAdapterBlob", MethodHandlesAdapterBlob.class); virtualConstructor.addMapping("VtableBlob", VtableBlob.class); + virtualConstructor.addMapping("UpcallStub", UpcallStub.class); virtualConstructor.addMapping("SafepointBlob", SafepointBlob.class); virtualConstructor.addMapping("DeoptimizationBlob", DeoptimizationBlob.class); if (VM.getVM().isServerCompiler()) { diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/code/UpcallStub.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/code/UpcallStub.java new file mode 100644 index 0000000000000..4e324ba38e4c6 --- /dev/null +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/code/UpcallStub.java @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2024, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +package sun.jvm.hotspot.code; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.utilities.Observable; +import sun.jvm.hotspot.utilities.Observer; + +public class UpcallStub extends RuntimeBlob { + + private static CIntegerField frameDataOffsetField; + private static AddressField lastJavaFPField; + private static AddressField lastJavaSPField; + private static AddressField lastJavaPCField; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static void initialize(TypeDataBase db) { + Type type = db.lookupType("UpcallStub"); + frameDataOffsetField = type.getCIntegerField("_frame_data_offset"); + + Type anchorType = db.lookupType("JavaFrameAnchor"); + lastJavaSPField = anchorType.getAddressField("_last_Java_sp"); + lastJavaPCField = anchorType.getAddressField("_last_Java_pc"); + + try { + lastJavaFPField = anchorType.getAddressField("_last_Java_fp"); + } catch (Exception e) { + // Some platforms (e.g. PPC64) does not have this field. + lastJavaFPField = null; + } + } + + public UpcallStub(Address addr) { + super(addr); + } + + protected Address getJavaFrameAnchor(Frame frame) { + var frameDataOffset = frameDataOffsetField.getValue(addr); + var frameDataAddr = frame.getUnextendedSP().addOffsetTo(frameDataOffset); + var frameData = VMObjectFactory.newObject(FrameData.class, frameDataAddr); + return frameData.getJavaFrameAnchor(); + } + + public Address getLastJavaSP(Frame frame) { + return lastJavaSPField.getValue(getJavaFrameAnchor(frame)); + } + + public Address getLastJavaFP(Frame frame) { + return lastJavaFPField == null ? null : lastJavaFPField.getValue(getJavaFrameAnchor(frame)); + } + + public Address getLastJavaPC(Frame frame) { + return lastJavaPCField.getValue(getJavaFrameAnchor(frame)); + } + + public boolean isUpcallStub() { + return true; + } + + public static class FrameData extends VMObject { + + private static AddressField jfaField; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static void initialize(TypeDataBase db) { + Type type = db.lookupType("UpcallStub::FrameData"); + jfaField = type.getAddressField("jfa"); + } + + public FrameData(Address addr) { + super(addr); + } + + public Address getJavaFrameAnchor() { + return addr.addOffsetTo(jfaField.getOffset()); + } + + } + +} diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/aarch64/AARCH64Frame.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/aarch64/AARCH64Frame.java index 5d8712002f835..dca5f2efa3bb6 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/aarch64/AARCH64Frame.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/aarch64/AARCH64Frame.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2015, 2019, Red Hat Inc. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -292,7 +292,7 @@ public Frame sender(RegisterMap regMap, CodeBlob cb) { } if (cb != null) { - return senderForCompiledFrame(map, cb); + return cb.isUpcallStub() ? senderForUpcallStub(map, (UpcallStub)cb) : senderForCompiledFrame(map, cb); } // Must be native-compiled frame, i.e. the marshaling code for native @@ -327,6 +327,34 @@ private Frame senderForEntryFrame(AARCH64RegisterMap map) { return fr; } + private Frame senderForUpcallStub(AARCH64RegisterMap map, UpcallStub stub) { + if (DEBUG) { + System.out.println("senderForUpcallStub"); + } + if (Assert.ASSERTS_ENABLED) { + Assert.that(map != null, "map must be set"); + } + + var lastJavaFP = stub.getLastJavaFP(this); + var lastJavaSP = stub.getLastJavaSP(this); + var lastJavaPC = stub.getLastJavaPC(this); + + if (Assert.ASSERTS_ENABLED) { + Assert.that(lastJavaSP.greaterThan(getSP()), "must be above this frame on stack"); + } + AARCH64Frame fr; + if (lastJavaPC != null) { + fr = new AARCH64Frame(lastJavaSP, lastJavaFP, lastJavaPC); + } else { + fr = new AARCH64Frame(lastJavaSP, lastJavaFP); + } + map.clear(); + if (Assert.ASSERTS_ENABLED) { + Assert.that(map.getIncludeArgumentOops(), "should be set by clear"); + } + return fr; + } + //------------------------------------------------------------------------------ // frame::adjust_unextended_sp private void adjustUnextendedSP() { diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ppc64/PPC64Frame.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ppc64/PPC64Frame.java index 710f30f19f239..224206ee6fe59 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ppc64/PPC64Frame.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ppc64/PPC64Frame.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2024, 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 @@ -279,7 +279,7 @@ public Frame sender(RegisterMap regMap, CodeBlob cb) { } if (cb != null) { - return senderForCompiledFrame(map, cb); + return cb.isUpcallStub() ? senderForUpcallStub(map, (UpcallStub)cb) : senderForCompiledFrame(map, cb); } // Must be native-compiled frame, i.e. the marshaling code for native @@ -314,6 +314,34 @@ private Frame senderForEntryFrame(PPC64RegisterMap map) { return fr; } + private Frame senderForUpcallStub(PPC64RegisterMap map, UpcallStub stub) { + if (DEBUG) { + System.out.println("senderForUpcallStub"); + } + if (Assert.ASSERTS_ENABLED) { + Assert.that(map != null, "map must be set"); + } + + var lastJavaFP = stub.getLastJavaFP(this); // This will be null + var lastJavaSP = stub.getLastJavaSP(this); + var lastJavaPC = stub.getLastJavaPC(this); + + if (Assert.ASSERTS_ENABLED) { + Assert.that(lastJavaSP.greaterThan(getSP()), "must be above this frame on stack"); + } + PPC64Frame fr; + if (lastJavaPC != null) { + fr = new PPC64Frame(lastJavaSP, lastJavaFP, lastJavaPC); + } else { + fr = new PPC64Frame(lastJavaSP, lastJavaFP); + } + map.clear(); + if (Assert.ASSERTS_ENABLED) { + Assert.that(map.getIncludeArgumentOops(), "should be set by clear"); + } + return fr; + } + //------------------------------------------------------------------------------ // frame::adjust_unextended_sp private void adjustUnextendedSP() { diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/riscv64/RISCV64Frame.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/riscv64/RISCV64Frame.java index c8e503db93bcb..948a3008016ea 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/riscv64/RISCV64Frame.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/riscv64/RISCV64Frame.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2015, 2019, Red Hat Inc. * Copyright (c) 2021, 2023, Huawei Technologies Co., Ltd. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -284,7 +284,7 @@ public Frame sender(RegisterMap regMap, CodeBlob cb) { } if (cb != null) { - return senderForCompiledFrame(map, cb); + return cb.isUpcallStub() ? senderForUpcallStub(map, (UpcallStub)cb) : senderForCompiledFrame(map, cb); } // Must be native-compiled frame, i.e. the marshaling code for native @@ -319,6 +319,34 @@ private Frame senderForEntryFrame(RISCV64RegisterMap map) { return fr; } + private Frame senderForUpcallStub(RISCV64RegisterMap map, UpcallStub stub) { + if (DEBUG) { + System.out.println("senderForUpcallStub"); + } + if (Assert.ASSERTS_ENABLED) { + Assert.that(map != null, "map must be set"); + } + + var lastJavaFP = stub.getLastJavaFP(this); + var lastJavaSP = stub.getLastJavaSP(this); + var lastJavaPC = stub.getLastJavaPC(this); + + if (Assert.ASSERTS_ENABLED) { + Assert.that(lastJavaSP.greaterThan(getSP()), "must be above this frame on stack"); + } + RISCV64Frame fr; + if (lastJavaPC != null) { + fr = new RISCV64Frame(lastJavaSP, lastJavaFP, lastJavaPC); + } else { + fr = new RISCV64Frame(lastJavaSP, lastJavaFP); + } + map.clear(); + if (Assert.ASSERTS_ENABLED) { + Assert.that(map.getIncludeArgumentOops(), "should be set by clear"); + } + return fr; + } + //------------------------------------------------------------------------------ // frame::adjust_unextended_sp private void adjustUnextendedSP() { diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/x86/X86Frame.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/x86/X86Frame.java index 1ffc9761a3336..169ecea156591 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/x86/X86Frame.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/x86/X86Frame.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2024, 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 @@ -289,7 +289,7 @@ public Frame sender(RegisterMap regMap, CodeBlob cb) { } if (cb != null) { - return senderForCompiledFrame(map, cb); + return cb.isUpcallStub() ? senderForUpcallStub(map, (UpcallStub)cb) : senderForCompiledFrame(map, cb); } // Must be native-compiled frame, i.e. the marshaling code for native @@ -324,6 +324,34 @@ private Frame senderForEntryFrame(X86RegisterMap map) { return fr; } + private Frame senderForUpcallStub(X86RegisterMap map, UpcallStub stub) { + if (DEBUG) { + System.out.println("senderForUpcallStub"); + } + if (Assert.ASSERTS_ENABLED) { + Assert.that(map != null, "map must be set"); + } + + var lastJavaFP = stub.getLastJavaFP(this); + var lastJavaSP = stub.getLastJavaSP(this); + var lastJavaPC = stub.getLastJavaPC(this); + + if (Assert.ASSERTS_ENABLED) { + Assert.that(lastJavaSP.greaterThan(getSP()), "must be above this frame on stack"); + } + X86Frame fr; + if (lastJavaPC != null) { + fr = new X86Frame(lastJavaSP, lastJavaFP, lastJavaPC); + } else { + fr = new X86Frame(lastJavaSP, lastJavaFP); + } + map.clear(); + if (Assert.ASSERTS_ENABLED) { + Assert.that(map.getIncludeArgumentOops(), "should be set by clear"); + } + return fr; + } + //------------------------------------------------------------------------------ // frame::adjust_unextended_sp private void adjustUnextendedSP() { diff --git a/test/hotspot/jtreg/serviceability/sa/LingeredAppWithFFMUpcall.java b/test/hotspot/jtreg/serviceability/sa/LingeredAppWithFFMUpcall.java new file mode 100644 index 0000000000000..d2977c491ab0c --- /dev/null +++ b/test/hotspot/jtreg/serviceability/sa/LingeredAppWithFFMUpcall.java @@ -0,0 +1,75 @@ + +/* + * Copyright (c) 2024, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.foreign.Arena; +import java.lang.foreign.FunctionDescriptor; +import java.lang.foreign.Linker; +import java.util.concurrent.CountDownLatch; + +import jdk.test.lib.apps.LingeredApp; + +public class LingeredAppWithFFMUpcall extends LingeredApp { + + public static final String THREAD_NAME = "Upcall thread"; + + private static final Object lockObj = new Object(); + + private static final CountDownLatch signal = new CountDownLatch(1); + + static { + System.loadLibrary("upcall"); + } + + public static void upcall() { + signal.countDown(); + synchronized(lockObj) { + } + } + + public static long createFunctionPointerForUpcall() throws NoSuchMethodException, IllegalAccessException { + var mh = MethodHandles.lookup() + .findStatic(LingeredAppWithFFMUpcall.class, "upcall", MethodType.methodType(void.class)); + var stub = Linker.nativeLinker() + .upcallStub(mh, FunctionDescriptor.ofVoid(), Arena.global()); + return stub.address(); + } + + public static native void callJNI(long upcallAddr); + + public static void main(String[] args) { + try { + long upcallAddr = createFunctionPointerForUpcall(); + var upcallThread = new Thread(() -> callJNI(upcallAddr), THREAD_NAME); + synchronized(lockObj) { + upcallThread.start(); + signal.await(); + LingeredApp.main(args); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } +} diff --git a/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackUpcall.java b/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackUpcall.java new file mode 100644 index 0000000000000..924f56c9b44fe --- /dev/null +++ b/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackUpcall.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2024, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import jdk.test.lib.JDKToolLauncher; +import jdk.test.lib.SA.SATestUtils; +import jdk.test.lib.Utils; +import jdk.test.lib.apps.LingeredApp; +import jdk.test.lib.process.OutputAnalyzer; + +/** + * @test + * @bug 8339307 + * @requires vm.hasSA + * @library /test/lib + * @run driver TestJhsdbJstackUpcall + */ +public class TestJhsdbJstackUpcall { + + private static void runJstack(LingeredApp app) throws Exception { + JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("jhsdb"); + launcher.addVMArgs(Utils.getTestJavaOpts()); + launcher.addToolArg("jstack"); + launcher.addToolArg("--pid"); + launcher.addToolArg(Long.toString(app.getPid())); + + ProcessBuilder pb = SATestUtils.createProcessBuilder(launcher); + Process jhsdb = pb.start(); + OutputAnalyzer out = new OutputAnalyzer(jhsdb); + + jhsdb.waitFor(); + + System.out.println(out.getStdout()); + System.err.println(out.getStderr()); + + out.shouldContain(LingeredAppWithFFMUpcall.THREAD_NAME); + out.shouldContain("LingeredAppWithFFMUpcall.upcall()"); + out.shouldContain("jdk.internal.foreign.abi.UpcallStub"); + out.shouldContain("LingeredAppWithFFMUpcall.callJNI"); + } + + public static void main(String... args) throws Exception { + SATestUtils.skipIfCannotAttach(); // throws SkippedException if attach not expected to work. + LingeredApp app = null; + + try { + // Needed for LingeredAppWithFFMUpcall to be able to resolve native library. + String libPath = System.getProperty("java.library.path"); + String[] vmArgs = (libPath != null) + ? Utils.prependTestJavaOpts("-Djava.library.path=" + libPath) + : Utils.getTestJavaOpts(); + + app = new LingeredAppWithFFMUpcall(); + LingeredApp.startAppExactJvmOpts(app, vmArgs); + System.out.println("Started LingeredAppWithFFMUpcall with pid " + app.getPid()); + runJstack(app); + System.out.println("Test Completed"); + } catch (Throwable e) { + e.printStackTrace(); + throw e; + } finally { + LingeredApp.stopApp(app); + } + } +} diff --git a/test/hotspot/jtreg/serviceability/sa/libupcall.c b/test/hotspot/jtreg/serviceability/sa/libupcall.c new file mode 100644 index 0000000000000..2139e717fef34 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/sa/libupcall.c @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2024, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include + +typedef void (*upcall_func)(void); + +JNIEXPORT void JNICALL +Java_LingeredAppWithFFMUpcall_callJNI(JNIEnv *env, jclass cls, jlong upcallAddr) { + upcall_func upcall = (upcall_func)upcallAddr; + upcall(); +} diff --git a/test/jdk/java/foreign/largestub/TestLargeStub.java b/test/jdk/java/foreign/largestub/TestLargeStub.java index eaf8264877810..e4dcb325c8f5a 100644 --- a/test/jdk/java/foreign/largestub/TestLargeStub.java +++ b/test/jdk/java/foreign/largestub/TestLargeStub.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2024, 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 @@ -25,29 +25,39 @@ * @test * @library ../ * @modules java.base/jdk.internal.foreign - * @run testng/othervm --enable-native-access=ALL-UNNAMED TestLargeStub + * @run junit/othervm --enable-native-access=ALL-UNNAMED TestLargeStub */ -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import java.lang.foreign.FunctionDescriptor; import java.lang.foreign.Linker; import java.lang.foreign.MemoryLayout; +import java.lang.foreign.ValueLayout; import java.util.stream.Stream; +import static org.junit.jupiter.params.provider.Arguments.arguments; + public class TestLargeStub extends NativeTestHelper { + private static final int DOWNCALL_AVAILABLE_SLOTS = 248; + private static final int UPCALL_AVAILABLE_SLOTS = 250; + MemoryLayout STRUCT_LL = MemoryLayout.structLayout( C_LONG_LONG, C_LONG_LONG ); // 16 byte struct triggers return buffer usage on SysV - @Test - public void testDowncall() { + @ParameterizedTest + @MethodSource("layouts") + public void testDowncall(ValueLayout layout, int numSlots) { // Link a handle with a large number of arguments, to try and overflow the code buffer Linker.nativeLinker().downcallHandle( FunctionDescriptor.of(STRUCT_LL, - Stream.generate(() -> C_DOUBLE).limit(124).toArray(MemoryLayout[]::new)), + Stream.generate(() -> layout).limit(DOWNCALL_AVAILABLE_SLOTS / numSlots).toArray(MemoryLayout[]::new)), Linker.Option.captureCallState("errno")); } @@ -62,11 +72,21 @@ public void testDowncallAllowHeap() { Linker.Option.critical(true)); } - @Test - public void testUpcall() { + @ParameterizedTest + @MethodSource("layouts") + public void testUpcall(ValueLayout layout, int numSlots) { // Link a handle with a large number of arguments, to try and overflow the code buffer Linker.nativeLinker().downcallHandle( FunctionDescriptor.of(STRUCT_LL, - Stream.generate(() -> C_DOUBLE).limit(125).toArray(MemoryLayout[]::new))); + Stream.generate(() -> layout).limit(UPCALL_AVAILABLE_SLOTS / numSlots).toArray(MemoryLayout[]::new))); + } + + private static Stream layouts() { + return Stream.of( + arguments(C_INT, 1), + arguments(C_LONG_LONG, 2), + arguments(C_FLOAT, 1), + arguments(C_DOUBLE, 2) + ); } }