diff --git a/0002-8179498-attach-in-linux-should-be-relative-to-proc-p.patch b/0002-8179498-attach-in-linux-should-be-relative-to-proc-p.patch new file mode 100644 index 0000000..4d77560 --- /dev/null +++ b/0002-8179498-attach-in-linux-should-be-relative-to-proc-p.patch @@ -0,0 +1,136 @@ +Date: Tue, 30 May 2023 03:43:28 +0000 +Subject: [PATCH 02/59] 8179498: attach in linux should be relative to + /proc/pid/root and namespace aware +Bug url: https://bugs.openjdk.org/browse/JDK-8179498 +--- + .../sun/tools/attach/LinuxVirtualMachine.java | 70 ++++++++++++++++--- + 1 file changed, 62 insertions(+), 8 deletions(-) + +diff --git a/jdk/src/solaris/classes/sun/tools/attach/LinuxVirtualMachine.java b/jdk/src/solaris/classes/sun/tools/attach/LinuxVirtualMachine.java +index 20fdb5c0d..cc2ac0df2 100644 +--- a/jdk/src/solaris/classes/sun/tools/attach/LinuxVirtualMachine.java ++++ b/jdk/src/solaris/classes/sun/tools/attach/LinuxVirtualMachine.java +@@ -32,6 +32,10 @@ import com.sun.tools.attach.spi.AttachProvider; + import java.io.InputStream; + import java.io.IOException; + import java.io.File; ++import java.nio.charset.StandardCharsets; ++import java.nio.file.Path; ++import java.nio.file.Paths; ++import java.nio.file.Files; + + /* + * Linux implementation of HotSpotVirtualMachine +@@ -66,12 +70,15 @@ public class LinuxVirtualMachine extends HotSpotVirtualMachine { + throw new AttachNotSupportedException("Invalid process identifier"); + } + ++ // Try to resolve to the "inner most" pid namespace ++ int ns_pid = getNamespacePid(pid); ++ + // Find the socket file. If not found then we attempt to start the + // attach mechanism in the target VM by sending it a QUIT signal. + // Then we attempt to find the socket file again. +- path = findSocketFile(pid); ++ path = findSocketFile(pid, ns_pid); + if (path == null) { +- File f = createAttachFile(pid); ++ File f = createAttachFile(pid, ns_pid); + try { + // On LinuxThreads each thread is a process and we don't have the + // pid of the VMThread which has SIGQUIT unblocked. To workaround +@@ -99,7 +106,7 @@ public class LinuxVirtualMachine extends HotSpotVirtualMachine { + try { + Thread.sleep(delay); + } catch (InterruptedException x) { } +- path = findSocketFile(pid); ++ path = findSocketFile(pid, ns_pid); + i++; + } while (i <= retries && path == null); + if (path == null) { +@@ -272,8 +279,12 @@ public class LinuxVirtualMachine extends HotSpotVirtualMachine { + } + + // Return the socket file for the given process. +- private String findSocketFile(int pid) { +- File f = new File(tmpdir, ".java_pid" + pid); ++ private String findSocketFile(int pid, int ns_pid) { ++ // A process may not exist in the same mount namespace as the caller. ++ // Instead, attach relative to the target root filesystem as exposed by ++ // procfs regardless of namespaces. ++ String root = "/proc/" + pid + "/root/" + tmpdir; ++ File f = new File(root, ".java_pid" + ns_pid); + if (!f.exists()) { + return null; + } +@@ -284,14 +295,23 @@ public class LinuxVirtualMachine extends HotSpotVirtualMachine { + // if not already started. The client creates a .attach_pid file in the + // target VM's working directory (or temp directory), and the SIGQUIT handler + // checks for the file. +- private File createAttachFile(int pid) throws IOException { +- String fn = ".attach_pid" + pid; ++ private File createAttachFile(int pid, int ns_pid) throws IOException { ++ String fn = ".attach_pid" + ns_pid; + String path = "/proc/" + pid + "/cwd/" + fn; + File f = new File(path); + try { + f.createNewFile(); + } catch (IOException x) { +- f = new File(tmpdir, fn); ++ String root; ++ if (pid != ns_pid) { ++ // A process may not exist in the same mount namespace as the caller. ++ // Instead, attach relative to the target root filesystem as exposed by ++ // procfs regardless of namespaces. ++ root = "/proc/" + pid + "/root/" + tmpdir; ++ } else { ++ root = tmpdir; ++ } ++ f = new File(root, fn); + f.createNewFile(); + } + return f; +@@ -317,6 +337,40 @@ public class LinuxVirtualMachine extends HotSpotVirtualMachine { + } + + ++ // Return the inner most namespaced PID if there is one, ++ // otherwise return the original PID. ++ private int getNamespacePid(int pid) throws AttachNotSupportedException, IOException { ++ // Assuming a real procfs sits beneath, reading this doesn't block ++ // nor will it consume a lot of memory. ++ String statusFile = "/proc/" + pid + "/status"; ++ File f = new File(statusFile); ++ if (!f.exists()) { ++ return pid; // Likely a bad pid, but this is properly handled later. ++ } ++ ++ Path statusPath = Paths.get(statusFile); ++ ++ try { ++ for (String line : Files.readAllLines(statusPath, StandardCharsets.UTF_8)) { ++ String[] parts = line.split(":"); ++ if (parts.length == 2 && parts[0].trim().equals("NSpid")) { ++ parts = parts[1].trim().split("\\s+"); ++ // The last entry represents the PID the JVM "thinks" it is. ++ // Even in non-namespaced pids these entries should be ++ // valid. You could refer to it as the inner most pid. ++ int ns_pid = Integer.parseInt(parts[parts.length - 1]); ++ return ns_pid; ++ } ++ } ++ // Old kernels may not have NSpid field (i.e. 3.10). ++ // Fallback to original pid in the event we cannot deduce. ++ return pid; ++ } catch (NumberFormatException | IOException x) { ++ throw new AttachNotSupportedException("Unable to parse namespace"); ++ } ++ } ++ ++ + //-- native methods + + static native boolean isLinuxThreads(); +-- +2.22.0 + diff --git a/0003-8187408-AbstractQueuedSynchronizer-wait-queue-corrup.patch b/0003-8187408-AbstractQueuedSynchronizer-wait-queue-corrup.patch new file mode 100644 index 0000000..771a4be --- /dev/null +++ b/0003-8187408-AbstractQueuedSynchronizer-wait-queue-corrup.patch @@ -0,0 +1,348 @@ +Date: Tue, 30 May 2023 15:42:59 +0800 +Subject: [PATCH 03/59] 8187408: AbstractQueuedSynchronizer wait queue corrupted when thread awaits without holding the lock + +Bug url: https://bugs.openjdk.org/browse/JDK-8187408 +--- + .../locks/AbstractQueuedLongSynchronizer.java | 20 +-- + .../locks/AbstractQueuedSynchronizer.java | 52 +++--- + jdk/test/java/util/Bug8187408/Bug8187408.java | 165 ++++++++++++++++++ + 3 files changed, 207 insertions(+), 30 deletions(-) + create mode 100644 jdk/test/java/util/Bug8187408/Bug8187408.java + +diff --git a/jdk/src/share/classes/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java b/jdk/src/share/classes/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java +index 8699fc9b8..5adc39e17 100644 +--- a/jdk/src/share/classes/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java ++++ b/jdk/src/share/classes/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java +@@ -64,11 +64,11 @@ public abstract class AbstractQueuedLongSynchronizer + private static final long serialVersionUID = 7373984972572414692L; + + /* +- To keep sources in sync, the remainder of this source file is +- exactly cloned from AbstractQueuedSynchronizer, replacing class +- name and changing ints related with sync state to longs. Please +- keep it that way. +- */ ++ * To keep sources in sync, the remainder of this source file is ++ * exactly cloned from AbstractQueuedSynchronizer, replacing class ++ * name and changing ints related with sync state to longs. Please ++ * keep it that way. ++ */ + + /** + * Creates a new {@code AbstractQueuedLongSynchronizer} instance +@@ -946,8 +946,7 @@ public abstract class AbstractQueuedLongSynchronizer + /** + * Returns {@code true} if synchronization is held exclusively with + * respect to the current (calling) thread. This method is invoked +- * upon each call to a non-waiting {@link ConditionObject} method. +- * (Waiting methods instead invoke {@link #release}.) ++ * upon each call to a {@link ConditionObject} method. + * + *

