8339307: jhsdb jstack could not trace FFM upcall frame

Reviewed-by: cjplummer, jvernee
This commit is contained in:
Yasumasa Suenaga 2024-09-07 05:46:47 +00:00
parent fbe2629303
commit deeb09a640
12 changed files with 447 additions and 10 deletions

View File

@ -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;

View File

@ -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<mtInternal>) \
declare_toplevel_type(FileMapHeader) \
declare_toplevel_type(CDSFileMapRegion) \
declare_toplevel_type(UpcallStub::FrameData) \
\
/************/ \
/* GC types */ \

View File

@ -143,6 +143,8 @@ public class CodeBlob extends VMObject {
public boolean isRuntimeStub() { return false; }
public boolean isUpcallStub() { return false; }
public boolean isDeoptimizationStub() { return false; }
public boolean isUncommonTrapStub() { return false; }

View File

@ -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 @@ public class CodeCache {
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()) {

View File

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

View File

@ -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 class AARCH64Frame extends Frame {
}
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 @@ public class AARCH64Frame extends Frame {
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() {

View File

@ -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 class PPC64Frame extends Frame {
}
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 @@ public class PPC64Frame extends Frame {
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() {

View File

@ -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 class RISCV64Frame extends Frame {
}
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 @@ public class RISCV64Frame extends Frame {
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() {

View File

@ -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 class X86Frame extends Frame {
}
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 @@ public class X86Frame extends Frame {
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() {

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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 <jni.h>
typedef void (*upcall_func)(void);
JNIEXPORT void JNICALL
Java_LingeredAppWithFFMUpcall_callJNI(JNIEnv *env, jclass cls, jlong upcallAddr) {
upcall_func upcall = (upcall_func)upcallAddr;
upcall();
}