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/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(); +}