# HG changeset patch # User sgehwolf # Date 1532362126 -7200 # Node ID 2e98c7737d8f71af000a135980b7caa013b36147 # Parent edcf0d52765889a9aa1421b7988d8dc5bd0d9f9e 8208091: SA: jhsdb jstack --mixed throws UnmappedAddressException on i686 Summary: Be sure to use the same register index in native and Java code. Reviewed-by: sballal, cjplummer, tbell diff -r edcf0d527658 -r 2e98c7737d8f make/common/TestFilesCompilation.gmk --- a/make/common/TestFilesCompilation.gmk Fri Aug 24 14:04:34 2018 +0200 +++ b/make/common/TestFilesCompilation.gmk Mon Jul 23 18:08:46 2018 +0200 @@ -94,7 +94,7 @@ CFLAGS := $$($1_CFLAGS) $$($1_CFLAGS_$$(name)), \ LDFLAGS := $$($1_LDFLAGS) $$($1_LDFLAGS_$$(name)), \ LIBS := $$($1_LIBS_$$(name)), \ - OPTIMIZATION := LOW, \ + OPTIMIZATION := $$(if $$($1_OPTIMIZATION_$$(name)),$$($1_OPTIMIZATION_$$(name)),LOW), \ COPY_DEBUG_SYMBOLS := false, \ STRIP_SYMBOLS := false, \ )) \ diff -r edcf0d527658 -r 2e98c7737d8f make/test/JtregNativeHotspot.gmk --- a/make/test/JtregNativeHotspot.gmk Fri Aug 24 14:04:34 2018 +0200 +++ b/make/test/JtregNativeHotspot.gmk Mon Jul 23 18:08:46 2018 +0200 @@ -139,6 +139,15 @@ -I$(VM_TESTBASE_DIR)/nsk/share/native \ -I$(VM_TESTBASE_DIR)/nsk/share/jni +NO_FRAMEPOINTER_CFLAGS := +ifeq ($(OPENJDK_TARGET_OS),linux) + NO_FRAMEPOINTER_CFLAGS := -fomit-frame-pointer +endif + +BUILD_HOTSPOT_JTREG_LIBRARIES_CFLAGS_libNoFramePointer := $(NO_FRAMEPOINTER_CFLAGS) +# Optimization -O3 needed, HIGH == -O3 +BUILD_HOTSPOT_JTREG_LIBRARIES_OPTIMIZATION_libNoFramePointer := HIGH + BUILD_HOTSPOT_JTREG_LIBRARIES_CFLAGS_libProcessUtils := $(VM_SHARE_INCLUDES) BUILD_HOTSPOT_JTREG_LIBRARIES_CFLAGS_libThreadController := $(NSK_MONITORING_INCLUDES) diff -r edcf0d527658 -r 2e98c7737d8f src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/x86/LinuxX86CFrame.java --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/x86/LinuxX86CFrame.java Fri Aug 24 14:04:34 2018 +0200 +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/x86/LinuxX86CFrame.java Mon Jul 23 18:08:46 2018 +0200 @@ -55,7 +55,15 @@ public CFrame sender(ThreadProxy thread) { X86ThreadContext context = (X86ThreadContext) thread.getContext(); - Address esp = context.getRegisterAsAddress(X86ThreadContext.ESP); + /* + * Native code fills in the stack pointer register value using index + * X86ThreadContext.SP. + * See file LinuxDebuggerLocal.c macro REG_INDEX(reg). + * + * Be sure to use SP, or UESP which is aliased to SP in Java code, + * for the frame pointer validity check. + */ + Address esp = context.getRegisterAsAddress(X86ThreadContext.SP); if ( (ebp == null) || ebp.lessThan(esp) ) { return null; diff -r edcf0d527658 -r 2e98c7737d8f src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/windows/x86/WindowsX86CFrame.java --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/windows/x86/WindowsX86CFrame.java Fri Aug 24 14:04:34 2018 +0200 +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/windows/x86/WindowsX86CFrame.java Mon Jul 23 18:08:46 2018 +0200 @@ -46,7 +46,15 @@ public CFrame sender(ThreadProxy thread) { X86ThreadContext context = (X86ThreadContext) thread.getContext(); - Address esp = context.getRegisterAsAddress(X86ThreadContext.ESP); + /* + * Native code fills in the stack pointer register value using index + * X86ThreadContext.SP. + * See file sawindbg.cpp macro REG_INDEX(x). + * + * Be sure to use SP, or UESP which is aliased to SP in Java code, + * for the frame pointer validity check. + */ + Address esp = context.getRegisterAsAddress(X86ThreadContext.SP); if ( (ebp == null) || ebp.lessThan(esp) ) { return null; diff -r edcf0d527658 -r 2e98c7737d8f test/hotspot/jtreg/serviceability/sa/LingeredAppWithNativeMethod.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/hotspot/jtreg/serviceability/sa/LingeredAppWithNativeMethod.java Mon Jul 23 18:08:46 2018 +0200 @@ -0,0 +1,74 @@ + +/* + * Copyright (c) 2018, 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.apps.LingeredApp; + +public class LingeredAppWithNativeMethod extends LingeredApp { + + public static final String THREAD_NAME = "NoFramePointerJNIFib"; + private static final int UPPER_BOUND = 55; + private static final int LOWER_BOUND = 40; + + static { + // JNI library compiled with no frame pointer info + System.loadLibrary("NoFramePointer"); + } + + public void callNative() { + // Call JNI code which does something compute + // intensive: fibonacci + // That is to ensure that the native bits run when + // jstack --mixed info is to be gathered. + // Results of fibonacci calculation from JNI are + // reported via callback(). That's where the process + // of calculating fibonacci restarts. + int num = (int) (Math.random() * UPPER_BOUND); + while (num < LOWER_BOUND) { + num = (int) (Math.random() * UPPER_BOUND); + } + System.out.print("fib(" + num + ") = "); + callJNI(this, num); + } + + // Called from JNI library libNoFramePointer + private void callback(long val) { + System.out.println(val); + // Call native again so as to increase chances of + // being currently in JNI code when jstack --mixed + // runs. + callNative(); + } + + public static native void callJNI(Object target, int num); + + public static void main(String[] args) { + LingeredAppWithNativeMethod app = new LingeredAppWithNativeMethod(); + Thread fibonacci = new Thread(() -> { + app.callNative(); + }); + fibonacci.setName(THREAD_NAME); + fibonacci.start(); + LingeredApp.main(args); + } +} diff -r edcf0d527658 -r 2e98c7737d8f test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixed.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixed.java Mon Jul 23 18:08:46 2018 +0200 @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2018, 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.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.Utils; +import jdk.test.lib.apps.LingeredApp; +import jdk.test.lib.process.OutputAnalyzer; + +/** + * @test + * @bug 8208091 + * @requires (os.family == "linux") & (vm.hasSAandCanAttach) + * @library /test/lib + * @run main/othervm TestJhsdbJstackMixed + */ +public class TestJhsdbJstackMixed { + + private static final int MAX_ITERATIONS = 20; + private static final String NATIVE_FUNCTION_NAME = "fib"; + private static final String LINE_MATCHER_STR = ".*" + NATIVE_FUNCTION_NAME + + ".*"; + private static final Pattern LINE_PATTERN = Pattern + .compile(LINE_MATCHER_STR); + private static final String HEX_STR_PATTERN = "0x([a-fA-F0-9]+)"; + private static final String FIB_SPLIT_PATTERN = NATIVE_FUNCTION_NAME + + "\\s+\\+"; + private static final Pattern HEX_PATTERN = Pattern.compile(HEX_STR_PATTERN); + private static final int ADDRESS_ALIGNMENT_X86 = 4; + + /* + * UnmappedAddressException will be thrown iff: + * - The JNI code is being compiled with -fomit-frame-pointer AND + * - The JNI code is currently executing at address A = pc() + offset + * where A % ADDRESS_SIZE == 0. + * + * In the below example we have: pc() == f6401546, offset == 56, + * ADDRESS_SIZE == 4. Thus, A == F640159C which satisfies this condition. + * + * "NoFramePointerJNIFib" #11 prio=5 tid=0xa357bc00 nid=0x6de9 runnable [0xa365b000] + * java.lang.Thread.State: RUNNABLE + * JavaThread state: _thread_in_native + * 0xf6401546 fib + 0x56 + */ + private static boolean isFibAndAlignedAddress(List lines) { + List fibLines = findFibLines(lines); + System.out.println("DEBUG: " + fibLines); + // we're only interested in the first matched line. + if (fibLines.size() >= 1) { + String line = fibLines.get(0); + return isMatchLine(line); + } + return false; + } + + private static boolean isMatchLine(String line) { + String[] tokens = line.split(FIB_SPLIT_PATTERN); + if (tokens.length != 2) { + return false; // NOT exactly two tokens, ignore. + } + String pcRaw = tokens[0].trim(); + String offsetRaw = tokens[1].trim(); + Matcher matcher = HEX_PATTERN.matcher(pcRaw); + long pcVal = 3; + boolean pcMatched = matcher.matches(); + if (pcMatched) { + String pc = matcher.group(1); + pcVal = Long.parseUnsignedLong(pc, 16); + } + matcher = HEX_PATTERN.matcher(offsetRaw); + long offsetVal = 0; + boolean offsetMatched = matcher.matches(); + if (offsetMatched) { + String offset = matcher.group(1); + offsetVal = Long.parseUnsignedLong(offset, 16); + } + if (offsetMatched && pcMatched + && (pcVal + offsetVal) % ADDRESS_ALIGNMENT_X86 == 0) { + return true; + } + return false; + } + + private static List findFibLines(List lines) { + boolean startReached = false; + boolean endReached = false; + List interestingLines = new ArrayList<>(); + for (String line : lines) { + if (line.contains(LingeredAppWithNativeMethod.THREAD_NAME)) { + startReached = true; + } + if (startReached && line.contains("-------")) { + endReached = true; + } + if (startReached && !endReached) { + Matcher matcher = LINE_PATTERN.matcher(line); + if (matcher.matches()) { + interestingLines.add(line); + } + } + } + return interestingLines; + } + + private static void runJstackMixedInLoop(LingeredApp app) throws Exception { + for (int i = 0; i < MAX_ITERATIONS; i++) { + JDKToolLauncher launcher = JDKToolLauncher + .createUsingTestJDK("jhsdb"); + launcher.addToolArg("jstack"); + launcher.addToolArg("--mixed"); + launcher.addToolArg("--pid"); + launcher.addToolArg(Long.toString(app.getPid())); + + ProcessBuilder pb = new ProcessBuilder(); + pb.command(launcher.getCommand()); + Process jhsdb = pb.start(); + OutputAnalyzer out = new OutputAnalyzer(jhsdb); + + jhsdb.waitFor(); + + System.out.println(out.getStdout()); + System.err.println(out.getStderr()); + + out.shouldContain(LingeredAppWithNativeMethod.THREAD_NAME); + if (isFibAndAlignedAddress(out.asLines())) { + System.out.println("DEBUG: Test triggered interesting condition."); + out.shouldNotContain("sun.jvm.hotspot.debugger.UnmappedAddressException:"); + System.out.println("DEBUG: Test PASSED."); + return; // If we've reached here, all is well. + } + System.out.println("DEBUG: Iteration: " + (i + 1) + + " - Test didn't trigger interesting condition."); + out.shouldNotContain("sun.jvm.hotspot.debugger.UnmappedAddressException:"); + } + System.out.println("DEBUG: Test didn't trigger interesting condition " + + "but no UnmappedAddressException was thrown. PASS!"); + } + + public static void main(String... args) throws Exception { + + LingeredApp app = null; + + try { + List vmArgs = new ArrayList(Utils.getVmOptions()); + // Needed for LingeredApp to be able to resolve native library. + String libPath = System.getProperty("java.library.path"); + if (libPath != null) { + vmArgs.add("-Djava.library.path=" + libPath); + } + + app = new LingeredAppWithNativeMethod(); + LingeredApp.startApp(vmArgs, app); + System.out.println("Started LingeredApp with pid " + app.getPid()); + runJstackMixedInLoop(app); + System.out.println("Test Completed"); + } catch (Throwable e) { + e.printStackTrace(); + throw e; + } finally { + LingeredApp.stopApp(app); + } + } +} diff -r edcf0d527658 -r 2e98c7737d8f test/hotspot/jtreg/serviceability/sa/libNoFramePointer.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/hotspot/jtreg/serviceability/sa/libNoFramePointer.c Mon Jul 23 18:08:46 2018 +0200 @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2018, 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 + +static jlong fib(jint num) { + if (num == 0) { + return 0; + } + if (num <= 2) { + return 1; + } + return fib(num - 2) + fib(num -1); +} + +static void callCallback(JNIEnv *env, jclass cls, jobject target, jlong result) { + jmethodID mid = (*env)->GetMethodID(env, cls, "callback", "(J)V"); + if (mid == NULL) { + jclass nsme = (jclass) (*env)->NewGlobalRef(env, (*env)->FindClass(env, "java/lang/NoSuchMethodException")); + if (nsme != NULL) { + (*env)->ThrowNew(env, nsme, "Can't find method callback()"); + } + return; + } + (*env)->CallVoidMethod(env, target, mid, result); +} + +static void calculateAndCallCallback(JNIEnv *env, jclass cls, jobject target, jint num) { + jlong result = -1; + result = fib(num); + callCallback(env, cls, target, result); +} + +JNIEXPORT void JNICALL +Java_LingeredAppWithNativeMethod_callJNI(JNIEnv *env, jclass cls, jobject target, jint num) { + calculateAndCallCallback(env, cls, target, num); +}