The default implementation throws {@link + * UnsupportedOperationException}. This method is invoked +@@ -1597,9 +1596,8 @@ public abstract class AbstractQueuedLongSynchronizer + } + + /** +- * Condition implementation for a {@link +- * AbstractQueuedLongSynchronizer} serving as the basis of a {@link +- * Lock} implementation. ++ * Condition implementation for a {@link AbstractQueuedLongSynchronizer} ++ * serving as the basis of a {@link Lock} implementation. + * + *

Method documentation for this class describes mechanics, + * not behavioral specifications from the point of view of Lock +@@ -1632,6 +1630,8 @@ public abstract class AbstractQueuedLongSynchronizer + * @return its new wait node + */ + private Node addConditionWaiter() { ++ if (!isHeldExclusively()) ++ throw new IllegalMonitorStateException(); + Node t = lastWaiter; + // If lastWaiter is cancelled, clean out. + if (t != null && t.waitStatus != Node.CONDITION) { +diff --git a/jdk/src/share/classes/java/util/concurrent/locks/AbstractQueuedSynchronizer.java b/jdk/src/share/classes/java/util/concurrent/locks/AbstractQueuedSynchronizer.java +index 9088e5894..0e2bcdfef 100644 +--- a/jdk/src/share/classes/java/util/concurrent/locks/AbstractQueuedSynchronizer.java ++++ b/jdk/src/share/classes/java/util/concurrent/locks/AbstractQueuedSynchronizer.java +@@ -192,19 +192,13 @@ import sun.misc.Unsafe; + * represent the locked state. While a non-reentrant lock + * does not strictly require recording of the current owner + * thread, this class does so anyway to make usage easier to monitor. +- * It also supports conditions and exposes +- * one of the instrumentation methods: ++ * It also supports conditions and exposes some instrumentation methods: + * + *

 {@code
+  * class Mutex implements Lock, java.io.Serializable {
+  *
+  *   // Our internal helper class
+  *   private static class Sync extends AbstractQueuedSynchronizer {
+- *     // Reports whether in locked state
+- *     protected boolean isHeldExclusively() {
+- *       return getState() == 1;
+- *     }
+- *
+  *     // Acquires the lock if state is zero
+  *     public boolean tryAcquire(int acquires) {
+  *       assert acquires == 1; // Otherwise unused
+@@ -218,14 +212,27 @@ import sun.misc.Unsafe;
+  *     // Releases the lock by setting state to zero
+  *     protected boolean tryRelease(int releases) {
+  *       assert releases == 1; // Otherwise unused
+- *       if (getState() == 0) throw new IllegalMonitorStateException();
++ *       if (!isHeldExclusively())
++ *         throw new IllegalMonitorStateException();
+  *       setExclusiveOwnerThread(null);
+  *       setState(0);
+  *       return true;
+  *     }
+  *
++ *     // Reports whether in locked state
++ *     public boolean isLocked() {
++ *       return getState() != 0;
++ *     }
++ *
++ *     public boolean isHeldExclusively() {
++ *       // a data race, but safe due to out-of-thin-air guarantees
++ *       return getExclusiveOwnerThread() == Thread.currentThread();
++ *     }
++ *
+  *     // Provides a Condition
+- *     Condition newCondition() { return new ConditionObject(); }
++ *     public Condition newCondition() {
++ *       return new ConditionObject();
++ *     }
+  *
+  *     // Deserializes properly
+  *     private void readObject(ObjectInputStream s)
+@@ -238,12 +245,17 @@ import sun.misc.Unsafe;
+  *   // The sync object does all the hard work. We just forward to it.
+  *   private final Sync sync = new Sync();
+  *
+- *   public void lock()                { sync.acquire(1); }
+- *   public boolean tryLock()          { return sync.tryAcquire(1); }
+- *   public void unlock()              { sync.release(1); }
+- *   public Condition newCondition()   { return sync.newCondition(); }
+- *   public boolean isLocked()         { return sync.isHeldExclusively(); }
+- *   public boolean hasQueuedThreads() { return sync.hasQueuedThreads(); }
++ *   public void lock()              { sync.acquire(1); }
++ *   public boolean tryLock()        { return sync.tryAcquire(1); }
++ *   public void unlock()            { sync.release(1); }
++ *   public Condition newCondition() { return sync.newCondition(); }
++ *   public boolean isLocked()       { return sync.isLocked(); }
++ *   public boolean isHeldByCurrentThread() {
++ *     return sync.isHeldExclusively();
++ *   }
++ *   public boolean hasQueuedThreads() {
++ *     return sync.hasQueuedThreads();
++ *   }
+  *   public void lockInterruptibly() throws InterruptedException {
+  *     sync.acquireInterruptibly(1);
+  *   }
+@@ -1168,8 +1180,7 @@ public abstract class AbstractQueuedSynchronizer
+     /**
+      * Returns {@code true} if synchronization is held exclusively with
+      * respect to the current (calling) thread.  This method is invoked
+-     * upon each call to a non-waiting {@link ConditionObject} method.
+-     * (Waiting methods instead invoke {@link #release}.)
++     * upon each call to a {@link ConditionObject} method.
+      *
+      * 

The default implementation throws {@link + * UnsupportedOperationException}. This method is invoked +@@ -1819,9 +1830,8 @@ public abstract class AbstractQueuedSynchronizer + } + + /** +- * Condition implementation for a {@link +- * AbstractQueuedSynchronizer} serving as the basis of a {@link +- * Lock} implementation. ++ * Condition implementation for a {@link AbstractQueuedSynchronizer} ++ * serving as the basis of a {@link Lock} implementation. + * + *

Method documentation for this class describes mechanics, + * not behavioral specifications from the point of view of Lock +@@ -1852,6 +1862,8 @@ public abstract class AbstractQueuedSynchronizer + * @return its new wait node + */ + private Node addConditionWaiter() { ++ if (!isHeldExclusively()) ++ throw new IllegalMonitorStateException(); + Node t = lastWaiter; + // If lastWaiter is cancelled, clean out. + if (t != null && t.waitStatus != Node.CONDITION) { +diff --git a/jdk/test/java/util/Bug8187408/Bug8187408.java b/jdk/test/java/util/Bug8187408/Bug8187408.java +new file mode 100644 +index 000000000..fee6c730d +--- /dev/null ++++ b/jdk/test/java/util/Bug8187408/Bug8187408.java +@@ -0,0 +1,165 @@ ++/* ++ * Copyright (c) 2023, 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. ++ */ ++ ++ /* ++ * @test ++ * @bug 8187408 ++ * @summary AbstractQueuedSynchronizer wait queue corrupted when thread awaits without holding the lock ++ */ ++ ++import static java.util.concurrent.TimeUnit.MILLISECONDS; ++import java.util.ArrayList; ++import java.util.Date; ++import java.util.concurrent.CountDownLatch; ++import java.util.concurrent.ThreadLocalRandom; ++import java.util.concurrent.locks.Condition; ++import java.util.concurrent.locks.Lock; ++import java.util.concurrent.locks.ReentrantLock; ++ ++public class Bug8187408 { ++ static final long LONG_DELAY_MS = 10000L; ++ ++ public static void main(String[] args) throws InterruptedException { ++ final ThreadLocalRandom rnd = ThreadLocalRandom.current(); ++ final AwaitMethod awaitMethod = randomAwaitMethod(); ++ final int nThreads = rnd.nextInt(2, 10); ++ final ReentrantLock lock = new ReentrantLock(); ++ final Condition cond = lock.newCondition(); ++ final CountDownLatch done = new CountDownLatch(nThreads); ++ final ArrayList threads = new ArrayList<>(); ++ ++ Runnable rogue = () -> { ++ while (done.getCount() > 0) { ++ try { ++ // call await without holding lock?! ++ await(cond, awaitMethod); ++ throw new AssertionError("should throw"); ++ } ++ catch (IllegalMonitorStateException success) {} ++ catch (Throwable fail) { threadUnexpectedException(fail); }}}; ++ Thread rogueThread = new Thread(rogue, "rogue"); ++ threads.add(rogueThread); ++ rogueThread.start(); ++ ++ Runnable waiter = () -> { ++ lock.lock(); ++ try { ++ done.countDown(); ++ cond.await(); ++ } catch (Throwable fail) { ++ threadUnexpectedException(fail); ++ } finally { ++ lock.unlock(); ++ }}; ++ for (int i = 0; i < nThreads; i++) { ++ Thread thread = new Thread(waiter, "waiter"); ++ threads.add(thread); ++ thread.start(); ++ } ++ ++ assertTrue(done.await(LONG_DELAY_MS, MILLISECONDS)); ++ lock.lock(); ++ try { ++ assertEquals(nThreads, lock.getWaitQueueLength(cond)); ++ } finally { ++ cond.signalAll(); ++ lock.unlock(); ++ } ++ for (Thread thread : threads) { ++ thread.join(LONG_DELAY_MS); ++ assertFalse(thread.isAlive()); ++ } ++ } ++ ++ private static void assertTrue(boolean expr) { ++ if (!expr) ++ throw new RuntimeException("assertion failed"); ++ } ++ ++ private static void assertFalse(boolean expr) { ++ if (expr) ++ throw new RuntimeException("assertion failed"); ++ } ++ ++ private static void assertEquals(int i, int j) { ++ if (i != j) ++ throw new AssertionError(i + " != " + j); ++ } ++ ++ /** ++ * Records the given exception using {@link #threadRecordFailure}, ++ * then rethrows the exception, wrapping it in an AssertionError ++ * if necessary. ++ */ ++ private static void threadUnexpectedException(Throwable t) { ++ t.printStackTrace(); ++ if (t instanceof RuntimeException) ++ throw (RuntimeException) t; ++ else if (t instanceof Error) ++ throw (Error) t; ++ else ++ throw new AssertionError("unexpected exception: " + t, t); ++ } ++ ++ enum AwaitMethod { await, awaitTimed, awaitNanos, awaitUntil } ++ private static AwaitMethod randomAwaitMethod() { ++ AwaitMethod[] awaitMethods = AwaitMethod.values(); ++ return awaitMethods[ThreadLocalRandom.current().nextInt(awaitMethods.length)]; ++ } ++ ++ /** ++ * Returns a new Date instance representing a time at least ++ * delayMillis milliseconds in the future. ++ */ ++ private static Date delayedDate(long delayMillis) { ++ // Add 1 because currentTimeMillis is known to round into the past. ++ return new Date(System.currentTimeMillis() + delayMillis + 1); ++ } ++ ++ /** ++ * Awaits condition "indefinitely" using the specified AwaitMethod. ++ */ ++ private static void await(Condition c, AwaitMethod awaitMethod) ++ throws InterruptedException { ++ long timeoutMillis = 2 * LONG_DELAY_MS; ++ switch (awaitMethod) { ++ case await: ++ c.await(); ++ break; ++ case awaitTimed: ++ assertTrue(c.await(timeoutMillis, MILLISECONDS)); ++ break; ++ case awaitNanos: ++ long timeoutNanos = MILLISECONDS.toNanos(timeoutMillis); ++ long nanosRemaining = c.awaitNanos(timeoutNanos); ++ assertTrue(nanosRemaining > timeoutNanos / 2); ++ assertTrue(nanosRemaining <= timeoutNanos); ++ break; ++ case awaitUntil: ++ assertTrue(c.awaitUntil(delayedDate(timeoutMillis))); ++ break; ++ default: ++ throw new AssertionError(); ++ } ++ } ++} +\ No newline at end of file +-- +2.22.0 + diff --git a/0004-8193710-jcmd-l-and-jps-commands-do-not-list-Java-pro.patch b/0004-8193710-jcmd-l-and-jps-commands-do-not-list-Java-pro.patch new file mode 100644 index 0000000..3da4354 --- /dev/null +++ b/0004-8193710-jcmd-l-and-jps-commands-do-not-list-Java-pro.patch @@ -0,0 +1,985 @@ +Date: Wed, 31 May 2023 03:55:21 +0000 +Subject: [PATCH 04/59] 8193710: jcmd -l and jps commands do not list Java processes running in Docker containers + +Bug url: https://bugs.openjdk.org/browse/JDK-8193710 +--- + hotspot/src/os/linux/vm/perfMemory_linux.cpp | 102 ++++++-- + .../classes/sun/jvmstat/PlatformSupport.java | 114 +++++++++ + .../sun/jvmstat/PlatformSupportImpl.java | 227 ++++++++++++++++++ + .../protocol/local/LocalVmManager.java | 93 +++---- + .../monitor/protocol/local/PerfDataFile.java | 179 ++++++++------ + 5 files changed, 582 insertions(+), 133 deletions(-) + create mode 100644 jdk/src/share/classes/sun/jvmstat/PlatformSupport.java + create mode 100644 jdk/src/share/classes/sun/jvmstat/PlatformSupportImpl.java + +diff --git a/hotspot/src/os/linux/vm/perfMemory_linux.cpp b/hotspot/src/os/linux/vm/perfMemory_linux.cpp +index 8293b7168..b45032edb 100644 +--- a/hotspot/src/os/linux/vm/perfMemory_linux.cpp ++++ b/hotspot/src/os/linux/vm/perfMemory_linux.cpp +@@ -147,11 +147,23 @@ static void save_memory_to_file(char* addr, size_t size) { + + // return the user specific temporary directory name. + // ++// If containerized process, get dirname of ++// /proc/{vmid}/root/tmp/{PERFDATA_NAME_user} ++// otherwise /tmp/{PERFDATA_NAME_user} ++// + // the caller is expected to free the allocated memory. + // +-static char* get_user_tmp_dir(const char* user) { ++#define TMP_BUFFER_LEN (4+22) ++static char* get_user_tmp_dir(const char* user, int vmid, int nspid) { ++ char buffer[TMP_BUFFER_LEN]; ++ char* tmpdir = (char *)os::get_temp_directory(); ++ assert(strlen(tmpdir) == 4, "No longer using /tmp - update buffer size"); ++ ++ if (nspid != -1) { ++ jio_snprintf(buffer, TMP_BUFFER_LEN, "/proc/%d/root%s", vmid, tmpdir); ++ tmpdir = buffer; ++ } + +- const char* tmpdir = os::get_temp_directory(); + const char* perfdir = PERFDATA_NAME; + size_t nbytes = strlen(tmpdir) + strlen(perfdir) + strlen(user) + 3; + char* dirname = NEW_C_HEAP_ARRAY(char, nbytes, mtInternal); +@@ -500,7 +512,10 @@ static char* get_user_name(uid_t uid) { + // + // the caller is expected to free the allocated memory. + // +-static char* get_user_name_slow(int vmid, TRAPS) { ++// If nspid != -1, look in /proc/{vmid}/root/tmp for directories ++// containing nspid, otherwise just look for vmid in /tmp ++// ++static char* get_user_name_slow(int vmid, int nspid, TRAPS) { + + // short circuit the directory search if the process doesn't even exist. + if (kill(vmid, 0) == OS_ERR) { +@@ -516,8 +531,19 @@ static char* get_user_name_slow(int vmid, TRAPS) { + // directory search + char* oldest_user = NULL; + time_t oldest_ctime = 0; ++ char buffer[TMP_BUFFER_LEN]; ++ int searchpid; ++ char* tmpdirname = (char *)os::get_temp_directory(); ++ assert(strlen(tmpdirname) == 4, "No longer using /tmp - update buffer size"); + +- const char* tmpdirname = os::get_temp_directory(); ++ if (nspid == -1) { ++ searchpid = vmid; ++ } ++ else { ++ jio_snprintf(buffer, MAXPATHLEN, "/proc/%d/root%s", vmid, tmpdirname); ++ tmpdirname = buffer; ++ searchpid = nspid; ++ } + + // open the temp directory + DIR* tmpdirp = os::opendir(tmpdirname); +@@ -528,7 +554,7 @@ static char* get_user_name_slow(int vmid, TRAPS) { + } + + // for each entry in the directory that matches the pattern hsperfdata_*, +- // open the directory and check if the file for the given vmid exists. ++ // open the directory and check if the file for the given vmid or nspid exists. + // The file with the expected name and the latest creation date is used + // to determine the user name for the process id. + // +@@ -571,7 +597,7 @@ static char* get_user_name_slow(int vmid, TRAPS) { + errno = 0; + while ((udentry = os::readdir(subdirp)) != NULL) { + +- if (filename_to_pid(udentry->d_name) == vmid) { ++ if (filename_to_pid(udentry->d_name) == searchpid) { + struct stat statbuf; + int result; + +@@ -620,10 +646,51 @@ static char* get_user_name_slow(int vmid, TRAPS) { + return(oldest_user); + } + ++// Determine if the vmid is the parent pid ++// for a child in a PID namespace. ++// return the namespace pid if so, otherwise -1 ++static int get_namespace_pid(int vmid) { ++ char fname[24]; ++ int retpid = -1; ++ ++ snprintf(fname, sizeof(fname), "/proc/%d/status", vmid); ++ FILE *fp = fopen(fname, "r"); ++ ++ if (fp) { ++ int pid, nspid; ++ int ret; ++ while (!feof(fp)) { ++ ret = fscanf(fp, "NSpid: %d %d", &pid, &nspid); ++ if (ret == 1) { ++ break; ++ } ++ if (ret == 2) { ++ retpid = nspid; ++ break; ++ } ++ for (;;) { ++ int ch = fgetc(fp); ++ if (ch == EOF || ch == (int)'\n') break; ++ } ++ } ++ fclose(fp); ++ } ++ return retpid; ++} ++ + // return the name of the user that owns the JVM indicated by the given vmid. + // +-static char* get_user_name(int vmid, TRAPS) { +- return get_user_name_slow(vmid, THREAD); ++static char* get_user_name(int vmid, int *nspid, TRAPS) { ++ char *result = get_user_name_slow(vmid, *nspid, THREAD); ++ ++ // If we are examining a container process without PID namespaces enabled ++ // we need to use /proc/{pid}/root/tmp to find hsperfdata files. ++ if (result == NULL) { ++ result = get_user_name_slow(vmid, vmid, THREAD); ++ // Enable nspid logic going forward ++ if (result != NULL) *nspid = vmid; ++ } ++ return result; + } + + // return the file name of the backing store file for the named +@@ -631,13 +698,15 @@ static char* get_user_name(int vmid, TRAPS) { + // + // the caller is expected to free the allocated memory. + // +-static char* get_sharedmem_filename(const char* dirname, int vmid) { ++static char* get_sharedmem_filename(const char* dirname, int vmid, int nspid) { ++ ++ int pid = (nspid == -1) ? vmid : nspid; + + // add 2 for the file separator and a null terminator. + size_t nbytes = strlen(dirname) + UINT_CHARS + 2; + + char* name = NEW_C_HEAP_ARRAY(char, nbytes, mtInternal); +- snprintf(name, nbytes, "%s/%d", dirname, vmid); ++ snprintf(name, nbytes, "%s/%d", dirname, pid); + + return name; + } +@@ -929,8 +998,8 @@ static char* mmap_create_shared(size_t size) { + if (user_name == NULL) + return NULL; + +- char* dirname = get_user_tmp_dir(user_name); +- char* filename = get_sharedmem_filename(dirname, vmid); ++ char* dirname = get_user_tmp_dir(user_name, vmid, -1); ++ char* filename = get_sharedmem_filename(dirname, vmid, -1); + // get the short filename + char* short_filename = strrchr(filename, '/'); + if (short_filename == NULL) { +@@ -1076,8 +1145,11 @@ static void mmap_attach_shared(const char* user, int vmid, PerfMemory::PerfMemor + "Illegal access mode"); + } + ++ // determine if vmid is for a containerized process ++ int nspid = get_namespace_pid(vmid); ++ + if (user == NULL || strlen(user) == 0) { +- luser = get_user_name(vmid, CHECK); ++ luser = get_user_name(vmid, &nspid, CHECK); + } + else { + luser = user; +@@ -1088,7 +1160,7 @@ static void mmap_attach_shared(const char* user, int vmid, PerfMemory::PerfMemor + "Could not map vmid to user Name"); + } + +- char* dirname = get_user_tmp_dir(luser); ++ char* dirname = get_user_tmp_dir(luser, vmid, nspid); + + // since we don't follow symbolic links when creating the backing + // store file, we don't follow them when attaching either. +@@ -1099,7 +1171,7 @@ static void mmap_attach_shared(const char* user, int vmid, PerfMemory::PerfMemor + "Process not found"); + } + +- char* filename = get_sharedmem_filename(dirname, vmid); ++ char* filename = get_sharedmem_filename(dirname, vmid, nspid); + + // copy heap memory to resource memory. the open_sharedmem_file + // method below need to use the filename, but could throw an +diff --git a/jdk/src/share/classes/sun/jvmstat/PlatformSupport.java b/jdk/src/share/classes/sun/jvmstat/PlatformSupport.java +new file mode 100644 +index 000000000..84f3b2274 +--- /dev/null ++++ b/jdk/src/share/classes/sun/jvmstat/PlatformSupport.java +@@ -0,0 +1,114 @@ ++/* ++ * 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. Oracle designates this ++ * particular file as subject to the "Classpath" exception as provided ++ * by Oracle in the LICENSE file that accompanied this code. ++ * ++ * 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.jvmstat; ++ ++import java.io.File; ++import java.lang.reflect.Constructor; ++import java.util.Collections; ++import java.util.List; ++import sun.misc.VMSupport; ++ ++/* ++ * Support routines handling temp directory locating ++ * and process ID extraction. ++ */ ++public class PlatformSupport { ++ private static final String tmpDirName; ++ static { ++ /* ++ * For this to work, the target VM and this code need to use ++ * the same directory. Instead of guessing which directory the ++ * VM is using, we will ask. ++ */ ++ String tmpdir = VMSupport.getVMTemporaryDirectory(); ++ ++ /* ++ * Assure that the string returned has a trailing File.separator ++ * character. This check was added because the Linux implementation ++ * changed such that the java.io.tmpdir string no longer terminates ++ * with a File.separator character. ++ */ ++ if (tmpdir.lastIndexOf(File.separator) != (tmpdir.length()-1)) { ++ tmpdir = tmpdir + File.separator; ++ } ++ tmpDirName = tmpdir; ++ } ++ ++ public static PlatformSupport getInstance() { ++ try { ++ Class c = Class.forName("sun.jvmstat.PlatformSupportImpl"); ++ @SuppressWarnings("unchecked") ++ Constructor cntr = (Constructor) c.getConstructor(); ++ return cntr.newInstance(); ++ } catch (ClassNotFoundException e) { ++ return new PlatformSupport(); ++ } catch (ReflectiveOperationException e) { ++ throw new InternalError(e); ++ } ++ } ++ ++ // package-private ++ PlatformSupport() {} ++ ++ /* ++ * Return the OS specific temporary directory ++ */ ++ public static String getTemporaryDirectory() { ++ return tmpDirName; ++ } ++ ++ /* ++ * Return a list of the temporary directories that the VM uses ++ * for the attach and perf data files. This function returns ++ * the traditional temp directory in addition to any paths ++ * accessible by the host which map to temp directories used ++ * by containers. The container functionality is only currently ++ * supported on Linux platforms. ++ * ++ * It is important that this directory is well-known and the ++ * same for all VM instances. It cannot be affected by configuration ++ * variables such as java.io.tmpdir. ++ */ ++ public List getTemporaryDirectories(int vmid) { ++ // Add the default temporary directory only ++ return Collections.singletonList(tmpDirName); ++ } ++ ++ /* ++ * Extract the host PID from a file path. ++ */ ++ public int getLocalVmId(File file) throws NumberFormatException { ++ return Integer.parseInt(file.getName()); ++ } ++ ++ ++ /* ++ * Return the inner most namespaced PID if there is one, ++ * otherwise return the original PID. ++ */ ++ public int getNamespaceVmId(int pid) { ++ return pid; ++ } ++} +diff --git a/jdk/src/share/classes/sun/jvmstat/PlatformSupportImpl.java b/jdk/src/share/classes/sun/jvmstat/PlatformSupportImpl.java +new file mode 100644 +index 000000000..4d1d718ab +--- /dev/null ++++ b/jdk/src/share/classes/sun/jvmstat/PlatformSupportImpl.java +@@ -0,0 +1,227 @@ ++/* ++ * 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. Oracle designates this ++ * particular file as subject to the "Classpath" exception as provided ++ * by Oracle in the LICENSE file that accompanied this code. ++ * ++ * 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.jvmstat; ++ ++import java.io.*; ++import java.util.*; ++import java.util.regex.*; ++import java.nio.file.Path; ++import java.nio.file.Paths; ++import java.nio.file.Files; ++import java.nio.charset.*; ++ ++/* ++ * Linux specific implementation of the PlatformSupport routines ++ * providing process ID and temp directory support for host and ++ * cgroup container processes. ++ */ ++public class PlatformSupportImpl extends PlatformSupport { ++ private static final String containerTmpPath = "/root" + getTemporaryDirectory(); ++ private static final String pidPatternStr = "^[0-9]+$"; ++ ++ /* ++ * Return the temporary directories that the VM uses for the attach ++ * and perf data files. This function returns the traditional ++ * /tmp directory in addition to paths within the /proc file system ++ * allowing access to container tmp directories such as /proc/{pid}/root/tmp. ++ * ++ * It is important that this directory is well-known and the ++ * same for all VM instances. It cannot be affected by configuration ++ * variables such as java.io.tmpdir. ++ * ++ * Implementation Details: ++ * ++ * Java processes that run in docker containers are typically running ++ * under cgroups with separate pid namespaces which means that pids ++ * within the container are different that the pid which is visible ++ * from the host. The container pids typically start with 1 and ++ * increase. The java process running in the container will use these ++ * pids when creating the hsperfdata files. In order to locate java ++ * processes that are running in containers, we take advantage of ++ * the Linux proc file system which maps the containers tmp directory ++ * to the hosts under /proc/{hostpid}/root/tmp. We use the /proc status ++ * file /proc/{hostpid}/status to determine the containers pid and ++ * then access the hsperfdata file. The status file contains an ++ * entry "NSPid:" which shows the mapping from the hostpid to the ++ * containers pid. ++ * ++ * Example: ++ * ++ * NSPid: 24345 11 ++ * ++ * In this example process 24345 is visible from the host, ++ * is running under the PID namespace and has a container specific ++ * pid of 11. ++ * ++ * The search for Java processes is done by first looking in the ++ * traditional /tmp for host process hsperfdata files and then ++ * the search will container in every /proc/{pid}/root/tmp directory. ++ * There are of course added complications to this search that ++ * need to be taken into account. ++ * ++ * 1. duplication of tmp directories ++ * ++ * /proc/{hostpid}/root/tmp directories exist for many processes ++ * that are running on a Linux kernel that has cgroups enabled even ++ * if they are not running in a container. To avoid this duplication, ++ * we compare the inode of the /proc tmp directories to /tmp and ++ * skip these duplicated directories. ++ * ++ * 2. Containerized processes without PID namespaces being enabled. ++ * ++ * If a container is running a Java process without namespaces being ++ * enabled, an hsperfdata file will only be located at ++ * /proc/{hostpid}/root/tmp/{hostpid}. This is handled by ++ * checking the last component in the path for both the hostpid ++ * and potential namespacepids (if one exists). ++ */ ++ public List getTemporaryDirectories(int pid) { ++ FilenameFilter pidFilter; ++ Matcher pidMatcher; ++ Pattern pidPattern = Pattern.compile(pidPatternStr); ++ long tmpInode = 0; ++ ++ File procdir = new File("/proc"); ++ ++ if (pid != 0) { ++ pidPattern = Pattern.compile(Integer.toString(pid)); ++ } ++ else { ++ pidPattern = Pattern.compile(pidPatternStr); ++ } ++ pidMatcher = pidPattern.matcher(""); ++ ++ // Add the default temporary directory first ++ List v = new ArrayList<>(); ++ v.add(getTemporaryDirectory()); ++ ++ try { ++ File f = new File(getTemporaryDirectory()); ++ tmpInode = (Long)Files.getAttribute(f.toPath(), "unix:ino"); ++ } ++ catch (IOException e) {} ++ ++ pidFilter = new FilenameFilter() { ++ public boolean accept(File dir, String name) { ++ if (!dir.isDirectory()) ++ return false; ++ pidMatcher.reset(name); ++ return pidMatcher.matches(); ++ } ++ }; ++ ++ File[] dirs = procdir.listFiles(pidFilter); ++ ++ // Add all unique /proc/{pid}/root/tmp dirs that are not mapped to /tmp ++ for (File dir : dirs) { ++ String containerTmpDir = dir.getAbsolutePath() + containerTmpPath; ++ File containerFile = new File(containerTmpDir); ++ ++ try { ++ long procInode = (Long)Files.getAttribute(containerFile.toPath(), "unix:ino"); ++ if (containerFile.exists() && containerFile.isDirectory() && ++ containerFile.canRead() && procInode != tmpInode) { ++ v.add(containerTmpDir); ++ } ++ } ++ catch (IOException e) {} ++ } ++ ++ return v; ++ } ++ ++ ++ /* ++ * Extract either the host PID or the NameSpace PID ++ * from a file path. ++ * ++ * File path should be in 1 of these 2 forms: ++ * ++ * /proc/{pid}/root/tmp/hsperfdata_{user}/{nspid} ++ * or ++ * /tmp/hsperfdata_{user}/{pid} ++ * ++ * In either case we want to return {pid} and NOT {nspid} ++ * ++ * This function filters out host pids which do not have ++ * associated hsperfdata files. This is due to the fact that ++ * getTemporaryDirectories will return /proc/{pid}/root/tmp ++ * paths for all container processes whether they are java ++ * processes or not causing duplicate matches. ++ */ ++ public int getLocalVmId(File file) throws NumberFormatException { ++ String p = file.getAbsolutePath(); ++ String s[] = p.split("\\/"); ++ ++ // Determine if this file is from a container ++ if (s.length == 7 && s[1].equals("proc")) { ++ int hostpid = Integer.parseInt(s[2]); ++ int nspid = Integer.parseInt(s[6]); ++ if (nspid == hostpid || nspid == getNamespaceVmId(hostpid)) { ++ return hostpid; ++ } ++ else { ++ return -1; ++ } ++ } ++ else { ++ return Integer.parseInt(file.getName()); ++ } ++ } ++ ++ ++ /* ++ * Return the inner most namespaced PID if there is one, ++ * otherwise return the original PID. ++ */ ++ public int getNamespaceVmId(int pid) { ++ // Assuming a real procfs sits beneath, reading this doesn't block ++ // nor will it consume a lot of memory. ++ Path statusPath = Paths.get("/proc", Integer.toString(pid), "status"); ++ if (Files.notExists(statusPath)) { ++ return pid; // Likely a bad pid, but this is properly handled later. ++ } ++ ++ try { ++ for (String line : Files.readAllLines(statusPath, StandardCharsets.UTF_8)) { ++ String[] parts = line.split(":"); ++ if (parts.length == 2 && parts[0].trim().equals("NSpid")) { ++ parts = parts[1].trim().split("\\s+"); ++ // The last entry represents the PID the JVM "thinks" it is. ++ // Even in non-namespaced pids these entries should be ++ // valid. You could refer to it as the inner most pid. ++ int ns_pid = Integer.parseInt(parts[parts.length - 1]); ++ return ns_pid; ++ } ++ } ++ // Old kernels may not have NSpid field (i.e. 3.10). ++ // Fallback to original pid in the event we cannot deduce. ++ return pid; ++ } catch (NumberFormatException | IOException x) { ++ return pid; ++ } ++ } ++} +diff --git a/jdk/src/share/classes/sun/jvmstat/perfdata/monitor/protocol/local/LocalVmManager.java b/jdk/src/share/classes/sun/jvmstat/perfdata/monitor/protocol/local/LocalVmManager.java +index fde75fb2d..35d25700d 100644 +--- a/jdk/src/share/classes/sun/jvmstat/perfdata/monitor/protocol/local/LocalVmManager.java ++++ b/jdk/src/share/classes/sun/jvmstat/perfdata/monitor/protocol/local/LocalVmManager.java +@@ -1,5 +1,5 @@ + /* +- * Copyright (c) 2004, 2007, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2004, 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 +@@ -45,7 +45,7 @@ import java.io.*; + */ + public class LocalVmManager { + private String userName; // user name for monitored jvm +- private File tmpdir; ++ private List tmpdirs; + private Pattern userPattern; + private Matcher userMatcher; + private FilenameFilter userFilter; +@@ -77,8 +77,9 @@ public class LocalVmManager { + public LocalVmManager(String user) { + this.userName = user; + ++ + if (userName == null) { +- tmpdir = new File(PerfDataFile.getTempDirectory()); ++ tmpdirs = PerfDataFile.getTempDirectories(null, 0); + userPattern = Pattern.compile(PerfDataFile.userDirNamePattern); + userMatcher = userPattern.matcher(""); + +@@ -89,7 +90,7 @@ public class LocalVmManager { + } + }; + } else { +- tmpdir = new File(PerfDataFile.getTempDirectory(userName)); ++ tmpdirs = PerfDataFile.getTempDirectories(userName, 0); + } + + filePattern = Pattern.compile(PerfDataFile.fileNamePattern); +@@ -134,65 +135,73 @@ public class LocalVmManager { + */ + Set jvmSet = new HashSet(); + +- if (! tmpdir.isDirectory()) { +- return jvmSet; +- } ++ for (String dir : tmpdirs) { ++ File tmpdir = new File(dir); ++ if (! tmpdir.isDirectory()) { ++ continue; ++ } + +- if (userName == null) { +- /* +- * get a list of all of the user temporary directories and +- * iterate over the list to find any files within those directories. +- */ +- File[] dirs = tmpdir.listFiles(userFilter); +- +- for (int i = 0 ; i < dirs.length; i ++) { +- if (!dirs[i].isDirectory()) { +- continue; ++ if (userName == null) { ++ /* ++ * get a list of all of the user temporary directories and ++ * iterate over the list to find any files within those directories. ++ */ ++ File[] dirs = tmpdir.listFiles(userFilter); ++ for (int i = 0 ; i < dirs.length; i ++) { ++ if (!dirs[i].isDirectory()) { ++ continue; ++ } ++ ++ // get a list of files from the directory ++ File[] files = dirs[i].listFiles(fileFilter); ++ if (files != null) { ++ for (int j = 0; j < files.length; j++) { ++ if (files[j].isFile() && files[j].canRead()) { ++ int vmid = PerfDataFile.getLocalVmId(files[j]); ++ if (vmid != -1) { ++ jvmSet.add(vmid); ++ } ++ } ++ } ++ } + } ++ } else { ++ /* ++ * Check if the user directory can be accessed. Any of these ++ * conditions may have asynchronously changed between subsequent ++ * calls to this method. ++ */ + +- // get a list of files from the directory +- File[] files = dirs[i].listFiles(fileFilter); ++ // get the list of files from the specified user directory ++ File[] files = tmpdir.listFiles(fileFilter); + + if (files != null) { + for (int j = 0; j < files.length; j++) { + if (files[j].isFile() && files[j].canRead()) { +- jvmSet.add(new Integer( +- PerfDataFile.getLocalVmId(files[j]))); ++ int vmid = PerfDataFile.getLocalVmId(files[j]); ++ if (vmid != -1) { ++ jvmSet.add(vmid); ++ } + } + } + } + } +- } else { +- /* +- * Check if the user directory can be accessed. Any of these +- * conditions may have asynchronously changed between subsequent +- * calls to this method. +- */ + +- // get the list of files from the specified user directory +- File[] files = tmpdir.listFiles(fileFilter); ++ // look for any 1.4.1 files ++ File[] files = tmpdir.listFiles(tmpFileFilter); + + if (files != null) { + for (int j = 0; j < files.length; j++) { + if (files[j].isFile() && files[j].canRead()) { +- jvmSet.add(new Integer( +- PerfDataFile.getLocalVmId(files[j]))); ++ int vmid = PerfDataFile.getLocalVmId(files[j]); ++ if (vmid != -1) { ++ jvmSet.add(vmid); ++ } + } + } + } + } + +- // look for any 1.4.1 files +- File[] files = tmpdir.listFiles(tmpFileFilter); +- if (files != null) { +- for (int j = 0; j < files.length; j++) { +- if (files[j].isFile() && files[j].canRead()) { +- jvmSet.add(new Integer( +- PerfDataFile.getLocalVmId(files[j]))); +- } +- } +- } +- + return jvmSet; + } + } +diff --git a/jdk/src/share/classes/sun/jvmstat/perfdata/monitor/protocol/local/PerfDataFile.java b/jdk/src/share/classes/sun/jvmstat/perfdata/monitor/protocol/local/PerfDataFile.java +index 62c64795b..26045ac5e 100644 +--- a/jdk/src/share/classes/sun/jvmstat/perfdata/monitor/protocol/local/PerfDataFile.java ++++ b/jdk/src/share/classes/sun/jvmstat/perfdata/monitor/protocol/local/PerfDataFile.java +@@ -1,5 +1,5 @@ + /* +- * Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2004, 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 +@@ -26,7 +26,11 @@ + package sun.jvmstat.perfdata.monitor.protocol.local; + + import java.io.File; ++import java.util.ArrayList; ++import java.util.List; ++import java.util.stream.Collectors; + import java.io.FilenameFilter; ++import sun.jvmstat.PlatformSupport; + + /** + * Class to provide translations from the local Vm Identifier +@@ -44,11 +48,6 @@ import java.io.FilenameFilter; + public class PerfDataFile { + private PerfDataFile() { }; + +- /** +- * The name of the of the system dependent temporary directory +- */ +- public static final String tmpDirName; +- + /** + * The file name prefix for PerfData shared memory files. + *

+@@ -79,6 +78,12 @@ public class PerfDataFile { + "^hsperfdata_[0-9]+(_[1-2]+)?$"; + + ++ /** ++ * Platform Specific methods for looking up temporary directories ++ * and process IDs. ++ */ ++ private static final PlatformSupport platSupport = PlatformSupport.getInstance(); ++ + /** + * Get a File object for the instrumentation backing store file + * for the JVM identified by the given local Vm Identifier. +@@ -93,7 +98,7 @@ public class PerfDataFile { + * @return File - a File object to the backing store file for the named + * shared memory region of the target JVM. + * @see java.io.File +- * @see #getTempDirectory() ++ * @see #getTempDirectories() + */ + public static File getFile(int lvmid) { + if (lvmid == 0) { +@@ -106,56 +111,65 @@ public class PerfDataFile { + return null; + } + +- /* +- * iterate over all files in all directories in tmpDirName that +- * match the file name patterns. +- */ +- File tmpDir = new File(tmpDirName); +- String[] files = tmpDir.list(new FilenameFilter() { +- public boolean accept(File dir, String name) { +- if (!name.startsWith(dirNamePrefix)) { +- return false; +- } +- File candidate = new File(dir, name); +- return ((candidate.isDirectory() || candidate.isFile()) +- && candidate.canRead()); +- } +- }); +- +- long newestTime = 0; ++ List tmpDirs = getTempDirectories(null, lvmid); + File newest = null; + +- for (int i = 0; i < files.length; i++) { +- File f = new File(tmpDirName + files[i]); +- File candidate = null; ++ for (String dir : tmpDirs) { ++ /* ++ * iterate over all files in all directories in this tmpDir that ++ * match the file name patterns. ++ */ ++ File tmpDir = new File(dir); ++ String[] files = tmpDir.list(new FilenameFilter() { ++ public boolean accept(File dir, String name) { ++ if (!name.startsWith(dirNamePrefix)) { ++ return false; ++ } ++ File candidate = new File(dir, name); ++ return ((candidate.isDirectory() || candidate.isFile()) ++ && candidate.canRead()); ++ } ++ }); + +- if (f.exists() && f.isDirectory()) { +- /* +- * found a directory matching the name patterns. This +- * is a 1.4.2 hsperfdata_ directory. Check for +- * file named in that directory +- */ +- String name = Integer.toString(lvmid); +- candidate = new File(f.getName(), name); ++ long newestTime = 0; + +- } else if (f.exists() && f.isFile()) { +- /* +- * found a file matching the name patterns. This +- * is a 1.4.1 hsperfdata_ file. +- */ +- candidate = f; ++ for (String file : files) { ++ File f = new File(dir + file); ++ File candidate = null; + +- } else { +- // unexpected - let conditional below filter this one out +- candidate = f; +- } ++ if (f.exists() && f.isDirectory()) { ++ /* ++ * found a directory matching the name patterns. This ++ * is a 1.4.2 hsperfdata_ directory. Check for ++ * file named in that directory ++ */ ++ String name = f.getAbsolutePath() + File.separator + lvmid; ++ candidate = new File(name); ++ // Try NameSpace Id if Host Id doesn't exist. ++ if (!candidate.exists()) { ++ name = f.getAbsolutePath() + File.separator + ++ platSupport.getNamespaceVmId(lvmid); ++ candidate = new File(name); ++ } ++ } else if (f.exists() && f.isFile()) { ++ /* ++ * found a file matching the name patterns. This ++ * is a 1.4.1 hsperfdata_ file. ++ */ ++ candidate = f; + +- if (candidate.exists() && candidate.isFile() +- && candidate.canRead()) { +- long modTime = candidate.lastModified(); +- if (modTime >= newestTime) { +- newestTime = modTime; +- newest = candidate; ++ } else { ++ // unexpected - let conditional below filter this one out ++ candidate = f; ++ } ++ ++ if (candidate.exists() && candidate.isFile() ++ && candidate.canRead()) { ++ long modTime = candidate.lastModified(); ++ if (modTime >= newestTime) { ++ newestTime = modTime; ++ newest = candidate; ++ } + } + } + } +@@ -176,7 +190,7 @@ public class PerfDataFile { + * @return File - a File object to the backing store file for the named + * shared memory region of the target JVM. + * @see java.io.File +- * @see #getTempDirectory() ++ * @see #getTempDirectories() + */ + public static File getFile(String user, int lvmid) { + if (lvmid == 0) { +@@ -190,11 +204,22 @@ public class PerfDataFile { + } + + // first try for 1.4.2 and later JVMs +- String basename = getTempDirectory(user) + Integer.toString(lvmid); +- File f = new File(basename); ++ List tmpDirs = getTempDirectories(user, lvmid); ++ String basename; ++ File f; + +- if (f.exists() && f.isFile() && f.canRead()) { +- return f; ++ for (String dir : tmpDirs) { ++ basename = dir + lvmid; ++ f = new File(basename); ++ if (f.exists() && f.isFile() && f.canRead()) { ++ return f; ++ } ++ // Try NameSpace Id if Host Id doesn't exist. ++ basename = dir + platSupport.getNamespaceVmId(lvmid); ++ f = new File(basename); ++ if (f.exists() && f.isFile() && f.canRead()) { ++ return f; ++ } + } + + // No hit on 1.4.2 JVMs, try 1.4.1 files +@@ -235,7 +260,7 @@ public class PerfDataFile { + public static int getLocalVmId(File file) { + try { + // try 1.4.2 and later format first +- return Integer.parseInt(file.getName()); ++ return(platSupport.getLocalVmId(file)); + } catch (NumberFormatException e) { } + + // now try the 1.4.1 format +@@ -266,7 +291,7 @@ public class PerfDataFile { + * @return String - the name of the temporary directory. + */ + public static String getTempDirectory() { +- return tmpDirName; ++ return PlatformSupport.getTemporaryDirectory(); + } + + /** +@@ -282,26 +307,28 @@ public class PerfDataFile { + * @return String - the name of the temporary directory. + */ + public static String getTempDirectory(String user) { +- return tmpDirName + dirNamePrefix + user + File.separator; ++ return getTempDirectory() + dirNamePrefix + user + File.separator; + } + +- static { +- /* +- * For this to work, the target VM and this code need to use +- * the same directory. Instead of guessing which directory the +- * VM is using, we will ask. +- */ +- String tmpdir = sun.misc.VMSupport.getVMTemporaryDirectory(); +- +- /* +- * Assure that the string returned has a trailing File.separator +- * character. This check was added because the Linux implementation +- * changed such that the java.io.tmpdir string no longer terminates +- * with a File.separator character. +- */ +- if (tmpdir.lastIndexOf(File.separator) != (tmpdir.length()-1)) { +- tmpdir = tmpdir + File.separator; ++ /** ++ * Return the names of the temporary directories being searched for ++ * HotSpot PerfData backing store files. ++ *

++ * This method returns the traditional host temp directory but also ++ * includes a list of temp directories used by containers. ++ * ++ * @return List - A List of temporary directories to search. ++ */ ++ public static List getTempDirectories(String userName, int vmid) { ++ List list = platSupport.getTemporaryDirectories(vmid); ++ if (userName == null) { ++ return list; + } +- tmpDirName = tmpdir; ++ ++ List nameList = list.stream() ++ .map(name -> name + dirNamePrefix + userName + File.separator) ++ .collect(Collectors.toList()); ++ ++ return nameList; + } + } +-- +2.22.0 + diff --git a/0005-8196743-jstatd-doesn-t-see-new-Java-processes-inside.patch b/0005-8196743-jstatd-doesn-t-see-new-Java-processes-inside.patch new file mode 100644 index 0000000..431510b --- /dev/null +++ b/0005-8196743-jstatd-doesn-t-see-new-Java-processes-inside.patch @@ -0,0 +1,57 @@ +Date: Wed, 31 May 2023 09:22:26 +0000 +Subject: [PATCH 05/59] 8196743: jstatd doesn't see new Java processes inside Docker container + +Bug url: https://bugs.openjdk.org/browse/JDK-8196743 +--- + .../perfdata/monitor/protocol/local/LocalVmManager.java | 8 ++------ + 1 file changed, 2 insertions(+), 6 deletions(-) + +diff --git a/jdk/src/share/classes/sun/jvmstat/perfdata/monitor/protocol/local/LocalVmManager.java b/jdk/src/share/classes/sun/jvmstat/perfdata/monitor/protocol/local/LocalVmManager.java +index 35d25700d..4be281f65 100644 +--- a/jdk/src/share/classes/sun/jvmstat/perfdata/monitor/protocol/local/LocalVmManager.java ++++ b/jdk/src/share/classes/sun/jvmstat/perfdata/monitor/protocol/local/LocalVmManager.java +@@ -1,5 +1,5 @@ + /* +- * Copyright (c) 2004, 2018, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2004, 2021, 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 +@@ -45,7 +45,6 @@ import java.io.*; + */ + public class LocalVmManager { + private String userName; // user name for monitored jvm +- private List tmpdirs; + private Pattern userPattern; + private Matcher userMatcher; + private FilenameFilter userFilter; +@@ -77,9 +76,7 @@ public class LocalVmManager { + public LocalVmManager(String user) { + this.userName = user; + +- + if (userName == null) { +- tmpdirs = PerfDataFile.getTempDirectories(null, 0); + userPattern = Pattern.compile(PerfDataFile.userDirNamePattern); + userMatcher = userPattern.matcher(""); + +@@ -89,8 +86,6 @@ public class LocalVmManager { + return userMatcher.lookingAt(); + } + }; +- } else { +- tmpdirs = PerfDataFile.getTempDirectories(userName, 0); + } + + filePattern = Pattern.compile(PerfDataFile.fileNamePattern); +@@ -134,6 +129,7 @@ public class LocalVmManager { + * we'd see strange file names being matched by the matcher. + */ + Set jvmSet = new HashSet(); ++ List tmpdirs = PerfDataFile.getTempDirectories(userName, 0); + + for (String dir : tmpdirs) { + File tmpdir = new File(dir); +-- +2.22.0 + diff --git a/0006-8284330-jcmd-may-not-be-able-to-find-processes-in-th.patch b/0006-8284330-jcmd-may-not-be-able-to-find-processes-in-th.patch new file mode 100644 index 0000000..d36ffc0 --- /dev/null +++ b/0006-8284330-jcmd-may-not-be-able-to-find-processes-in-th.patch @@ -0,0 +1,113 @@ +Date: Wed, 31 May 2023 09:34:28 +0000 +Subject: [PATCH 06/59] 8284330: jcmd may not be able to find processes in the container + +Bug url: https://bugs.openjdk.org/browse/JDK-8284330 +--- + .../sun/jvmstat/PlatformSupportImpl.java | 57 ++++++++++++------- + 1 file changed, 37 insertions(+), 20 deletions(-) + +diff --git a/jdk/src/share/classes/sun/jvmstat/PlatformSupportImpl.java b/jdk/src/share/classes/sun/jvmstat/PlatformSupportImpl.java +index 4d1d718ab..38da80cc7 100644 +--- a/jdk/src/share/classes/sun/jvmstat/PlatformSupportImpl.java ++++ b/jdk/src/share/classes/sun/jvmstat/PlatformSupportImpl.java +@@ -1,5 +1,5 @@ + /* +- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2018, 2022, 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 +@@ -42,6 +42,32 @@ public class PlatformSupportImpl extends PlatformSupport { + private static final String containerTmpPath = "/root" + getTemporaryDirectory(); + private static final String pidPatternStr = "^[0-9]+$"; + ++ private long tmpInode; ++ private long tmpDev; ++ ++ public PlatformSupportImpl() { ++ super(); ++ try { ++ File f = new File(getTemporaryDirectory()); ++ Path tmpPath = f.toPath(); ++ tmpInode = (Long)Files.getAttribute(tmpPath, "unix:ino"); ++ tmpDev = (Long)Files.getAttribute(tmpPath, "unix:dev"); ++ } catch (IOException e) { ++ tmpInode = -1L; ++ tmpDev = -1L; ++ } ++ } ++ ++ private boolean tempDirectoryEquals(Path p) { ++ try { ++ long ino = (Long)Files.getAttribute(p, "unix:ino"); ++ long dev = (Long)Files.getAttribute(p, "unix:dev"); ++ return (ino == tmpInode) && (dev == tmpDev); ++ } catch (IOException e) { ++ return false; ++ } ++ } ++ + /* + * Return the temporary directories that the VM uses for the attach + * and perf data files. This function returns the traditional +@@ -84,11 +110,12 @@ public class PlatformSupportImpl extends PlatformSupport { + * + * 1. duplication of tmp directories + * +- * /proc/{hostpid}/root/tmp directories exist for many processes +- * that are running on a Linux kernel that has cgroups enabled even +- * if they are not running in a container. To avoid this duplication, +- * we compare the inode of the /proc tmp directories to /tmp and +- * skip these duplicated directories. ++ * When cgroups is enabled, the directory /proc/{pid}/root/tmp may ++ * exist even if the given pid is not running inside a container. In ++ * this case, this directory is usually the same as /tmp and should ++ * be skipped, or else we would get duplicated hsperfdata files. ++ * This case can be detected if the inode and device id of ++ * /proc/{pid}/root/tmp are the same as /tmp. + * + * 2. Containerized processes without PID namespaces being enabled. + * +@@ -102,7 +129,6 @@ public class PlatformSupportImpl extends PlatformSupport { + FilenameFilter pidFilter; + Matcher pidMatcher; + Pattern pidPattern = Pattern.compile(pidPatternStr); +- long tmpInode = 0; + + File procdir = new File("/proc"); + +@@ -118,12 +144,6 @@ public class PlatformSupportImpl extends PlatformSupport { + List v = new ArrayList<>(); + v.add(getTemporaryDirectory()); + +- try { +- File f = new File(getTemporaryDirectory()); +- tmpInode = (Long)Files.getAttribute(f.toPath(), "unix:ino"); +- } +- catch (IOException e) {} +- + pidFilter = new FilenameFilter() { + public boolean accept(File dir, String name) { + if (!dir.isDirectory()) +@@ -140,14 +160,11 @@ public class PlatformSupportImpl extends PlatformSupport { + String containerTmpDir = dir.getAbsolutePath() + containerTmpPath; + File containerFile = new File(containerTmpDir); + +- try { +- long procInode = (Long)Files.getAttribute(containerFile.toPath(), "unix:ino"); +- if (containerFile.exists() && containerFile.isDirectory() && +- containerFile.canRead() && procInode != tmpInode) { +- v.add(containerTmpDir); +- } ++ if (containerFile.exists() && containerFile.isDirectory() && ++ containerFile.canRead() && ++ !tempDirectoryEquals(containerFile.toPath())) { ++ v.add(containerTmpDir); + } +- catch (IOException e) {} + } + + return v; +-- +2.22.0 + diff --git a/0007-8241670-Enhance-heap-region-size-ergonomics-to-impro.patch b/0007-8241670-Enhance-heap-region-size-ergonomics-to-impro.patch new file mode 100644 index 0000000..e89d761 --- /dev/null +++ b/0007-8241670-Enhance-heap-region-size-ergonomics-to-impro.patch @@ -0,0 +1,146 @@ +Date: Mon, 5 Jun 2023 20:12:44 +0800 +Subject: [PATCH 07/59] 8241670: Enhance heap region size ergonomics to improve OOTB performance + +--- + .../g1/g1CollectorPolicy.cpp | 3 +- + .../vm/gc_implementation/g1/heapRegion.cpp | 29 ++++++++----------- + .../vm/gc_implementation/g1/heapRegion.hpp | 3 +- + .../gc_implementation/g1/heapRegionBounds.hpp | 3 +- + .../gc/arguments/TestG1HeapRegionSize.java | 3 +- + 5 files changed, 20 insertions(+), 21 deletions(-) + +diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1CollectorPolicy.cpp b/hotspot/src/share/vm/gc_implementation/g1/g1CollectorPolicy.cpp +index 05ce59987..0acdd2b69 100644 +--- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectorPolicy.cpp ++++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectorPolicy.cpp +@@ -1,5 +1,6 @@ + /* + * Copyright (c) 2001, 2020, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. 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 +@@ -184,7 +185,7 @@ G1CollectorPolicy::G1CollectorPolicy() : + // the region size on the heap size, but the heap size should be + // aligned with the region size. To get around this we use the + // unaligned values for the heap. +- HeapRegion::setup_heap_region_size(InitialHeapSize, MaxHeapSize); ++ HeapRegion::setup_heap_region_size(MaxHeapSize); + HeapRegionRemSet::setup_remset_size(); + + G1ErgoVerbose::initialize(); +diff --git a/hotspot/src/share/vm/gc_implementation/g1/heapRegion.cpp b/hotspot/src/share/vm/gc_implementation/g1/heapRegion.cpp +index 87cc73bee..28b21a9be 100644 +--- a/hotspot/src/share/vm/gc_implementation/g1/heapRegion.cpp ++++ b/hotspot/src/share/vm/gc_implementation/g1/heapRegion.cpp +@@ -1,5 +1,6 @@ + /* + * Copyright (c) 2001, 2015, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. 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 +@@ -107,29 +108,23 @@ size_t HeapRegion::max_region_size() { + return HeapRegionBounds::max_size(); + } + +-void HeapRegion::setup_heap_region_size(size_t initial_heap_size, size_t max_heap_size) { +- uintx region_size = G1HeapRegionSize; +- if (FLAG_IS_DEFAULT(G1HeapRegionSize)) { +- size_t average_heap_size = (initial_heap_size + max_heap_size) / 2; +- region_size = MAX2(average_heap_size / HeapRegionBounds::target_number(), ++void HeapRegion::setup_heap_region_size(size_t max_heap_size) { ++ uintx region_size = G1HeapRegionSize; ++ // G1HeapRegionSize = 0 means decide ergonomically. ++ if (region_size == 0) { ++ region_size = MAX2(max_heap_size / HeapRegionBounds::target_number(), + (uintx) HeapRegionBounds::min_size()); + } + +- int region_size_log = log2_long((jlong) region_size); +- // Recalculate the region size to make sure it's a power of +- // 2. This means that region_size is the largest power of 2 that's +- // <= what we've calculated so far. +- region_size = ((uintx)1 << region_size_log); ++ // Make sure region size is a power of 2. Rounding up since this ++ // is beneficial in most cases. ++ region_size = is_power_of_2(region_size) ? region_size : (size_t)1 << (log2_intptr(region_size) + 1); + + // Now make sure that we don't go over or under our limits. +- if (region_size < HeapRegionBounds::min_size()) { +- region_size = HeapRegionBounds::min_size(); +- } else if (region_size > HeapRegionBounds::max_size()) { +- region_size = HeapRegionBounds::max_size(); +- } ++ region_size = MIN2(MAX2(region_size, HeapRegionBounds::min_size()), HeapRegionBounds::max_size()); + +- // And recalculate the log. +- region_size_log = log2_long((jlong) region_size); ++ // Calculate the log for the region size. ++ int region_size_log = exact_log2_long((jlong)region_size); + + // Now, set up the globals. + guarantee(LogOfHRGrainBytes == 0, "we should only set it once"); +diff --git a/hotspot/src/share/vm/gc_implementation/g1/heapRegion.hpp b/hotspot/src/share/vm/gc_implementation/g1/heapRegion.hpp +index 5d2415e84..4e0afbac1 100644 +--- a/hotspot/src/share/vm/gc_implementation/g1/heapRegion.hpp ++++ b/hotspot/src/share/vm/gc_implementation/g1/heapRegion.hpp +@@ -1,5 +1,6 @@ + /* + * Copyright (c) 2001, 2014, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. 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 +@@ -333,7 +334,7 @@ class HeapRegion: public G1OffsetTableContigSpace { + // CardsPerRegion). All those fields are considered constant + // throughout the JVM's execution, therefore they should only be set + // up once during initialization time. +- static void setup_heap_region_size(size_t initial_heap_size, size_t max_heap_size); ++ static void setup_heap_region_size(size_t max_heap_size); + + // All allocated blocks are occupied by objects in a HeapRegion + bool block_is_obj(const HeapWord* p) const; +diff --git a/hotspot/src/share/vm/gc_implementation/g1/heapRegionBounds.hpp b/hotspot/src/share/vm/gc_implementation/g1/heapRegionBounds.hpp +index 1da7f24c1..c76dead88 100644 +--- a/hotspot/src/share/vm/gc_implementation/g1/heapRegionBounds.hpp ++++ b/hotspot/src/share/vm/gc_implementation/g1/heapRegionBounds.hpp +@@ -1,5 +1,6 @@ + /* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2022, Huawei Technologies Co., Ltd. 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 +@@ -40,7 +41,7 @@ private: + static const size_t MAX_REGION_SIZE = 32 * 1024 * 1024; + + // The automatic region size calculation will try to have around this +- // many regions in the heap (based on the min heap size). ++ // many regions in the heap. + static const size_t TARGET_REGION_NUMBER = 2048; + + public: +diff --git a/hotspot/test/gc/arguments/TestG1HeapRegionSize.java b/hotspot/test/gc/arguments/TestG1HeapRegionSize.java +index 0442d2c61..a9b5fa0cb 100644 +--- a/hotspot/test/gc/arguments/TestG1HeapRegionSize.java ++++ b/hotspot/test/gc/arguments/TestG1HeapRegionSize.java +@@ -1,5 +1,6 @@ + /* + * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. ++* Copyright (c) 2022, Huawei Technologies Co., Ltd. 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 +@@ -28,7 +29,7 @@ + * @summary Verify that the flag G1HeapRegionSize is updated properly + * @run main/othervm -Xmx64m TestG1HeapRegionSize 1048576 + * @run main/othervm -XX:G1HeapRegionSize=2m -Xmx64m TestG1HeapRegionSize 2097152 +- * @run main/othervm -XX:G1HeapRegionSize=3m -Xmx64m TestG1HeapRegionSize 2097152 ++ * @run main/othervm -XX:G1HeapRegionSize=3m -Xmx64m TestG1HeapRegionSize 4194304 + * @run main/othervm -XX:G1HeapRegionSize=64m -Xmx256m TestG1HeapRegionSize 33554432 + */ + +-- +2.22.0 + diff --git a/0008-8223162-Improve-ergonomics-for-Sparse-PRT-entry-sizi.patch b/0008-8223162-Improve-ergonomics-for-Sparse-PRT-entry-sizi.patch new file mode 100644 index 0000000..85f830a --- /dev/null +++ b/0008-8223162-Improve-ergonomics-for-Sparse-PRT-entry-sizi.patch @@ -0,0 +1,60 @@ +Date: Mon, 5 Jun 2023 20:26:02 +0800 +Subject: 8223162: Improve ergonomics for Sparse PRT entry sizing + +--- + .../share/vm/gc_implementation/g1/heapRegionRemSet.cpp | 8 ++++---- + .../share/vm/gc_implementation/g1/heapRegionRemSet.hpp | 1 + + hotspot/src/share/vm/gc_implementation/g1/sparsePRT.hpp | 4 +--- + 3 files changed, 6 insertions(+), 7 deletions(-) + +diff --git a/hotspot/src/share/vm/gc_implementation/g1/heapRegionRemSet.cpp b/hotspot/src/share/vm/gc_implementation/g1/heapRegionRemSet.cpp +index 8167d2b09..9e9391ba6 100644 +--- a/hotspot/src/share/vm/gc_implementation/g1/heapRegionRemSet.cpp ++++ b/hotspot/src/share/vm/gc_implementation/g1/heapRegionRemSet.cpp +@@ -868,12 +868,12 @@ HeapRegionRemSet::HeapRegionRemSet(G1BlockOffsetSharedArray* bosa, + } + + void HeapRegionRemSet::setup_remset_size() { +- // Setup sparse and fine-grain tables sizes. +- // table_size = base * (log(region_size / 1M) + 1) + const int LOG_M = 20; +- int region_size_log_mb = MAX2(HeapRegion::LogOfHRGrainBytes - LOG_M, 0); ++ guarantee(HeapRegion::LogOfHRGrainBytes >= LOG_M, err_msg("Code assumes the region size >= 1M, but is " SIZE_FORMAT "B", HeapRegion::GrainBytes)); ++ ++ int region_size_log_mb = HeapRegion::LogOfHRGrainBytes - LOG_M; + if (FLAG_IS_DEFAULT(G1RSetSparseRegionEntries)) { +- G1RSetSparseRegionEntries = G1RSetSparseRegionEntriesBase * (region_size_log_mb + 1); ++ G1RSetSparseRegionEntries = G1RSetSparseRegionEntriesBase * ((size_t)1 << (region_size_log_mb + 1)); + } + if (FLAG_IS_DEFAULT(G1RSetRegionEntries)) { + G1RSetRegionEntries = G1RSetRegionEntriesBase * (region_size_log_mb + 1); +diff --git a/hotspot/src/share/vm/gc_implementation/g1/heapRegionRemSet.hpp b/hotspot/src/share/vm/gc_implementation/g1/heapRegionRemSet.hpp +index 77751b4a9..6659dc550 100644 +--- a/hotspot/src/share/vm/gc_implementation/g1/heapRegionRemSet.hpp ++++ b/hotspot/src/share/vm/gc_implementation/g1/heapRegionRemSet.hpp +@@ -271,6 +271,7 @@ public: + HeapRegionRemSet(G1BlockOffsetSharedArray* bosa, HeapRegion* hr); + + static uint num_par_rem_sets(); ++ // Setup sparse and fine-grain tables sizes. + static void setup_remset_size(); + + HeapRegion* hr() const { +diff --git a/hotspot/src/share/vm/gc_implementation/g1/sparsePRT.hpp b/hotspot/src/share/vm/gc_implementation/g1/sparsePRT.hpp +index 17bd4a145..3d2de1a95 100644 +--- a/hotspot/src/share/vm/gc_implementation/g1/sparsePRT.hpp ++++ b/hotspot/src/share/vm/gc_implementation/g1/sparsePRT.hpp +@@ -218,9 +218,7 @@ class SparsePRT VALUE_OBJ_CLASS_SPEC { + + HeapRegion* _hr; + +- enum SomeAdditionalPrivateConstants { +- InitialCapacity = 16 +- }; ++ static const size_t InitialCapacity = 8; + + void expand(); + +-- +2.22.0 + diff --git a/0009-8262316-Reducing-locks-in-RSA-Blinding.patch b/0009-8262316-Reducing-locks-in-RSA-Blinding.patch new file mode 100644 index 0000000..bbd8a51 --- /dev/null +++ b/0009-8262316-Reducing-locks-in-RSA-Blinding.patch @@ -0,0 +1,165 @@ +Date: Mon, 5 Jun 2023 20:27:38 +0800 +Subject: 8262316: Reducing locks in RSA Blinding + +Bug url: https://bugs.openjdk.org/browse/JDK-8262316 +--- + .../classes/sun/security/rsa/RSACore.java | 101 +++++++++++------- + 1 file changed, 60 insertions(+), 41 deletions(-) + +diff --git a/jdk/src/share/classes/sun/security/rsa/RSACore.java b/jdk/src/share/classes/sun/security/rsa/RSACore.java +index 9809639a0..ae187b5a5 100644 +--- a/jdk/src/share/classes/sun/security/rsa/RSACore.java ++++ b/jdk/src/share/classes/sun/security/rsa/RSACore.java +@@ -25,20 +25,26 @@ + + package sun.security.rsa; + +-import java.math.BigInteger; +-import java.util.*; +- +-import java.security.SecureRandom; +-import java.security.interfaces.*; ++import sun.security.jca.JCAUtil; + + import javax.crypto.BadPaddingException; + +-import sun.security.jca.JCAUtil; ++import java.math.BigInteger; ++import java.security.SecureRandom; ++import java.security.interfaces.RSAKey; ++import java.security.interfaces.RSAPrivateCrtKey; ++import java.security.interfaces.RSAPrivateKey; ++import java.security.interfaces.RSAPublicKey; ++import java.util.Arrays; ++import java.util.Map; ++import java.util.WeakHashMap; ++import java.util.concurrent.ConcurrentLinkedQueue; ++import java.util.concurrent.locks.ReentrantLock; + + /** + * Core of the RSA implementation. Has code to perform public and private key + * RSA operations (with and without CRT for private key ops). Private CRT ops +- * also support blinding to twart timing attacks. ++ * also support blinding to thwart timing attacks. + * + * The code in this class only does the core RSA operation. Padding and + * unpadding must be done externally. +@@ -53,11 +59,14 @@ public final class RSACore { + // globally enable/disable use of blinding + private final static boolean ENABLE_BLINDING = true; + +- // cache for blinding parameters. Map +- // use a weak hashmap so that cached values are automatically cleared +- // when the modulus is GC'ed +- private final static Map ++ // cache for blinding parameters. Map> use a weak hashmap so that, ++ // cached values are automatically cleared when the modulus is GC'ed. ++ // Multiple BlindingParameters can be queued during times of heavy load, ++ // like performance testing. ++ private static final Map> + blindingCache = new WeakHashMap<>(); ++ private static final ReentrantLock lock = new ReentrantLock(); + + private RSACore() { + // empty +@@ -402,56 +411,66 @@ public final class RSACore { + if ((this.e != null && this.e.equals(e)) || + (this.d != null && this.d.equals(d))) { + +- BlindingRandomPair brp = null; +- synchronized (this) { +- if (!u.equals(BigInteger.ZERO) && +- !v.equals(BigInteger.ZERO)) { +- +- brp = new BlindingRandomPair(u, v); +- if (u.compareTo(BigInteger.ONE) <= 0 || +- v.compareTo(BigInteger.ONE) <= 0) { +- +- // need to reset the random pair next time +- u = BigInteger.ZERO; +- v = BigInteger.ZERO; +- } else { +- u = u.modPow(BIG_TWO, n); +- v = v.modPow(BIG_TWO, n); +- } +- } // Otherwise, need to reset the random pair. ++ BlindingRandomPair brp = new BlindingRandomPair(u, v); ++ if (u.compareTo(BigInteger.ONE) <= 0 || ++ v.compareTo(BigInteger.ONE) <= 0) { ++ // Reset so the parameters will be not queued later ++ u = BigInteger.ZERO; ++ v = BigInteger.ZERO; ++ } else { ++ u = u.modPow(BIG_TWO, n); ++ v = v.modPow(BIG_TWO, n); + } + return brp; + } + + return null; + } ++ ++ // Check if reusable, return true if both u & v are not zero. ++ boolean isReusable() { ++ return !u.equals(BigInteger.ZERO) && !v.equals(BigInteger.ZERO); ++ } + } + + private static BlindingRandomPair getBlindingRandomPair( + BigInteger e, BigInteger d, BigInteger n) { + +- BlindingParameters bps = null; +- synchronized (blindingCache) { +- bps = blindingCache.get(n); ++ ConcurrentLinkedQueue queue; ++ ++ // Get queue from map, if there is none then create one ++ lock.lock(); ++ try { ++ queue = blindingCache.computeIfAbsent(n, ++ ignored -> new ConcurrentLinkedQueue<>()); ++ } finally { ++ lock.unlock(); + } + ++ BlindingParameters bps = queue.poll(); + if (bps == null) { + bps = new BlindingParameters(e, d, n); +- synchronized (blindingCache) { +- blindingCache.putIfAbsent(n, bps); +- } + } ++ BlindingRandomPair brp = null; + +- BlindingRandomPair brp = bps.getBlindingRandomPair(e, d, n); +- if (brp == null) { +- // need to reset the blinding parameters +- bps = new BlindingParameters(e, d, n); +- synchronized (blindingCache) { +- blindingCache.replace(n, bps); +- } ++ // Loops to get a valid pair, going through the queue or create a new ++ // parameters if needed. ++ while (brp == null) { + brp = bps.getBlindingRandomPair(e, d, n); ++ if (brp == null) { ++ // need to reset the blinding parameters, first check for ++ // another in the queue. ++ bps = queue.poll(); ++ if (bps == null) { ++ bps = new BlindingParameters(e, d, n); ++ } ++ } + } + ++ // If this parameters are still usable, put them back into the queue. ++ if (bps.isReusable()) { ++ queue.add(bps); ++ } + return brp; + } + +-- +2.22.0 + diff --git a/0010-8283994-Make-Xerces-DatatypeException-stackless.patch b/0010-8283994-Make-Xerces-DatatypeException-stackless.patch new file mode 100644 index 0000000..2348d63 --- /dev/null +++ b/0010-8283994-Make-Xerces-DatatypeException-stackless.patch @@ -0,0 +1,26 @@ +Date: Mon, 5 Jun 2023 20:31:43 +0800 +Subject: 8283994: Make Xerces DatatypeException stackless + +Bug url: https://bugs.openjdk.org/browse/JDK-8283994 +--- + .../apache/xerces/internal/impl/dv/DatatypeException.java | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/jaxp/src/com/sun/org/apache/xerces/internal/impl/dv/DatatypeException.java b/jaxp/src/com/sun/org/apache/xerces/internal/impl/dv/DatatypeException.java +index 17efe6aa0..9a428649a 100644 +--- a/jaxp/src/com/sun/org/apache/xerces/internal/impl/dv/DatatypeException.java ++++ b/jaxp/src/com/sun/org/apache/xerces/internal/impl/dv/DatatypeException.java +@@ -107,4 +107,10 @@ public class DatatypeException extends Exception { + + return msg; + } ++ ++ @Override ++ public Throwable fillInStackTrace() { ++ // This is an internal exception; the stack trace is irrelevant. ++ return this; ++ } + } +-- +2.22.0 + diff --git a/0011-Optimizing-ObjectInputStream-by-FreqInlineSize.patch b/0011-Optimizing-ObjectInputStream-by-FreqInlineSize.patch new file mode 100644 index 0000000..0b2eb71 --- /dev/null +++ b/0011-Optimizing-ObjectInputStream-by-FreqInlineSize.patch @@ -0,0 +1,51 @@ +Date: Mon, 5 Jun 2023 20:35:04 +0800 +Subject: Optimizing ObjectInputStream by FreqInlineSize + +--- + hotspot/src/share/vm/opto/bytecodeInfo.cpp | 2 +- + .../share/classes/java/io/ObjectInputStream.java | 2 +- + .../share/classes/java/io/ObjectOutputStream.java | 2 +- + 4 files changed, 3 insertions(+), 3 deletions(-) + +diff --git a/hotspot/src/share/vm/opto/bytecodeInfo.cpp b/hotspot/src/share/vm/opto/bytecodeInfo.cpp +index 4fa8e12f1..f9191ec06 100644 +--- a/hotspot/src/share/vm/opto/bytecodeInfo.cpp ++++ b/hotspot/src/share/vm/opto/bytecodeInfo.cpp +@@ -171,7 +171,7 @@ bool InlineTree::should_inline(ciMethod* callee_method, ciMethod* caller_method, + is_unboxing_method(callee_method, C) || + is_init_with_ea(callee_method, caller_method, C)) { + +- max_inline_size = C->freq_inline_size(); ++ max_inline_size = (int)FreqInlineSize; + if (size <= max_inline_size && TraceFrequencyInlining) { + CompileTask::print_inline_indent(inline_level()); + tty->print_cr("Inlined frequent method (freq=%d count=%d):", freq, call_site_count); +diff --git a/jdk/src/share/classes/java/io/ObjectInputStream.java b/jdk/src/share/classes/java/io/ObjectInputStream.java +index 85e3958b4..6a7280eab 100644 +--- a/jdk/src/share/classes/java/io/ObjectInputStream.java ++++ b/jdk/src/share/classes/java/io/ObjectInputStream.java +@@ -387,7 +387,7 @@ public class ObjectInputStream + /** + * value of "useFastSerializer" property + */ +- private static final boolean defaultFastSerializer = UNSAFE.getUseFastSerializer(); ++ private final boolean defaultFastSerializer = UNSAFE.getUseFastSerializer(); + + /** + * true or false for open FastSerilizer +diff --git a/jdk/src/share/classes/java/io/ObjectOutputStream.java b/jdk/src/share/classes/java/io/ObjectOutputStream.java +index 23c1fff59..328f47589 100644 +--- a/jdk/src/share/classes/java/io/ObjectOutputStream.java ++++ b/jdk/src/share/classes/java/io/ObjectOutputStream.java +@@ -240,7 +240,7 @@ public class ObjectOutputStream + * Value of "UseFastSerializer" property. The fastSerializer is turned + * on when it is true. + */ +- private static final boolean useFastSerializer = UNSAFE.getUseFastSerializer(); ++ private final boolean useFastSerializer = UNSAFE.getUseFastSerializer(); + + /** + * value of "printFastSerializer" property, +-- +2.22.0 + diff --git a/0012-8301187-Memory-leaks-in-OopMapCache.patch b/0012-8301187-Memory-leaks-in-OopMapCache.patch new file mode 100644 index 0000000..a4b19cb --- /dev/null +++ b/0012-8301187-Memory-leaks-in-OopMapCache.patch @@ -0,0 +1,30 @@ +Date: Mon, 5 Jun 2023 20:37:13 +0800 +Subject: [PATCH 12/59] 8301187: Memory leaks in OopMapCache + +Bug url: https://bugs.openjdk.org/browse/JDK-8301187 +--- + hotspot/src/share/vm/interpreter/oopMapCache.cpp | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/hotspot/src/share/vm/interpreter/oopMapCache.cpp b/hotspot/src/share/vm/interpreter/oopMapCache.cpp +index 528906267..bd7d4f100 100644 +--- a/hotspot/src/share/vm/interpreter/oopMapCache.cpp ++++ b/hotspot/src/share/vm/interpreter/oopMapCache.cpp +@@ -561,6 +561,7 @@ void OopMapCache::lookup(methodHandle method, + // at this time. We give the caller of lookup() a copy of the + // interesting info via parameter entry_for, but we don't add it to + // the cache. See the gory details in Method*.cpp. ++ tmp->flush(); + FREE_C_HEAP_OBJ(tmp, mtClass); + return; + } +@@ -635,5 +636,6 @@ void OopMapCache::compute_one_oop_map(methodHandle method, int bci, InterpreterO + tmp->initialize(); + tmp->fill(method, bci); + entry->resource_copy(tmp); ++ tmp->flush(); + FREE_C_HEAP_ARRAY(OopMapCacheEntry, tmp, mtInternal); + } +-- +2.22.0 + diff --git a/0013-8287349-AArch64-Merge-LDR-instructions-to-improve-C1.patch b/0013-8287349-AArch64-Merge-LDR-instructions-to-improve-C1.patch new file mode 100644 index 0000000..ea3d9aa --- /dev/null +++ b/0013-8287349-AArch64-Merge-LDR-instructions-to-improve-C1.patch @@ -0,0 +1,28 @@ +Date: Mon, 5 Jun 2023 20:39:01 +0800 +Subject: [PATCH 13/59] 8287349: AArch64: Merge LDR instructions to improve C1 OSR performance + +Bug url: https://bugs.openjdk.org/browse/JDK-8287349 +--- + hotspot/src/cpu/aarch64/vm/c1_LIRAssembler_aarch64.cpp | 5 ++--- + 1 file changed, 2 insertions(+), 3 deletions(-) + +diff --git a/hotspot/src/cpu/aarch64/vm/c1_LIRAssembler_aarch64.cpp b/hotspot/src/cpu/aarch64/vm/c1_LIRAssembler_aarch64.cpp +index 528eeae32..22dfd1008 100644 +--- a/hotspot/src/cpu/aarch64/vm/c1_LIRAssembler_aarch64.cpp ++++ b/hotspot/src/cpu/aarch64/vm/c1_LIRAssembler_aarch64.cpp +@@ -292,10 +292,9 @@ void LIR_Assembler::osr_entry() { + __ bind(L); + } + #endif +- __ ldr(r19, Address(OSR_buf, slot_offset + 0)); ++ __ ldp(r19, r20, Address(OSR_buf, slot_offset)); + __ str(r19, frame_map()->address_for_monitor_lock(i)); +- __ ldr(r19, Address(OSR_buf, slot_offset + 1*BytesPerWord)); +- __ str(r19, frame_map()->address_for_monitor_object(i)); ++ __ str(r20, frame_map()->address_for_monitor_object(i)); + } + } + } +-- +2.22.0 + diff --git a/0014-8280511-AArch64-Combine-shift-and-negate-to-a-single.patch b/0014-8280511-AArch64-Combine-shift-and-negate-to-a-single.patch new file mode 100644 index 0000000..6365756 --- /dev/null +++ b/0014-8280511-AArch64-Combine-shift-and-negate-to-a-single.patch @@ -0,0 +1,382 @@ +Date: Mon, 5 Jun 2023 20:43:22 +0800 +Subject: [PATCH 14/59] 8280511: AArch64: Combine shift and negate to a single instruction + +Bug url: https://bugs.openjdk.org/browse/JDK-8280511 +--- + hotspot/src/cpu/aarch64/vm/aarch64.ad | 102 ++++++++++ + hotspot/src/cpu/aarch64/vm/aarch64_ad.m4 | 25 +++ + hotspot/test/compiler/codegen/ShiftTest.java | 199 +++++++++++++++++++ + 3 files changed, 326 insertions(+) + create mode 100644 hotspot/test/compiler/codegen/ShiftTest.java + +diff --git a/hotspot/src/cpu/aarch64/vm/aarch64.ad b/hotspot/src/cpu/aarch64/vm/aarch64.ad +index 511ce913e..d73d5d457 100644 +--- a/hotspot/src/cpu/aarch64/vm/aarch64.ad ++++ b/hotspot/src/cpu/aarch64/vm/aarch64.ad +@@ -9818,6 +9818,108 @@ instruct regI_not_reg(iRegINoSp dst, + ins_pipe(ialu_reg); + %} + ++// This pattern is automatically generated from aarch64_ad.m4 ++// DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE ++instruct NegI_reg_URShift_reg(iRegINoSp dst, ++ immI0 zero, iRegIorL2I src1, immI src2) %{ ++ match(Set dst (SubI zero (URShiftI src1 src2))); ++ ++ ins_cost(1.9 * INSN_COST); ++ format %{ "negw $dst, $src1, LSR $src2" %} ++ ++ ins_encode %{ ++ __ negw(as_Register($dst$$reg), as_Register($src1$$reg), ++ Assembler::LSR, $src2$$constant & 0x1f); ++ %} ++ ++ ins_pipe(ialu_reg_shift); ++%} ++ ++// This pattern is automatically generated from aarch64_ad.m4 ++// DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE ++instruct NegI_reg_RShift_reg(iRegINoSp dst, ++ immI0 zero, iRegIorL2I src1, immI src2) %{ ++ match(Set dst (SubI zero (RShiftI src1 src2))); ++ ++ ins_cost(1.9 * INSN_COST); ++ format %{ "negw $dst, $src1, ASR $src2" %} ++ ++ ins_encode %{ ++ __ negw(as_Register($dst$$reg), as_Register($src1$$reg), ++ Assembler::ASR, $src2$$constant & 0x1f); ++ %} ++ ++ ins_pipe(ialu_reg_shift); ++%} ++ ++// This pattern is automatically generated from aarch64_ad.m4 ++// DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE ++instruct NegI_reg_LShift_reg(iRegINoSp dst, ++ immI0 zero, iRegIorL2I src1, immI src2) %{ ++ match(Set dst (SubI zero (LShiftI src1 src2))); ++ ++ ins_cost(1.9 * INSN_COST); ++ format %{ "negw $dst, $src1, LSL $src2" %} ++ ++ ins_encode %{ ++ __ negw(as_Register($dst$$reg), as_Register($src1$$reg), ++ Assembler::LSL, $src2$$constant & 0x1f); ++ %} ++ ++ ins_pipe(ialu_reg_shift); ++%} ++ ++// This pattern is automatically generated from aarch64_ad.m4 ++// DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE ++instruct NegL_reg_URShift_reg(iRegLNoSp dst, ++ immL0 zero, iRegL src1, immI src2) %{ ++ match(Set dst (SubL zero (URShiftL src1 src2))); ++ ++ ins_cost(1.9 * INSN_COST); ++ format %{ "neg $dst, $src1, LSR $src2" %} ++ ++ ins_encode %{ ++ __ neg(as_Register($dst$$reg), as_Register($src1$$reg), ++ Assembler::LSR, $src2$$constant & 0x3f); ++ %} ++ ++ ins_pipe(ialu_reg_shift); ++%} ++ ++// This pattern is automatically generated from aarch64_ad.m4 ++// DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE ++instruct NegL_reg_RShift_reg(iRegLNoSp dst, ++ immL0 zero, iRegL src1, immI src2) %{ ++ match(Set dst (SubL zero (RShiftL src1 src2))); ++ ++ ins_cost(1.9 * INSN_COST); ++ format %{ "neg $dst, $src1, ASR $src2" %} ++ ++ ins_encode %{ ++ __ neg(as_Register($dst$$reg), as_Register($src1$$reg), ++ Assembler::ASR, $src2$$constant & 0x3f); ++ %} ++ ++ ins_pipe(ialu_reg_shift); ++%} ++ ++// This pattern is automatically generated from aarch64_ad.m4 ++// DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE ++instruct NegL_reg_LShift_reg(iRegLNoSp dst, ++ immL0 zero, iRegL src1, immI src2) %{ ++ match(Set dst (SubL zero (LShiftL src1 src2))); ++ ++ ins_cost(1.9 * INSN_COST); ++ format %{ "neg $dst, $src1, LSL $src2" %} ++ ++ ins_encode %{ ++ __ neg(as_Register($dst$$reg), as_Register($src1$$reg), ++ Assembler::LSL, $src2$$constant & 0x3f); ++ %} ++ ++ ins_pipe(ialu_reg_shift); ++%} ++ + instruct AndI_reg_not_reg(iRegINoSp dst, + iRegIorL2I src1, iRegIorL2I src2, immI_M1 m1, + rFlagsReg cr) %{ +diff --git a/hotspot/src/cpu/aarch64/vm/aarch64_ad.m4 b/hotspot/src/cpu/aarch64/vm/aarch64_ad.m4 +index 9fb793023..6ec8fde08 100644 +--- a/hotspot/src/cpu/aarch64/vm/aarch64_ad.m4 ++++ b/hotspot/src/cpu/aarch64/vm/aarch64_ad.m4 +@@ -47,6 +47,24 @@ instruct $2$1_reg_$4_reg(iReg$1NoSp dst, + + ins_pipe(ialu_reg_reg_shift); + %}')dnl ++define(`NEG_SHIFT_INSN', ++`// This pattern is automatically generated from aarch64_ad.m4 ++// DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE ++instruct Neg$1_reg_$2_reg(iReg$1NoSp dst, ++ imm$1`0' zero, iReg$1`'ORL2I($1) src1, immI src2) %{ ++ match(Set dst (Sub$1 zero ($2$1 src1 src2))); ++ ++ ins_cost(1.9 * INSN_COST); ++ format %{ "ifelse($1, I, negw, neg) $dst, $src1, $3 $src2" %} ++ ++ ins_encode %{ ++ __ ifelse($1, I, negw, neg)(as_Register($dst$$reg), as_Register($src1$$reg), ++ Assembler::$3, $src2$$constant & ifelse($1,I,0x1f,0x3f)); ++ %} ++ ++ ins_pipe(ialu_reg_shift); ++%} ++')dnl + define(`BASE_INVERTED_INSN', + ` + instruct $2$1_reg_not_reg(iReg$1NoSp dst, +@@ -110,6 +128,11 @@ define(`NOT_INSN', + ins_pipe(ialu_reg); + %}')dnl + dnl ++define(`BOTH_NEG_SHIFT_INSNS', ++`NEG_SHIFT_INSN($1, URShift, LSR) ++NEG_SHIFT_INSN($1, RShift, ASR) ++NEG_SHIFT_INSN($1, LShift, LSL)')dnl ++dnl + define(`BOTH_SHIFT_INSNS', + `BASE_SHIFT_INSN(I, $1, ifelse($2,andr,andw,$2w), $3, $4) + BASE_SHIFT_INSN(L, $1, $2, $3, $4)')dnl +@@ -134,6 +157,8 @@ BOTH_INVERTED_SHIFT_INSNS($1, $2, LShift, LSL)')dnl + dnl + NOT_INSN(L, eon) + NOT_INSN(I, eonw) ++BOTH_NEG_SHIFT_INSNS(I) ++BOTH_NEG_SHIFT_INSNS(L) + BOTH_INVERTED_INSNS(And, bic) + BOTH_INVERTED_INSNS(Or, orn) + BOTH_INVERTED_INSNS(Xor, eon) +diff --git a/hotspot/test/compiler/codegen/ShiftTest.java b/hotspot/test/compiler/codegen/ShiftTest.java +new file mode 100644 +index 000000000..45d192341 +--- /dev/null ++++ b/hotspot/test/compiler/codegen/ShiftTest.java +@@ -0,0 +1,199 @@ ++/* ++ * Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2023, Huawei Technologies Co., Ltd. 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. ++ */ ++ ++/* ++ * @test ++ * @bug 4093292 8280511 ++ * @summary Test for correct code generation by the JIT ++ * @library /testlibrary ++ * @run main compiler.codegen.ShiftTest ++ * @run main/othervm -XX:-TieredCompilation compiler.codegen.ShiftTest ++ */ ++ ++package compiler.codegen; ++import com.oracle.java.testlibrary.Asserts; ++ ++public class ShiftTest { ++ static final int w = 32; ++ ++ private static void doTest(long ct) throws Exception { ++ int S22 = 0xc46cf7c2; ++ int S23 = 0xcfda9162; ++ int S24 = 0xd029aa4c; ++ int S25 = 0x17cf1801; ++ int A = (int)(ct & 0xffffffffL); ++ int B = (int)(ct >>> 32); ++ int x, y; ++ x = B - S25; ++ y = A & (w-1); ++ B = ((x >>> y) | (x << (w-y))) ^ A; ++ x = A - S24; ++ y = B & (w-1); ++ A = ((x >>> y) | (x << (w-y))) ^ B; ++ x = B - S23; ++ y = A & (w-1); ++ B = ((x >>> y) | (x << (w-y))) ^ A; ++ x = A - S22; ++ y = B & (w-1); ++ A = ((x >>> y) | (x << (w-y))) ^ B; ++ String astr = Integer.toHexString(A); ++ String bstr = Integer.toHexString(B); ++ System.err.println("A = " + astr + " B = " + bstr); ++ if ((!astr.equals("dcb38144")) || ++ (!bstr.equals("1916de73"))) { ++ throw new RuntimeException("Unexpected shift results!"); ++ } ++ System.err.println("Test passed"); ++ } ++ ++ private static int[] ispecial = { ++ 0, Integer.MAX_VALUE, -Integer.MAX_VALUE, Integer.MIN_VALUE, -42, 42, -1, 1 ++ }; ++ ++ private static long[] lspecial = { ++ 0, Long.MAX_VALUE, -Long.MAX_VALUE, Long.MIN_VALUE, Integer.MAX_VALUE, -Integer.MAX_VALUE, Integer.MIN_VALUE, -42, 42, -1, 1 ++ }; ++ ++ private static int[] ispecial_LeftShift_expected = { ++ 0, 32, -32, 0, 1344, -1344, 32, -32 ++ }; ++ ++ private static int[] ispecial_UnsignedRightShift_expected = { ++ 0, -33554431, -33554432, -33554432 ,-67108863, 0, -67108863, 0 ++ }; ++ ++ private static int[] ispecial_SignedRightShift_expected = { ++ 0, -16777215, 16777216, 16777216, 1, 0, 1, 0 ++ }; ++ ++ private static int[] ispecial_LeftShiftCorner_expected = { ++ 0, -2147483647, 2147483647, -2147483648, 42, -42, 1, -1 ++ }; ++ ++ private static int[] ispecial_UnsignedRightShiftCorner_expected = { ++ 0, -1073741823, -1073741824, -1073741824, -2147483627, -21, -2147483647, 0 ++ }; ++ ++ private static int[] ispecial_SignedRightShiftCorner_expected = { ++ 0, -536870911, 536870912, 536870912, 11, -10, 1, 0 ++ }; ++ ++ private static long[] lspecial_LeftShift_expected = { ++ 0, 256, -256, 0, -549755813632L, 549755813632L, 549755813888L, 10752, -10752, 256, -256 ++ }; ++ ++ private static long[] lspecial_UnsignedRightShift_expected = { ++ 0, -18014398509481983L, -18014398509481984L, -18014398509481984L, -4194303, -36028797014769664L, -36028797014769664L, -36028797018963967L, 0, -36028797018963967L, 0 ++ }; ++ ++ private static long[] lspecial_SignedRightShift_expected = { ++ 0, -9007199254740991L, 9007199254740992L, 9007199254740992L, -2097151, 2097152, 2097152, 1, 0, 1, 0 ++ }; ++ ++ private static long[] lspecial_LeftShiftCorner_expected = { ++ 0, -9223372036854775807L, 9223372036854775807L, -9223372036854775808L, -2147483647, 2147483647, 2147483648L, 42, -42, 1, -1 ++ }; ++ ++ private static long[] lspecial_UnsignedRightShiftCorner_expected = { ++ 0, -4611686018427387903L, -4611686018427387904L, -4611686018427387904L, -1073741823, -9223372035781033984L, -9223372035781033984L, -9223372036854775787L, -21, -9223372036854775807L, 0 ++ }; ++ ++ private static long[] lspecial_SignedRightShiftCorner_expected = { ++ 0, -2305843009213693951L, 2305843009213693952L, 2305843009213693952L, -536870911, 536870912, 536870912, 11, -10, 1, 0 ++ }; ++ ++ private static int negLeftShiftInt(int input) { ++ return -(input << 5); ++ } ++ ++ private static int negUnsignedRightShiftInt(int input) { ++ return -(input >>> 6); ++ } ++ ++ private static int negSignedRightShiftInt(int input) { ++ return -(input >> 7); ++ } ++ ++ private static int negLeftShiftICorner(int input) { ++ return -(input << 32); ++ } ++ ++ private static int negUnsignedRightShiftICorner(int input) { ++ return -(input >>> 33); ++ } ++ ++ private static int negSignedRightShiftICorner(int input) { ++ return -(input >> 34); ++ } ++ ++ private static long negLeftShiftLong(long input) { ++ return -(input << 8); ++ } ++ ++ private static long negUnsignedRightShiftLong(long input) { ++ return -(input >>> 9); ++ } ++ ++ private static long negSignedRightShiftLong(long input) { ++ return -(input >> 10); ++ } ++ ++ private static long negLeftShiftLCorner(long input) { ++ return -(input << 64); ++ } ++ ++ private static long negUnsignedRightShiftLCorner(long input) { ++ return -(input >>> 65); ++ } ++ ++ private static long negSignedRightShiftLCorner(long input) { ++ return -(input >> 66); ++ } ++ ++ private static void testNegShift() { ++ for (int i = 0; i < 20_000; i++) { ++ for (int j = 0; j < ispecial.length; j++) { ++ Asserts.assertEquals(negLeftShiftInt(ispecial[j]), ispecial_LeftShift_expected[j]); ++ Asserts.assertEquals(negUnsignedRightShiftInt(ispecial[j]), ispecial_UnsignedRightShift_expected[j]); ++ Asserts.assertEquals(negSignedRightShiftInt(ispecial[j]), ispecial_SignedRightShift_expected[j]); ++ Asserts.assertEquals(negLeftShiftICorner(ispecial[j]), ispecial_LeftShiftCorner_expected[j]); ++ Asserts.assertEquals(negUnsignedRightShiftICorner(ispecial[j]), ispecial_UnsignedRightShiftCorner_expected[j]); ++ Asserts.assertEquals(negSignedRightShiftICorner(ispecial[j]), ispecial_SignedRightShiftCorner_expected[j]); ++ } ++ for (int j = 0; j < lspecial.length; j++) { ++ Asserts.assertEquals(negLeftShiftLong(lspecial[j]), lspecial_LeftShift_expected[j]); ++ Asserts.assertEquals(negUnsignedRightShiftLong(lspecial[j]), lspecial_UnsignedRightShift_expected[j]); ++ Asserts.assertEquals(negSignedRightShiftLong(lspecial[j]), lspecial_SignedRightShift_expected[j]); ++ Asserts.assertEquals(negLeftShiftLCorner(lspecial[j]), lspecial_LeftShiftCorner_expected[j]); ++ Asserts.assertEquals(negUnsignedRightShiftLCorner(lspecial[j]), lspecial_UnsignedRightShiftCorner_expected[j]); ++ Asserts.assertEquals(negSignedRightShiftLCorner(lspecial[j]), lspecial_SignedRightShiftCorner_expected[j]); ++ } ++ } ++ } ++ ++ public static void main(String[] args) throws Exception { ++ doTest(0x496def29b74be041L); ++ testNegShift(); ++ } ++} +-- +2.22.0 + diff --git a/0015-6605915-jinfo-flag-flag-name-functionality-doesn-t-w.patch b/0015-6605915-jinfo-flag-flag-name-functionality-doesn-t-w.patch new file mode 100644 index 0000000..e89706f --- /dev/null +++ b/0015-6605915-jinfo-flag-flag-name-functionality-doesn-t-w.patch @@ -0,0 +1,34 @@ +Date: Tue, 6 Jun 2023 02:06:23 +0000 +Subject: [PATCH 15/59] 6605915: jinfo -flag functionality doesn't work with core files + +Bug url: https://bugs.openjdk.org/browse/JDK-6605915 +--- + jdk/src/share/classes/sun/tools/jinfo/JInfo.java | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/jdk/src/share/classes/sun/tools/jinfo/JInfo.java b/jdk/src/share/classes/sun/tools/jinfo/JInfo.java +index 7c817ba86..d5adc3537 100644 +--- a/jdk/src/share/classes/sun/tools/jinfo/JInfo.java ++++ b/jdk/src/share/classes/sun/tools/jinfo/JInfo.java +@@ -196,15 +196,17 @@ public class JInfo { + if (usageSA) { + System.err.println(" jinfo [option] "); + System.err.println(" (to connect to running process)"); +- System.err.println(" jinfo [option] "); ++ System.err.println(" jinfo [option] "); + System.err.println(" (to connect to a core file)"); + System.err.println(" jinfo [option] [server_id@]"); + System.err.println(" (to connect to remote debug server)"); + System.err.println(""); + System.err.println("where