From 58668e69ca53315ae6907490ca07992a7e34143e Mon Sep 17 00:00:00 2001 From: Yasumasa Suenaga Date: Fri, 17 Oct 2025 21:31:39 +0900 Subject: [PATCH 1/8] Mixed jstack does not work without -XX:+PreserveFramePointer --- .../sun/jvm/hotspot/debugger/cdbg/CFrame.java | 5 + .../linux/amd64/LinuxAMD64CFrame.java | 28 +++-- .../classes/sun/jvm/hotspot/tools/PStack.java | 49 +++++--- .../sa/TestJhsdbJstackMixedWithXComp.java | 111 ++++++++++++++++++ 4 files changed, 168 insertions(+), 25 deletions(-) create mode 100644 test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixedWithXComp.java diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/cdbg/CFrame.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/cdbg/CFrame.java index 7640ddaa35e0a..13951447903e4 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/cdbg/CFrame.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/cdbg/CFrame.java @@ -36,6 +36,11 @@ public interface CFrame { /** Returns null when no more frames on stack */ public CFrame sender(ThreadProxy th); + /** Find sender frame with given FP and PC */ + public default CFrame sender(ThreadProxy th, Address fp, Address pc) { + return sender(th); + } + /** Get the program counter of this frame */ public Address pc(); diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/amd64/LinuxAMD64CFrame.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/amd64/LinuxAMD64CFrame.java index 3dfb83c9f5a43..30eb3642011c3 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/amd64/LinuxAMD64CFrame.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/amd64/LinuxAMD64CFrame.java @@ -96,19 +96,22 @@ private Address getNextPC(boolean useDwarf) { } private boolean isValidFrame(Address nextCFA, ThreadContext context) { - return (nextCFA != null) && - !nextCFA.lessThan(context.getRegisterAsAddress(AMD64ThreadContext.RSP)); + return nextCFA != null; } - private Address getNextCFA(DwarfParser nextDwarf, ThreadContext context) { + private Address getNextCFA(DwarfParser nextDwarf, ThreadContext context, Address senderFP) { Address nextCFA; + if (senderFP == null) { + senderFP = cfa.getAddressAt(0); // RBP by default + } + if (nextDwarf == null) { // Next frame is Java - nextCFA = (dwarf == null) ? cfa.getAddressAt(0) // Current frame is Java (Use RBP) + nextCFA = (dwarf == null) ? senderFP // Current frame is Java : cfa.getAddressAt(dwarf.getBasePointerOffsetFromCFA()); // Current frame is Native } else { // Next frame is Native - if (dwarf == null) { // Current frame is Java (Use RBP) - nextCFA = cfa.getAddressAt(0); + if (dwarf == null) { // Current frame is Java + nextCFA = senderFP; } else { // Current frame is Native int nextCFAReg = nextDwarf.getCFARegister(); if (!dwarf.isBPOffsetAvailable() && // Use RBP as CFA @@ -132,14 +135,19 @@ private Address getNextCFA(DwarfParser nextDwarf, ThreadContext context) { } @Override - public CFrame sender(ThreadProxy thread) { + public CFrame sender(ThreadProxy th) { + return sender(th, null, null); + } + + @Override + public CFrame sender(ThreadProxy th, Address fp, Address pc) { if (finalFrame) { return null; } - ThreadContext context = thread.getContext(); + ThreadContext context = th.getContext(); - Address nextPC = getNextPC(dwarf != null); + Address nextPC = pc != null ? pc : getNextPC(dwarf != null); if (nextPC == null) { return null; } @@ -165,7 +173,7 @@ public CFrame sender(ThreadProxy thread) { } } - Address nextCFA = getNextCFA(nextDwarf, context); + Address nextCFA = getNextCFA(nextDwarf, context, fp); return isValidFrame(nextCFA, context) ? new LinuxAMD64CFrame(dbg, nextCFA, nextPC, nextDwarf) : null; } diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/PStack.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/PStack.java index a0099b4c4de88..9865fbbe0f202 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/PStack.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/PStack.java @@ -109,6 +109,8 @@ public void run(PrintStream out, Debugger dbg) { jthread.printThreadInfoOn(out); } while (f != null) { + Address senderFP = null; + Address senderPC = null; ClosestSymbol sym = f.closestSymbolToPC(); Address pc = f.pc(); out.print(pc + "\t"); @@ -125,13 +127,13 @@ public void run(PrintStream out, Debugger dbg) { out.println(); } else { // look for one or more java frames - String[] names = null; + JavaNameInfo nameInfo = null; // check interpreter frame Interpreter interp = VM.getVM().getInterpreter(); if (interp.contains(pc)) { - names = getJavaNames(th, f.localVariableBase()); + nameInfo = getJavaNames(th, f.localVariableBase()); // print codelet name if we can't determine method - if (names == null || names.length == 0) { + if (nameInfo == null || nameInfo.names() == null || nameInfo.names().length == 0) { out.print(" "); InterpreterCodelet ic = interp.getCodeletContaining(pc); if (ic != null) { @@ -154,9 +156,9 @@ public void run(PrintStream out, Debugger dbg) { } out.println(" (Native method)"); } else { - names = getJavaNames(th, f.localVariableBase()); + nameInfo = getJavaNames(th, f.localVariableBase()); // just print compiled code, if can't determine method - if (names == null || names.length == 0) { + if (nameInfo == null || nameInfo.names() == null || nameInfo.names().length == 0) { out.println(""); } } @@ -168,17 +170,21 @@ public void run(PrintStream out, Debugger dbg) { } } // print java frames, if any - if (names != null && names.length != 0) { - // print java frame(s) - for (int i = 0; i < names.length; i++) { - if (i > 0) { - out.print(fillerForAddress); + if (nameInfo != null) { + if (nameInfo.names() != null && nameInfo.names().length != 0) { + // print java frame(s) + for (int i = 0; i < nameInfo.names().length; i++) { + if (i > 0) { + out.print(fillerForAddress); + } + out.println(nameInfo.names()[i]); } - out.println(names[i]); } + senderFP = nameInfo.senderFP(); + senderPC = nameInfo.senderPC(); } } - f = f.sender(th); + f = f.sender(th, senderFP, senderPC); } } catch (Exception exp) { exp.printStackTrace(); @@ -239,17 +245,22 @@ private void printUnknown(PrintStream out) { out.println("\t????????"); } - private String[] getJavaNames(ThreadProxy th, Address fp) { + private static record JavaNameInfo(String[] names, Address senderFP, Address senderPC) {}; + + private JavaNameInfo getJavaNames(ThreadProxy th, Address fp) { if (fp == null) { return null; } JavaVFrame[] jvframes = jframeCache.get(th); if (jvframes == null) return null; // not a java thread + List names = new ArrayList<>(10); + JavaVFrame bottomJVFrame = null; for (int fCount = 0; fCount < jvframes.length; fCount++) { JavaVFrame vf = jvframes[fCount]; Frame f = vf.getFrame(); if (fp.equals(f.getFP())) { + bottomJVFrame = vf; StringBuilder sb = new StringBuilder(); Method method = vf.getMethod(); // a special char to identify java frames in output @@ -280,8 +291,16 @@ private String[] getJavaNames(ThreadProxy th, Address fp) { names.add(sb.toString()); } } - String[] res = names.toArray(new String[0]); - return res; + + Address senderFP = null; + Address senderPC = null; + if (bottomJVFrame != null) { + Frame senderFrame = bottomJVFrame.getFrame().sender((RegisterMap)bottomJVFrame.getRegisterMap().clone()); + senderFP = senderFrame.getFP(); + senderPC = senderFrame.getPC(); + } + + return new JavaNameInfo(names.toArray(new String[0]), senderFP, senderPC); } public void setVerbose(boolean verbose) { diff --git a/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixedWithXComp.java b/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixedWithXComp.java new file mode 100644 index 0000000000000..047cedfd1fc34 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixedWithXComp.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, NTT DATA + * 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.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +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 8369505 + * @requires vm.hasSA + * @requires os.family == "linux" + * @requires (os.arch == "amd64" | os.arch == "aarch64") + * @library /test/lib + * @run driver TestJhsdbJstackMixedWithXComp + */ +public class TestJhsdbJstackMixedWithXComp { + + private static void runJstack(LingeredApp app) throws Exception { + JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("jhsdb"); + launcher.addVMArgs(Utils.getTestJavaOpts()); + launcher.addToolArg("jstack"); + launcher.addToolArg("--mixed"); + 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(); + + String stdout = out.getStdout(); + System.out.println(stdout); + System.err.println(out.getStderr()); + + out.stderrShouldBeEmptyIgnoreDeprecatedWarnings(); + + List targetStackTrace = new ArrayList<>(); + boolean inStack = false; + System.out.println("DEBUG: before loop"); + for (String line : stdout.split("\n")) { + if (line.contains("")) { + inStack = true; + System.out.println("DEBUG: IN"); + } else if (inStack && line.contains("-----------------")) { + inStack = false; + System.out.println("DEBUG: OUT"); + break; + } + + if (inStack) { + System.out.println("DEBUG: " + line); + targetStackTrace.add(line); + } + } + System.out.println("DEBUG: after loop"); + + boolean found = targetStackTrace.stream() + .anyMatch(l -> l.contains("thread_native_entry")); + if (!found) { + throw new RuntimeException("Test failed!"); + } + } + + public static void main(String... args) throws Exception { + SATestUtils.skipIfCannotAttach(); // throws SkippedException if attach not expected to work. + LingeredApp app = null; + + try { + app = new LingeredAppWithVirtualThread(); + LingeredApp.startApp(app, "-Xcomp"); + System.out.println("Started LingeredApp with pid " + app.getPid()); + runJstack(app); + System.out.println("Test Completed"); + } catch (Throwable e) { + e.printStackTrace(); + throw e; + } finally { + LingeredApp.stopApp(app); + } + } +} From 39274ffe393cb3ef7d9f62335bc9e29b47f38e20 Mon Sep 17 00:00:00 2001 From: Yasumasa Suenaga Date: Sun, 19 Oct 2025 12:23:08 +0900 Subject: [PATCH 2/8] Add change for AArch64 --- .../linux/aarch64/LinuxAARCH64CFrame.java | 32 +++++++++++++------ 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/aarch64/LinuxAARCH64CFrame.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/aarch64/LinuxAARCH64CFrame.java index eb463d53a2e91..93edb43eb822c 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/aarch64/LinuxAARCH64CFrame.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/aarch64/LinuxAARCH64CFrame.java @@ -53,24 +53,36 @@ public Address localVariableBase() { return fp; } + @Override public CFrame sender(ThreadProxy thread) { - AARCH64ThreadContext context = (AARCH64ThreadContext) thread.getContext(); - Address rsp = context.getRegisterAsAddress(AARCH64ThreadContext.SP); + return sender(thread, null, null); + } - if ((fp == null) || fp.lessThan(rsp)) { - return null; + @Override + public CFrame sender(ThreadProxy thread, Address nextFP, Address nextPC) { + // Check fp + // Skip if both nextFP and nextPC are given - do not need to load from fp. + if (nextFP == null && nextPC == null) { + if (fp == null) { + return null; + } + + // Check alignment of fp + if (dbg.getAddressValue(fp) % (2 * ADDRESS_SIZE) != 0) { + return null; + } } - // Check alignment of fp - if (dbg.getAddressValue(fp) % (2 * ADDRESS_SIZE) != 0) { + if (nextFP == null) { + nextFP = fp.getAddressAt(0 * ADDRESS_SIZE); + } + if (nextFP == null) { return null; } - Address nextFP = fp.getAddressAt(0 * ADDRESS_SIZE); - if (nextFP == null || nextFP.lessThanOrEqual(fp)) { - return null; + if (nextPC == null) { + nextPC = fp.getAddressAt(1 * ADDRESS_SIZE); } - Address nextPC = fp.getAddressAt(1 * ADDRESS_SIZE); if (nextPC == null) { return null; } From 9d023257940c4dc71d0c851c98fa776e508c8225 Mon Sep 17 00:00:00 2001 From: Yasumasa Suenaga Date: Sun, 19 Oct 2025 12:43:10 +0900 Subject: [PATCH 3/8] Update bug ID in testcase --- .../jtreg/serviceability/sa/TestJhsdbJstackMixedWithXComp.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixedWithXComp.java b/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixedWithXComp.java index 047cedfd1fc34..cf5e64888fb6d 100644 --- a/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixedWithXComp.java +++ b/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixedWithXComp.java @@ -35,7 +35,7 @@ /** * @test - * @bug 8369505 + * @bug 8370176 * @requires vm.hasSA * @requires os.family == "linux" * @requires (os.arch == "amd64" | os.arch == "aarch64") From a75aa29db3bb3b92c1cd1409d6aaf52581dc9a32 Mon Sep 17 00:00:00 2001 From: Yasumasa Suenaga Date: Mon, 20 Oct 2025 15:55:20 +0900 Subject: [PATCH 4/8] Update testcase --- .../jtreg/serviceability/sa/TestJhsdbJstackMixedWithXComp.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixedWithXComp.java b/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixedWithXComp.java index cf5e64888fb6d..042784c1e1671 100644 --- a/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixedWithXComp.java +++ b/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixedWithXComp.java @@ -46,7 +46,7 @@ public class TestJhsdbJstackMixedWithXComp { private static void runJstack(LingeredApp app) throws Exception { JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("jhsdb"); - launcher.addVMArgs(Utils.getTestJavaOpts()); + launcher.addVMArgs(Utils.getFilteredTestJavaOpts("-showversion")); launcher.addToolArg("jstack"); launcher.addToolArg("--mixed"); launcher.addToolArg("--pid"); From 3f7697fa6f4e950fd33ec599cf9e19f92ccdf273 Mon Sep 17 00:00:00 2001 From: Yasumasa Suenaga Date: Thu, 23 Oct 2025 13:37:42 +0900 Subject: [PATCH 5/8] Remove debug messages --- .../serviceability/sa/TestJhsdbJstackMixedWithXComp.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixedWithXComp.java b/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixedWithXComp.java index 042784c1e1671..2555123a062a3 100644 --- a/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixedWithXComp.java +++ b/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixedWithXComp.java @@ -66,23 +66,18 @@ private static void runJstack(LingeredApp app) throws Exception { List targetStackTrace = new ArrayList<>(); boolean inStack = false; - System.out.println("DEBUG: before loop"); for (String line : stdout.split("\n")) { if (line.contains("")) { inStack = true; - System.out.println("DEBUG: IN"); } else if (inStack && line.contains("-----------------")) { inStack = false; - System.out.println("DEBUG: OUT"); break; } if (inStack) { - System.out.println("DEBUG: " + line); targetStackTrace.add(line); } } - System.out.println("DEBUG: after loop"); boolean found = targetStackTrace.stream() .anyMatch(l -> l.contains("thread_native_entry")); From 0e7c4ccdbe609da240c8211cda9948be9e81dad3 Mon Sep 17 00:00:00 2001 From: Yasumasa Suenaga Date: Thu, 23 Oct 2025 13:40:17 +0900 Subject: [PATCH 6/8] Add the fix for RISC-V --- .../linux/riscv64/LinuxRISCV64CFrame.java | 33 +++++++++++++------ .../sa/TestJhsdbJstackMixedWithXComp.java | 2 +- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/riscv64/LinuxRISCV64CFrame.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/riscv64/LinuxRISCV64CFrame.java index f06da24bd0e30..76f45891c47db 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/riscv64/LinuxRISCV64CFrame.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/riscv64/LinuxRISCV64CFrame.java @@ -57,27 +57,40 @@ public Address localVariableBase() { return fp; } + @Override public CFrame sender(ThreadProxy thread) { - RISCV64ThreadContext context = (RISCV64ThreadContext) thread.getContext(); - Address rsp = context.getRegisterAsAddress(RISCV64ThreadContext.SP); + return sender(thread, null, null); + } - if ((fp == null) || fp.lessThan(rsp)) { - return null; + @Override + public CFrame sender(ThreadProxy thread, Address nextFP, Address nextPC) { + // Check fp + // Skip if both nextFP and nextPC are given - do not need to load from fp. + if (nextFP == null && nextPC == null) { + if (fp == null) { + return null; + } + + // Check alignment of fp + if (dbg.getAddressValue(fp) % (2 * ADDRESS_SIZE) != 0) { + return null; + } } - // Check alignment of fp - if (dbg.getAddressValue(fp) % (2 * ADDRESS_SIZE) != 0) { + if (nextFP == null) { + nextFP = fp.getAddressAt(C_FRAME_LINK_OFFSET * ADDRESS_SIZE); + } + if (nextFP == null) { return null; } - Address nextFP = fp.getAddressAt(C_FRAME_LINK_OFFSET * ADDRESS_SIZE); - if (nextFP == null || nextFP.lessThanOrEqual(fp)) { - return null; + if (nextPC == null) { + nextPC = fp.getAddressAt(C_FRAME_RETURN_ADDR_OFFSET * ADDRESS_SIZE); } - Address nextPC = fp.getAddressAt(C_FRAME_RETURN_ADDR_OFFSET * ADDRESS_SIZE); if (nextPC == null) { return null; } + return new LinuxRISCV64CFrame(dbg, nextFP, nextPC); } diff --git a/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixedWithXComp.java b/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixedWithXComp.java index 2555123a062a3..8085ca6ebefa0 100644 --- a/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixedWithXComp.java +++ b/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixedWithXComp.java @@ -38,7 +38,7 @@ * @bug 8370176 * @requires vm.hasSA * @requires os.family == "linux" - * @requires (os.arch == "amd64" | os.arch == "aarch64") + * @requires (os.arch == "amd64" | os.arch == "aarch64" | os.arch == "riscv64") * @library /test/lib * @run driver TestJhsdbJstackMixedWithXComp */ From 434e8d28f996064a7508cfa94bdeb56cef7bbbf1 Mon Sep 17 00:00:00 2001 From: Yasumasa Suenaga Date: Sat, 25 Oct 2025 11:48:45 +0900 Subject: [PATCH 7/8] Add glibc version check to TestJhsdbJstackMixedWithXComp.java --- .../sa/TestJhsdbJstackMixedWithXComp.java | 63 ++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixedWithXComp.java b/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixedWithXComp.java index 8085ca6ebefa0..816dc26a05a60 100644 --- a/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixedWithXComp.java +++ b/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixedWithXComp.java @@ -22,6 +22,11 @@ * questions. */ +import java.lang.foreign.FunctionDescriptor; +import java.lang.foreign.Linker; +import java.lang.foreign.MemorySegment; +import java.lang.foreign.ValueLayout; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; @@ -33,6 +38,8 @@ import jdk.test.lib.apps.LingeredApp; import jdk.test.lib.process.OutputAnalyzer; +import jtreg.SkippedException; + /** * @test * @bug 8370176 @@ -40,10 +47,60 @@ * @requires os.family == "linux" * @requires (os.arch == "amd64" | os.arch == "aarch64" | os.arch == "riscv64") * @library /test/lib - * @run driver TestJhsdbJstackMixedWithXComp + * @run main/othervm --enable-native-access=ALL-UNNAMED TestJhsdbJstackMixedWithXComp */ public class TestJhsdbJstackMixedWithXComp { + /** + * On Linux, check glibc version before the test and return true if it is + * 2.39 or later. The test which needs to unwind native call stacks like + * "jhsdb jstack --mixed" should be skip the test when this checker method + * returns false. + * The problem is not to unwind all of call stacks the process is running on + * older glibc. It happens Debian 12, Ubuntu 22.04 (glibc 2.35) and + * Ubuntu 23.04 (glibc 2.37) at least. It works on Ubuntu 24.04 (glibc 2.39). + * The problem happenes both AMD64 and AArch64. + */ + private static boolean canAttachLinuxOnCurrentGLIBC() { + var linker = Linker.nativeLinker(); + var lookup = linker.defaultLookup(); + var sym = lookup.find("gnu_get_libc_version"); + if (sym.isEmpty()) { + // Maybe the platform is not on glibc (Windows, Mac, musl on Alpine). + // Go ahead. + return true; + } + + // Call gnu_get_libc_version() + var desc = FunctionDescriptor.of(ValueLayout.ADDRESS); + var func = linker.downcallHandle(sym.get(), desc); + MemorySegment result; + try { + result = (MemorySegment)func.invoke(); + } catch (Throwable t) { + throw new RuntimeException(t); + } + + // Set the length of glibc version because FFM does not know memory size + // returned by gnu_get_libc_version(). + var strlenSym = lookup.find("strlen"); + var strlenDesc = FunctionDescriptor.of(linker.canonicalLayouts().get("size_t"), ValueLayout.ADDRESS); + var strlenFunc = linker.downcallHandle(strlenSym.get(), strlenDesc); + long len; + try { + len = (long)strlenFunc.invoke(result); + } catch (Throwable t) { + throw new RuntimeException(t); + } + + result = result.reinterpret(len + 1); // includes NUL + String[] ver = result.getString(0, StandardCharsets.US_ASCII).split("\\."); + int major = Integer.parseInt(ver[0]); + int minor = Integer.parseInt(ver[1]); + + return major > 2 || (major == 2 && minor >= 39); + } + private static void runJstack(LingeredApp app) throws Exception { JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("jhsdb"); launcher.addVMArgs(Utils.getFilteredTestJavaOpts("-showversion")); @@ -88,6 +145,10 @@ private static void runJstack(LingeredApp app) throws Exception { public static void main(String... args) throws Exception { SATestUtils.skipIfCannotAttach(); // throws SkippedException if attach not expected to work. + if (!canAttachLinuxOnCurrentGLIBC()) { + throw new SkippedException("SA Attach not expected to work. glibc is 2.38 or earlier."); + } + LingeredApp app = null; try { From ff070a3a2b58fdf80fc8cd64a3f12c9c25d00b54 Mon Sep 17 00:00:00 2001 From: Yasumasa Suenaga Date: Sun, 26 Oct 2025 22:26:26 +0900 Subject: [PATCH 8/8] Update testcase --- .../sa/TestJhsdbJstackMixedWithXComp.java | 65 +------------------ test/lib/jdk/test/lib/SA/SATestUtils.java | 63 +++++++++++++++++- 2 files changed, 64 insertions(+), 64 deletions(-) diff --git a/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixedWithXComp.java b/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixedWithXComp.java index 816dc26a05a60..e0cd250cd708d 100644 --- a/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixedWithXComp.java +++ b/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixedWithXComp.java @@ -22,15 +22,8 @@ * questions. */ -import java.lang.foreign.FunctionDescriptor; -import java.lang.foreign.Linker; -import java.lang.foreign.MemorySegment; -import java.lang.foreign.ValueLayout; -import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import jdk.test.lib.JDKToolLauncher; import jdk.test.lib.SA.SATestUtils; @@ -38,8 +31,6 @@ import jdk.test.lib.apps.LingeredApp; import jdk.test.lib.process.OutputAnalyzer; -import jtreg.SkippedException; - /** * @test * @bug 8370176 @@ -47,60 +38,10 @@ * @requires os.family == "linux" * @requires (os.arch == "amd64" | os.arch == "aarch64" | os.arch == "riscv64") * @library /test/lib - * @run main/othervm --enable-native-access=ALL-UNNAMED TestJhsdbJstackMixedWithXComp + * @run driver TestJhsdbJstackMixedWithXComp */ public class TestJhsdbJstackMixedWithXComp { - /** - * On Linux, check glibc version before the test and return true if it is - * 2.39 or later. The test which needs to unwind native call stacks like - * "jhsdb jstack --mixed" should be skip the test when this checker method - * returns false. - * The problem is not to unwind all of call stacks the process is running on - * older glibc. It happens Debian 12, Ubuntu 22.04 (glibc 2.35) and - * Ubuntu 23.04 (glibc 2.37) at least. It works on Ubuntu 24.04 (glibc 2.39). - * The problem happenes both AMD64 and AArch64. - */ - private static boolean canAttachLinuxOnCurrentGLIBC() { - var linker = Linker.nativeLinker(); - var lookup = linker.defaultLookup(); - var sym = lookup.find("gnu_get_libc_version"); - if (sym.isEmpty()) { - // Maybe the platform is not on glibc (Windows, Mac, musl on Alpine). - // Go ahead. - return true; - } - - // Call gnu_get_libc_version() - var desc = FunctionDescriptor.of(ValueLayout.ADDRESS); - var func = linker.downcallHandle(sym.get(), desc); - MemorySegment result; - try { - result = (MemorySegment)func.invoke(); - } catch (Throwable t) { - throw new RuntimeException(t); - } - - // Set the length of glibc version because FFM does not know memory size - // returned by gnu_get_libc_version(). - var strlenSym = lookup.find("strlen"); - var strlenDesc = FunctionDescriptor.of(linker.canonicalLayouts().get("size_t"), ValueLayout.ADDRESS); - var strlenFunc = linker.downcallHandle(strlenSym.get(), strlenDesc); - long len; - try { - len = (long)strlenFunc.invoke(result); - } catch (Throwable t) { - throw new RuntimeException(t); - } - - result = result.reinterpret(len + 1); // includes NUL - String[] ver = result.getString(0, StandardCharsets.US_ASCII).split("\\."); - int major = Integer.parseInt(ver[0]); - int minor = Integer.parseInt(ver[1]); - - return major > 2 || (major == 2 && minor >= 39); - } - private static void runJstack(LingeredApp app) throws Exception { JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("jhsdb"); launcher.addVMArgs(Utils.getFilteredTestJavaOpts("-showversion")); @@ -145,9 +86,7 @@ private static void runJstack(LingeredApp app) throws Exception { public static void main(String... args) throws Exception { SATestUtils.skipIfCannotAttach(); // throws SkippedException if attach not expected to work. - if (!canAttachLinuxOnCurrentGLIBC()) { - throw new SkippedException("SA Attach not expected to work. glibc is 2.38 or earlier."); - } + SATestUtils.skipIfRunsOnOlderGLIBC(); // throws SkippedException if this test runs on older GLIBC. LingeredApp app = null; diff --git a/test/lib/jdk/test/lib/SA/SATestUtils.java b/test/lib/jdk/test/lib/SA/SATestUtils.java index cdb0caf84d4a9..2104b94524062 100644 --- a/test/lib/jdk/test/lib/SA/SATestUtils.java +++ b/test/lib/jdk/test/lib/SA/SATestUtils.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2025, 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 @@ -26,13 +26,19 @@ import jdk.test.lib.Platform; import jtreg.SkippedException; +import java.lang.foreign.FunctionDescriptor; import java.lang.foreign.Linker; +import java.lang.foreign.MemorySegment; +import java.lang.foreign.ValueLayout; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.concurrent.TimeUnit; import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; public class SATestUtils { /** @@ -73,6 +79,61 @@ public static void skipIfCannotAttach() { } } + /** + * Checks whether the test runs on older glibc or not. + * On Linux, check glibc version before the test and return true if it is + * 2.39 or later. The test which needs to unwind native call stacks like + * "jhsdb jstack --mixed" should be skip the test when this checker method + * returns false. + * The problem is not to unwind all of call stacks the process is running on + * older glibc. It happens Debian 12, Ubuntu 22.04 (glibc 2.35) and + * Ubuntu 23.04 (glibc 2.37) at least. It works on Ubuntu 24.04 (glibc 2.39). + * The problem happenes both AMD64 and AArch64. + * + * @return true if the test runs on modern glibc (2.39 or later). + * @throws SkippedException if the test runs on older glibc (2.38 or earlier). + */ + @SuppressWarnings("restricted") + public static boolean skipIfRunsOnOlderGLIBC() { + var linker = Linker.nativeLinker(); + var lookup = linker.defaultLookup(); + var sym = lookup.find("gnu_get_libc_version"); + if (sym.isEmpty()) { + // Maybe the platform is not on glibc (Windows, Mac, musl on Alpine). + // Go ahead. + return true; + } + + // Call gnu_get_libc_version() + var desc = FunctionDescriptor.of(ValueLayout.ADDRESS); + var func = linker.downcallHandle(sym.get(), desc); + MemorySegment result; + try { + result = (MemorySegment)func.invoke(); + } catch (Throwable t) { + throw new RuntimeException(t); + } + + // Set the length of glibc version because FFM does not know memory size + // returned by gnu_get_libc_version(). + var strlenSym = lookup.find("strlen"); + var strlenDesc = FunctionDescriptor.of(linker.canonicalLayouts().get("size_t"), ValueLayout.ADDRESS); + var strlenFunc = linker.downcallHandle(strlenSym.get(), strlenDesc); + long len; + try { + len = (long)strlenFunc.invoke(result); + } catch (Throwable t) { + throw new RuntimeException(t); + } + + result = result.reinterpret(len + 1); // includes NUL + String[] ver = result.getString(0, StandardCharsets.US_ASCII).split("\\."); + int major = Integer.parseInt(ver[0]); + int minor = Integer.parseInt(ver[1]); + + return major > 2 || (major == 2 && minor >= 39); + } + /** * Returns true if this platform is expected to require extra privileges (running using sudo). * If we are running as root or developer mode is enabled, then sudo is not needed.