diff --git a/8137165-Tests-fail-in-SR_Handler-because-thread-is-n.patch b/8137165-Tests-fail-in-SR_Handler-because-thread-is-n.patch new file mode 100644 index 0000000000000000000000000000000000000000..0eb257dca5b1764f287371e2fef0f5bc2fb4adf8 --- /dev/null +++ b/8137165-Tests-fail-in-SR_Handler-because-thread-is-n.patch @@ -0,0 +1,72 @@ +From 11116ea71b22635302759817b43c555ded53f882 Mon Sep 17 00:00:00 2001 +Subject: 8137165: Tests fail in SR_Handler because thread is not VMThread or JavaThread + +--- + hotspot/src/os/linux/vm/os_linux.cpp | 16 +++++++++++++--- + 1 file changed, 13 insertions(+), 3 deletions(-) + +diff --git a/hotspot/src/os/linux/vm/os_linux.cpp b/hotspot/src/os/linux/vm/os_linux.cpp +index 6ee49eedc..773c746af 100644 +--- a/hotspot/src/os/linux/vm/os_linux.cpp ++++ b/hotspot/src/os/linux/vm/os_linux.cpp +@@ -1070,6 +1070,13 @@ void os::free_thread(OSThread* osthread) { + assert(osthread != NULL, "osthread not set"); + + if (Thread::current()->osthread() == osthread) { ++#ifdef ASSERT ++ sigset_t current; ++ sigemptyset(¤t); ++ pthread_sigmask(SIG_SETMASK, NULL, ¤t); ++ assert(!sigismember(¤t, SR_signum), "SR signal should not be blocked!"); ++#endif ++ + // Restore caller's signal mask + sigset_t sigmask = osthread->caller_sigmask(); + pthread_sigmask(SIG_SETMASK, &sigmask, NULL); +@@ -4723,7 +4730,8 @@ static void SR_handler(int sig, siginfo_t* siginfo, ucontext_t* context) { + // after sigsuspend. + int old_errno = errno; + +- Thread* thread = Thread::current(); ++ Thread* thread = Thread::current_or_null(); ++ assert(thread != NULL, "Missing current thread in SR_handler"); + OSThread* osthread = thread->osthread(); + assert(thread->is_VM_thread() || thread->is_Java_thread(), "Must be VMThread or JavaThread"); + +@@ -4735,7 +4743,7 @@ static void SR_handler(int sig, siginfo_t* siginfo, ucontext_t* context) { + os::SuspendResume::State state = osthread->sr.suspended(); + if (state == os::SuspendResume::SR_SUSPENDED) { + sigset_t suspend_set; // signals for sigsuspend() +- ++ sigemptyset(&suspend_set); + // get current set of blocked signals and unblock resume signal + pthread_sigmask(SIG_BLOCK, NULL, &suspend_set); + sigdelset(&suspend_set, SR_signum); +@@ -5025,6 +5033,7 @@ static bool call_chained_handler(struct sigaction *actp, int sig, + + // try to honor the signal mask + sigset_t oset; ++ sigemptyset(&oset); + pthread_sigmask(SIG_SETMASK, &(actp->sa_mask), &oset); + + // call into the chained handler +@@ -5035,7 +5044,7 @@ static bool call_chained_handler(struct sigaction *actp, int sig, + } + + // restore the signal mask +- pthread_sigmask(SIG_SETMASK, &oset, 0); ++ pthread_sigmask(SIG_SETMASK, &oset, NULL); + } + // Tell jvm's signal handler the signal is taken care of. + return true; +@@ -6699,6 +6708,7 @@ void Parker::park(bool isAbsolute, jlong time) { + // Don't catch signals while blocked; let the running threads have the signals. + // (This allows a debugger to break into the running thread.) + sigset_t oldsigs; ++ sigemptyset(&oldsigs); + sigset_t* allowdebug_blocked = os::Linux::allowdebug_blocked_signals(); + pthread_sigmask(SIG_BLOCK, allowdebug_blocked, &oldsigs); + #endif +-- +2.19.1 + diff --git a/Enhance-SIGBUS-and-rlimit-information-in-errlog.patch b/Enhance-SIGBUS-and-rlimit-information-in-errlog.patch new file mode 100644 index 0000000000000000000000000000000000000000..368d0e477968c9f5c358e4b3d8deef7800660ac5 --- /dev/null +++ b/Enhance-SIGBUS-and-rlimit-information-in-errlog.patch @@ -0,0 +1,67 @@ +Subject: [PATCH][Huawei] Enhance SIGBUS and rlimit information in errlog + +--- + hotspot/src/os/posix/vm/os_posix.cpp | 26 ++++++++++++++++++++++ + hotspot/src/share/vm/utilities/vmError.cpp | 2 +- + 2 files changed, 27 insertions(+), 1 deletion(-) + +diff --git a/hotspot/src/os/posix/vm/os_posix.cpp b/hotspot/src/os/posix/vm/os_posix.cpp +index f7dab3c7f..a83ae1476 100644 +--- a/hotspot/src/os/posix/vm/os_posix.cpp ++++ b/hotspot/src/os/posix/vm/os_posix.cpp +@@ -207,6 +207,26 @@ void os::Posix::print_rlimit_info(outputStream* st) { + if (rlim.rlim_cur == RLIM_INFINITY) st->print("infinity"); + else st->print("%uk", rlim.rlim_cur >> 10); + ++ st->print(", DATA "); ++ getrlimit(RLIMIT_DATA, &rlim); ++ if (rlim.rlim_cur == RLIM_INFINITY) st->print("infinity"); ++ else st->print("%uk", rlim.rlim_cur >> 10); ++ ++ st->print(", FSIZE "); ++ getrlimit(RLIMIT_FSIZE, &rlim); ++ if (rlim.rlim_cur == RLIM_INFINITY) st->print("infinity"); ++ else st->print("%u", rlim.rlim_cur >> 10); ++ ++ st->print(", CPU "); ++ getrlimit(RLIMIT_CPU, &rlim); ++ if (rlim.rlim_cur == RLIM_INFINITY) st->print("infinity"); ++ else st->print("%uk seconds", rlim.rlim_cur >> 10); ++ ++ st->print(", RSS "); ++ getrlimit(RLIMIT_RSS, &rlim); ++ if (rlim.rlim_cur == RLIM_INFINITY) st->print("infinity"); ++ else st->print("%u", rlim.rlim_cur >> 10); ++ + // Isn't there on solaris + #if !defined(TARGET_OS_FAMILY_solaris) && !defined(TARGET_OS_FAMILY_aix) + st->print(", NPROC "); +@@ -765,6 +785,12 @@ static bool get_signal_code_description(const siginfo_t* si, enum_sigcode_desc_t + { SIGBUS, BUS_ADRALN, "BUS_ADRALN", "Invalid address alignment." }, + { SIGBUS, BUS_ADRERR, "BUS_ADRERR", "Nonexistent physical address." }, + { SIGBUS, BUS_OBJERR, "BUS_OBJERR", "Object-specific hardware error." }, ++#ifdef BUS_MCEERR_AR ++ { SIGBUS, BUS_MCEERR_AR,"BUS_MCEERR_AR","hardware memory error consumed on a machine check: action required."}, ++#endif ++#ifdef BUS_MCEERR_AO ++ { SIGBUS, BUS_MCEERR_AO,"BUS_MCEERR_AO","hardware memory error detected in process but not consumed: action optional."}, ++#endif + { SIGTRAP, TRAP_BRKPT, "TRAP_BRKPT", "Process breakpoint." }, + { SIGTRAP, TRAP_TRACE, "TRAP_TRACE", "Process trace trap." }, + { SIGCHLD, CLD_EXITED, "CLD_EXITED", "Child has exited." }, +diff --git a/hotspot/src/share/vm/utilities/vmError.cpp b/hotspot/src/share/vm/utilities/vmError.cpp +index 0c5c955bf..3233e4b31 100644 +--- a/hotspot/src/share/vm/utilities/vmError.cpp ++++ b/hotspot/src/share/vm/utilities/vmError.cpp +@@ -813,7 +813,7 @@ void VMError::report(outputStream* st) { + #if defined(AARCH64) || defined(X86) + STEP(207, "(printing file descriptor)" ) + +- if (ExtensiveErrorReports && _verbose) { ++ if (_verbose) { + // File Descriptor + os::print_file_descriptor(st); + st->cr(); +-- +2.19.1 + diff --git a/Extending-the-IV-Length-Supported-by-KAEProvider-AES.patch b/Extending-the-IV-Length-Supported-by-KAEProvider-AES.patch new file mode 100644 index 0000000000000000000000000000000000000000..03e949a677b51dbcac637e123b27f2cd08158248 --- /dev/null +++ b/Extending-the-IV-Length-Supported-by-KAEProvider-AES.patch @@ -0,0 +1,135 @@ +From f4b0357c01f51e813642c3ac5c8ff33c1576eb72 Mon Sep 17 00:00:00 2001 +Subject: Extending the IV Length Supported by KAEProvider AES/Gcm +--- + .../security/openssl/kae_symmetric_cipher.c | 23 ++++++-- + .../security/openssl/KAEGcmIvLenTest.java | 52 +++++++++++++++++++ + 2 files changed, 72 insertions(+), 3 deletions(-) + create mode 100644 jdk/test/org/openeuler/security/openssl/KAEGcmIvLenTest.java + +diff --git a/jdk/src/solaris/native/org/openeuler/security/openssl/kae_symmetric_cipher.c b/jdk/src/solaris/native/org/openeuler/security/openssl/kae_symmetric_cipher.c +index ec8894f1a..7618d6e16 100644 +--- a/jdk/src/solaris/native/org/openeuler/security/openssl/kae_symmetric_cipher.c ++++ b/jdk/src/solaris/native/org/openeuler/security/openssl/kae_symmetric_cipher.c +@@ -146,6 +146,7 @@ Java_org_openeuler_security_openssl_KAESymmetricCipherBase_nativeInit(JNIEnv* en + const EVP_CIPHER* cipher = NULL; + ENGINE* kaeEngine = NULL; + int keyLength = (*env)->GetArrayLength(env, key); ++ int ivLength = 0; + + const char* algo = (*env)->GetStringUTFChars(env, cipherType, 0); + if (StartsWith("aes", algo)) { +@@ -158,7 +159,6 @@ Java_org_openeuler_security_openssl_KAESymmetricCipherBase_nativeInit(JNIEnv* en + + KAE_TRACE("KAESymmetricCipherBase_nativeInit: kaeEngine => %p", kaeEngine); + +- (*env)->ReleaseStringUTFChars(env, cipherType, algo); + if (cipher == NULL) { + KAE_ThrowOOMException(env, "create EVP_CIPHER fail"); + goto cleanup; +@@ -170,19 +170,35 @@ Java_org_openeuler_security_openssl_KAESymmetricCipherBase_nativeInit(JNIEnv* en + + if (iv != NULL) { + ivBytes = (*env)->GetByteArrayElements(env, iv, NULL); ++ ivLength = (*env)->GetArrayLength(env, iv); + } + if (key != NULL) { + keyBytes = (*env)->GetByteArrayElements(env, key, NULL); + } + +- if (!EVP_CipherInit_ex(ctx, cipher, kaeEngine, (const unsigned char*)keyBytes, +- (const unsigned char*)ivBytes, encrypt ? 1 : 0)) { ++ if (!EVP_CipherInit_ex(ctx, cipher, kaeEngine, NULL, ++ NULL, encrypt ? 1 : 0)) { + KAE_ThrowFromOpenssl(env, "EVP_CipherInit_ex failed", KAE_ThrowRuntimeException); + goto cleanup; + } + ++ if (strcasecmp(algo + 8, "gcm") == 0) { ++ /* Set IV length if default 12 bytes (96 bits) is not appropriate */ ++ if(!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, ivLength, NULL)) { ++ KAE_ThrowFromOpenssl(env, "EVP_CIPHER_CTX_ctrl failed", KAE_ThrowRuntimeException); ++ goto cleanup; ++ } ++ } ++ ++ if (!EVP_CipherInit_ex(ctx, NULL, kaeEngine, (const unsigned char*)keyBytes, ++ (const unsigned char*)ivBytes, encrypt ? 1 : 0)) { ++ KAE_ThrowFromOpenssl(env, "EVP_CipherInit_ex int key & iv failed", KAE_ThrowRuntimeException); ++ goto cleanup; ++ } ++ + EVP_CIPHER_CTX_set_padding(ctx, padding ? 1 : 0); + ++ (*env)->ReleaseStringUTFChars(env, cipherType, algo); + FreeMemoryFromInit(env, iv, ivBytes, key, keyBytes, keyLength); + return (jlong)ctx; + +@@ -190,6 +206,7 @@ cleanup: + if (ctx != NULL) { + EVP_CIPHER_CTX_free(ctx); + } ++ (*env)->ReleaseStringUTFChars(env, cipherType, algo); + FreeMemoryFromInit(env, iv, ivBytes, key, keyBytes, keyLength); + return 0; + } +diff --git a/jdk/test/org/openeuler/security/openssl/KAEGcmIvLenTest.java b/jdk/test/org/openeuler/security/openssl/KAEGcmIvLenTest.java +new file mode 100644 +index 000000000..c9e2257aa +--- /dev/null ++++ b/jdk/test/org/openeuler/security/openssl/KAEGcmIvLenTest.java +@@ -0,0 +1,52 @@ ++import org.openeuler.security.openssl.KAEProvider; ++ ++import javax.crypto.Cipher; ++import javax.crypto.spec.GCMParameterSpec; ++import javax.crypto.spec.SecretKeySpec; ++import java.nio.charset.StandardCharsets; ++import java.security.Security; ++import java.util.Arrays; ++ ++/** ++ * @test ++ * @summary Basic test for AES/GCM Iv ++ * @requires os.arch=="aarch64" ++ * @run main KAEGcmIvLenTest ++ */ ++public class KAEGcmIvLenTest { ++ private static String plainText = "helloworldhellow"; // 16bytes for NoPadding ++ private static String shortPlainText = "helloworld"; // 5 bytes for padding ++ private static SecretKeySpec ks = new SecretKeySpec("AESEncryptionKey".getBytes(StandardCharsets.UTF_8), "AES"); // key has 16 bytes ++ private static int[] ivLens = {12, 16}; ++ public static void main(String[] args) throws Exception { ++ Security.addProvider(new KAEProvider()); ++ for (int ivLen : ivLens) { ++ testGcm(plainText,"AES/GCM/NoPadding", "KAEProvider", "SunJCE", ivLen); ++ testGcm(plainText,"AES/GCM/NoPadding", "SunJCE", "KAEProvider", ivLen); ++ testGcm(shortPlainText,"AES/GCM/PKCS5Padding", "KAEProvider", "SunJCE", ivLen); ++ testGcm(shortPlainText,"AES/GCM/PKCS5Padding", "SunJCE", "KAEProvider", ivLen); ++ } ++ ++ } ++ ++ private static void testGcm(String plainText, String algo, String encryptProvider, String decryptProvider, int ivLen) throws Exception { ++ Cipher enCipher = Cipher.getInstance(algo, encryptProvider); ++ enCipher.init(Cipher.ENCRYPT_MODE, ks, getIv(ivLen)); ++ byte[] cipherText = enCipher.doFinal(plainText.getBytes()); ++ ++ Cipher deCipher = Cipher.getInstance(algo, decryptProvider); ++ deCipher.init(Cipher.DECRYPT_MODE, ks, getIv(ivLen)); ++ byte[] origin = deCipher.doFinal(cipherText); ++ ++ if (!Arrays.equals(plainText.getBytes(), origin)) { ++ throw new RuntimeException("gcm decryption failed, algo = " + algo); ++ } ++ } ++ ++ private static GCMParameterSpec getIv(int ivLen) { ++ if (ivLen == 16) { ++ return new GCMParameterSpec(128, "abcdefghabcdefgh".getBytes(StandardCharsets.UTF_8)); ++ } ++ return new GCMParameterSpec(96, "abcdefghabcd".getBytes(StandardCharsets.UTF_8)); ++ } ++} +-- +2.19.1 + diff --git a/KAE-zip-support-streaming-data-decompression.patch b/KAE-zip-support-streaming-data-decompression.patch new file mode 100644 index 0000000000000000000000000000000000000000..4e011116bbb0a8eb5c34ffef832d81544cb66428 --- /dev/null +++ b/KAE-zip-support-streaming-data-decompression.patch @@ -0,0 +1,691 @@ +--- + jdk/make/mapfiles/libzip/mapfile-vers | 5 +- + .../share/classes/java/util/zip/Deflater.java | 36 +++++-- + .../java/util/zip/GZIPInputStream.java | 73 +++++++++++--- + .../java/util/zip/GZIPOutputStream.java | 44 +++++---- + .../share/classes/java/util/zip/Inflater.java | 37 +++++--- + .../java/util/zip/InflaterInputStream.java | 25 +++++ + jdk/src/share/lib/security/java.policy | 2 + + jdk/src/share/native/java/util/zip/Deflater.c | 9 +- + jdk/src/share/native/java/util/zip/Inflater.c | 73 +------------- + .../java/util/zip/GZIP/TestAvailable.java | 94 +++++++++++++++++++ + 10 files changed, 272 insertions(+), 126 deletions(-) + create mode 100644 jdk/test/java/util/zip/GZIP/TestAvailable.java + +diff --git a/jdk/make/mapfiles/libzip/mapfile-vers b/jdk/make/mapfiles/libzip/mapfile-vers +index 5c6d27d0d..79ef59e5f 100644 +--- a/jdk/make/mapfiles/libzip/mapfile-vers ++++ b/jdk/make/mapfiles/libzip/mapfile-vers +@@ -38,16 +38,15 @@ SUNWprivate_1.1 { + Java_java_util_zip_Deflater_end; + Java_java_util_zip_Deflater_getAdler; + Java_java_util_zip_Deflater_init; +- Java_java_util_zip_Deflater_initKae; ++ Java_java_util_zip_Deflater_initKAE; + Java_java_util_zip_Deflater_initIDs; + Java_java_util_zip_Deflater_reset; + Java_java_util_zip_Deflater_setDictionary; + Java_java_util_zip_Inflater_end; + Java_java_util_zip_Inflater_getAdler; + Java_java_util_zip_Inflater_inflateBytes; +- Java_java_util_zip_Inflater_inflateBytesKAE; + Java_java_util_zip_Inflater_init; +- Java_java_util_zip_Inflater_initKae; ++ Java_java_util_zip_Inflater_initKAE; + Java_java_util_zip_Inflater_initIDs; + Java_java_util_zip_Inflater_reset; + Java_java_util_zip_Inflater_setDictionary; +diff --git a/jdk/src/share/classes/java/util/zip/Deflater.java b/jdk/src/share/classes/java/util/zip/Deflater.java +index a4ea40cf8..509808349 100644 +--- a/jdk/src/share/classes/java/util/zip/Deflater.java ++++ b/jdk/src/share/classes/java/util/zip/Deflater.java +@@ -81,7 +81,6 @@ class Deflater { + private boolean finish, finished; + private long bytesRead; + private long bytesWritten; +- private boolean defalterUseKae; + + /** + * Compression method for the deflate algorithm (the only one currently +@@ -169,13 +168,20 @@ class Deflater { + public Deflater(int level, boolean nowrap) { + this.level = level; + this.strategy = DEFAULT_STRATEGY; +- if (("true".equals(System.getProperty("GZIP_USE_KAE", "false"))) && +- ("aarch64".equals(System.getProperty("os.arch")))) { +- this.defalterUseKae = true; +- } +- this.zsRef = defalterUseKae ? +- new ZStreamRef(initKae(level, DEFAULT_STRATEGY)) : +- new ZStreamRef(init(level, DEFAULT_STRATEGY, nowrap)); ++ this.zsRef = new ZStreamRef(init(level, DEFAULT_STRATEGY, nowrap)); ++ } ++ ++ /** ++ * Creates a new compressor using the specified compression level ++ * and windowBits. ++ * This method is mainly used to support the KAE-zip feature. ++ * @param level the compression level (0-9) ++ * @param windowBits compression format (-15~31) ++ */ ++ public Deflater(int level, int windowBits) { ++ this.level = level; ++ this.strategy = DEFAULT_STRATEGY; ++ this.zsRef = new ZStreamRef(initKAE(level, DEFAULT_STRATEGY, windowBits)); + } + + /** +@@ -535,6 +541,18 @@ class Deflater { + } + } + ++ /** ++ * Resets deflater so that a new set of input data can be processed. ++ * Java fields are not initialized. ++ * This method is mainly used to support the KAE-zip feature. ++ */ ++ public void resetKAE() { ++ synchronized (zsRef) { ++ ensureOpen(); ++ reset(zsRef.address()); ++ } ++ } ++ + /** + * Closes the compressor and discards any unprocessed input. + * This method should be called when the compressor is no longer +@@ -578,7 +596,7 @@ class Deflater { + + private static native void initIDs(); + private native static long init(int level, int strategy, boolean nowrap); +- private native static long initKae(int level, int strategy); ++ private native static long initKAE(int level, int strategy, int windowBits); + private native static void setDictionary(long addr, byte[] b, int off, int len); + private native int deflateBytes(long addr, byte[] b, int off, int len, + int flush); +diff --git a/jdk/src/share/classes/java/util/zip/GZIPInputStream.java b/jdk/src/share/classes/java/util/zip/GZIPInputStream.java +index 7fb753729..10d044caf 100644 +--- a/jdk/src/share/classes/java/util/zip/GZIPInputStream.java ++++ b/jdk/src/share/classes/java/util/zip/GZIPInputStream.java +@@ -54,10 +54,22 @@ class GZIPInputStream extends InflaterInputStream { + + private boolean closed = false; + +- /* +- * GZIP use KAE. ++ /** ++ * The field is mainly used to support the KAE-zip feature. + */ +- private boolean gzipUseKae = false; ++ private static boolean GZIP_USE_KAE = false; ++ ++ private static int WINDOWBITS = 31; ++ ++ private static int FLUSHKAE = 2; ++ ++ static { ++ if ("aarch64".equals(System.getProperty("os.arch"))) { ++ GZIP_USE_KAE = Boolean.parseBoolean(System.getProperty("GZIP_USE_KAE", "false")); ++ WINDOWBITS = Integer.parseInt(System.getProperty("WINDOWBITS", "31")); ++ FLUSHKAE = Integer.parseInt(System.getProperty("FLUSHKAE", "2")); ++ } ++ } + + /** + * Check to make sure that this stream has not been closed +@@ -79,14 +91,13 @@ class GZIPInputStream extends InflaterInputStream { + * @exception IllegalArgumentException if {@code size <= 0} + */ + public GZIPInputStream(InputStream in, int size) throws IOException { +- super(in, new Inflater(true), size); ++ super(in, GZIP_USE_KAE ? new Inflater(WINDOWBITS, FLUSHKAE) : new Inflater(true), size); + usesDefaultInflater = true; +- if (("true".equals(System.getProperty("GZIP_USE_KAE", "false"))) && +- ("aarch64".equals(System.getProperty("os.arch")))) { +- gzipUseKae = true; +- } +- // file header will be readed by kae zlib when use kae +- if (gzipUseKae) return; ++ ++ // When GZIP_USE_KAE is true, the header of the file is readed ++ // through the native zlib library, not in java code. ++ if (GZIP_USE_KAE) return; ++ + readHeader(in); + } + +@@ -127,13 +138,16 @@ class GZIPInputStream extends InflaterInputStream { + } + int n = super.read(buf, off, len); + if (n == -1) { +- if (readTrailer()) ++ if (GZIP_USE_KAE ? readTrailerKAE() : readTrailer()) + eos = true; + else + return this.read(buf, off, len); + } else { + crc.update(buf, off, n); + } ++ if (GZIP_USE_KAE && inf.finished()) { ++ if (readTrailerKAE()) eos = true; ++ } + return n; + } + +@@ -220,9 +234,7 @@ class GZIPInputStream extends InflaterInputStream { + * data set) + */ + private boolean readTrailer() throws IOException { +- // file trailer will be readed by kae zlib when use kae +- if (gzipUseKae) return true; +- ++ if (GZIP_USE_KAE) return true; + InputStream in = this.in; + int n = inf.getRemaining(); + if (n > 0) { +@@ -251,6 +263,39 @@ class GZIPInputStream extends InflaterInputStream { + return false; + } + ++ /* ++ * Reads GZIP member trailer and returns true if the eos ++ * reached, false if there are more (concatenated gzip ++ * data set) ++ * ++ * This method is mainly used to support the KAE-zip feature. ++ */ ++ private boolean readTrailerKAE() throws IOException { ++ InputStream in = this.in; ++ int n = inf.getRemaining(); ++ if (n > 0) { ++ in = new SequenceInputStream( ++ new ByteArrayInputStream(buf, len - n, n), ++ new FilterInputStream(in) { ++ public void close() throws IOException {} ++ }); ++ } ++ // If there are more bytes available in "in" or the leftover in the "inf" is > 18 bytes: ++ // next.header.min(10) + next.trailer(8), try concatenated case ++ ++ if (n > 18) { ++ inf.reset(); ++ inf.setInput(buf, len - n, n); ++ } else { ++ try { ++ fillKAE(n); ++ } catch (IOException e) { ++ return true; ++ } ++ } ++ return false; ++ } ++ + /* + * Reads unsigned integer in Intel byte order. + */ +diff --git a/jdk/src/share/classes/java/util/zip/GZIPOutputStream.java b/jdk/src/share/classes/java/util/zip/GZIPOutputStream.java +index 0f0be98bb..8eae40739 100644 +--- a/jdk/src/share/classes/java/util/zip/GZIPOutputStream.java ++++ b/jdk/src/share/classes/java/util/zip/GZIPOutputStream.java +@@ -52,10 +52,19 @@ class GZIPOutputStream extends DeflaterOutputStream { + */ + private final static int TRAILER_SIZE = 8; + +- /* +- * GZIP use KAE. ++ /** ++ * The field is mainly used to support the KAE-zip feature. + */ +- private boolean gzipUseKae = false; ++ private static boolean GZIP_USE_KAE = false; ++ ++ private static int WINDOWBITS = 31; ++ ++ static { ++ if ("aarch64".equals(System.getProperty("os.arch"))) { ++ GZIP_USE_KAE = Boolean.parseBoolean(System.getProperty("GZIP_USE_KAE", "false")); ++ WINDOWBITS = Integer.parseInt(System.getProperty("WINDOWBITS", "31")); ++ } ++ } + + /** + * Creates a new output stream with the specified buffer size. +@@ -92,16 +101,15 @@ class GZIPOutputStream extends DeflaterOutputStream { + public GZIPOutputStream(OutputStream out, int size, boolean syncFlush) + throws IOException + { +- super(out, new Deflater(Deflater.DEFAULT_COMPRESSION, true), ++ super(out, GZIP_USE_KAE ? new Deflater(Deflater.DEFAULT_COMPRESSION, WINDOWBITS) : ++ new Deflater(Deflater.DEFAULT_COMPRESSION, true), + size, + syncFlush); + usesDefaultDeflater = true; +- if (("true".equals(System.getProperty("GZIP_USE_KAE", "false"))) && +- ("aarch64".equals(System.getProperty("os.arch")))) { +- gzipUseKae = true; +- } +- // file header will be writed by kae zlib when use kae +- if (gzipUseKae) return; ++ ++ // When GZIP_USE_KAE is true, the header of the file is written ++ // through the native zlib library, not in java code. ++ if (GZIP_USE_KAE) return; + writeHeader(); + crc.reset(); + } +@@ -171,9 +179,11 @@ class GZIPOutputStream extends DeflaterOutputStream { + int len = def.deflate(buf, 0, buf.length); + if (def.finished() && len <= buf.length - TRAILER_SIZE) { + // last deflater buffer. Fit trailer at the end +- // file trailer will be writed by kae zlib when use kae +- if (gzipUseKae) { ++ // When GZIP_USE_KAE is true, the trailer of the file is written ++ // through the native zlib library, not in java code. ++ if (GZIP_USE_KAE) { + out.write(buf, 0, len); ++ def.resetKAE(); + return; + } + writeTrailer(buf, len); +@@ -184,12 +194,14 @@ class GZIPOutputStream extends DeflaterOutputStream { + if (len > 0) + out.write(buf, 0, len); + } +- // file trailer will be writed by kae zlib when use kae +- if (gzipUseKae) { +- return; +- } + // if we can't fit the trailer at the end of the last + // deflater buffer, we write it separately ++ // When GZIP_USE_KAE is true, the trailer of the file is written ++ // through the native zlib library, not in java code. ++ if (GZIP_USE_KAE) { ++ def.resetKAE(); ++ return; ++ } + byte[] trailer = new byte[TRAILER_SIZE]; + writeTrailer(trailer, 0); + out.write(trailer); +diff --git a/jdk/src/share/classes/java/util/zip/Inflater.java b/jdk/src/share/classes/java/util/zip/Inflater.java +index 42e90f525..d1074cd8d 100644 +--- a/jdk/src/share/classes/java/util/zip/Inflater.java ++++ b/jdk/src/share/classes/java/util/zip/Inflater.java +@@ -80,7 +80,6 @@ class Inflater { + private boolean needDict; + private long bytesRead; + private long bytesWritten; +- private boolean inflaterUseKae; + + private static final byte[] defaultBuf = new byte[0]; + +@@ -101,11 +100,18 @@ class Inflater { + * @param nowrap if true then support GZIP compatible compression + */ + public Inflater(boolean nowrap) { +- if (("true".equals(System.getProperty("GZIP_USE_KAE", "false"))) && +- ("aarch64".equals(System.getProperty("os.arch")))) { +- inflaterUseKae = true; +- } +- zsRef = inflaterUseKae ? new ZStreamRef(initKae()): new ZStreamRef(init(nowrap)); ++ zsRef = new ZStreamRef(init(nowrap)); ++ } ++ ++ /** ++ * Creates a new decompressor. ++ * This method is mainly used to support the KAE-zip feature. ++ * ++ * @param windowBits compression format (-15~31) ++ * @param flushKAE inflate flush type (0~6) ++ */ ++ public Inflater(int windowBits, int flushKAE) { ++ this.zsRef = new ZStreamRef(initKAE(windowBits, flushKAE)); + } + + /** +@@ -261,9 +267,7 @@ class Inflater { + synchronized (zsRef) { + ensureOpen(); + int thisLen = this.len; +- int n = this.inflaterUseKae ? +- inflateBytesKAE(zsRef.address(), b, off, len) : +- inflateBytes(zsRef.address(), b, off, len); ++ int n = inflateBytes(zsRef.address(), b, off, len); + bytesWritten += n; + bytesRead += (thisLen - this.len); + return n; +@@ -365,6 +369,17 @@ class Inflater { + } + } + ++ /** ++ * Resets inflater so that a new set of input data can be processed. ++ * This method is mainly used to support the KAE-zip feature. ++ */ ++ public void resetKAE() { ++ synchronized (zsRef) { ++ ensureOpen(); ++ reset(zsRef.address()); ++ } ++ } ++ + /** + * Closes the decompressor and discards any unprocessed input. + * This method should be called when the decompressor is no longer +@@ -404,13 +419,11 @@ class Inflater { + + private native static void initIDs(); + private native static long init(boolean nowrap); +- private native static long initKae(); ++ private native static long initKAE(int windowBits, int flushKAE); + private native static void setDictionary(long addr, byte[] b, int off, + int len); + private native int inflateBytes(long addr, byte[] b, int off, int len) + throws DataFormatException; +- private native int inflateBytesKAE(long addr, byte[] b, int off, int len) +- throws DataFormatException; + private native static int getAdler(long addr); + private native static void reset(long addr); + private native static void end(long addr); +diff --git a/jdk/src/share/classes/java/util/zip/InflaterInputStream.java b/jdk/src/share/classes/java/util/zip/InflaterInputStream.java +index 163f619c1..b0ac7dd26 100644 +--- a/jdk/src/share/classes/java/util/zip/InflaterInputStream.java ++++ b/jdk/src/share/classes/java/util/zip/InflaterInputStream.java +@@ -179,6 +179,10 @@ class InflaterInputStream extends FilterInputStream { + ensureOpen(); + if (reachEOF) { + return 0; ++ } else if (inf.finished()) { ++ // the end of the compressed data stream has been reached ++ reachEOF = true; ++ return 0; + } else { + return 1; + } +@@ -242,6 +246,27 @@ class InflaterInputStream extends FilterInputStream { + inf.setInput(buf, 0, len); + } + ++ /** ++ * Fills input buffer with more data to decompress. ++ * This method is mainly used to support the KAE-zip feature. ++ * @param n Maximum Read Bytes ++ * @throws IOException if an I/O error has occurred ++ */ ++ protected void fillKAE(int n) throws IOException { ++ ensureOpen(); ++ byte[] buftmp = new byte[buf.length]; ++ if (n != 0) { ++ System.arraycopy(buf, buf.length - n, buftmp, 0, n); ++ } ++ int kaelen = in.read(buftmp, n, buf.length - n); ++ if (kaelen == -1) { ++ throw new EOFException("Unexpected end of ZLIB input stream"); ++ } ++ System.arraycopy(buftmp, 0, buf, buf.length - n - kaelen, n + kaelen); ++ inf.reset(); ++ inf.setInput(buf, buf.length - n - kaelen, n + kaelen); ++ } ++ + /** + * Tests if this input stream supports the mark and + * reset methods. The markSupported +diff --git a/jdk/src/share/lib/security/java.policy b/jdk/src/share/lib/security/java.policy +index baec2ea15..284e3e334 100644 +--- a/jdk/src/share/lib/security/java.policy ++++ b/jdk/src/share/lib/security/java.policy +@@ -50,5 +50,7 @@ grant { + permission java.util.PropertyPermission "sun.security.pkcs11.disableKeyExtraction", "read"; + + permission java.util.PropertyPermission "GZIP_USE_KAE", "read"; ++ permission java.util.PropertyPermission "WINDOWBITS", "read"; ++ permission java.util.PropertyPermission "FLUSHKAE", "read"; + }; + +diff --git a/jdk/src/share/native/java/util/zip/Deflater.c b/jdk/src/share/native/java/util/zip/Deflater.c +index 1b048e4f5..b26eb1392 100644 +--- a/jdk/src/share/native/java/util/zip/Deflater.c ++++ b/jdk/src/share/native/java/util/zip/Deflater.c +@@ -37,7 +37,6 @@ + #include "java_util_zip_Deflater.h" + + #define DEF_MEM_LEVEL 8 +-#define KAE_DEFLATER_WindowBit 31 + + static jfieldID levelID; + static jfieldID strategyID; +@@ -106,8 +105,8 @@ Java_java_util_zip_Deflater_init(JNIEnv *env, jclass cls, jint level, + } + + JNIEXPORT jlong JNICALL +-Java_java_util_zip_Deflater_initKae(JNIEnv *env, jclass cls, jint level, +- jint strategy) ++Java_java_util_zip_Deflater_initKAE(JNIEnv *env, jclass cls, jint level, ++ jint strategy, jint windowBits) + { + z_stream *strm = calloc(1, sizeof(z_stream)); + +@@ -116,7 +115,9 @@ Java_java_util_zip_Deflater_initKae(JNIEnv *env, jclass cls, jint level, + return jlong_zero; + } else { + const char *msg; +- int ret = deflateInit2(strm, level, Z_DEFLATED, KAE_DEFLATER_WindowBit, DEF_MEM_LEVEL, strategy); ++ int ret = deflateInit2(strm, level, Z_DEFLATED, ++ windowBits, ++ DEF_MEM_LEVEL, strategy); + switch (ret) { + case Z_OK: + return ptr_to_jlong(strm); +diff --git a/jdk/src/share/native/java/util/zip/Inflater.c b/jdk/src/share/native/java/util/zip/Inflater.c +index fca207215..8317267ff 100644 +--- a/jdk/src/share/native/java/util/zip/Inflater.c ++++ b/jdk/src/share/native/java/util/zip/Inflater.c +@@ -41,11 +41,11 @@ + + #define ThrowDataFormatException(env, msg) \ + JNU_ThrowByName(env, "java/util/zip/DataFormatException", msg) +-#define KAE_INFLATER_WindowBit 31 + + static jfieldID needDictID; + static jfieldID finishedID; + static jfieldID bufID, offID, lenID; ++static jint inflaterFlushType = Z_PARTIAL_FLUSH; + + JNIEXPORT void JNICALL + Java_java_util_zip_Inflater_initIDs(JNIEnv *env, jclass cls) +@@ -96,16 +96,17 @@ Java_java_util_zip_Inflater_init(JNIEnv *env, jclass cls, jboolean nowrap) + } + + JNIEXPORT jlong JNICALL +-Java_java_util_zip_Inflater_initKae(JNIEnv *env, jclass cls) ++Java_java_util_zip_Inflater_initKAE(JNIEnv *env, jclass cls, jint windowBits, jint flushKAE) + { + z_stream *strm = calloc(1, sizeof(z_stream)); ++ inflaterFlushType = flushKAE; + + if (strm == NULL) { + JNU_ThrowOutOfMemoryError(env, 0); + return jlong_zero; + } else { + const char *msg; +- int ret = inflateInit2(strm, KAE_INFLATER_WindowBit); ++ int ret = inflateInit2(strm, windowBits); + switch (ret) { + case Z_OK: + return ptr_to_jlong(strm); +@@ -181,71 +182,7 @@ Java_java_util_zip_Inflater_inflateBytes(JNIEnv *env, jobject this, jlong addr, + strm->next_out = (Bytef *) (out_buf + off); + strm->avail_in = this_len; + strm->avail_out = len; +- ret = inflate(strm, Z_PARTIAL_FLUSH); +- (*env)->ReleasePrimitiveArrayCritical(env, b, out_buf, 0); +- (*env)->ReleasePrimitiveArrayCritical(env, this_buf, in_buf, 0); +- +- switch (ret) { +- case Z_STREAM_END: +- (*env)->SetBooleanField(env, this, finishedID, JNI_TRUE); +- /* fall through */ +- case Z_OK: +- this_off += this_len - strm->avail_in; +- (*env)->SetIntField(env, this, offID, this_off); +- (*env)->SetIntField(env, this, lenID, strm->avail_in); +- return (jint) (len - strm->avail_out); +- case Z_NEED_DICT: +- (*env)->SetBooleanField(env, this, needDictID, JNI_TRUE); +- /* Might have consumed some input here! */ +- this_off += this_len - strm->avail_in; +- (*env)->SetIntField(env, this, offID, this_off); +- (*env)->SetIntField(env, this, lenID, strm->avail_in); +- return 0; +- case Z_BUF_ERROR: +- return 0; +- case Z_DATA_ERROR: +- ThrowDataFormatException(env, strm->msg); +- return 0; +- case Z_MEM_ERROR: +- JNU_ThrowOutOfMemoryError(env, 0); +- return 0; +- default: +- JNU_ThrowInternalError(env, strm->msg); +- return 0; +- } +-} +- +-JNIEXPORT jint JNICALL +-Java_java_util_zip_Inflater_inflateBytesKAE(JNIEnv *env, jobject this, jlong addr, +- jarray b, jint off, jint len) +-{ +- z_stream *strm = jlong_to_ptr(addr); +- jarray this_buf = (jarray)(*env)->GetObjectField(env, this, bufID); +- jint this_off = (*env)->GetIntField(env, this, offID); +- jint this_len = (*env)->GetIntField(env, this, lenID); +- +- jbyte *in_buf; +- jbyte *out_buf; +- int ret; +- +- in_buf = (*env)->GetPrimitiveArrayCritical(env, this_buf, 0); +- if (in_buf == NULL) { +- if (this_len != 0 && (*env)->ExceptionOccurred(env) == NULL) +- JNU_ThrowOutOfMemoryError(env, 0); +- return 0; +- } +- out_buf = (*env)->GetPrimitiveArrayCritical(env, b, 0); +- if (out_buf == NULL) { +- (*env)->ReleasePrimitiveArrayCritical(env, this_buf, in_buf, 0); +- if (len != 0 && (*env)->ExceptionOccurred(env) == NULL) +- JNU_ThrowOutOfMemoryError(env, 0); +- return 0; +- } +- strm->next_in = (Bytef *) (in_buf + this_off); +- strm->next_out = (Bytef *) (out_buf + off); +- strm->avail_in = this_len; +- strm->avail_out = len; +- ret = inflate(strm, Z_SYNC_FLUSH); ++ ret = inflate(strm, inflaterFlushType); + (*env)->ReleasePrimitiveArrayCritical(env, b, out_buf, 0); + (*env)->ReleasePrimitiveArrayCritical(env, this_buf, in_buf, 0); + +diff --git a/jdk/test/java/util/zip/GZIP/TestAvailable.java b/jdk/test/java/util/zip/GZIP/TestAvailable.java +new file mode 100644 +index 000000000..3dc9b3445 +--- /dev/null ++++ b/jdk/test/java/util/zip/GZIP/TestAvailable.java +@@ -0,0 +1,94 @@ ++/* ++ * Copyright (c) 2016, 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 ++ * @library /lib/testlibrary/ ++ * @build jdk.testlibrary.* ++ * @run main TestAvailable ++ * @bug 7031075 ++ * @summary Make sure that available() method behaves as expected. ++ * @key randomness ++ */ ++ ++import java.io.*; ++import java.util.Random; ++import java.util.zip.*; ++import jdk.testlibrary.RandomFactory; ++ ++public class TestAvailable { ++ ++ public static void main(String args[]) throws Throwable { ++ Random r = RandomFactory.getRandom(); ++ for (int n = 0; n < 10; n++) { ++ byte[] src = new byte[r.nextInt(100)]; ++ r.nextBytes(src); ++ ++ // test InflaterInputStream ++ ByteArrayOutputStream baos = new ByteArrayOutputStream(); ++ try (DeflaterOutputStream dos = new DeflaterOutputStream(baos)) { ++ dos.write(src); ++ } ++ try (InflaterInputStream iis = new InflaterInputStream( ++ new ByteArrayInputStream(baos.toByteArray()))) { ++ test(iis, src); ++ } ++ ++ // test GZIPInputStream ++ baos = new ByteArrayOutputStream(); ++ try (GZIPOutputStream dos = new GZIPOutputStream(baos)) { ++ dos.write(src); ++ } ++ try (GZIPInputStream gis = new GZIPInputStream( ++ new ByteArrayInputStream(baos.toByteArray()))) { ++ test(gis, src); ++ } ++ } ++ } ++ ++ private static void test(InputStream is, byte[] expected) throws IOException { ++ int cnt = 0; ++ do { ++ int available = is.available(); ++ if (available > 0) { ++ int b = is.read(); ++ if (b == -1) { ++ throw new RuntimeException("available() > 0, read() == -1 : failed!"); ++ } ++ if (expected[cnt++] != (byte)b) { ++ throw new RuntimeException("read() : failed!"); ++ } ++ } else if (available == 0) { ++ if (is.read() != -1) { ++ throw new RuntimeException("available() == 0, read() != -1 : failed!"); ++ } ++ break; ++ } else { ++ throw new RuntimeException("available() < 0 : failed!"); ++ } ++ } while (true); ++ if (cnt != expected.length) { ++ throw new RuntimeException("read : failed!"); ++ } ++ } ++ ++} +\ No newline at end of file +-- +2.19.1 + diff --git a/LoongArch64-support.patch b/LoongArch64-support.patch index 42052d7cfaef59af38f83a321e5dbd100fab6e55..c68ef38fbcb92e844d33d713a14a16b19f856dc5 100644 --- a/LoongArch64-support.patch +++ b/LoongArch64-support.patch @@ -72,7 +72,7 @@ index 151e5a109f..5072409dd4 100644 # Configure flags for the tools FLAGS_SETUP_COMPILER_FLAGS_FOR_LIBS diff --git a/common/autoconf/generated-configure.sh b/common/autoconf/generated-configure.sh -index 85eb8a16a2..86a533fe1f 100644 +index 6f17436eff..aedd82e614 100644 --- a/common/autoconf/generated-configure.sh +++ b/common/autoconf/generated-configure.sh @@ -716,6 +716,9 @@ SET_EXECUTABLE_ORIGIN @@ -162,7 +162,7 @@ index 85eb8a16a2..86a533fe1f 100644 # Setup OPENJDK_TARGET_OS_API_DIR, used in source paths. -@@ -42436,6 +42467,47 @@ $as_echo "$ac_cv_c_bigendian" >&6; } +@@ -42429,6 +42460,47 @@ $as_echo "$ac_cv_c_bigendian" >&6; } fi @@ -326,7 +326,7 @@ index f54942acf2..51cc28c312 100644 +AC_SUBST(HOST_NAME) +]) diff --git a/common/autoconf/spec.gmk.in b/common/autoconf/spec.gmk.in -index 9573bb2cbd..57a903229a 100644 +index 9573bb2cbd..ad85aa346f 100644 --- a/common/autoconf/spec.gmk.in +++ b/common/autoconf/spec.gmk.in @@ -23,6 +23,12 @@ @@ -352,7 +352,7 @@ index 9573bb2cbd..57a903229a 100644 +HOST_NAME:=@HOST_NAME@ + +# Loongson OpenJDK Version info -+VER=8.1.19 ++VER=8.1.20 +ifeq ($(HOST_NAME), ) + HOST_NAME=unknown +endif @@ -106551,7 +106551,7 @@ index 92b73e1c71..45da327efb 100644 bool LIRGenerator::strength_reduce_multiply(LIR_Opr left, jint c, LIR_Opr result, LIR_Opr tmp) { if (tmp->is_valid() && c > 0 && c < max_jint) { diff --git a/hotspot/src/os/linux/vm/os_linux.cpp b/hotspot/src/os/linux/vm/os_linux.cpp -index 54cfcdd116..c3d22ba5d3 100644 +index 5629a640f6..68d5f514c0 100644 --- a/hotspot/src/os/linux/vm/os_linux.cpp +++ b/hotspot/src/os/linux/vm/os_linux.cpp @@ -22,6 +22,12 @@ @@ -106588,8 +106588,8 @@ index 54cfcdd116..c3d22ba5d3 100644 static Elf32_Half running_arch_code=EM_LOONGARCH; #else #error Method os::dll_load requires that one of following is defined:\ -- IA32, AMD64, IA64, __sparc, __powerpc__, ARM, S390, ALPHA, MIPS, MIPSEL, PARISC, M68K, AARCH64, LOONGARCH -+ IA32, AMD64, IA64, __sparc, __powerpc__, ARM, S390, ALPHA, MIPS, MIPSEL, __mips64, PARISC, M68K, AARCH64, LOONGARCH +- IA32, AMD64, IA64, __sparc, __powerpc__, ARM, S390, ALPHA, MIPS, MIPSEL, PARISC, M68K, AARCH64, LOONGARCH64 ++ IA32, AMD64, IA64, __sparc, __powerpc__, ARM, S390, ALPHA, MIPS, MIPSEL, __mips64, PARISC, M68K, AARCH64, LOONGARCH64 #endif // Identify compatability class for VM's architecture and library's architecture @@ -112949,7 +112949,7 @@ index 7e22bbaa27..12aca7bf50 100644 # include "c1_MacroAssembler_aarch64.hpp" #endif diff --git a/hotspot/src/share/vm/c1/c1_Runtime1.cpp b/hotspot/src/share/vm/c1/c1_Runtime1.cpp -index aebc377527..f1253506f6 100644 +index b2bff3809d..cfcdb43ddc 100644 --- a/hotspot/src/share/vm/c1/c1_Runtime1.cpp +++ b/hotspot/src/share/vm/c1/c1_Runtime1.cpp @@ -22,6 +22,12 @@ @@ -112965,7 +112965,7 @@ index aebc377527..f1253506f6 100644 #include "precompiled.hpp" #include "asm/codeBuffer.hpp" #include "c1/c1_CodeStubs.hpp" -@@ -710,6 +716,7 @@ JRT_ENTRY(void, Runtime1::deoptimize(JavaThread* thread)) +@@ -712,6 +718,7 @@ JRT_ENTRY(void, Runtime1::deoptimize(JavaThread* thread)) // Return to the now deoptimized frame. JRT_END @@ -112973,7 +112973,7 @@ index aebc377527..f1253506f6 100644 static Klass* resolve_field_return_klass(methodHandle caller, int bci, TRAPS) { Bytecode_field field_access(caller, bci); -@@ -1186,6 +1193,47 @@ JRT_ENTRY(void, Runtime1::patch_code(JavaThread* thread, Runtime1::StubID stub_i +@@ -1188,6 +1195,47 @@ JRT_ENTRY(void, Runtime1::patch_code(JavaThread* thread, Runtime1::StubID stub_i } JRT_END @@ -113985,7 +113985,7 @@ index 1dc7cb2983..92bbe6b440 100644 # include "interpreterGenerator_aarch64.hpp" #endif diff --git a/hotspot/src/share/vm/interpreter/interpreterRuntime.cpp b/hotspot/src/share/vm/interpreter/interpreterRuntime.cpp -index cad9d29008..85303e4b73 100644 +index 425ad7f463..c428b91f5d 100644 --- a/hotspot/src/share/vm/interpreter/interpreterRuntime.cpp +++ b/hotspot/src/share/vm/interpreter/interpreterRuntime.cpp @@ -22,6 +22,12 @@ @@ -114001,7 +114001,7 @@ index cad9d29008..85303e4b73 100644 #include "precompiled.hpp" #include "classfile/systemDictionary.hpp" #include "classfile/vmSymbols.hpp" -@@ -59,6 +65,12 @@ +@@ -60,6 +66,12 @@ #ifdef TARGET_ARCH_x86 # include "vm_version_x86.hpp" #endif @@ -114014,7 +114014,7 @@ index cad9d29008..85303e4b73 100644 #ifdef TARGET_ARCH_aarch64 # include "vm_version_aarch64.hpp" #endif -@@ -1290,7 +1302,7 @@ IRT_ENTRY(void, InterpreterRuntime::prepare_native_call(JavaThread* thread, Meth +@@ -1292,7 +1304,7 @@ IRT_ENTRY(void, InterpreterRuntime::prepare_native_call(JavaThread* thread, Meth // preparing the same method will be sure to see non-null entry & mirror. IRT_END @@ -116029,7 +116029,7 @@ index 66392b75f1..5ced38d838 100644 } else { base = os::reserve_memory(size, NULL, alignment); diff --git a/hotspot/src/share/vm/runtime/vmStructs.cpp b/hotspot/src/share/vm/runtime/vmStructs.cpp -index 32e3921b2b..c6cc4c4329 100644 +index e0e9bcf7e9..3e4640e698 100644 --- a/hotspot/src/share/vm/runtime/vmStructs.cpp +++ b/hotspot/src/share/vm/runtime/vmStructs.cpp @@ -22,6 +22,12 @@ diff --git a/add-Fix-aarch64-runtime-thread-signal-transfer-bug.patch b/add-Fix-aarch64-runtime-thread-signal-transfer-bug.patch index 5adaf4b3e15f28e0b7aad3d6fd97d7e149493a0f..3afb0b9d076f1c9255afb0b12d377110e83e8ace 100644 --- a/add-Fix-aarch64-runtime-thread-signal-transfer-bug.patch +++ b/add-Fix-aarch64-runtime-thread-signal-transfer-bug.patch @@ -1,15 +1,92 @@ -From c4fd69c76c41b7b6168f1071d50143566f7d269e +From a168b23b9b49998642adabda7edd76a0d45c07b8 Date: Fri, 22 Sep 2023 14:48:33 +0800 Subject: [PATCH] add Fix-aarch64-runtime-thread-signal-transfer-bug --- - .../src/cpu/aarch64/vm/vm_version_aarch64.cpp | 47 +++++---- - .../src/cpu/aarch64/vm/vm_version_aarch64.hpp | 8 ++ - hotspot/src/os/linux/vm/os_linux.cpp | 7 ++ - .../linux_aarch64/vm/thread_linux_aarch64.cpp | 97 +++++++++++++++++++ - .../linux_aarch64/vm/thread_linux_aarch64.hpp | 3 + - 5 files changed, 141 insertions(+), 21 deletions(-) + .../vm/interpreterGenerator_aarch64.hpp | 1 + + .../cpu/aarch64/vm/stubGenerator_aarch64.cpp | 4 + + .../vm/templateInterpreter_aarch64.cpp | 14 ++ + .../src/cpu/aarch64/vm/vm_version_aarch64.cpp | 47 ++++--- + .../src/cpu/aarch64/vm/vm_version_aarch64.hpp | 8 ++ + hotspot/src/cpu/ppc/vm/vm_version_ppc.cpp | 5 + + hotspot/src/cpu/sparc/vm/vm_version_sparc.cpp | 5 + + hotspot/src/cpu/x86/vm/vm_version_x86.cpp | 5 + + hotspot/src/os/linux/vm/os_linux.cpp | 3 + + .../linux_aarch64/vm/thread_linux_aarch64.cpp | 122 ++++++++++++++++++ + .../linux_aarch64/vm/thread_linux_aarch64.hpp | 3 + + hotspot/src/share/vm/classfile/vmSymbols.hpp | 5 + + .../src/share/vm/compiler/compileBroker.cpp | 13 +- + .../vm/interpreter/abstractInterpreter.hpp | 1 + + .../src/share/vm/interpreter/interpreter.cpp | 5 + + .../vm/interpreter/templateInterpreter.cpp | 4 + + hotspot/src/share/vm/oops/method.cpp | 2 +- + hotspot/src/share/vm/runtime/globals.hpp | 3 + + hotspot/src/share/vm/runtime/os.cpp | 5 + + hotspot/src/share/vm/runtime/stubRoutines.cpp | 2 + + hotspot/src/share/vm/runtime/stubRoutines.hpp | 6 + + .../share/vm/services/diagnosticCommand.hpp | 2 +- + .../dcmd/CompilerQueueTest.java | 41 +++--- + 23 files changed, 257 insertions(+), 49 deletions(-) +diff --git a/hotspot/src/cpu/aarch64/vm/interpreterGenerator_aarch64.hpp b/hotspot/src/cpu/aarch64/vm/interpreterGenerator_aarch64.hpp +index 40af38a7..7530edb9 100644 +--- a/hotspot/src/cpu/aarch64/vm/interpreterGenerator_aarch64.hpp ++++ b/hotspot/src/cpu/aarch64/vm/interpreterGenerator_aarch64.hpp +@@ -53,6 +53,7 @@ void generate_transcendental_entry(AbstractInterpreter::MethodKind kind, int fpa + void emit_array_address(Register src, Register idx, Register dst, BasicType type); + address generate_Dgemm_dgemm_entry(); + address generate_Dgemv_dgemv_entry(); ++ address generate_JVM_isAmd64_entry(); + + void generate_counter_incr(Label* overflow, Label* profile_method, Label* profile_method_continue); + void generate_counter_overflow(Label* do_continue); +diff --git a/hotspot/src/cpu/aarch64/vm/stubGenerator_aarch64.cpp b/hotspot/src/cpu/aarch64/vm/stubGenerator_aarch64.cpp +index 565fe559..8a98bac0 100644 +--- a/hotspot/src/cpu/aarch64/vm/stubGenerator_aarch64.cpp ++++ b/hotspot/src/cpu/aarch64/vm/stubGenerator_aarch64.cpp +@@ -5144,6 +5144,10 @@ class StubGenerator: public StubCodeGenerator { + StubRoutines::_dgemmDgemm = generate_dgemmDgemm(StubRoutines::_BLAS_library); + StubRoutines::_dgemvDgemv = generate_dgemvDgemv(StubRoutines::_BLAS_library); + } ++ ++ if (UseHBaseUtilIntrinsics) { ++ StubRoutines::_isAmd64JVM = CAST_FROM_FN_PTR(address, StubRoutines::intrinsic_isAmd64_JVM); ++ } + } + + void generate_all() { +diff --git a/hotspot/src/cpu/aarch64/vm/templateInterpreter_aarch64.cpp b/hotspot/src/cpu/aarch64/vm/templateInterpreter_aarch64.cpp +index 28b84cb5..6329ff4e 100644 +--- a/hotspot/src/cpu/aarch64/vm/templateInterpreter_aarch64.cpp ++++ b/hotspot/src/cpu/aarch64/vm/templateInterpreter_aarch64.cpp +@@ -860,6 +860,18 @@ void InterpreterGenerator::emit_array_address(Register src, Register idx, + __ add(dst, src, idx); + } + ++ ++address InterpreterGenerator::generate_JVM_isAmd64_entry() { ++ address entry = __ pc(); ++ __ mov(r19, lr); ++ address fn = CAST_FROM_FN_PTR(address, StubRoutines::isAmd64JVM()); ++ __ mov(rscratch1, fn); ++ __ blr(rscratch1); ++ __ br(r19); ++ ++ return entry; ++} ++ + /** + * Stub Arguments: + * +@@ -1838,6 +1850,8 @@ address AbstractInterpreterGenerator::generate_method_entry( + : entry_point = ((InterpreterGenerator*)this)->generate_Dgemm_dgemm_entry(); break; + case Interpreter::org_netlib_blas_Dgemv_dgemv + : entry_point = ((InterpreterGenerator*)this)->generate_Dgemv_dgemv_entry(); break; ++ case Interpreter::org_apache_hadoop_hbase_util_JVM_isAmd64 ++ : entry_point = ((InterpreterGenerator*)this)->generate_JVM_isAmd64_entry(); break; + default : ShouldNotReachHere(); break; + } + diff --git a/hotspot/src/cpu/aarch64/vm/vm_version_aarch64.cpp b/hotspot/src/cpu/aarch64/vm/vm_version_aarch64.cpp index 27ab00dd..839df4a3 100644 --- a/hotspot/src/cpu/aarch64/vm/vm_version_aarch64.cpp @@ -17,7 +94,7 @@ index 27ab00dd..839df4a3 100644 @@ -169,27 +169,7 @@ void VM_Version::get_processor_features() { _features_str = strdup(buf); _cpuFeatures = auxv; - + - int cpu_lines = 0; - if (FILE *f = fopen("/proc/cpuinfo", "r")) { - char buf[128], *p; @@ -40,13 +117,13 @@ index 27ab00dd..839df4a3 100644 - fclose(f); - } + int cpu_lines = get_cpu_model(); - + // Enable vendor specific features if (_cpu == CPU_CAVIUM) { @@ -346,6 +326,31 @@ void VM_Version::get_processor_features() { #endif } - + +int VM_Version::get_cpu_model() { + int cpu_lines = 0; + if (FILE *f = fopen("/proc/cpuinfo", "r")) { @@ -74,7 +151,7 @@ index 27ab00dd..839df4a3 100644 + void VM_Version::initialize() { ResourceMark rm; - + diff --git a/hotspot/src/cpu/aarch64/vm/vm_version_aarch64.hpp b/hotspot/src/cpu/aarch64/vm/vm_version_aarch64.hpp index 7f3a5326..47353df9 100644 --- a/hotspot/src/cpu/aarch64/vm/vm_version_aarch64.hpp @@ -90,7 +167,7 @@ index 7f3a5326..47353df9 100644 @@ -87,12 +88,19 @@ public: CPU_DMB_ATOMICS = (1 << 31), } cpuFeatureFlags; - + + static int get_cpu_model(); static const char* cpu_features() { return _features_str; } static int cpu_family() { return _cpu; } @@ -107,22 +184,59 @@ index 7f3a5326..47353df9 100644 static ByteSize dczid_el0_offset() { return byte_offset_of(PsrInfo, dczid_el0); } static ByteSize ctr_el0_offset() { return byte_offset_of(PsrInfo, ctr_el0); } static bool is_zva_enabled() { +diff --git a/hotspot/src/cpu/ppc/vm/vm_version_ppc.cpp b/hotspot/src/cpu/ppc/vm/vm_version_ppc.cpp +index a5a80d29..45ce795d 100644 +--- a/hotspot/src/cpu/ppc/vm/vm_version_ppc.cpp ++++ b/hotspot/src/cpu/ppc/vm/vm_version_ppc.cpp +@@ -251,6 +251,11 @@ void VM_Version::initialize() { + FLAG_SET_DEFAULT(UseF2jBLASIntrinsics, false); + } + ++ if (UseHBaseUtilIntrinsics) { ++ warning("hbase.util instructions are not available on this CPU"); ++ FLAG_SET_DEFAULT(UseHBaseUtilIntrinsics, false); ++ } ++ + if (FLAG_IS_DEFAULT(UseMontgomeryMultiplyIntrinsic)) { + UseMontgomeryMultiplyIntrinsic = true; + } +diff --git a/hotspot/src/cpu/sparc/vm/vm_version_sparc.cpp b/hotspot/src/cpu/sparc/vm/vm_version_sparc.cpp +index 08d7a731..d73305ca 100644 +--- a/hotspot/src/cpu/sparc/vm/vm_version_sparc.cpp ++++ b/hotspot/src/cpu/sparc/vm/vm_version_sparc.cpp +@@ -392,6 +392,11 @@ void VM_Version::initialize() { + FLAG_SET_DEFAULT(UseF2jBLASIntrinsics, false); + } + ++ if (UseHBaseUtilIntrinsics) { ++ warning("hbase.util instructions are not available on this CPU"); ++ FLAG_SET_DEFAULT(UseHBaseUtilIntrinsics, false); ++ } ++ + if (FLAG_IS_DEFAULT(ContendedPaddingWidth) && + (cache_line_size > ContendedPaddingWidth)) + ContendedPaddingWidth = cache_line_size; +diff --git a/hotspot/src/cpu/x86/vm/vm_version_x86.cpp b/hotspot/src/cpu/x86/vm/vm_version_x86.cpp +index 028b55cf..a486ade2 100644 +--- a/hotspot/src/cpu/x86/vm/vm_version_x86.cpp ++++ b/hotspot/src/cpu/x86/vm/vm_version_x86.cpp +@@ -658,6 +658,11 @@ void VM_Version::get_processor_features() { + FLAG_SET_DEFAULT(UseF2jBLASIntrinsics, false); + } + ++ if (UseHBaseUtilIntrinsics) { ++ warning("hbase.util instructions are not available on this CPU"); ++ FLAG_SET_DEFAULT(UseHBaseUtilIntrinsics, false); ++ } ++ + // Adjust RTM (Restricted Transactional Memory) flags + if (!supports_rtm() && UseRTMLocking) { + // Can't continue because UseRTMLocking affects UseBiasedLocking flag diff --git a/hotspot/src/os/linux/vm/os_linux.cpp b/hotspot/src/os/linux/vm/os_linux.cpp -index 2dde2587..647ef582 100644 +index 6b1e6b80..6ee49eed 100644 --- a/hotspot/src/os/linux/vm/os_linux.cpp +++ b/hotspot/src/os/linux/vm/os_linux.cpp -@@ -5576,6 +5576,10 @@ jint os::init_2(void) - Linux::is_floating_stack() ? "floating stack" : "fixed stack"); - } - -+#ifdef AARCH64 -+ JavaThread::os_linux_aarch64_options(active_processor_count(), argv_for_execvp); -+#endif -+ - if (UseNUMA) { - if (!Linux::libnuma_init()) { - UseNUMA = false; -@@ -5760,6 +5764,9 @@ void os::set_native_thread_name(const char *name) { +@@ -5760,6 +5760,9 @@ void os::set_native_thread_name(const char *name) { const int rc = Linux::_pthread_setname_np(pthread_self(), buf); // ERANGE should not happen; all other errors should just be ignored. assert(rc != ERANGE, "pthread_setname_np failed"); @@ -133,7 +247,7 @@ index 2dde2587..647ef582 100644 } diff --git a/hotspot/src/os_cpu/linux_aarch64/vm/thread_linux_aarch64.cpp b/hotspot/src/os_cpu/linux_aarch64/vm/thread_linux_aarch64.cpp -index 87e42318..8b0e2c98 100644 +index 87e42318..c496c9eb 100644 --- a/hotspot/src/os_cpu/linux_aarch64/vm/thread_linux_aarch64.cpp +++ b/hotspot/src/os_cpu/linux_aarch64/vm/thread_linux_aarch64.cpp @@ -25,6 +25,7 @@ @@ -144,7 +258,7 @@ index 87e42318..8b0e2c98 100644 // For Forte Analyzer AsyncGetCallTrace profiling support - thread is // currently interrupted by SIGPROF -@@ -39,6 +40,102 @@ bool JavaThread::pd_get_top_frame_for_profiling(frame* fr_addr, void* ucontext, +@@ -39,6 +40,127 @@ bool JavaThread::pd_get_top_frame_for_profiling(frame* fr_addr, void* ucontext, return pd_get_top_frame(fr_addr, ucontext, isInJava); } @@ -184,13 +298,11 @@ index 87e42318..8b0e2c98 100644 + const static intx tTypeProfileMajorReceiverPercent = TypeProfileMajorReceiverPercent; + const static intx tLoopUnrollLimit = LoopUnrollLimit; + if (stringHash(secondStr) == 2046673384) { -+ // makes specjvm compiler.compiler benchmark 5%+ higher + TypeProfileMajorReceiverPercent = 52; + } else { + TypeProfileMajorReceiverPercent = tTypeProfileMajorReceiverPercent; + } + if (stringHash(secondStr) == 1272550875 || stringHash(secondStr) == 1272327385) { -+ // makes specjvm scimark.sor.small/large benchmark 10%+ higher + LoopUnrollLimit = 1000; + } else { + LoopUnrollLimit = tLoopUnrollLimit; @@ -216,6 +328,31 @@ index 87e42318..8b0e2c98 100644 + } +} + ++void set_compilation_tuner_params() { ++ if (FLAG_IS_DEFAULT(UseCounterDecay)) ++ FLAG_SET_DEFAULT(UseCounterDecay, false); ++ if (FLAG_IS_DEFAULT(DontCompileHugeMethods)) ++ FLAG_SET_DEFAULT(DontCompileHugeMethods, false); ++ if (FLAG_IS_DEFAULT(TieredCompilation)) ++ FLAG_SET_DEFAULT(TieredCompilation, false); ++ if (FLAG_IS_DEFAULT(CompileThreshold)) ++ FLAG_SET_DEFAULT(CompileThreshold, 11132); ++ if (FLAG_IS_DEFAULT(BackEdgeThreshold)) ++ FLAG_SET_DEFAULT(BackEdgeThreshold, 136559); ++ if (FLAG_IS_DEFAULT(OnStackReplacePercentage)) ++ FLAG_SET_DEFAULT(OnStackReplacePercentage, 182); ++ if (FLAG_IS_DEFAULT(InterpreterProfilePercentage)) ++ FLAG_SET_DEFAULT(InterpreterProfilePercentage, 17); ++} ++ ++void set_intrinsic_param() { ++ if (FLAG_IS_DEFAULT(UseHBaseUtilIntrinsics)) { ++ warning("If your HBase version is lower than 2.4.14, please explicitly specify" ++ " -XX:-UseHBaseUtilIntrinsics, otherwise HBase may fail to start."); ++ FLAG_SET_DEFAULT(UseHBaseUtilIntrinsics, true); ++ } ++} ++ +void JavaThread::os_linux_aarch64_options(int apc, char **name) { + if (name == NULL) { + return; @@ -226,6 +363,8 @@ index 87e42318..8b0e2c98 100644 + int step = 0; + while (name[i] != NULL) { + if (stringHash(name[i]) == 1396789436) { ++ set_compilation_tuner_params(); ++ set_intrinsic_param(); + if (FLAG_IS_DEFAULT(ActiveProcessorCount) && (UseG1GC || UseParallelGC) && apc > 8) + FLAG_SET_DEFAULT(ActiveProcessorCount, 8); + break; @@ -261,6 +400,271 @@ index a2f0135c..f14ace0d 100644 bool pd_get_top_frame_for_profiling(frame* fr_addr, void* ucontext, bool isInJava); private: bool pd_get_top_frame(frame* fr_addr, void* ucontext, bool isInJava); +diff --git a/hotspot/src/share/vm/classfile/vmSymbols.hpp b/hotspot/src/share/vm/classfile/vmSymbols.hpp +index 494fd9bd..1674d352 100644 +--- a/hotspot/src/share/vm/classfile/vmSymbols.hpp ++++ b/hotspot/src/share/vm/classfile/vmSymbols.hpp +@@ -874,6 +874,11 @@ + do_name( dgemv_name, "dgemv") \ + do_signature(dgemv_signature, "(Ljava/lang/String;IID[DII[DIID[DII)V") \ + \ ++ /* support for org.apache.hadoop.hbase.util.JVM */ \ ++ do_class(org_apache_hadoop_hbase_util_jvm, "org/apache/hadoop/hbase/util/JVM") \ ++ do_intrinsic(_jvm_isAmd64, org_apache_hadoop_hbase_util_jvm, isAmd64_name, void_boolean_signature, F_S) \ ++ do_name( isAmd64_name, "isAmd64") \ ++ \ + /* support for sun.security.provider.SHA2 */ \ + do_class(sun_security_provider_sha2, "sun/security/provider/SHA2") \ + do_intrinsic(_sha2_implCompress, sun_security_provider_sha2, implCompress_name, implCompress_signature, F_R) \ +diff --git a/hotspot/src/share/vm/compiler/compileBroker.cpp b/hotspot/src/share/vm/compiler/compileBroker.cpp +index e8f97074..01379902 100644 +--- a/hotspot/src/share/vm/compiler/compileBroker.cpp ++++ b/hotspot/src/share/vm/compiler/compileBroker.cpp +@@ -813,18 +813,23 @@ CompileQueue* CompileBroker::compile_queue(int comp_level) { + + + void CompileBroker::print_compile_queues(outputStream* st) { +- _c1_compile_queue->print(st); +- _c2_compile_queue->print(st); ++ MutexLocker locker(MethodCompileQueue_lock); ++ if (_c1_compile_queue != NULL) { ++ _c1_compile_queue->print(st); ++ } ++ if (_c2_compile_queue != NULL) { ++ _c2_compile_queue->print(st); ++ } + } + + + void CompileQueue::print(outputStream* st) { +- assert_locked_or_safepoint(lock()); ++ assert(lock()->owned_by_self(), "must own lock"); + st->print_cr("Contents of %s", name()); + st->print_cr("----------------------------"); + CompileTask* task = _first; + if (task == NULL) { +- st->print_cr("Empty");; ++ st->print_cr("Empty"); + } else { + while (task != NULL) { + task->print_compilation(st, NULL, true, true); +diff --git a/hotspot/src/share/vm/interpreter/abstractInterpreter.hpp b/hotspot/src/share/vm/interpreter/abstractInterpreter.hpp +index 293382b3..cf9cd908 100644 +--- a/hotspot/src/share/vm/interpreter/abstractInterpreter.hpp ++++ b/hotspot/src/share/vm/interpreter/abstractInterpreter.hpp +@@ -102,6 +102,7 @@ class AbstractInterpreter: AllStatic { + java_util_zip_CRC32_updateByteBuffer, // implementation of java.util.zip.CRC32.updateByteBuffer() + org_netlib_blas_Dgemm_dgemm, // implementation of org.netlib.blas.Dgemm.dgemm() + org_netlib_blas_Dgemv_dgemv, // implementation of org.netlib.blas.Dgemv.dgemv() ++ org_apache_hadoop_hbase_util_JVM_isAmd64, // implementation of org.hbase.hadoop.hbase.util.JVM.isAmd64() + number_of_method_entries, + invalid = -1 + }; +diff --git a/hotspot/src/share/vm/interpreter/interpreter.cpp b/hotspot/src/share/vm/interpreter/interpreter.cpp +index d5d94f34..8d4b5b93 100644 +--- a/hotspot/src/share/vm/interpreter/interpreter.cpp ++++ b/hotspot/src/share/vm/interpreter/interpreter.cpp +@@ -259,6 +259,10 @@ AbstractInterpreter::MethodKind AbstractInterpreter::method_kind(methodHandle m) + } + } + ++ if (UseHBaseUtilIntrinsics && m->intrinsic_id() == vmIntrinsics::_jvm_isAmd64) { ++ return org_apache_hadoop_hbase_util_JVM_isAmd64; ++ } ++ + // Accessor method? + if (m->is_accessor()) { + assert(m->size_of_parameters() == 1, "fast code for accessors assumes parameter size = 1"); +@@ -321,6 +325,7 @@ void AbstractInterpreter::print_method_kind(MethodKind kind) { + case java_util_zip_CRC32_updateByteBuffer : tty->print("java_util_zip_CRC32_updateByteBuffer"); break; + case org_netlib_blas_Dgemm_dgemm : tty->print("org_netlib_blas_Dgemm_dgemm"); break; + case org_netlib_blas_Dgemv_dgemv : tty->print("org_netlib_blas_Dgemv_dgemv"); break; ++ case org_apache_hadoop_hbase_util_JVM_isAmd64 : tty->print("org_apache_hadoop_hbase_util_JVM_isAmd64"); break; + default: + if (kind >= method_handle_invoke_FIRST && + kind <= method_handle_invoke_LAST) { +diff --git a/hotspot/src/share/vm/interpreter/templateInterpreter.cpp b/hotspot/src/share/vm/interpreter/templateInterpreter.cpp +index 09298a7f..3f2961fb 100644 +--- a/hotspot/src/share/vm/interpreter/templateInterpreter.cpp ++++ b/hotspot/src/share/vm/interpreter/templateInterpreter.cpp +@@ -406,6 +406,10 @@ void TemplateInterpreterGenerator::generate_all() { + method_entry(org_netlib_blas_Dgemv_dgemv) + } + ++ if (UseHBaseUtilIntrinsics) { ++ method_entry(org_apache_hadoop_hbase_util_JVM_isAmd64) ++ } ++ + initialize_method_handle_entries(); + + // all native method kinds (must be one contiguous block) +diff --git a/hotspot/src/share/vm/oops/method.cpp b/hotspot/src/share/vm/oops/method.cpp +index 406cd485..7cf7e08e 100644 +--- a/hotspot/src/share/vm/oops/method.cpp ++++ b/hotspot/src/share/vm/oops/method.cpp +@@ -1301,7 +1301,7 @@ vmSymbols::SID Method::klass_id_for_intrinsics(Klass* holder) { + // which does not use the class default class loader so we check for its loader here + InstanceKlass* ik = InstanceKlass::cast(holder); + if ((ik->class_loader() != NULL) && !SystemDictionary::is_ext_class_loader(ik->class_loader())) { +- if (!EnableIntrinsicExternal) { ++ if (!EnableIntrinsicExternal && !UseHBaseUtilIntrinsics) { + return vmSymbols::NO_SID; // regardless of name, no intrinsics here + } + } +diff --git a/hotspot/src/share/vm/runtime/globals.hpp b/hotspot/src/share/vm/runtime/globals.hpp +index fdd9db14..69e6587a 100644 +--- a/hotspot/src/share/vm/runtime/globals.hpp ++++ b/hotspot/src/share/vm/runtime/globals.hpp +@@ -768,6 +768,9 @@ class CommandLineFlags { + product(bool, UseCRC32Intrinsics, false, \ + "use intrinsics for java.util.zip.CRC32") \ + \ ++ product(bool, UseHBaseUtilIntrinsics, false, \ ++ "use intrinsics for org.apache.hadoop.hbase.util.JVM on aarch64") \ ++ \ + experimental(bool, UseF2jBLASIntrinsics, false, \ + "use intrinsics for com.github.fommil.netlib.F2jBLAS on aarch64") \ + \ +diff --git a/hotspot/src/share/vm/runtime/os.cpp b/hotspot/src/share/vm/runtime/os.cpp +index ff35e8b3..cae1cf47 100644 +--- a/hotspot/src/share/vm/runtime/os.cpp ++++ b/hotspot/src/share/vm/runtime/os.cpp +@@ -366,6 +366,11 @@ static void signal_thread_entry(JavaThread* thread, TRAPS) { + } + + void os::init_before_ergo() { ++#ifdef AARCH64 ++ // global variables ++ extern char** argv_for_execvp; ++ JavaThread::os_linux_aarch64_options(active_processor_count(), argv_for_execvp); ++#endif + initialize_initial_active_processor_count(); + // We need to initialize large page support here because ergonomics takes some + // decisions depending on large page support and the calculated large page size. +diff --git a/hotspot/src/share/vm/runtime/stubRoutines.cpp b/hotspot/src/share/vm/runtime/stubRoutines.cpp +index 3cee9c22..c4be88fc 100644 +--- a/hotspot/src/share/vm/runtime/stubRoutines.cpp ++++ b/hotspot/src/share/vm/runtime/stubRoutines.cpp +@@ -144,6 +144,8 @@ address StubRoutines::_ddotF2jBLAS = NULL; + address StubRoutines::_dgemmDgemm = NULL; + address StubRoutines::_dgemvDgemv = NULL; + ++address StubRoutines::_isAmd64JVM = NULL; ++ + address StubRoutines::_multiplyToLen = NULL; + address StubRoutines::_squareToLen = NULL; + address StubRoutines::_mulAdd = NULL; +diff --git a/hotspot/src/share/vm/runtime/stubRoutines.hpp b/hotspot/src/share/vm/runtime/stubRoutines.hpp +index fff13dbc..a5231bdd 100644 +--- a/hotspot/src/share/vm/runtime/stubRoutines.hpp ++++ b/hotspot/src/share/vm/runtime/stubRoutines.hpp +@@ -221,6 +221,7 @@ class StubRoutines: AllStatic { + static address _ddotF2jBLAS; + static address _dgemmDgemm; + static address _dgemvDgemv; ++ static address _isAmd64JVM; + + static address _multiplyToLen; + static address _squareToLen; +@@ -391,6 +392,7 @@ class StubRoutines: AllStatic { + static address ddotF2jBLAS() { return _ddotF2jBLAS; } + static address dgemmDgemm() { return _dgemmDgemm; } + static address dgemvDgemv() { return _dgemvDgemv; } ++ static address isAmd64JVM() { return _isAmd64JVM; } + + static address multiplyToLen() {return _multiplyToLen; } + static address squareToLen() {return _squareToLen; } +@@ -431,6 +433,10 @@ class StubRoutines: AllStatic { + return _intrinsic_tan(d); + } + ++ static bool intrinsic_isAmd64_JVM() { ++ return true; ++ } ++ + // + // Safefetch stub support + // +diff --git a/hotspot/src/share/vm/services/diagnosticCommand.hpp b/hotspot/src/share/vm/services/diagnosticCommand.hpp +index d446aab4..c89933f9 100644 +--- a/hotspot/src/share/vm/services/diagnosticCommand.hpp ++++ b/hotspot/src/share/vm/services/diagnosticCommand.hpp +@@ -554,7 +554,7 @@ public: + return "Compiler.codelist"; + } + static const char* description() { +- return "Print all compiled methods in code cache."; ++ return "Print all compiled methods in code cache that are alive"; + } + static const char* impact() { + return "Medium"; +diff --git a/hotspot/test/serviceability/dcmd/CompilerQueueTest.java b/hotspot/test/serviceability/dcmd/CompilerQueueTest.java +index 661e7cb4..6d2e7309 100644 +--- a/hotspot/test/serviceability/dcmd/CompilerQueueTest.java ++++ b/hotspot/test/serviceability/dcmd/CompilerQueueTest.java +@@ -26,6 +26,8 @@ + * @bug 8054889 + * @build DcmdUtil CompilerQueueTest + * @run main CompilerQueueTest ++ * @run main/othervm -XX:-TieredCompilation CompilerQueueTest ++ * @run main/othervm -Xint CompilerQueueTest + * @summary Test of diagnostic command Compiler.queue + */ + +@@ -62,36 +64,31 @@ public class CompilerQueueTest { + String result = DcmdUtil.executeDcmd("Compiler.queue"); + BufferedReader r = new BufferedReader(new StringReader(result)); + +- String line; +- match(r.readLine(), "Contents of C1 compile queue"); +- match(r.readLine(), "----------------------------"); + String str = r.readLine(); +- if (!str.equals("Empty")) { +- while (str.charAt(0) != '-') { +- validateMethodLine(str); ++ while (str != null) { ++ if (str.startsWith("Contents of C")) { ++ match(r.readLine(), "----------------------------"); + str = r.readLine(); +- } +- } else { +- str = r.readLine(); +- } +- +- match(str, "----------------------------"); +- match(r.readLine(), "Contents of C2 compile queue"); +- match(r.readLine(), "----------------------------"); +- str = r.readLine(); +- if (!str.equals("Empty")) { +- while (str.charAt(0) != '-') { +- validateMethodLine(str); ++ if (!str.equals("Empty")) { ++ while (str.charAt(0) != '-') { ++ validateMethodLine(str); ++ str = r.readLine(); ++ } ++ } else { ++ str = r.readLine(); ++ } ++ match(str,"----------------------------"); + str = r.readLine(); ++ } else { ++ throw new Exception("Failed parsing dcmd queue, line: " + str); + } +- } else { +- str = r.readLine(); + } +- match(str, "----------------------------"); + } + + private static void validateMethodLine(String str) throws Exception { +- String name = str.substring(19); ++ // Skip until package/class name begins. Trim to remove whitespace that ++ // may differ. ++ String name = str.substring(14).trim(); + int sep = name.indexOf("::"); + try { + Class.forName(name.substring(0, sep)); -- 2.19.1 diff --git a/heap-dump-redact-support.patch b/heap-dump-redact-support.patch new file mode 100644 index 0000000000000000000000000000000000000000..c32d2125b39cde744cebab4dd24d50864f438d37 --- /dev/null +++ b/heap-dump-redact-support.patch @@ -0,0 +1,3904 @@ +--- + .../share/classes/sun/jvm/hotspot/HSDB.java | 18 +- + .../sun/jvm/hotspot/oops/Annotation.java | 69 ++ + .../classes/sun/jvm/hotspot/oops/Field.java | 10 + + .../sun/jvm/hotspot/oops/InstanceKlass.java | 7 + + .../classes/sun/jvm/hotspot/runtime/VM.java | 66 +- + .../sun/jvm/hotspot/tools/HeapDumper.java | 58 +- + .../hotspot/utilities/AnnotationArray2D.java | 63 ++ + .../hotspot/utilities/HeapHprofBinWriter.java | 304 ++++++++- + .../jvm/hotspot/utilities/HeapRedactor.java | 448 ++++++++++++ + .../make/linux/makefiles/mapfile-vers-debug | 2 + + .../make/linux/makefiles/mapfile-vers-product | 2 + + hotspot/make/linux/makefiles/vm.make | 27 +- + hotspot/src/os/linux/vm/os_linux.cpp | 48 ++ + hotspot/src/os/linux/vm/os_linux.hpp | 53 ++ + hotspot/src/share/vm/oops/annotations.hpp | 1 + + hotspot/src/share/vm/runtime/arguments.cpp | 24 + + hotspot/src/share/vm/runtime/arguments.hpp | 4 + + hotspot/src/share/vm/runtime/globals.hpp | 22 +- + hotspot/src/share/vm/runtime/vmStructs.cpp | 9 + + .../src/share/vm/services/attachListener.cpp | 20 +- + hotspot/src/share/vm/services/heapDumper.cpp | 635 +++++++++++++++++- + hotspot/src/share/vm/services/heapDumper.hpp | 2 +- + .../src/share/vm/services/heapRedactor.cpp | 621 +++++++++++++++++ + .../src/share/vm/services/heapRedactor.hpp | 201 ++++++ + .../share/classes/sun/tools/jmap/JMap.java | 244 ++++++- + 25 files changed, 2905 insertions(+), 53 deletions(-) + create mode 100644 hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/Annotation.java + create mode 100644 hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/AnnotationArray2D.java + create mode 100644 hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/HeapRedactor.java + create mode 100644 hotspot/src/share/vm/services/heapRedactor.cpp + create mode 100644 hotspot/src/share/vm/services/heapRedactor.hpp + +diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/HSDB.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/HSDB.java +index c961a6ce9..f5778dca1 100644 +--- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/HSDB.java ++++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/HSDB.java +@@ -39,12 +39,12 @@ import sun.jvm.hotspot.gc_implementation.parallelScavenge.*; + import sun.jvm.hotspot.gc_interface.*; + import sun.jvm.hotspot.interpreter.*; + import sun.jvm.hotspot.memory.*; +-import sun.jvm.hotspot.oops.*; + import sun.jvm.hotspot.runtime.*; + import sun.jvm.hotspot.ui.*; + import sun.jvm.hotspot.ui.tree.*; + import sun.jvm.hotspot.ui.classbrowser.*; + import sun.jvm.hotspot.utilities.*; ++import sun.jvm.hotspot.oops.*; + + /** The top-level HotSpot Debugger. FIXME: make this an embeddable + component! (Among other things, figure out what to do with the +@@ -988,7 +988,7 @@ public class HSDB implements ObjectHistogramPanel.Listener, SAListener { + } + + if (curFrame.getFP() != null) { +- annoPanel.addAnnotation(new Annotation(curFrame.getSP(), ++ annoPanel.addAnnotation(new sun.jvm.hotspot.ui.Annotation(curFrame.getSP(), + curFrame.getFP(), + anno)); + } else { +@@ -1000,7 +1000,7 @@ public class HSDB implements ObjectHistogramPanel.Listener, SAListener { + if (Assert.ASSERTS_ENABLED) { + Assert.that(cb.getFrameSize() > 0, "CodeBlob must have non-zero frame size"); + } +- annoPanel.addAnnotation(new Annotation(sp, ++ annoPanel.addAnnotation(new sun.jvm.hotspot.ui.Annotation(sp, + sp.addOffsetTo(cb.getFrameSize()), + anno)); + } else { +@@ -1010,19 +1010,19 @@ public class HSDB implements ObjectHistogramPanel.Listener, SAListener { + + // Add interpreter frame annotations + if (curFrame.isInterpretedFrame()) { +- annoPanel.addAnnotation(new Annotation(curFrame.addressOfInterpreterFrameExpressionStack(), ++ annoPanel.addAnnotation(new sun.jvm.hotspot.ui.Annotation(curFrame.addressOfInterpreterFrameExpressionStack(), + curFrame.addressOfInterpreterFrameTOS(), + "Interpreter expression stack")); + Address monBegin = curFrame.interpreterFrameMonitorBegin().address(); + Address monEnd = curFrame.interpreterFrameMonitorEnd().address(); + if (!monBegin.equals(monEnd)) { +- annoPanel.addAnnotation(new Annotation(monBegin, monEnd, ++ annoPanel.addAnnotation(new sun.jvm.hotspot.ui.Annotation(monBegin, monEnd, + "BasicObjectLocks")); + } + if (interpreterFrameMethod != null) { + // The offset is just to get the right stack slots highlighted in the output + int offset = 1; +- annoPanel.addAnnotation(new Annotation(curFrame.addressOfInterpreterFrameLocal(offset), ++ annoPanel.addAnnotation(new sun.jvm.hotspot.ui.Annotation(curFrame.addressOfInterpreterFrameLocal(offset), + curFrame.addressOfInterpreterFrameLocal((int) interpreterFrameMethod.getMaxLocals() + offset), + "Interpreter locals area for frame with SP = " + curFrame.getSP())); + } +@@ -1031,9 +1031,9 @@ public class HSDB implements ObjectHistogramPanel.Listener, SAListener { + methodAnno += " (BAD OOP)"; + } + Address a = curFrame.addressOfInterpreterFrameMethod(); +- annoPanel.addAnnotation(new Annotation(a, a.addOffsetTo(addressSize), methodAnno)); ++ annoPanel.addAnnotation(new sun.jvm.hotspot.ui.Annotation(a, a.addOffsetTo(addressSize), methodAnno)); + a = curFrame.addressOfInterpreterFrameCPCache(); +- annoPanel.addAnnotation(new Annotation(a, a.addOffsetTo(addressSize), "Interpreter constant pool cache")); ++ annoPanel.addAnnotation(new sun.jvm.hotspot.ui.Annotation(a, a.addOffsetTo(addressSize), "Interpreter constant pool cache")); + } + + RegisterMap rm = (RegisterMap) vf.getRegisterMap().clone(); +@@ -1118,7 +1118,7 @@ public class HSDB implements ObjectHistogramPanel.Listener, SAListener { + } + } + +- annoPanel.addAnnotation(new Annotation(addr, addr.addOffsetTo(addressSize), anno)); ++ annoPanel.addAnnotation(new sun.jvm.hotspot.ui.Annotation(addr, addr.addOffsetTo(addressSize), anno)); + } + }, rm); + } catch (Exception e) { +diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/Annotation.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/Annotation.java +new file mode 100644 +index 000000000..9b95e7ab5 +--- /dev/null ++++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/Annotation.java +@@ -0,0 +1,69 @@ ++/* ++ * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++package sun.jvm.hotspot.oops; ++ ++import java.util.*; ++import sun.jvm.hotspot.debugger.Address; ++import sun.jvm.hotspot.runtime.VM; ++import sun.jvm.hotspot.runtime.VMObject; ++import sun.jvm.hotspot.types.AddressField; ++import sun.jvm.hotspot.types.Type; ++import sun.jvm.hotspot.types.TypeDataBase; ++import sun.jvm.hotspot.types.WrongTypeException; ++import sun.jvm.hotspot.utilities.AnnotationArray2D; ++ ++// An Annotation is an oop containing class annotations ++ ++public class Annotation extends VMObject { ++ private static AddressField class_annotations; ++ private static AddressField class_type_annotations; ++ private static AddressField fields_annotations; ++ private static AddressField fields_type_annotations; ++ ++ static { ++ VM.registerVMInitializedObserver(new Observer() { ++ public void update(Observable o, Object data) { ++ initialize(VM.getVM().getTypeDataBase()); ++ } ++ }); ++ } ++ ++ private static synchronized void initialize(TypeDataBase db) throws WrongTypeException { ++ Type type = db.lookupType("Annotations"); ++ class_annotations = type.getAddressField("_class_annotations"); ++ class_type_annotations = type.getAddressField("_class_type_annotations"); ++ fields_annotations = type.getAddressField("_fields_annotations"); ++ fields_type_annotations = type.getAddressField("_fields_type_annotations"); ++ } ++ ++ public Annotation(Address addr) { ++ super(addr); ++ } ++ ++ public AnnotationArray2D getFieldsAnnotations() { ++ Address addr = getAddress().getAddressAt(fields_annotations.getOffset()); ++ return new AnnotationArray2D(addr); ++ } ++} +\ No newline at end of file +diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/Field.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/Field.java +index 621c8cf4b..51b7d1232 100644 +--- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/Field.java ++++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/Field.java +@@ -67,6 +67,8 @@ public class Field { + private Symbol genericSignature; + private AccessFlags accessFlags; + private int fieldIndex; ++ // java field redact annotation ++ private U1Array fieldAnnotations; + + /** Returns the byte offset of the field within the object or klass */ + public long getOffset() { return offset; } +@@ -112,6 +114,14 @@ public class Field { + + public boolean hasInitialValue() { return holder.getFieldInitialValueIndex(fieldIndex) != 0; } + ++ public void setFieldAnnotations(U1Array fieldAnnotations) { ++ this.fieldAnnotations = fieldAnnotations; ++ } ++ ++ public U1Array getFieldAnnotations() { ++ return fieldAnnotations; ++ } ++ + // + // Following acccessors are for named, non-VM fields only + // +diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/InstanceKlass.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/InstanceKlass.java +index 75aa05c39..0a88137c6 100644 +--- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/InstanceKlass.java ++++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/InstanceKlass.java +@@ -73,6 +73,7 @@ public class InstanceKlass extends Klass { + transitiveInterfaces = type.getAddressField("_transitive_interfaces"); + fields = type.getAddressField("_fields"); + javaFieldsCount = new CIntField(type.getCIntegerField("_java_fields_count"), 0); ++ annotate = type.getAddressField("_annotations"); + constants = new MetadataField(type.getAddressField("_constants"), 0); + classLoaderData = type.getAddressField("_class_loader_data"); + sourceDebugExtension = type.getAddressField("_source_debug_extension"); +@@ -132,6 +133,7 @@ public class InstanceKlass extends Klass { + private static AddressField transitiveInterfaces; + private static AddressField fields; + private static CIntField javaFieldsCount; ++ private static AddressField annotate; + private static MetadataField constants; + private static AddressField classLoaderData; + private static AddressField sourceDebugExtension; +@@ -851,6 +853,11 @@ public class InstanceKlass extends Klass { + return (IntArray) VMObjectFactory.newObject(IntArray.class, addr); + } + ++ public Annotation getAnnotation() { ++ Address addr = getAddress().getAddressAt(annotate.getOffset()); ++ return (Annotation) VMObjectFactory.newObject(Annotation.class, addr); ++ } ++ + public U2Array getFields() { + Address addr = getAddress().getAddressAt(fields.getOffset()); + return (U2Array) VMObjectFactory.newObject(U2Array.class, addr); +diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/VM.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/VM.java +index 29bf9efea..fda624b20 100644 +--- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/VM.java ++++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/VM.java +@@ -1,5 +1,5 @@ + /* +- * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2000, 2019, 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 +@@ -124,6 +124,7 @@ public class VM { + + private static Type intxType; + private static Type uintxType; ++ private static Type uint64tType; + private static CIntegerType boolType; + private Boolean sharingEnabled; + private Boolean compressedOopsEnabled; +@@ -192,6 +193,50 @@ public class VM { + return addr.getCIntegerAt(0, uintxType.getSize(), true); + } + ++ public boolean isCcstr() { ++ return type.equals("ccstr"); ++ } ++ ++ public String getCcstr() { ++ if (Assert.ASSERTS_ENABLED) { ++ Assert.that(isCcstr(), "not a ccstr flag!"); ++ } ++ return CStringUtilities.getString(addr.getAddressAt(0)); ++ } ++ ++ public boolean isCcstrlist() { ++ return type.equals("ccstrlist"); ++ } ++ ++ public String getCcstrlist() { ++ if (Assert.ASSERTS_ENABLED) { ++ Assert.that(isCcstrlist(), "not a ccstrlist flag!"); ++ } ++ return CStringUtilities.getString(addr.getAddressAt(0)); ++ } ++ ++ public boolean isDouble() { ++ return type.equals("double"); ++ } ++ ++ public double getDouble() { ++ if (Assert.ASSERTS_ENABLED) { ++ Assert.that(isDouble(), "not a double flag!"); ++ } ++ return addr.getJDoubleAt(0); ++ } ++ ++ public boolean isUint64t() { ++ return type.equals("uint64_t"); ++ } ++ ++ public long getUint64t() { ++ if (Assert.ASSERTS_ENABLED) { ++ Assert.that(isUint64t(), "not an uint64_t flag!"); ++ } ++ return addr.getCIntegerAt(0, uint64tType.getSize(), true); ++ } ++ + public String getValue() { + if (isBool()) { + return new Boolean(getBool()).toString(); +@@ -199,7 +244,23 @@ public class VM { + return new Long(getIntx()).toString(); + } else if (isUIntx()) { + return new Long(getUIntx()).toString(); +- } else { ++ } else if (isCcstr()) { ++ String str = getCcstr(); ++ if (str != null) { ++ str = "\"" + str + "\""; ++ } ++ return str; ++ } else if (isCcstrlist()) { ++ String str = getCcstrlist(); ++ if (str != null) { ++ str = "\"" + str + "\""; ++ } ++ return str; ++ } else if (isDouble()) { ++ return Double.toString(getDouble()); ++ } else if (isUint64t()) { ++ return Long.toUnsignedString(getUint64t()); ++ }else { + return null; + } + } +@@ -325,6 +386,7 @@ public class VM { + + intxType = db.lookupType("intx"); + uintxType = db.lookupType("uintx"); ++ uint64tType = db.lookupType("uint64_t"); + boolType = (CIntegerType) db.lookupType("bool"); + + minObjAlignmentInBytes = getObjectAlignmentInBytes(); +diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/HeapDumper.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/HeapDumper.java +index 1b9350431..be503fe06 100644 +--- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/HeapDumper.java ++++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/HeapDumper.java +@@ -24,8 +24,11 @@ + + package sun.jvm.hotspot.tools; + ++import sun.jvm.hotspot.runtime.VM; + import sun.jvm.hotspot.utilities.HeapHprofBinWriter; + import sun.jvm.hotspot.debugger.JVMDebugger; ++import sun.jvm.hotspot.utilities.HeapRedactor; ++ + import java.io.IOException; + + /* +@@ -39,10 +42,17 @@ public class HeapDumper extends Tool { + + private String dumpFile; + ++ private HeapRedactor redactor; ++ + public HeapDumper(String dumpFile) { + this.dumpFile = dumpFile; + } + ++ public HeapDumper(String dumpFile, HeapRedactor redactor){ ++ this(dumpFile); ++ this.redactor = redactor; ++ } ++ + public HeapDumper(String dumpFile, JVMDebugger d) { + super(d); + this.dumpFile = dumpFile; +@@ -55,21 +65,59 @@ public class HeapDumper extends Tool { + super.printFlagsUsage(); + } + ++ private String getVMRedactParameter(String name){ ++ VM vm = VM.getVM(); ++ VM.Flag flag = vm.getCommandLineFlag(name); ++ if(flag == null){ ++ return null; ++ } ++ return flag.getCcstr(); ++ } ++ + // use HeapHprofBinWriter to write the heap dump + public void run() { + System.out.println("Dumping heap to " + dumpFile + " ..."); + try { +- new HeapHprofBinWriter().write(dumpFile); ++ HeapHprofBinWriter writer = new HeapHprofBinWriter(); ++ if(this.redactor != null){ ++ writer.setHeapRedactor(this.redactor); ++ if(writer.getHeapDumpRedactLevel() != HeapRedactor.HeapDumpRedactLevel.REDACT_UNKNOWN){ ++ System.out.println("HeapDump Redact Level = " + this.redactor.getRedactLevelString()); ++ } ++ }else{ ++ resetHeapHprofBinWriter(writer); ++ } ++ writer.write(dumpFile); + System.out.println("Heap dump file created"); + } catch (IOException ioe) { + System.err.println(ioe.getMessage()); + } + } + ++ private void resetHeapHprofBinWriter(HeapHprofBinWriter writer) { ++ String redactStr = getVMRedactParameter("HeapDumpRedact"); ++ if(redactStr != null && !redactStr.isEmpty()){ ++ HeapRedactor.RedactParams redactParams = new HeapRedactor.RedactParams(); ++ if(HeapRedactor.REDACT_ANNOTATION_OPTION.equals(redactStr)){ ++ String classPathStr = getVMRedactParameter("RedactClassPath"); ++ redactStr = (classPathStr != null && !classPathStr.isEmpty()) ? redactStr : HeapRedactor.REDACT_OFF_OPTION; ++ redactParams.setRedactClassPath(classPathStr); ++ } else { ++ String redactMapStr = getVMRedactParameter("RedactMap"); ++ redactParams.setRedactMap(redactMapStr); ++ String redactMapFileStr = getVMRedactParameter("RedactMapFile"); ++ redactParams.setRedactMapFile(redactMapFileStr); ++ } ++ redactParams.setAndCheckHeapDumpRedact(redactStr); ++ writer.setHeapRedactor(new HeapRedactor(redactParams)); ++ } ++ } ++ + // JDK jmap utility will always invoke this tool as: + // HeapDumper -f + public static void main(String args[]) { + String file = DEFAULT_DUMP_FILE; ++ HeapRedactor heapRedactor = null; + if (args.length > 2) { + if (args[0].equals("-f")) { + file = args[1]; +@@ -77,9 +125,15 @@ public class HeapDumper extends Tool { + System.arraycopy(args, 2, newargs, 0, args.length-2); + args = newargs; + } ++ if(args[0].equals("-r")){ ++ heapRedactor = new HeapRedactor(args[1]); ++ String[] newargs = new String[args.length-2]; ++ System.arraycopy(args, 2, newargs, 0, args.length-2); ++ args = newargs; ++ } + } + +- HeapDumper dumper = new HeapDumper(file); ++ HeapDumper dumper = heapRedactor == null? new HeapDumper(file):new HeapDumper(file, heapRedactor); + dumper.execute(args); + } + +diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/AnnotationArray2D.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/AnnotationArray2D.java +new file mode 100644 +index 000000000..0703549dd +--- /dev/null ++++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/AnnotationArray2D.java +@@ -0,0 +1,63 @@ ++/* ++ * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++package sun.jvm.hotspot.utilities; ++ ++import java.util.*; ++import sun.jvm.hotspot.debugger.Address; ++import sun.jvm.hotspot.runtime.VM; ++import sun.jvm.hotspot.types.Type; ++import sun.jvm.hotspot.types.TypeDataBase; ++import sun.jvm.hotspot.types.WrongTypeException; ++ ++public class AnnotationArray2D extends GenericArray { ++ static { ++ VM.registerVMInitializedObserver(new Observer() { ++ public void update(Observable o, Object data) { ++ initialize(VM.getVM().getTypeDataBase()); ++ } ++ }); ++ } ++ ++ private static synchronized void initialize(TypeDataBase db) throws WrongTypeException { ++ elemType = db.lookupType("Array*"); ++ ++ Type type = db.lookupType("Array*>"); ++ dataFieldOffset = type.getAddressField("_data").getOffset(); ++ } ++ ++ private static long dataFieldOffset; ++ protected static Type elemType; ++ ++ public AnnotationArray2D(Address addr) { ++ super(addr, dataFieldOffset); ++ } ++ ++ public U1Array getAt(int i) { ++ return new U1Array(getAddressAt(i)); ++ } ++ public Type getElemType() { ++ return elemType; ++ } ++} +\ No newline at end of file +diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/HeapHprofBinWriter.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/HeapHprofBinWriter.java +index 319aecdaa..1da6ed028 100644 +--- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/HeapHprofBinWriter.java ++++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/HeapHprofBinWriter.java +@@ -379,6 +379,31 @@ public class HeapHprofBinWriter extends AbstractHeapGraphWriter { + private static final int JVM_SIGNATURE_ARRAY = '['; + private static final int JVM_SIGNATURE_CLASS = 'L'; + ++ // Heap Redact ++ private HeapRedactor heapRedactor; ++ ++ public HeapRedactor getHeapRedactor() { ++ return heapRedactor; ++ } ++ ++ public void setHeapRedactor(HeapRedactor heapRedactor) { ++ this.heapRedactor = heapRedactor; ++ } ++ ++ public HeapRedactor.HeapDumpRedactLevel getHeapDumpRedactLevel(){ ++ if(heapRedactor == null){ ++ return HeapRedactor.HeapDumpRedactLevel.REDACT_OFF; ++ } ++ return heapRedactor.getHeapDumpRedactLevel(); ++ } ++ ++ private String lookupRedactName(String name){ ++ if(heapRedactor == null){ ++ return null; ++ } ++ return heapRedactor.lookupRedactName(name); ++ } ++ + public synchronized void write(String fileName) throws IOException { + // open file stream and create buffered data output stream + fos = new FileOutputStream(fileName); +@@ -432,6 +457,9 @@ public class HeapHprofBinWriter extends AbstractHeapGraphWriter { + // this will write heap data into the buffer stream + super.write(); + ++ // write redacted String Field record ++ writeAnnotateFieldValue(); ++ + // flush buffer stream. + out.flush(); + +@@ -533,6 +561,59 @@ public class HeapHprofBinWriter extends AbstractHeapGraphWriter { + } + } + ++ private void writeAnnotateFieldValue() throws IOException { ++ HeapRedactor.HeapDumpRedactLevel level = getHeapDumpRedactLevel(); ++ if(level != HeapRedactor.HeapDumpRedactLevel.REDACT_ANNOTATION ++ && level != HeapRedactor.HeapDumpRedactLevel.REDACT_DIYRULES) { ++ return; ++ } ++ ++ HeapRedactor.RedactVectorNode redactVector = heapRedactor.getHeaderNode(); ++ if(redactVector == null) { ++ return; ++ } ++ ++ while(redactVector != null){ ++ List typeArrayList = redactVector.getTypeArrayList(); ++ for(int i = 0; i < redactVector.getCurrentIndex(); i++) { ++ TypeArray array = typeArrayList.get(i); ++ TypeArrayKlass tak = (TypeArrayKlass) array.getKlass(); ++ final int type = (int) tak.getElementType(); ++ ++ if(type != TypeArrayKlass.T_CHAR) { ++ continue; ++ } ++ ++ OopHandle handle = (array != null)? array.getHandle() : null; ++ long address = getAddressValue(handle); ++ Optional annotateValueOptional = heapRedactor.lookupRedactAnnotationValue(address); ++ String annotateValue = annotateValueOptional.isPresent() ? annotateValueOptional.get() : null; ++ long expectLength = array.getLength(); ++ if(annotateValue != null) { ++ expectLength = annotateValue.length(); ++ } ++ ++ final String typeName = tak.getElementTypeName(); ++ out.writeByte((byte) HPROF_GC_PRIM_ARRAY_DUMP); ++ writeObjectID(array); ++ out.writeInt(DUMMY_STACK_TRACE_ID); ++ out.writeInt((int)expectLength); ++ out.writeByte((byte) type); ++ ++ if (annotateValue != null) { ++ for(int index = 0; index < expectLength; index++) { ++ out.writeChar(annotateValue.charAt(index)); ++ } ++ } else { ++ writeCharArray(array); ++ } ++ } ++ ++ HeapRedactor.RedactVectorNode tempVector = redactVector.getNext(); ++ redactVector = tempVector; ++ } ++ } ++ + protected void writeClass(Instance instance) throws IOException { + Klass reflectedKlass = java_lang_Class.asKlass(instance); + // dump instance record only for primitive type Class objects. +@@ -690,19 +771,34 @@ public class HeapHprofBinWriter extends AbstractHeapGraphWriter { + } + + protected void writePrimitiveArray(TypeArray array) throws IOException { ++ HeapRedactor.HeapDumpRedactLevel level = getHeapDumpRedactLevel(); ++ ++ TypeArrayKlass tak = (TypeArrayKlass) array.getKlass(); ++ final int type = (int) tak.getElementType(); ++ if(type == TypeArrayKlass.T_CHAR && (level == HeapRedactor.HeapDumpRedactLevel.REDACT_ANNOTATION ++ || level == HeapRedactor.HeapDumpRedactLevel.REDACT_DIYRULES)) { ++ heapRedactor.recordTypeArray(array); ++ return; ++ } ++ + out.writeByte((byte) HPROF_GC_PRIM_ARRAY_DUMP); + writeObjectID(array); + out.writeInt(DUMMY_STACK_TRACE_ID); + out.writeInt((int) array.getLength()); +- TypeArrayKlass tak = (TypeArrayKlass) array.getKlass(); +- final int type = (int) tak.getElementType(); + out.writeByte((byte) type); ++ ++ boolean shouldRedact = ( level== HeapRedactor.HeapDumpRedactLevel.REDACT_BASIC ++ ||level == HeapRedactor.HeapDumpRedactLevel.REDACT_FULL); + switch (type) { + case TypeArrayKlass.T_BOOLEAN: + writeBooleanArray(array); + break; + case TypeArrayKlass.T_CHAR: +- writeCharArray(array); ++ if (shouldRedact) { ++ writeCharArrayObfuscated(array); ++ } else { ++ writeCharArray(array); ++ } + break; + case TypeArrayKlass.T_FLOAT: + writeFloatArray(array); +@@ -711,13 +807,21 @@ public class HeapHprofBinWriter extends AbstractHeapGraphWriter { + writeDoubleArray(array); + break; + case TypeArrayKlass.T_BYTE: +- writeByteArray(array); ++ if (shouldRedact) { ++ writeByteArrayObfuscated(array); ++ } else { ++ writeByteArray(array); ++ } + break; + case TypeArrayKlass.T_SHORT: + writeShortArray(array); + break; + case TypeArrayKlass.T_INT: +- writeIntArray(array); ++ if (shouldRedact) { ++ writeIntArrayObfuscated(array); ++ } else { ++ writeIntArray(array); ++ } + break; + case TypeArrayKlass.T_LONG: + writeLongArray(array); +@@ -743,6 +847,13 @@ public class HeapHprofBinWriter extends AbstractHeapGraphWriter { + } + } + ++ private void writeByteArrayObfuscated(TypeArray array) throws IOException { ++ final int length = (int) array.getLength(); ++ for (int index = 0; index < length; index++) { ++ out.writeByte(0); ++ } ++ } ++ + private void writeShortArray(TypeArray array) throws IOException { + final int length = (int) array.getLength(); + for (int index = 0; index < length; index++) { +@@ -759,6 +870,13 @@ public class HeapHprofBinWriter extends AbstractHeapGraphWriter { + } + } + ++ private void writeIntArrayObfuscated(TypeArray array) throws IOException { ++ final int length = (int) array.getLength(); ++ for (int index = 0; index < length; index++) { ++ out.writeInt(0); ++ } ++ } ++ + private void writeLongArray(TypeArray array) throws IOException { + final int length = (int) array.getLength(); + for (int index = 0; index < length; index++) { +@@ -775,6 +893,13 @@ public class HeapHprofBinWriter extends AbstractHeapGraphWriter { + } + } + ++ private void writeCharArrayObfuscated(TypeArray array) throws IOException { ++ final int length = (int) array.getLength(); ++ for (int index = 0; index < length; index++) { ++ out.writeChar(0); ++ } ++ } ++ + private void writeFloatArray(TypeArray array) throws IOException { + final int length = (int) array.getLength(); + for (int index = 0; index < length; index++) { +@@ -820,6 +945,20 @@ public class HeapHprofBinWriter extends AbstractHeapGraphWriter { + for (Iterator itr = fields.iterator(); itr.hasNext();) { + writeField((Field) itr.next(), instance); + } ++ ++ if(getHeapDumpRedactLevel() != HeapRedactor.HeapDumpRedactLevel.REDACT_ANNOTATION ++ && getHeapDumpRedactLevel() != HeapRedactor.HeapDumpRedactLevel.REDACT_DIYRULES) { ++ return; ++ } ++ // record the anonymous value for every field ++ if(klass instanceof InstanceKlass && heapRedactor != null) { ++ if(heapRedactor.getHeapDumpRedactLevel() == HeapRedactor.HeapDumpRedactLevel.REDACT_ANNOTATION ++ && heapRedactor.getRedactAnnotationClassPath() != null && !heapRedactor.getRedactAnnotationClassPath().isEmpty()) { ++ recordAnnotationValueMap(fields, instance); ++ } else if( heapRedactor.getHeapDumpRedactLevel() == HeapRedactor.HeapDumpRedactLevel.REDACT_DIYRULES) { ++ recordDiyRulesValueMap(fields, instance); ++ } ++ } + } + + //-- Internals only below this point +@@ -842,6 +981,130 @@ public class HeapHprofBinWriter extends AbstractHeapGraphWriter { + } + } + ++ private void recordAnnotationValueMap(List fields, Instance instance) { ++ Klass klass = instance.getKlass(); ++ boolean inJavaPackage = false; ++ Symbol classNameSymbol = klass.getName(); ++ if(classNameSymbol != null) { ++ String className = classNameSymbol.asString(); ++ inJavaPackage = (className != null && className.startsWith("java/")); ++ } ++ if(inJavaPackage){ ++ return; ++ } ++ for (Field field : fields) { ++ Symbol fieldSignature = field.getSignature(); ++ if(fieldSignature == null || fieldSignature.asString() == null || !"Ljava/lang/String;".equals(fieldSignature.asString())) { ++ continue; ++ } ++ try { ++ InstanceKlass fieldHolder = field.getFieldHolder(); ++ U1Array fieldAnnotations = field.getFieldAnnotations(); ++ Optional anonymousValueOption = getAnonymousValue(fieldAnnotations, fieldHolder.getConstants()); ++ if(!anonymousValueOption.isPresent()) { ++ continue; ++ } ++ long address = getStringFieldAddress(field, instance); ++ if(address > 0L) { ++ heapRedactor.recordRedactAnnotationValue(address, anonymousValueOption.get()); ++ } ++ } catch (Exception e) { ++ } ++ } ++ } ++ ++ private Optional getAnonymousValue(U1Array fieldAnnotations, ConstantPool cp) { ++ Optional anonymousValueOption = Optional.empty(); ++ if (fieldAnnotations.getAddress() == null) { ++ return anonymousValueOption; ++ } ++ ++ int fieldAnnotationsTagsLen = fieldAnnotations.length(); ++ boolean isAnonymousAnnotation = false; ++ int annotationStart = 0; ++ int annotationEnd = 0; ++ for (int j = 0; j < fieldAnnotationsTagsLen; j++) { ++ int cpIndex = fieldAnnotations.at(j); ++ if (cpIndex >= cp.getLength() || cpIndex < 0) { ++ continue; ++ } ++ byte cpConstType = cp.getTags().at(cpIndex); ++ if (cpConstType == ConstantPool.JVM_CONSTANT_Utf8) { ++ annotationStart += (isAnonymousAnnotation ? 0 : 1); ++ annotationEnd++; ++ Symbol symbol = cp.getSymbolAt(cpIndex); ++ if (symbol.asString() == null || symbol.asString().isEmpty()) { ++ continue; ++ } ++ if (symbol.asString().equals("L" + heapRedactor.getRedactAnnotationClassPath() + ";")) { ++ isAnonymousAnnotation = true; ++ } ++ if(annotationEnd - annotationStart == 1 && !"value".equals(symbol.asString())) { ++ break; ++ } ++ if(annotationEnd - annotationStart == 2) { ++ anonymousValueOption = Optional.ofNullable(cp.getSymbolAt(cpIndex).asString()); ++ break; ++ } ++ } ++ } ++ return anonymousValueOption; ++ } ++ ++ private void recordDiyRulesValueMap(List fields, Instance instance) { ++ Klass klass = instance.getKlass(); ++ boolean diyRulesFlag = false; ++ Symbol classNameSymbol = klass.getName(); ++ Map redactRulesMap = null; ++ if(classNameSymbol != null) { ++ String className = classNameSymbol.asString(); ++ Optional> redactRulesMapOptional = className == null ? Optional.>empty() : heapRedactor.getRedactRulesTable(className); ++ redactRulesMap = redactRulesMapOptional.isPresent() ? redactRulesMapOptional.get() : null; ++ diyRulesFlag = (redactRulesMap != null); ++ } ++ if(!diyRulesFlag){ ++ return; ++ } ++ for (Field field : fields) { ++ Symbol fieldSignature = field.getSignature(); ++ if(fieldSignature == null || fieldSignature.asString() == null || !"Ljava/lang/String;".equals(fieldSignature.asString())) { ++ continue; ++ } ++ ++ try { ++ FieldIdentifier fieldIdentifier = field.getID(); ++ if(fieldIdentifier == null || !(fieldIdentifier instanceof NamedFieldIdentifier)) { ++ continue; ++ } ++ ++ String filedName = fieldIdentifier.getName(); ++ String replaceValue = filedName == null ? null : redactRulesMap.get(filedName); ++ long address = getStringFieldAddress(field, instance); ++ if(address > 0L && replaceValue != null) { ++ heapRedactor.recordRedactAnnotationValue(address, replaceValue); ++ } ++ } catch (Exception e) { ++ } ++ } ++ } ++ ++ private long getStringFieldAddress(Field field, Instance instance) { ++ long address = 0L; ++ if(field instanceof OopField) { ++ Oop fieldOop = ((OopField) field).getValue(instance); ++ Field stringField = null; ++ if (fieldOop != null && fieldOop.getKlass() instanceof InstanceKlass) { ++ List oopFiledSubs = ((InstanceKlass) fieldOop.getKlass()).getAllFields(); ++ stringField = oopFiledSubs.iterator().next(); ++ } ++ if (stringField != null && stringField instanceof OopField) { ++ OopHandle handle = ((OopField) stringField).getValueAsOopHandle(fieldOop); ++ address = getAddressValue(handle); ++ } ++ } ++ return address; ++ } ++ + public static int signatureToHprofKind(char ch) { + switch (ch) { + case JVM_SIGNATURE_CLASS: +@@ -941,7 +1204,20 @@ public class HeapHprofBinWriter extends AbstractHeapGraphWriter { + } + + private void writeSymbol(Symbol sym) throws IOException { +- byte[] buf = sym.asString().getBytes("UTF-8"); ++ String symbolStr = sym.asString(); ++ HeapRedactor.HeapDumpRedactLevel level = getHeapDumpRedactLevel(); ++ boolean shouldRedact = (level == HeapRedactor.HeapDumpRedactLevel.REDACT_NAMES || ++ level == HeapRedactor.HeapDumpRedactLevel.REDACT_FULL); ++ byte[] buf = null; ++ if (shouldRedact) { ++ String redactFiled = lookupRedactName(symbolStr); ++ if (redactFiled != null) { ++ buf = redactFiled.getBytes("UTF-8"); ++ } ++ } ++ if (buf == null) { ++ buf = symbolStr.getBytes("UTF-8"); ++ } + writeHeader(HPROF_UTF8, buf.length + OBJ_ID_SIZE); + writeSymbolID(sym); + out.write(buf); +@@ -1019,11 +1295,23 @@ public class HeapHprofBinWriter extends AbstractHeapGraphWriter { + List res = new ArrayList(); + while (klass != null) { + List curFields = klass.getImmediateFields(); ++ Annotation annotation = klass.getAnnotation(); ++ AnnotationArray2D fieldsAnnotations = (annotation == null) ? null : annotation.getFieldsAnnotations(); ++ boolean hasAnnotations = false; ++ if(fieldsAnnotations != null && fieldsAnnotations.getAddress() != null) { ++ hasAnnotations = true; ++ } ++ int fieldIndex = 0; + for (Iterator itr = curFields.iterator(); itr.hasNext();) { + Field f = (Field) itr.next(); + if (! f.isStatic()) { +- res.add(f); +- } ++ // record annotation for class Field ++ res.add(f); ++ if(hasAnnotations) { ++ f.setFieldAnnotations(fieldsAnnotations.getAt(fieldIndex)); ++ } ++ } ++ fieldIndex++; + } + klass = (InstanceKlass) klass.getSuper(); + } +diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/HeapRedactor.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/HeapRedactor.java +new file mode 100644 +index 000000000..26782b879 +--- /dev/null ++++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/HeapRedactor.java +@@ -0,0 +1,448 @@ ++/* ++ * 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. Huawei designates this ++ * particular file as subject to the "Classpath" exception as provided ++ * by Huawei 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 visit https://gitee.com/openeuler/bishengjdk-8 if you need additional ++ * information or have any questions. ++ */ ++ ++package sun.jvm.hotspot.utilities; ++ ++import sun.jvm.hotspot.oops.TypeArray; ++ ++import java.io.BufferedReader; ++import java.io.File; ++import java.io.FileReader; ++import java.io.IOException; ++import java.util.HashMap; ++import java.util.Locale; ++import java.util.Map; ++import java.util.ArrayList; ++import java.util.List; ++import java.util.Optional; ++ ++public class HeapRedactor { ++ public enum HeapDumpRedactLevel { ++ REDACT_UNKNOWN, ++ REDACT_OFF, ++ REDACT_NAMES, ++ REDACT_BASIC, ++ REDACT_DIYRULES, ++ REDACT_ANNOTATION, ++ REDACT_FULL ++ } ++ ++ private HeapDumpRedactLevel redactLevel; ++ private Map redactNameTable; ++ private Map> redactClassTable; ++ private String redactClassFullName = null; ++ private Map redactValueTable; ++ private RedactVectorNode headerNode; ++ private RedactVectorNode currentNode; ++ ++ private RedactParams redactParams; ++ ++ public static final String HEAP_DUMP_REDACT_PREFIX = "HeapDumpRedact="; ++ public static final String REDACT_MAP_PREFIX = "RedactMap="; ++ public static final String REDACT_MAP_FILE_PREFIX = "RedactMapFile="; ++ public static final String REDACT_CLASS_PATH_PREFIX = "RedactClassPath="; ++ ++ public static final String REDACT_UNKNOWN_STR = "UNKNOWN"; ++ public static final String REDACT_OFF_STR = "OFF"; ++ public static final String REDACT_NAME_STR = "NAMES"; ++ public static final String REDACT_BASIC_STR = "BASIC"; ++ public static final String REDACT_DIYRULES_STR = "DIYRULES"; ++ public static final String REDACT_ANNOTATION_STR = "ANNOTATION"; ++ public static final String REDACT_FULL_STR = "FULL"; ++ ++ public static final String REDACT_UNKNOWN_OPTION = REDACT_UNKNOWN_STR.toLowerCase(Locale.ROOT); ++ public static final String REDACT_OFF_OPTION = REDACT_OFF_STR.toLowerCase(Locale.ROOT); ++ public static final String REDACT_NAME_OPTION = REDACT_NAME_STR.toLowerCase(Locale.ROOT); ++ public static final String REDACT_BASIC_OPTION = REDACT_BASIC_STR.toLowerCase(Locale.ROOT); ++ public static final String REDACT_DIYRULES_OPTION = REDACT_DIYRULES_STR.toLowerCase(Locale.ROOT); ++ public static final String REDACT_ANNOTATION_OPTION = REDACT_ANNOTATION_STR.toLowerCase(Locale.ROOT); ++ public static final String REDACT_FULL_OPTION = REDACT_FULL_STR.toLowerCase(Locale.ROOT); ++ ++ public static final int PATH_MAX = 4096; ++ public static final int REDACT_VECTOR_SIZE = 1024; ++ ++ public HeapRedactor(String options) { ++ redactLevel = HeapDumpRedactLevel.REDACT_UNKNOWN; ++ redactNameTable = null; ++ redactClassTable = null; ++ redactValueTable = null; ++ init(options); ++ } ++ ++ public HeapRedactor(RedactParams redactParams) { ++ this.redactParams = redactParams; ++ redactLevel = HeapDumpRedactLevel.REDACT_UNKNOWN; ++ redactNameTable = null; ++ redactClassTable = null; ++ redactValueTable = null; ++ init(null); ++ } ++ ++ private void init(String options) { ++ if (redactLevel == HeapDumpRedactLevel.REDACT_UNKNOWN) { ++ initHeapdumpRedactLevel(options); ++ } ++ } ++ ++ public HeapDumpRedactLevel getHeapDumpRedactLevel() { ++ return redactLevel; ++ } ++ ++ public String getRedactLevelString() { ++ switch (redactLevel) { ++ case REDACT_BASIC: ++ return REDACT_BASIC_STR; ++ case REDACT_NAMES: ++ return REDACT_NAME_STR; ++ case REDACT_FULL: ++ return REDACT_FULL_STR; ++ case REDACT_DIYRULES: ++ return REDACT_DIYRULES_STR; ++ case REDACT_ANNOTATION: ++ return REDACT_ANNOTATION_STR; ++ case REDACT_OFF: ++ return REDACT_OFF_STR; ++ case REDACT_UNKNOWN: ++ default: ++ return REDACT_UNKNOWN_STR; ++ } ++ } ++ ++ public String lookupRedactName(String name) { ++ if (redactNameTable == null) { ++ return null; ++ } ++ return redactNameTable.get(name); ++ } ++ ++ public void recordTypeArray(TypeArray oop) { ++ int tmp_index = currentNode.getCurrentIndex(); ++ if(tmp_index == REDACT_VECTOR_SIZE){ ++ RedactVectorNode newNode = new RedactVectorNode(); ++ List list = new ArrayList<>(REDACT_VECTOR_SIZE); ++ newNode.setTypeArrayList(list); ++ newNode.setNext(null); ++ newNode.setCurrentIndex(0); ++ tmp_index = 0; ++ currentNode.setNext(newNode); ++ currentNode = newNode; ++ } ++ currentNode.getTypeArrayList().add(tmp_index, oop); ++ tmp_index++; ++ currentNode.setCurrentIndex(tmp_index); ++ ++ } ++ ++ public RedactVectorNode getHeaderNode(){ ++ return headerNode; ++ } ++ ++ public void recordRedactAnnotationValue(Long addr, String value) { ++ redactValueTable.put(addr, value); ++ } ++ ++ public Optional lookupRedactAnnotationValue(Long addr){ ++ return Optional.ofNullable(redactValueTable == null ? null : redactValueTable.get(addr)); ++ } ++ ++ public String getRedactAnnotationClassPath(){ ++ return redactParams.getRedactClassPath(); ++ } ++ ++ public Optional> getRedactRulesTable(String key) { ++ return Optional.>ofNullable(redactClassTable == null ? null: redactClassTable.get(key)); ++ } ++ ++ public HeapDumpRedactLevel initHeapdumpRedactLevel(String options) { ++ RedactParams customizedParams = parseRedactOptions(options); ++ ++ if (customizedParams.isEnableRedact() || this.redactParams == null) { ++ this.redactParams = customizedParams; ++ } ++ ++ if (redactParams.heapDumpRedact == null) { ++ redactLevel = HeapDumpRedactLevel.REDACT_OFF; ++ } else { ++ if (REDACT_BASIC_OPTION.equals(redactParams.heapDumpRedact)) { ++ redactLevel = HeapDumpRedactLevel.REDACT_BASIC; ++ } else if (REDACT_NAME_OPTION.equals(redactParams.heapDumpRedact)) { ++ redactLevel = HeapDumpRedactLevel.REDACT_NAMES; ++ initRedactMap(); ++ } else if (REDACT_FULL_OPTION.equals(redactParams.heapDumpRedact)) { ++ redactLevel = HeapDumpRedactLevel.REDACT_FULL; ++ initRedactMap(); ++ } else if (REDACT_DIYRULES_OPTION.equals(redactParams.heapDumpRedact)) { ++ redactLevel = HeapDumpRedactLevel.REDACT_DIYRULES; ++ initRedactMap(); ++ initRedactVector(); ++ } else if (REDACT_ANNOTATION_OPTION.equals(redactParams.heapDumpRedact)) { ++ redactLevel = HeapDumpRedactLevel.REDACT_ANNOTATION; ++ initRedactVector(); ++ } else { ++ redactLevel = HeapDumpRedactLevel.REDACT_OFF; ++ } ++ } ++ return redactLevel; ++ } ++ ++ private void initRedactVector(){ ++ if(redactValueTable == null) { ++ redactValueTable = new HashMap<>(); ++ } ++ if(headerNode == null) { ++ headerNode = new RedactVectorNode(); ++ List list = new ArrayList<>(REDACT_VECTOR_SIZE); ++ headerNode.setTypeArrayList(list); ++ headerNode.setNext(null); ++ headerNode.setCurrentIndex(0); ++ currentNode = headerNode; ++ } ++ } ++ ++ private RedactParams parseRedactOptions(String optionStr) { ++ RedactParams params = new RedactParams(REDACT_OFF_OPTION, null, null, null); ++ if (optionStr != null) { ++ String[] options = optionStr.split(","); ++ for (String option : options) { ++ if (option.startsWith(HEAP_DUMP_REDACT_PREFIX)) { ++ params.setAndCheckHeapDumpRedact(option.substring(HEAP_DUMP_REDACT_PREFIX.length())); ++ } else if (option.startsWith(REDACT_MAP_PREFIX)) { ++ params.setRedactMap(option.substring(REDACT_MAP_PREFIX.length())); ++ } else if (option.startsWith(REDACT_MAP_FILE_PREFIX)) { ++ params.setRedactMapFile(option.substring(REDACT_MAP_FILE_PREFIX.length())); ++ } else if (option.startsWith(REDACT_CLASS_PATH_PREFIX)) { ++ params.setRedactClassPath(option.substring(REDACT_CLASS_PATH_PREFIX.length())); ++ }else{ ++ // None matches ++ } ++ } ++ } ++ return params; ++ } ++ ++ private void initRedactMap() { ++ if (redactParams.redactMapFile != null) { ++ readRedactMapFromFile(redactParams.redactMapFile); ++ } ++ if (redactParams.redactMap != null) { ++ parseRedactMapStringDependOnMode(redactParams.redactMap, redactLevel); ++ } ++ } ++ ++ private void readRedactMapFromFile(String path) { ++ if (path == null || path.isEmpty()) { ++ // RedactMapFile= not specified ++ } else { ++ if (path.length() >= PATH_MAX) { ++ System.err.println("RedactMap File path is too long"); ++ return; ++ } ++ File file = new File(path); ++ if (!file.exists() || !file.isFile()) { ++ System.err.println("RedactMap File does not exist"); ++ } ++ try (BufferedReader reader = new BufferedReader(new FileReader(path))) { ++ String line; ++ while ((line = reader.readLine()) != null) { ++ parseRedactMapStringDependOnMode(line, redactLevel); ++ } ++ } catch (IOException e) { ++ System.err.println("Encounter an error when reading " + path + " , skip processing RedactMap File."); ++ e.printStackTrace(); ++ return; ++ } ++ } ++ } ++ ++ private void parseRedactMapStringDependOnMode(String nameMapList, HeapDumpRedactLevel redactLevel) { ++ if(redactLevel == HeapDumpRedactLevel.REDACT_DIYRULES) { ++ parseRedactDiyRulesString(nameMapList); ++ } else { ++ parseRedactMapString(nameMapList); ++ } ++ } ++ ++ private void parseRedactMapString(String nameMapList) { ++ if (redactNameTable == null) { ++ redactNameTable = new HashMap<>(); ++ } ++ String[] tokens = nameMapList.split("[,;\\s+]"); ++ for (String token : tokens) { ++ String[] pair = token.split(":"); ++ if (pair.length == 2) { ++ redactNameTable.put(pair[0], pair[1]); ++ } ++ } ++ } ++ ++ private void parseRedactDiyRulesString(String nameMapList) { ++ if (redactClassTable == null) { ++ redactClassTable = new HashMap<>(); ++ } ++ Map redactRulesTable = redactClassFullName == null ? null : redactClassTable.get(redactClassFullName); ++ String[] tokens = nameMapList.split("[,;\\s+]"); ++ for (String token : tokens) { ++ String[] pair = token.split(":"); ++ if (pair.length == 1) { ++ redactClassFullName = pair[0].replace(".", "/"); ++ redactRulesTable = redactClassTable.get(redactClassFullName); ++ if(redactRulesTable == null) { ++ redactRulesTable = new HashMap<>(); ++ redactClassTable.put(redactClassFullName, redactRulesTable); ++ } ++ } ++ if (pair.length == 2 && redactRulesTable != null) { ++ redactRulesTable.put(pair[0], pair[1]); ++ } ++ } ++ } ++ ++ public static class RedactParams { ++ private String heapDumpRedact; ++ private String redactMap; ++ private String redactMapFile; ++ private String redactClassPath; ++ private boolean enableRedact = false; ++ ++ public RedactParams() { ++ } ++ ++ public RedactParams(String heapDumpRedact, String redactMap, String redactMapFile, String redactClassPath) { ++ this.heapDumpRedact = heapDumpRedact; ++ this.redactMap = redactMap; ++ this.redactMapFile = redactMapFile; ++ this.redactClassPath = redactClassPath; ++ } ++ ++ @Override ++ public String toString() { ++ StringBuilder builder = new StringBuilder(); ++ if (heapDumpRedact != null) { ++ builder.append(HEAP_DUMP_REDACT_PREFIX); ++ builder.append(heapDumpRedact); ++ builder.append(","); ++ } ++ if (redactMap != null) { ++ builder.append(REDACT_MAP_PREFIX); ++ builder.append(redactMap); ++ builder.append(","); ++ } ++ if (redactMapFile != null) { ++ builder.append(REDACT_MAP_FILE_PREFIX); ++ builder.append(redactMapFile); ++ builder.append(","); ++ } ++ if (redactClassPath != null) { ++ builder.append(REDACT_CLASS_PATH_PREFIX); ++ builder.append(redactClassPath); ++ } ++ return builder.toString(); ++ } ++ ++ public String getHeapDumpRedact() { ++ return heapDumpRedact; ++ } ++ ++ public boolean setAndCheckHeapDumpRedact(String heapDumpRedact) { ++ if (!checkLauncherHeapdumpRedactSupport(heapDumpRedact)) { ++ return false; ++ } ++ this.heapDumpRedact = heapDumpRedact; ++ this.enableRedact = true; ++ return true; ++ } ++ ++ public String getRedactMap() { ++ return redactMap; ++ } ++ ++ public void setRedactMap(String redactMap) { ++ this.redactMap = redactMap; ++ } ++ ++ public String getRedactMapFile() { ++ return redactMapFile; ++ } ++ ++ public void setRedactMapFile(String redactMapFile) { ++ this.redactMapFile = redactMapFile; ++ } ++ ++ public String getRedactClassPath() { ++ return redactClassPath; ++ } ++ ++ public void setRedactClassPath(String redactClassPath) { ++ this.redactClassPath = redactClassPath; ++ } ++ ++ public static boolean checkLauncherHeapdumpRedactSupport(String value) { ++ String[] validValues = {REDACT_BASIC_OPTION, REDACT_NAME_OPTION, REDACT_FULL_OPTION, REDACT_DIYRULES_OPTION, REDACT_ANNOTATION_OPTION, REDACT_OFF_OPTION}; ++ for (String validValue : validValues) { ++ if (validValue.equals(value)) { ++ return true; ++ } ++ } ++ return false; ++ } ++ ++ public boolean isEnableRedact() { ++ return enableRedact; ++ } ++ ++ public void setEnableRedact(boolean enableRedact) { ++ this.enableRedact = enableRedact; ++ } ++ } ++ ++ public class RedactVectorNode{ ++ private List typeArrayList; ++ private RedactVectorNode next; ++ private int currentIndex; ++ ++ public List getTypeArrayList() { ++ return typeArrayList; ++ } ++ ++ public void setTypeArrayList(List list) { ++ this.typeArrayList = list; ++ } ++ ++ public RedactVectorNode getNext() { ++ return next; ++ } ++ ++ public void setNext(RedactVectorNode next) { ++ this.next = next; ++ } ++ ++ public int getCurrentIndex() { ++ return currentIndex; ++ } ++ ++ public void setCurrentIndex(int index) { ++ this.currentIndex = index; ++ } ++ } ++} +diff --git a/hotspot/make/linux/makefiles/mapfile-vers-debug b/hotspot/make/linux/makefiles/mapfile-vers-debug +index b006a84c2..b5e0d809a 100644 +--- a/hotspot/make/linux/makefiles/mapfile-vers-debug ++++ b/hotspot/make/linux/makefiles/mapfile-vers-debug +@@ -278,6 +278,8 @@ SUNWprivate_1.1 { + # This is for Forte Analyzer profiling support. + AsyncGetCallTrace; + ++ # INSERT EXTENDED SYMBOLS HERE ++ + # INSERT VTABLE SYMBOLS HERE + + local: +diff --git a/hotspot/make/linux/makefiles/mapfile-vers-product b/hotspot/make/linux/makefiles/mapfile-vers-product +index 64ccc47fb..554db7bdf 100644 +--- a/hotspot/make/linux/makefiles/mapfile-vers-product ++++ b/hotspot/make/linux/makefiles/mapfile-vers-product +@@ -273,6 +273,8 @@ SUNWprivate_1.1 { + # This is for Forte Analyzer profiling support. + AsyncGetCallTrace; + ++ # INSERT EXTENDED SYMBOLS HERE ++ + # INSERT VTABLE SYMBOLS HERE + + local: +diff --git a/hotspot/make/linux/makefiles/vm.make b/hotspot/make/linux/makefiles/vm.make +index 04b7c2028..0646301d0 100644 +--- a/hotspot/make/linux/makefiles/vm.make ++++ b/hotspot/make/linux/makefiles/vm.make +@@ -50,6 +50,15 @@ else + include $(if $(wildcard $(ALT_BUILDARCH_MAKE)),$(ALT_BUILDARCH_MAKE),$(BUILDARCH_MAKE)) + endif + ++# PLUGIN PATH ++JVM_KUNPENG_PLUGIN_DIR := $(shell find $(GAMMADIR)/../jdk/src/share/* -type d -name plugin) ++JVM_KUNPENG_PLUGIN_SRC := $(JVM_KUNPENG_PLUGIN_DIR)/feature ++ifeq ($(wildcard $(JVM_KUNPENG_PLUGIN_SRC)), $(JVM_KUNPENG_PLUGIN_SRC)) ++ JVM_KUNPENG_PLUGIN_SRCS := $(shell find $(JVM_KUNPENG_PLUGIN_SRC)/ -type d) ++ Src_Dirs_V += $(JVM_KUNPENG_PLUGIN_SRCS) ++ Src_Dirs_I += $(JVM_KUNPENG_PLUGIN_SRCS) ++endif ++ + # set VPATH so make knows where to look for source files + # Src_Dirs_V is everything in src/share/vm/*, plus the right os/*/vm and cpu/*/vm + # The adfiles directory contains ad_.[ch]pp. +@@ -186,6 +195,11 @@ Src_Dirs/ZERO := $(CORE_PATHS) + Src_Dirs/SHARK := $(CORE_PATHS) $(SHARK_PATHS) + Src_Dirs := $(Src_Dirs/$(TYPE)) + ++ifeq ($(wildcard $(JVM_KUNPENG_PLUGIN_SRC)), $(JVM_KUNPENG_PLUGIN_SRC)) ++ JVM_KUNPENG_PLUGIN_SRCS := $(shell find $(JVM_KUNPENG_PLUGIN_SRC)/ -type d) ++ Src_Dirs += $(JVM_KUNPENG_PLUGIN_SRCS) ++endif ++ + COMPILER2_SPECIFIC_FILES := opto libadt bcEscapeAnalyzer.cpp c2_\* runtime_\* + COMPILER1_SPECIFIC_FILES := c1_\* + SHARK_SPECIFIC_FILES := shark +@@ -233,7 +247,18 @@ JVM_OBJ_FILES = $(Obj_Files) + + vm_version.o: $(filter-out vm_version.o,$(JVM_OBJ_FILES)) + +-mapfile : $(MAPFILE) vm.def mapfile_ext ++JVM_KUNPENG_PLUGIN_SYMBOLS_SRC := $(JVM_KUNPENG_PLUGIN_DIR)/make/hotspot-symbols/symbols-plugin ++EXTENDED_SYMBOLS_START=$(shell awk '/EXTENDED SYMBOLS START/{print NR}' $(MAPFILE)) ++EXTENDED_SYMBOLS_END=$(shell awk '/EXTENDED SYMBOLS END/{print NR}' $(MAPFILE)) ++ ++mapfile_extend : $(MAPFILE) ++ if [ "$(EXTENDED_SYMBOLS_START)" != "" ] && [ "$(EXTENDED_SYMBOLS_END)" != "" ]; then\ ++ sed -i '$(EXTENDED_SYMBOLS_START), $(EXTENDED_SYMBOLS_END)d' $(MAPFILE);\ ++ fi ++ sed -i '/INSERT EXTENDED SYMBOLS HERE/r $(JVM_KUNPENG_PLUGIN_SYMBOLS_SRC)' $(MAPFILE) ++ ++ ++mapfile : mapfile_extend vm.def mapfile_ext + rm -f $@ + awk '{ if ($$0 ~ "INSERT VTABLE SYMBOLS HERE") \ + { system ("cat mapfile_ext"); system ("cat vm.def"); } \ +diff --git a/hotspot/src/os/linux/vm/os_linux.cpp b/hotspot/src/os/linux/vm/os_linux.cpp +index 773c746af..8d846b57b 100644 +--- a/hotspot/src/os/linux/vm/os_linux.cpp ++++ b/hotspot/src/os/linux/vm/os_linux.cpp +@@ -5513,6 +5513,52 @@ void os::pd_init_container_support() { + OSContainer::init(); + } + ++os::Linux::heap_dict_add_t os::Linux::_heap_dict_add; ++os::Linux::heap_dict_lookup_t os::Linux::_heap_dict_lookup; ++os::Linux::heap_dict_free_t os::Linux::_heap_dict_free; ++os::Linux::heap_vector_add_t os::Linux::_heap_vector_add; ++os::Linux::heap_vector_get_next_t os::Linux::_heap_vector_get_next; ++os::Linux::heap_vector_free_t os::Linux::_heap_vector_free; ++ ++void os::Linux::load_plugin_library() { ++ _heap_dict_add = CAST_TO_FN_PTR(heap_dict_add_t, dlsym(RTLD_DEFAULT, "HeapDict_Add")); ++ _heap_dict_lookup = CAST_TO_FN_PTR(heap_dict_lookup_t, dlsym(RTLD_DEFAULT, "HeapDict_Lookup")); ++ _heap_dict_free = CAST_TO_FN_PTR(heap_dict_free_t, dlsym(RTLD_DEFAULT, "HeapDict_Free")); ++ _heap_vector_add = CAST_TO_FN_PTR(heap_vector_add_t, dlsym(RTLD_DEFAULT, "HeapVector_Add")); ++ _heap_vector_get_next = CAST_TO_FN_PTR(heap_vector_get_next_t, dlsym(RTLD_DEFAULT, "HeapVector_GetNext")); ++ _heap_vector_free= CAST_TO_FN_PTR(heap_vector_free_t, dlsym(RTLD_DEFAULT, "HeapVector_Free")); ++ ++ char path[JVM_MAXPATHLEN]; ++ char ebuf[1024]; ++ void* handle = NULL; ++ if (os::dll_build_name(path, sizeof(path), Arguments::get_dll_dir(), "jvm8_kunpeng")) { ++ handle = dlopen(path, RTLD_LAZY); ++ } ++ if(handle == NULL && os::dll_build_name(path, sizeof(path), "/usr/lib64", "jvm8_kunpeng")) { ++ handle = dlopen(path, RTLD_LAZY); ++ } ++ if (handle != NULL) { ++ if(_heap_dict_add == NULL) { ++ _heap_dict_add = CAST_TO_FN_PTR(heap_dict_add_t, dlsym(handle, "HeapDict_Add")); ++ } ++ if(_heap_dict_lookup == NULL) { ++ _heap_dict_lookup = CAST_TO_FN_PTR(heap_dict_lookup_t, dlsym(handle, "HeapDict_Lookup")); ++ } ++ if(_heap_dict_free == NULL) { ++ _heap_dict_free = CAST_TO_FN_PTR(heap_dict_free_t, dlsym(handle, "HeapDict_Free")); ++ } ++ if(_heap_vector_add == NULL) { ++ _heap_vector_add = CAST_TO_FN_PTR(heap_vector_add_t, dlsym(handle, "HeapVector_Add")); ++ } ++ if(_heap_vector_get_next == NULL) { ++ _heap_vector_get_next = CAST_TO_FN_PTR(heap_vector_get_next_t, dlsym(handle, "HeapVector_GetNext")); ++ } ++ if(_heap_vector_free == NULL) { ++ _heap_vector_free= CAST_TO_FN_PTR(heap_vector_free_t, dlsym(handle, "HeapVector_Free")); ++ } ++ } ++} ++ + // this is called _after_ the global arguments have been parsed + jint os::init_2(void) + { +@@ -5585,6 +5631,8 @@ jint os::init_2(void) + Linux::is_floating_stack() ? "floating stack" : "fixed stack"); + } + ++ Linux::load_plugin_library(); ++ + if (UseNUMA) { + if (!Linux::libnuma_init()) { + UseNUMA = false; +diff --git a/hotspot/src/os/linux/vm/os_linux.hpp b/hotspot/src/os/linux/vm/os_linux.hpp +index 19dde2e58..d6866c67e 100644 +--- a/hotspot/src/os/linux/vm/os_linux.hpp ++++ b/hotspot/src/os/linux/vm/os_linux.hpp +@@ -197,6 +197,7 @@ class Linux { + // stack or fixed stack. + static bool is_floating_stack() { return _is_floating_stack; } + ++ static void load_plugin_library(); + static void libpthread_init(); + static void parse_numa_nodes(); + static bool libnuma_init(); +@@ -297,6 +298,18 @@ private: + typedef int (*numa_bitmask_isbitset_func_t)(struct bitmask *bmp, unsigned int n); + typedef int (*numa_distance_func_t)(int node1, int node2); + ++ typedef void* (*heap_dict_add_t)(void* key, void* val, void* heap_dict, uint8_t type); ++ typedef void* (*heap_dict_lookup_t)(void* key, void* heap_dict, bool deletable); ++ typedef void (*heap_dict_free_t)(void* heap_dict, bool is_nested); ++ typedef void* (*heap_vector_add_t)(void* val, void* heap_vector, bool &_inserted); ++ typedef void* (*heap_vector_get_next_t)(void* heap_vector, void* heap_vector_node, int &_cnt, void** &_items); ++ typedef void (*heap_vector_free_t)(void* heap_vector); ++ static heap_dict_add_t _heap_dict_add; ++ static heap_dict_lookup_t _heap_dict_lookup; ++ static heap_dict_free_t _heap_dict_free; ++ static heap_vector_add_t _heap_vector_add; ++ static heap_vector_get_next_t _heap_vector_get_next; ++ static heap_vector_free_t _heap_vector_free; + static sched_getcpu_func_t _sched_getcpu; + static numa_node_to_cpus_func_t _numa_node_to_cpus; + static numa_max_node_func_t _numa_max_node; +@@ -530,6 +543,46 @@ public: + _numa_bitmask_free(bitmask); + } + } ++ ++ static void* heap_dict_add(void* key, void* val, void* heap_dict, uint8_t type) { ++ if(_heap_dict_add == NULL) { ++ return NULL; ++ } ++ return _heap_dict_add(key, val, heap_dict, type); ++ } ++ ++ static void* heap_dict_lookup(void* key, void* heap_dict, bool deletable) { ++ if(_heap_dict_lookup == NULL) { ++ return NULL; ++ } ++ return _heap_dict_lookup(key, heap_dict, deletable); ++ } ++ ++ static void heap_dict_free(void* heap_dict, bool is_nested) { ++ if(_heap_dict_free != NULL) { ++ _heap_dict_free(heap_dict, is_nested); ++ } ++ } ++ ++ static void* heap_vector_add(void* val, void* heap_vector, bool &_inserted) { ++ if(_heap_vector_add == NULL) { ++ return NULL; ++ } ++ return _heap_vector_add(val, heap_vector, _inserted); ++ } ++ ++ static void* heap_vector_get_next(void* heap_vector, void* heap_vector_node, int &_cnt, void** &_items) { ++ if(_heap_vector_get_next == NULL) { ++ return NULL; ++ } ++ return _heap_vector_get_next(heap_vector, heap_vector_node, _cnt, _items); ++ } ++ ++ static void heap_vector_free(void* heap_vector) { ++ if(_heap_vector_free != NULL) { ++ _heap_vector_free(heap_vector); ++ } ++ } + }; + + +diff --git a/hotspot/src/share/vm/oops/annotations.hpp b/hotspot/src/share/vm/oops/annotations.hpp +index d1f7bc71b..1f9345503 100644 +--- a/hotspot/src/share/vm/oops/annotations.hpp ++++ b/hotspot/src/share/vm/oops/annotations.hpp +@@ -44,6 +44,7 @@ typedef Array AnnotationArray; + // a type_annotation instance. + + class Annotations: public MetaspaceObj { ++ friend class VMStructs; + + // Annotations for this class, or null if none. + AnnotationArray* _class_annotations; +diff --git a/hotspot/src/share/vm/runtime/arguments.cpp b/hotspot/src/share/vm/runtime/arguments.cpp +index 9db056b5e..360a87159 100644 +--- a/hotspot/src/share/vm/runtime/arguments.cpp ++++ b/hotspot/src/share/vm/runtime/arguments.cpp +@@ -42,6 +42,7 @@ + #include "runtime/java.hpp" + #include "services/management.hpp" + #include "services/memTracker.hpp" ++#include "services/heapRedactor.hpp" + #include "utilities/defaultStream.hpp" + #include "utilities/macros.hpp" + #include "utilities/stringUtils.hpp" +@@ -151,6 +152,8 @@ char* Arguments::_meta_index_dir = NULL; + + bool Arguments::_transletEnhance = false; + ++char* Arguments::_heap_dump_redact_auth = NULL; ++ + // Check if head of 'option' matches 'name', and sets 'tail' remaining part of option string + + static bool match_option(const JavaVMOption *option, const char* name, +@@ -4169,6 +4172,27 @@ jint Arguments::parse(const JavaVMInitArgs* args) { + #endif + } + ++ if (match_option(option, "-XX:HeapDumpRedact", &tail)) { ++ if (!HeapRedactor::check_launcher_heapdump_redact_support(tail)) { ++ warning("Heap dump redacting did not setup properly, using wrong argument?"); ++ vm_exit_during_initialization("Syntax error, expecting -XX:HeapDumpRedact=[off|names|basic|full|diyrules|annotation]",NULL); ++ } ++ } ++ ++ // heapDump redact password ++ if(match_option(option, "-XX:RedactPassword=", &tail)) { ++ if(tail == NULL || strlen(tail) == 0) { ++ VerifyRedactPassword = false; ++ jio_fprintf(defaultStream::output_stream(), "redact password is null, disable verify heap dump authority.\n"); ++ } else { ++ VerifyRedactPassword = true; ++ size_t redact_password_len = strlen(tail); ++ _heap_dump_redact_auth = NEW_C_HEAP_ARRAY(char, redact_password_len+1, mtInternal); ++ memcpy(_heap_dump_redact_auth, tail, redact_password_len); ++ _heap_dump_redact_auth[redact_password_len] = '\0'; ++ memset((void*)tail, '0', redact_password_len); ++ } ++ } + + #ifndef PRODUCT + if (match_option(option, "-XX:+PrintFlagsWithComments", &tail)) { +diff --git a/hotspot/src/share/vm/runtime/arguments.hpp b/hotspot/src/share/vm/runtime/arguments.hpp +index fdd1d14b0..945f487e1 100644 +--- a/hotspot/src/share/vm/runtime/arguments.hpp ++++ b/hotspot/src/share/vm/runtime/arguments.hpp +@@ -449,6 +449,8 @@ class Arguments : AllStatic { + + static char* SharedDynamicArchivePath; + ++ static char* _heap_dump_redact_auth; ++ + public: + // Parses the arguments, first phase + static jint parse(const JavaVMInitArgs* args); +@@ -562,6 +564,8 @@ class Arguments : AllStatic { + + static const char* GetSharedDynamicArchivePath() { return SharedDynamicArchivePath; } + ++ static const char* get_heap_dump_redact_auth() { return _heap_dump_redact_auth; } ++ + static bool init_shared_archive_paths(); + + static void extract_shared_archive_paths(const char* archive_path, +diff --git a/hotspot/src/share/vm/runtime/globals.hpp b/hotspot/src/share/vm/runtime/globals.hpp +index b47c10431..28bdd336f 100644 +--- a/hotspot/src/share/vm/runtime/globals.hpp ++++ b/hotspot/src/share/vm/runtime/globals.hpp +@@ -453,8 +453,8 @@ class CommandLineFlags { + // notproduct flags are settable / visible only during development and are not declared in the PRODUCT version + + // A flag must be declared with one of the following types: +-// bool, intx, uintx, ccstr. +-// The type "ccstr" is an alias for "const char*" and is used ++// bool, intx, uintx, ccstr, ccstrlist, double or uint64_t. ++// The type "ccstr" and "ccstrlist" are an alias for "const char*" and is used + // only in this file, because the macrology requires single-token type names. + + // Note: Diagnostic options not meant for VM tuning or for product modes. +@@ -992,6 +992,24 @@ class CommandLineFlags { + "directory) of the dump file (defaults to java_pid.hprof " \ + "in the working directory)") \ + \ ++ manageable(ccstr, HeapDumpRedact, NULL, \ ++ "Redact the heapdump information to remove sensitive data") \ ++ \ ++ manageable(ccstr, RedactMap, NULL, \ ++ "Redact the class and field names to other strings") \ ++ \ ++ manageable(ccstr, RedactMapFile, NULL, \ ++ "File path of the Redact Map") \ ++ \ ++ manageable(ccstr, RedactClassPath, NULL, \ ++ "full path of the Redact Annotation") \ ++ \ ++ product(bool, VerifyRedactPassword, false, \ ++ "verify authority for operating heapDump redact feature") \ ++ \ ++ product(ccstr, RedactPassword, "", \ ++ "authority for operating heapDump redact feature") \ ++ \ + develop(uintx, SegmentedHeapDumpThreshold, 2*G, \ + "Generate a segmented heap dump (JAVA PROFILE 1.0.2 format) " \ + "when the heap usage is larger than this") \ +diff --git a/hotspot/src/share/vm/runtime/vmStructs.cpp b/hotspot/src/share/vm/runtime/vmStructs.cpp +index 94726e498..f4061055f 100644 +--- a/hotspot/src/share/vm/runtime/vmStructs.cpp ++++ b/hotspot/src/share/vm/runtime/vmStructs.cpp +@@ -399,6 +399,10 @@ typedef OffsetCompactHashtable*) \ ++ nonstatic_field(Annotations, _class_type_annotations, Array*) \ ++ nonstatic_field(Annotations, _fields_annotations, Array*>*) \ ++ nonstatic_field(Annotations, _fields_type_annotations, Array*>*) \ + \ + /***********************/ \ + /* Constant Pool Cache */ \ +@@ -767,6 +771,8 @@ typedef OffsetCompactHashtable, _length, int) \ + nonstatic_field(Array, _data[0], Klass*) \ ++ nonstatic_field(Array*>, _length, int) \ ++ nonstatic_field(Array*>, _data[0], Array*) \ + \ + /*******************/ \ + /* GrowableArrays */ \ +@@ -1264,6 +1270,7 @@ typedef OffsetCompactHashtable, _data, sizeof(u2)) \ + unchecked_nonstatic_field(Array, _data, sizeof(Method*)) \ + unchecked_nonstatic_field(Array, _data, sizeof(Klass*)) \ ++ unchecked_nonstatic_field(Array*>, _data, sizeof(Array*)) \ + \ + /*********************************/ \ + /* java_lang_Class fields */ \ +@@ -1460,6 +1467,7 @@ typedef OffsetCompactHashtable, MetaspaceObj) \ + declare_type(Array, MetaspaceObj) \ + declare_type(Array, MetaspaceObj) \ ++ declare_type(Array*>, MetaspaceObj) \ + \ + declare_integer_type(AccessFlags) /* FIXME: wrong type (not integer) */\ + declare_toplevel_type(address) /* FIXME: should this be an integer type? */\ +diff --git a/hotspot/src/share/vm/services/attachListener.cpp b/hotspot/src/share/vm/services/attachListener.cpp +index 7c5763744..31411d061 100644 +--- a/hotspot/src/share/vm/services/attachListener.cpp ++++ b/hotspot/src/share/vm/services/attachListener.cpp +@@ -181,6 +181,7 @@ static jint jcmd(AttachOperation* op, outputStream* out) { + // Input arguments :- + // arg0: Name of the dump file + // arg1: "-live" or "-all" ++// arg2: "-HeapDumpRedact=,RedactMap=,RedactMapFile=" + jint dump_heap(AttachOperation* op, outputStream* out) { + const char* path = op->arg(0); + if (path == NULL || path[0] == '\0') { +@@ -196,11 +197,20 @@ jint dump_heap(AttachOperation* op, outputStream* out) { + live_objects_only = strcmp(arg1, "-live") == 0; + } + ++ const char* arg2 = op->arg(2); ++ if (arg2 != NULL && (strlen(arg2) > 0)) { ++ size_t len = strlen("-HeapDumpRedact="); ++ if (strncmp(arg2, "-HeapDumpRedact=", len) != 0){ ++ out->print_cr("Invalid argument to dumpheap operation: %s", arg2); ++ return JNI_ERR; ++ } ++ } ++ + // Request a full GC before heap dump if live_objects_only = true + // This helps reduces the amount of unreachable objects in the dump + // and makes it easier to browse. + HeapDumper dumper(live_objects_only /* request GC */); +- int res = dumper.dump(op->arg(0)); ++ int res = dumper.dump(op->arg(0), arg2, out); + if (res == 0) { + out->print_cr("Heap dump file created"); + } else { +@@ -371,6 +381,14 @@ static jint set_flag(AttachOperation* op, outputStream* out) { + + Flag* f = Flag::find_flag((char*)name, strlen(name)); + if (f && f->is_external() && f->is_writeable()) { ++ if(VerifyRedactPassword) { ++ if(strcmp(name, "HeapDumpRedact") == 0 || strcmp(name, "RedactMap") == 0 || strcmp(name, "RedactMapFile") == 0 ++ || strcmp(name, "RedactClassPath") == 0) { ++ out->print_cr("has no authority to reset redact params"); ++ return JNI_ERR; ++ } ++ } ++ + if (f->is_bool()) { + return set_bool_flag(name, op, out); + } else if (f->is_intx()) { +diff --git a/hotspot/src/share/vm/services/heapDumper.cpp b/hotspot/src/share/vm/services/heapDumper.cpp +index b5915c412..92bb81d01 100644 +--- a/hotspot/src/share/vm/services/heapDumper.cpp ++++ b/hotspot/src/share/vm/services/heapDumper.cpp +@@ -37,6 +37,7 @@ + #include "runtime/vframe.hpp" + #include "runtime/vmThread.hpp" + #include "runtime/vm_operations.hpp" ++#include "runtime/fieldDescriptor.hpp" + #include "services/heapDumper.hpp" + #include "services/threadService.hpp" + #include "utilities/ostream.hpp" +@@ -44,7 +45,7 @@ + #if INCLUDE_ALL_GCS + #include "gc_implementation/parallelScavenge/parallelScavengeHeap.hpp" + #endif // INCLUDE_ALL_GCS +- ++#include "heapRedactor.hpp" + /* + * HPROF binary format - description copied from: + * src/share/demo/jvmti/hprof/hprof_io.c +@@ -398,6 +399,8 @@ class DumpWriter : public StackObj { + // all I/O go through this function + void write_internal(void* s, size_t len); + ++ HeapRedactor* redactor; ++ + public: + DumpWriter(const char* path); + ~DumpWriter(); +@@ -435,6 +438,9 @@ class DumpWriter : public StackObj { + void write_symbolID(Symbol* o); + void write_classID(Klass* k); + void write_id(u4 x); ++ void setHeapRedactor(HeapRedactor *value); ++ HeapRedactor* heapRedactor(); ++ HeapDumpRedactLevel getHeapDumpRedactLevel(); + }; + + DumpWriter::DumpWriter(const char* path) { +@@ -460,6 +466,21 @@ DumpWriter::DumpWriter(const char* path) { + } + } + ++void DumpWriter::setHeapRedactor(HeapRedactor *value) { ++ redactor = value; ++} ++ ++HeapRedactor* DumpWriter::heapRedactor() { ++ return DumpWriter::redactor; ++} ++ ++HeapDumpRedactLevel DumpWriter::getHeapDumpRedactLevel() { ++ if(redactor == NULL) { ++ return REDACT_OFF; ++ } ++ return redactor->redact_level(); ++} ++ + DumpWriter::~DumpWriter() { + // flush and close dump file + if (is_open()) { +@@ -618,8 +639,9 @@ void DumpWriter::write_classID(Klass* k) { + write_objectID(k->java_mirror()); + } + +- +- ++typedef char* (*CALL_DO_LOOKUP_REPLACE_VALUE)(DumpWriter*, typeArrayOop); ++typedef void (*CALL_DUMP_INSTANCE_FIELDS_DESCRIPTORS)(DumpWriter*, Klass*); ++typedef void (*CALL_DUMP_PRIM_ARRAY)(DumpWriter*, typeArrayOop); + // Support class with a collection of functions used when dumping the heap + + class DumperSupport : AllStatic { +@@ -646,13 +668,25 @@ class DumperSupport : AllStatic { + static void dump_static_fields(DumpWriter* writer, Klass* k); + // dump the raw values of the instance fields of the given object + static void dump_instance_fields(DumpWriter* writer, oop o); ++ // dump the diyrules values of the instance fields of the given object ++ static void dump_instance_redact_fields(DumpWriter* writer, oop o, void* replace_value_table); + // dumps the definition of the instance fields for a given class + static void dump_instance_field_descriptors(DumpWriter* writer, Klass* k); ++ // dumps the definition of the instance fields with annotation info for a given class ++ static void dump_instance_annotation_field_descriptors(DumpWriter* writer, Klass* k); ++ // dumps the definition of the instance fields with diyrules info for a given class ++ static void dump_instance_diyrules_field_descriptors(DumpWriter* writer, Klass* k); + // creates HPROF_GC_INSTANCE_DUMP record for the given object + static void dump_instance(DumpWriter* writer, oop o); ++ // creates HPROF_GC_INSTANCE_REDACT_DUMP record for the given object ++ static void dump_redact_instance(DumpWriter* writer, oop o); ++ // lookup different value type depend on redact mode ++ static char* do_lookup_replace_value_with_symbol(DumpWriter* writer, typeArrayOop array); ++ static char* do_lookup_replace_value_with_char(DumpWriter* writer, typeArrayOop array); ++ static bool dump_replace_value(CALL_DO_LOOKUP_REPLACE_VALUE fn, DumpWriter* writer, typeArrayOop array); + // creates HPROF_GC_CLASS_DUMP record for the given class and each of its + // array classes +- static void dump_class_and_array_classes(DumpWriter* writer, Klass* k); ++ static void dump_class_and_array_classes(CALL_DUMP_INSTANCE_FIELDS_DESCRIPTORS fn, DumpWriter* writer, Klass* k); + // creates HPROF_GC_CLASS_DUMP record for a given primitive array + // class (and each multi-dimensional array class too) + static void dump_basic_type_array_class(DumpWriter* writer, Klass* k); +@@ -661,11 +695,15 @@ class DumperSupport : AllStatic { + static void dump_object_array(DumpWriter* writer, objArrayOop array); + // creates HPROF_GC_PRIM_ARRAY_DUMP record for the given type array + static void dump_prim_array(DumpWriter* writer, typeArrayOop array); ++ // creates HPROF_GC_PRIM_ARRAY_REDACT_DUMP record for the given type array ++ static void redact_basic_dump_prim_array(DumpWriter* writer, typeArrayOop array); ++ static bool redact_replace_dump_prim_array(CALL_DO_LOOKUP_REPLACE_VALUE fn, DumpWriter* writer, typeArrayOop array); ++ static void redact_dump_prim_array(CALL_DUMP_PRIM_ARRAY fn, DumpWriter* dumpWriter, typeArrayOop o); + // create HPROF_FRAME record for the given method and bci + static void dump_stack_frame(DumpWriter* writer, int frame_serial_num, int class_serial_num, Method* m, int bci); + + // check if we need to truncate an array +- static int calculate_array_max_length(DumpWriter* writer, arrayOop array, short header_size); ++ static int calculate_array_max_length(DumpWriter* writer, arrayOop array, short header_size, int char_length = 0); + + // writes a HPROF_HEAP_DUMP_SEGMENT record + static void write_dump_header(DumpWriter* writer); +@@ -929,6 +967,45 @@ void DumperSupport::dump_instance_fields(DumpWriter* writer, oop o) { + } + } + ++// dump the diyrules values of the instance fields of the given object ++void DumperSupport::dump_instance_redact_fields(DumpWriter* writer, oop o, void* replace_value_table) { ++ InstanceKlass* ik = InstanceKlass::cast(o->klass()); ++ ++ for (FieldStream fld(ik, false, false); !fld.eos(); fld.next()) { ++ if (fld.access_flags().is_static()) { ++ continue; ++ } ++ Symbol *sig = fld.signature(); ++ address addr = (address)o + fld.offset(); ++ // only redact string field ++ ResourceMark rm; ++ Symbol *field_name_symbol = fld.name(); ++ address field_adr = (address) ((uintptr_t) field_name_symbol); ++ void* replace_value = writer->heapRedactor()->lookup_value(field_adr, replace_value_table, false); ++ if (replace_value != NULL) { ++ oop field_oop = NULL; ++ if (UseCompressedOops) { ++ field_oop = oopDesc::load_decode_heap_oop((narrowOop*)addr); ++ } else { ++ field_oop = oopDesc::load_decode_heap_oop((oop*)addr); ++ } ++ if (!java_lang_String::is_instance(field_oop)) { ++ // data not completed, skip this field value; ++ writer->write_objectID(NULL); ++ continue; ++ } ++ ++ typeArrayOop field_value_oop = java_lang_String::value(field_oop); ++ address type_array_addr = cast_from_oop
(field_value_oop); ++ writer->heapRedactor()->insert_anonymous_value(type_array_addr, replace_value); ++ writer->write_objectID(field_oop); ++ continue; ++ } else { ++ dump_field_value(writer, sig->byte_at(0), addr); ++ } ++ } ++} ++ + // dumps the definition of the instance fields for a given class + void DumperSupport::dump_instance_field_descriptors(DumpWriter* writer, Klass* k) { + HandleMark hm; +@@ -953,6 +1030,128 @@ void DumperSupport::dump_instance_field_descriptors(DumpWriter* writer, Klass* k + } + } + ++// dumps the definition of the instance fields for a given class ++void DumperSupport::dump_instance_annotation_field_descriptors(DumpWriter* writer, Klass* k) { ++ HandleMark hm; ++ instanceKlassHandle ikh = instanceKlassHandle(Thread::current(), k); ++ ++ // pass 1 - count the instance fields ++ u2 field_count = 0; ++ for (FieldStream fldc(ikh, true, true); !fldc.eos(); fldc.next()) { ++ if (!fldc.access_flags().is_static()) field_count++; ++ } ++ ++ writer->write_u2(field_count); ++ ++ Symbol *class_name_symbol = ikh->name(); ++ bool in_exclude_package = false; ++ char *class_name = class_name_symbol->as_C_string(); ++ in_exclude_package = (strncmp("java/", class_name, 5) == 0) || (strncmp("org/springframework", class_name, 19) == 0); ++ if(in_exclude_package) { ++ // pass 2 - dump the field descriptors ++ for (FieldStream fld(ikh, true, true); !fld.eos(); fld.next()) { ++ if (!fld.access_flags().is_static()) { ++ Symbol* sig = fld.signature(); ++ ++ writer->write_symbolID(fld.name()); // name ++ writer->write_u1(sig2tag(sig)); // type ++ } ++ } ++ return; ++ } ++ ++ address obj_adr = (address)((uintptr_t)class_name_symbol); ++ // dump the field descriptors ++ for (FieldStream fld(ikh, true, true); !fld.eos(); fld.next()) { ++ if (!fld.access_flags().is_static()) { ++ Symbol* sig = fld.signature(); ++ Symbol* field_name = fld.name(); ++ ++ writer->write_symbolID(field_name); // name ++ writer->write_u1(sig2tag(sig)); // type ++ ++ if(strcmp(sig->as_C_string(), "Ljava/lang/String;") != 0) { ++ continue; ++ } ++ ++ AnnotationArray *field_annotations = fld.field_descriptor().annotations(); ++ if (field_annotations == NULL || field_annotations->length() == 0) { ++ continue; ++ } ++ ++ // byte index into field_annotations ++ ConstantPool *cp = fld.field_descriptor().field_holder()->constants(); ++ int byte_i = 0; ++ if (writer->heapRedactor()->lookup_annotation_index_in_constant_pool(field_annotations, cp, byte_i)) { ++ address element_value_addr = (address) field_annotations->adr_at(byte_i); ++ u2 cp_str_index = Bytes::get_Java_u2(element_value_addr); ++ Symbol *element_value_symbol = cp->symbol_at(cp_str_index); ++ ++ address field_adr = (address) ((uintptr_t) field_name); ++ writer->heapRedactor()->insert_class_field_value(obj_adr, field_adr, element_value_symbol); ++ } ++ } ++ } ++} ++ ++// dumps the definition of the instance fields for a given class ++void DumperSupport::dump_instance_diyrules_field_descriptors(DumpWriter *writer, Klass *k) { ++ HandleMark hm; ++ instanceKlassHandle ikh = instanceKlassHandle(Thread::current(), k); ++ ++ // pass 1 - count the instance fields ++ u2 field_count = 0; ++ for (FieldStream fldc(ikh, true, true); !fldc.eos(); fldc.next()) { ++ if (!fldc.access_flags().is_static()) field_count++; ++ } ++ ++ writer->write_u2(field_count); ++ ++ Symbol *class_name_symbol = ikh->name(); ++ void* redact_class_table = NULL; ++ bool has_diyrules = false; ++ char *class_name = class_name_symbol->as_C_string(); ++ redact_class_table = writer->heapRedactor()->lookup_class_rules(class_name); ++ has_diyrules = (redact_class_table != NULL); ++ if (!has_diyrules) { ++ // pass 2 - dump the field descriptors ++ for (FieldStream fld(ikh, true, true); !fld.eos(); fld.next()) { ++ if (!fld.access_flags().is_static()) { ++ Symbol* sig = fld.signature(); ++ ++ writer->write_symbolID(fld.name()); // name ++ writer->write_u1(sig2tag(sig)); // type ++ } ++ } ++ return; ++ } ++ ++ // pass 2 - dump the field descriptors ++ ++ address obj_adr = (address) ((uintptr_t) class_name_symbol); ++ // dump the field descriptors ++ for (FieldStream fld(ikh, true, true); !fld.eos(); fld.next()) { ++ if (!fld.access_flags().is_static()) { ++ Symbol* sig = fld.signature(); ++ Symbol* field_name = fld.name(); ++ ++ writer->write_symbolID(field_name); // name ++ writer->write_u1(sig2tag(sig)); // type ++ ++ if(strcmp(sig->as_C_string(), "Ljava/lang/String;") != 0) { ++ continue; ++ } ++ ++ char *field_name_str = field_name->as_C_string(); ++ char *replace_value = (char *) writer->heapRedactor()->lookup_value(field_name_str, redact_class_table, false); ++ if (replace_value != NULL) { ++ address field_adr = (address) ((uintptr_t) field_name); ++ writer->heapRedactor()->insert_class_field_value(obj_adr, field_adr, replace_value); ++ } ++ } ++ } ++} ++ + // creates HPROF_GC_INSTANCE_DUMP record for the given object + void DumperSupport::dump_instance(DumpWriter* writer, oop o) { + Klass* k = o->klass(); +@@ -971,9 +1170,101 @@ void DumperSupport::dump_instance(DumpWriter* writer, oop o) { + dump_instance_fields(writer, o); + } + ++// creates HPROF_GC_INSTANCE_REDACT_DUMP record for the given object ++void DumperSupport::dump_redact_instance(DumpWriter* writer, oop o) { ++ Klass* k = o->klass(); ++ ++ writer->write_u1(HPROF_GC_INSTANCE_DUMP); ++ writer->write_objectID(o); ++ writer->write_u4(STACK_TRACE_ID); ++ ++ // class ID ++ writer->write_classID(k); ++ ++ // number of bytes that follow ++ writer->write_u4(instance_size(k) ); ++ ++ // field values ++ void* replace_value_table = NULL; ++ InstanceKlass* java_super = InstanceKlass::cast(k); ++ do { ++ Symbol * class_name_symbol = java_super->name(); ++ address obj_adr = (address)((uintptr_t)class_name_symbol); ++ replace_value_table = writer->heapRedactor()->lookup_class_value(obj_adr); ++ java_super = java_super->java_super(); ++ } while (replace_value_table == NULL && java_super != NULL); ++ ++ bool has_rules = replace_value_table != NULL; ++ if(has_rules) { ++ dump_instance_redact_fields(writer, o, replace_value_table); ++ } else { ++ dump_instance_fields(writer, o); ++ } ++} ++ ++char* DumperSupport::do_lookup_replace_value_with_symbol(DumpWriter* writer, typeArrayOop array) { ++ address obj_addr = cast_from_oop
(array); ++ Symbol* anonymous_value_symbol = writer->heapRedactor()->lookup_replace_value(obj_addr); ++ if(anonymous_value_symbol == NULL) { ++ return NULL; ++ } ++ return anonymous_value_symbol->as_C_string(); ++} ++ ++char* DumperSupport::do_lookup_replace_value_with_char(DumpWriter* writer, typeArrayOop array) { ++ address obj_addr = cast_from_oop
(array); ++ char* anonymous_value = writer->heapRedactor()->lookup_replace_value(obj_addr); ++ return anonymous_value; ++} ++ ++bool DumperSupport::dump_replace_value(CALL_DO_LOOKUP_REPLACE_VALUE fn, DumpWriter* writer, typeArrayOop array) { ++ BasicType type = T_CHAR; ++ ++ // 2 * sizeof(u1) + 2 * sizeof(u4) + sizeof(objectID) ++ short header_size = 2 * 1 + 2 * 4 + sizeof(address); ++ ++ int length = 0; ++ ++ char *anonymous_value = NULL; ++ anonymous_value = fn(writer, array); ++ if(anonymous_value == NULL) { ++ if(!writer->heapRedactor()->record_typeArrayOop(array)) { ++ DumperSupport::dump_prim_array(writer, array); ++ return true; ++ } else { ++ return false; ++ } ++ } ++ ++ size_t char_length = strlen(anonymous_value); ++ length = DumperSupport::calculate_array_max_length(writer, array, header_size, char_length); ++ ++ int type_size = type2aelembytes(type); ++ u4 length_in_bytes = (u4)length * type_size; ++ u4 size = header_size + length_in_bytes; ++ ++ writer->write_u1(HPROF_GC_PRIM_ARRAY_DUMP); ++ writer->write_objectID(array); ++ writer->write_u4(STACK_TRACE_ID); ++ writer->write_u4(length); ++ writer->write_u1(HPROF_CHAR); ++ ++ // nothing to copy ++ if (length == 0) { ++ return true; ++ } ++ ++ if (Bytes::is_Java_byte_ordering_different()) { ++ for (int i = 0; i < length; i++) { writer->write_u2((u2) anonymous_value[i]); } ++ } else { ++ writer->write_raw(anonymous_value, length_in_bytes); ++ } ++ return true; ++} ++ + // creates HPROF_GC_CLASS_DUMP record for the given class and each of + // its array classes +-void DumperSupport::dump_class_and_array_classes(DumpWriter* writer, Klass* k) { ++void DumperSupport::dump_class_and_array_classes(CALL_DUMP_INSTANCE_FIELDS_DESCRIPTORS fn, DumpWriter* writer, Klass* k) { + Klass* klass = k; + InstanceKlass* ik = InstanceKlass::cast(k); + +@@ -1016,7 +1307,7 @@ void DumperSupport::dump_class_and_array_classes(DumpWriter* writer, Klass* k) { + dump_static_fields(writer, k); + + // description of instance fields +- dump_instance_field_descriptors(writer, k); ++ fn(writer, k); + + // array classes + k = klass->array_klass_or_null(); +@@ -1083,11 +1374,11 @@ void DumperSupport::dump_basic_type_array_class(DumpWriter* writer, Klass* k) { + + // Hprof uses an u4 as record length field, + // which means we need to truncate arrays that are too long. +-int DumperSupport::calculate_array_max_length(DumpWriter* writer, arrayOop array, short header_size) { ++int DumperSupport::calculate_array_max_length(DumpWriter* writer, arrayOop array, short header_size, int char_length) { + BasicType type = ArrayKlass::cast(array->klass())->element_type(); + assert(type >= T_BOOLEAN && type <= T_OBJECT, "invalid array element type"); + +- int length = array->length(); ++ int length = char_length == 0 ? array->length() : char_length; + + int type_size; + if (type == T_OBJECT) { +@@ -1150,6 +1441,9 @@ void DumperSupport::dump_object_array(DumpWriter* writer, objArrayOop array) { + #define WRITE_ARRAY(Array, Type, Size, Length) \ + for (int i = 0; i < Length; i++) { writer->write_##Size((Size)array->Type##_at(i)); } + ++#define WRITE_ARRAY_OBFUSCATED(Array, Type, Size, Length) \ ++ for (int i = 0; i < Length; i++) { writer->write_##Size(0); } ++ + // creates HPROF_GC_PRIM_ARRAY_DUMP record for the given type array + void DumperSupport::dump_prim_array(DumpWriter* writer, typeArrayOop array) { + BasicType type = TypeArrayKlass::cast(array->klass())->element_type(); +@@ -1172,14 +1466,12 @@ void DumperSupport::dump_prim_array(DumpWriter* writer, typeArrayOop array) { + return; + } + +- // If the byte ordering is big endian then we can copy most types directly +- + switch (type) { + case T_INT : { + if (Bytes::is_Java_byte_ordering_different()) { + WRITE_ARRAY(array, int, u4, length); + } else { +- writer->write_raw((void*)(array->int_at_addr(0)), length_in_bytes); ++ writer->write_raw((void *) (array->int_at_addr(0)), length_in_bytes); + } + break; + } +@@ -1240,6 +1532,96 @@ void DumperSupport::dump_prim_array(DumpWriter* writer, typeArrayOop array) { + } + } + ++// creates HPROF_GC_PRIM_ARRAY_DUMP redact record for the given type array ++void DumperSupport::redact_basic_dump_prim_array(DumpWriter* writer, typeArrayOop array) { ++ BasicType type = TypeArrayKlass::cast(array->klass())->element_type(); ++ ++ // 2 * sizeof(u1) + 2 * sizeof(u4) + sizeof(objectID) ++ short header_size = 2 * 1 + 2 * 4 + sizeof(address); ++ ++ int length = calculate_array_max_length(writer, array, header_size); ++ int type_size = type2aelembytes(type); ++ u4 length_in_bytes = (u4)length * type_size; ++ ++ writer->write_u1(HPROF_GC_PRIM_ARRAY_DUMP); ++ writer->write_objectID(array); ++ writer->write_u4(STACK_TRACE_ID); ++ writer->write_u4(length); ++ writer->write_u1(type2tag(type)); ++ ++ // nothing to copy ++ if (length == 0) { ++ return; ++ } ++ ++ switch (type) { ++ case T_INT : { ++ WRITE_ARRAY_OBFUSCATED(array, int, u4, length); ++ break; ++ } ++ case T_BYTE : { ++ WRITE_ARRAY_OBFUSCATED(array, int, u1, length); ++ break; ++ } ++ case T_CHAR : { ++ WRITE_ARRAY_OBFUSCATED(array, char, u2, length); ++ break; ++ } ++ case T_SHORT : { ++ if (Bytes::is_Java_byte_ordering_different()) { ++ WRITE_ARRAY(array, short, u2, length); ++ } else { ++ writer->write_raw((void*)(array->short_at_addr(0)), length_in_bytes); ++ } ++ break; ++ } ++ case T_BOOLEAN : { ++ if (Bytes::is_Java_byte_ordering_different()) { ++ WRITE_ARRAY(array, bool, u1, length); ++ } else { ++ writer->write_raw((void*)(array->bool_at_addr(0)), length_in_bytes); ++ } ++ break; ++ } ++ case T_LONG : { ++ if (Bytes::is_Java_byte_ordering_different()) { ++ WRITE_ARRAY(array, long, u8, length); ++ } else { ++ writer->write_raw((void*)(array->long_at_addr(0)), length_in_bytes); ++ } ++ break; ++ } ++ ++ // handle float/doubles in a special value to ensure than NaNs are ++ // written correctly. TO DO: Check if we can avoid this on processors that ++ // use IEEE 754. ++ ++ case T_FLOAT : { ++ for (int i = 0; i < length; i++) { ++ dump_float(writer, array->float_at(i)); ++ } ++ break; ++ } ++ case T_DOUBLE : { ++ for (int i = 0; i < length; i++) { ++ dump_double(writer, array->double_at(i)); ++ } ++ break; ++ } ++ default : ShouldNotReachHere(); ++ } ++} ++ ++// creates HPROF_GC_PRIM_ARRAY_DUMP redact record for the given type array ++bool DumperSupport::redact_replace_dump_prim_array(CALL_DO_LOOKUP_REPLACE_VALUE fn, DumpWriter *writer, typeArrayOop array) { ++ BasicType type = TypeArrayKlass::cast(array->klass())->element_type(); ++ if(type != T_CHAR) { ++ DumperSupport::dump_prim_array(writer, array); ++ return true; ++ } ++ return dump_replace_value(fn, writer, array); ++} ++ + // create a HPROF_FRAME record of the given Method* and bci + void DumperSupport::dump_stack_frame(DumpWriter* writer, + int frame_serial_num, +@@ -1289,6 +1671,38 @@ void SymbolTableDumper::do_symbol(Symbol** p) { + } + } + ++// Support class used to generate HPROF_UTF8 records from the entries in the ++// SymbolTable and Redact the sensitive String. ++ ++class SymbolTableRedactDumper : public SymbolClosure { ++private: ++ DumpWriter* _writer; ++ DumpWriter* writer() const { return _writer; } ++public: ++ SymbolTableRedactDumper(DumpWriter* writer) { _writer = writer; } ++ void do_symbol(Symbol** p); ++}; ++ ++void SymbolTableRedactDumper::do_symbol(Symbol** p) { ++ ResourceMark rm; ++ Symbol* sym = load_symbol(p); ++ int len = sym->utf8_length(); ++ if (len > 0) { ++ char* s = sym->as_utf8(); ++ ++ char* redact_field = NULL; ++ HeapDumpRedactLevel level = writer()->getHeapDumpRedactLevel(); ++ if((redact_field = writer()->heapRedactor()->lookup_redact_name(s)) != NULL){ ++ len = strlen(redact_field); ++ s = redact_field; ++ } ++ ++ DumperSupport::write_header(writer(), HPROF_UTF8, oopSize + len); ++ writer()->write_symbolID(sym); ++ writer()->write_raw(s, len); ++ } ++} ++ + // Support class used to generate HPROF_GC_ROOT_JNI_LOCAL records + + class JNILocalsDumper : public OopClosure { +@@ -1397,6 +1811,7 @@ class HeapObjectDumper : public ObjectClosure { + private: + VM_HeapDumper* _dumper; + DumpWriter* _writer; ++ CALL_DUMP_PRIM_ARRAY _redact_dump_prim_array; + + VM_HeapDumper* dumper() { return _dumper; } + DumpWriter* writer() { return _writer; } +@@ -1405,9 +1820,10 @@ class HeapObjectDumper : public ObjectClosure { + void mark_end_of_record(); + + public: +- HeapObjectDumper(VM_HeapDumper* dumper, DumpWriter* writer) { ++ HeapObjectDumper(VM_HeapDumper* dumper, DumpWriter* writer, CALL_DUMP_PRIM_ARRAY fn = DumperSupport::dump_prim_array) { + _dumper = dumper; + _writer = writer; ++ _redact_dump_prim_array = fn; + } + + // called for each object in the heap +@@ -1435,8 +1851,64 @@ void HeapObjectDumper::do_object(oop o) { + mark_end_of_record(); + } else if (o->is_typeArray()) { + // create a HPROF_GC_PRIM_ARRAY_DUMP record for each type array +- DumperSupport::dump_prim_array(writer(), typeArrayOop(o)); ++ DumperSupport::redact_dump_prim_array(_redact_dump_prim_array, writer(), typeArrayOop(o)); ++ mark_end_of_record(); ++ } ++} ++ ++void DumperSupport::redact_dump_prim_array(CALL_DUMP_PRIM_ARRAY fn, DumpWriter* dumpWriter, typeArrayOop o){ ++ fn(dumpWriter, o); ++} ++ ++class HeapObjectRedactDumper : public ObjectClosure { ++private: ++ VM_HeapDumper* _dumper; ++ DumpWriter* _writer; ++ char* _redact_level; ++ CALL_DO_LOOKUP_REPLACE_VALUE _do_lookup_replace_value; ++ ++ VM_HeapDumper* dumper() { return _dumper; } ++ DumpWriter* writer() { return _writer; } ++ ++ // used to indicate that a record has been writen ++ void mark_end_of_record(); ++ ++public: ++ HeapObjectRedactDumper(VM_HeapDumper* dumper, DumpWriter* writer, ++ CALL_DO_LOOKUP_REPLACE_VALUE do_lookup_replace_value) { ++ _dumper = dumper; ++ _writer = writer; ++ _do_lookup_replace_value = do_lookup_replace_value; ++ } ++ // called for each object in the heap ++ void do_object(oop o); ++}; ++ ++void HeapObjectRedactDumper::do_object(oop o) { ++ // hide the sentinel for deleted handles ++ if (o == JNIHandles::deleted_handle()) return; ++ ++ // skip classes as these emitted as HPROF_GC_CLASS_DUMP records ++ if (o->klass() == SystemDictionary::Class_klass()) { ++ if (!java_lang_Class::is_primitive(o)) { ++ return; ++ } ++ } ++ ++ if (o->is_instance()) { ++ // create a HPROF_GC_INSTANCE record for each object ++ DumperSupport::dump_redact_instance(writer(), o); + mark_end_of_record(); ++ } else if (o->is_objArray()) { ++ // create a HPROF_GC_OBJ_ARRAY_DUMP record for each object array ++ DumperSupport::dump_object_array(writer(), objArrayOop(o)); ++ mark_end_of_record(); ++ } else if (o->is_typeArray()) { ++ // create a HPROF_GC_PRIM_ARRAY_DUMP record for each type array ++ bool is_end_of_record = DumperSupport::redact_replace_dump_prim_array(_do_lookup_replace_value, writer(), typeArrayOop(o)); ++ if(is_end_of_record) { ++ mark_end_of_record(); ++ } + } + } + +@@ -1453,6 +1925,8 @@ class VM_HeapDumper : public VM_GC_Operation { + ThreadStackTrace** _stack_traces; + int _num_threads; + ++ static CALL_DUMP_INSTANCE_FIELDS_DESCRIPTORS _dump_instance_fields_descriptors; ++ + // accessors and setters + static VM_HeapDumper* dumper() { assert(_global_dumper != NULL, "Error"); return _global_dumper; } + static DumpWriter* writer() { assert(_global_writer != NULL, "Error"); return _global_writer; } +@@ -1491,6 +1965,9 @@ class VM_HeapDumper : public VM_GC_Operation { + // HPROF_TRACE and HPROF_FRAME records + void dump_stack_traces(); + ++ // HeapVector Records ++ void do_heapVector(); ++ + public: + VM_HeapDumper(DumpWriter* writer, bool gc_before_heap_dump, bool oome) : + VM_GC_Operation(0 /* total collections, dummy, ignored */, +@@ -1502,6 +1979,14 @@ class VM_HeapDumper : public VM_GC_Operation { + _klass_map = new (ResourceObj::C_HEAP, mtInternal) GrowableArray(INITIAL_CLASS_COUNT, true); + _stack_traces = NULL; + _num_threads = 0; ++ if(writer->getHeapDumpRedactLevel() == REDACT_ANNOTATION) { ++ _dump_instance_fields_descriptors = DumperSupport::dump_instance_annotation_field_descriptors; ++ } else if(writer->getHeapDumpRedactLevel() == REDACT_DIYRULES) { ++ _dump_instance_fields_descriptors = DumperSupport::dump_instance_diyrules_field_descriptors; ++ } else { ++ _dump_instance_fields_descriptors = DumperSupport::dump_instance_field_descriptors; ++ } ++ + if (oome) { + assert(!Thread::current()->is_VM_thread(), "Dump from OutOfMemoryError cannot be called by the VMThread"); + // get OutOfMemoryError zero-parameter constructor +@@ -1533,6 +2018,7 @@ class VM_HeapDumper : public VM_GC_Operation { + + VM_HeapDumper* VM_HeapDumper::_global_dumper = NULL; + DumpWriter* VM_HeapDumper::_global_writer = NULL; ++CALL_DUMP_INSTANCE_FIELDS_DESCRIPTORS VM_HeapDumper::_dump_instance_fields_descriptors = NULL; + + bool VM_HeapDumper::skip_operation() const { + return false; +@@ -1603,7 +2089,12 @@ void DumperSupport::end_of_dump(DumpWriter* writer) { + + // marks sub-record boundary + void HeapObjectDumper::mark_end_of_record() { +- dumper()->check_segment_length(); ++ dumper()->check_segment_length(); ++} ++ ++// marks sub-record boundary ++void HeapObjectRedactDumper::mark_end_of_record() { ++ dumper()->check_segment_length(); + } + + // writes a HPROF_LOAD_CLASS record for the class (and each of its +@@ -1642,7 +2133,7 @@ void VM_HeapDumper::do_load_class(Klass* k) { + // writes a HPROF_GC_CLASS_DUMP record for the given class + void VM_HeapDumper::do_class_dump(Klass* k) { + if (k->oop_is_instance()) { +- DumperSupport::dump_class_and_array_classes(writer(), k); ++ DumperSupport::dump_class_and_array_classes(_dump_instance_fields_descriptors, writer(), k); + } + } + +@@ -1811,8 +2302,14 @@ void VM_HeapDumper::doit() { + writer()->write_u8(os::javaTimeMillis()); + + // HPROF_UTF8 records +- SymbolTableDumper sym_dumper(writer()); +- SymbolTable::symbols_do(&sym_dumper); ++ if(writer()->heapRedactor() != NULL && (writer()->heapRedactor()->redact_level() == REDACT_NAMES || ++ writer()->heapRedactor()->redact_level() == REDACT_FULL)){ ++ SymbolTableRedactDumper sym_dumper(writer()); ++ SymbolTable::symbols_do(&sym_dumper); ++ } else{ ++ SymbolTableDumper sym_dumper(writer()); ++ SymbolTable::symbols_do(&sym_dumper); ++ } + + // write HPROF_LOAD_CLASS records + ClassLoaderDataGraph::classes_do(&do_load_class); +@@ -1836,8 +2333,25 @@ void VM_HeapDumper::doit() { + // segment is started. + // The HPROF_GC_CLASS_DUMP and HPROF_GC_INSTANCE_DUMP are the vast bulk + // of the heap dump. +- HeapObjectDumper obj_dumper(this, writer()); +- Universe::heap()->safe_object_iterate(&obj_dumper); ++ if(writer()->heapRedactor() != NULL && (writer()->heapRedactor()->redact_level() == REDACT_BASIC || ++ writer()->heapRedactor()->redact_level() == REDACT_FULL)) { ++ HeapObjectDumper obj_dumper(this, writer(), DumperSupport::redact_basic_dump_prim_array); ++ Universe::heap()->object_iterate(&obj_dumper); ++ } else if(writer()->heapRedactor() != NULL && writer()->heapRedactor()->redact_level() == REDACT_ANNOTATION) { ++ HeapObjectRedactDumper obj_dumper(this, writer(), DumperSupport::do_lookup_replace_value_with_symbol); ++ Universe::heap()->safe_object_iterate(&obj_dumper); ++ } else if(writer()->heapRedactor() != NULL && writer()->heapRedactor()->redact_level() == REDACT_DIYRULES) { ++ HeapObjectRedactDumper obj_dumper(this, writer(), DumperSupport::do_lookup_replace_value_with_char); ++ Universe::heap()->object_iterate(&obj_dumper); ++ } else { ++ HeapObjectDumper obj_dumper(this, writer()); ++ Universe::heap()->safe_object_iterate(&obj_dumper); ++ } ++ ++ // if value in INSTANCE is sensitive, ++ // and redact level is REDACT_ANNOTATION ++ // writes HeapVector records ++ do_heapVector(); + + // HPROF_GC_ROOT_THREAD_OBJ + frames + jni locals + do_threads(); +@@ -1921,9 +2435,80 @@ void VM_HeapDumper::dump_stack_traces() { + } + } + ++#define WRITE_ARRAY_IN_HEAPVECTOR(Array, Type, Size, Length) \ ++ for (int i = 0; i < Length; i++) { writer()->write_##Size((Size)array->Type##_at(i)); } ++ ++void VM_HeapDumper::do_heapVector(){ ++ CALL_DO_LOOKUP_REPLACE_VALUE fn = NULL; ++ if(writer()->getHeapDumpRedactLevel() == REDACT_ANNOTATION) { ++ fn = DumperSupport::do_lookup_replace_value_with_symbol; ++ } else if(writer()->getHeapDumpRedactLevel() == REDACT_DIYRULES) { ++ fn = DumperSupport::do_lookup_replace_value_with_char; ++ } else { ++ return; ++ } ++ ++ BasicType type = T_CHAR; ++ short header_size = 2 * 1 + 2 * 4 + sizeof(address); ++ int type_size = type2aelembytes(type); ++ uint max_bytes = max_juint - header_size; ++ ++ int node_len = 0, i =0; ++ void** items = NULL; ++ void *vector_node = writer()->heapRedactor()->get_vector_node_next(NULL, node_len, items); ++ while (vector_node != NULL && items != NULL) { ++ for (i = 0; i < node_len; i++) { ++ typeArrayOop array = (typeArrayOop)items[i]; ++ ++ char *anonymous_value = fn(writer(), array); ++ int length = anonymous_value == NULL ? array->length() : strlen(anonymous_value); ++ ++ u4 length_in_bytes = (u4) length * type_size; ++ if (length_in_bytes > max_bytes) { ++ length = max_bytes / type_size; ++ length_in_bytes = (size_t)length * type_size; ++ } ++ u4 size = header_size + length_in_bytes; ++ ++ writer()->write_u1(HPROF_GC_PRIM_ARRAY_DUMP); ++ writer()->write_objectID(array); ++ writer()->write_u4(STACK_TRACE_ID); ++ writer()->write_u4(length); ++ writer()->write_u1(HPROF_CHAR); ++ ++ // nothing to copy ++ if (length == 0) { ++ dumper()->check_segment_length(); ++ continue; ++ } ++ ++ if (anonymous_value != NULL) { ++ if (Bytes::is_Java_byte_ordering_different()) { ++ for (int i = 0; i < length; i++) { writer()->write_u2((u2) anonymous_value[i]); } ++ } else { ++ writer()->write_raw(anonymous_value, length_in_bytes); ++ } ++ } else { ++ if (Bytes::is_Java_byte_ordering_different()) { ++ WRITE_ARRAY_IN_HEAPVECTOR(array, char, u2, length); ++ } else { ++ writer()->write_raw((void*)(array->char_at_addr(0)), length_in_bytes); ++ } ++ } ++ dumper()->check_segment_length(); ++ } ++ ++ // clear current node info, maybe next node items is NULL, node_len = 0 will skip this NULL point error ++ node_len = 0; ++ items = NULL; ++ void *temp = writer()->heapRedactor()->get_vector_node_next(vector_node, node_len, items); ++ vector_node = temp; ++ } ++} ++ + // dump the heap to given path. + PRAGMA_FORMAT_NONLITERAL_IGNORED_EXTERNAL +-int HeapDumper::dump(const char* path) { ++int HeapDumper::dump(const char* path, const char* redact_params, outputStream* out) { + assert(path != NULL && strlen(path) > 0, "path missing"); + + // print message in interactive case +@@ -1932,8 +2517,16 @@ int HeapDumper::dump(const char* path) { + timer()->start(); + } + ++ HeapRedactor heapRedactor(redact_params, out); + // create the dump writer. If the file can be opened then bail + DumpWriter writer(path); ++ if(heapRedactor.redact_level() > REDACT_UNKNOWN) { ++ if(out != NULL) { ++ out->print_cr("HeapDump Redact Level = %s", heapRedactor.get_redact_level_string()); ++ } ++ } ++ writer.setHeapRedactor(&heapRedactor); ++ + if (!writer.is_open()) { + set_error(writer.error()); + if (print_to_tty()) { +diff --git a/hotspot/src/share/vm/services/heapDumper.hpp b/hotspot/src/share/vm/services/heapDumper.hpp +index 0bf6ba7be..5711d318e 100644 +--- a/hotspot/src/share/vm/services/heapDumper.hpp ++++ b/hotspot/src/share/vm/services/heapDumper.hpp +@@ -71,7 +71,7 @@ class HeapDumper : public StackObj { + ~HeapDumper(); + + // dumps the heap to the specified file, returns 0 if success. +- int dump(const char* path); ++ int dump(const char* path, const char* redact_params = NULL, outputStream* out = NULL); + + // returns error message (resource allocated), or NULL if no error + char* error_as_C_string() const; +diff --git a/hotspot/src/share/vm/services/heapRedactor.cpp b/hotspot/src/share/vm/services/heapRedactor.cpp +new file mode 100644 +index 000000000..6120e9458 +--- /dev/null ++++ b/hotspot/src/share/vm/services/heapRedactor.cpp +@@ -0,0 +1,621 @@ ++/* ++ * 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. Huawei designates this ++ * particular file as subject to the "Classpath" exception as provided ++ * by Huawei 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 visit https://gitee.com/openeuler/bishengjdk-8 if you need additional ++ * information or have any questions. ++ */ ++ ++#include "../runtime/globals.hpp" ++#include "../runtime/os.hpp" ++#include "../runtime/arguments.hpp" ++#include "../utilities/ostream.hpp" ++#include "../memory/allocation.hpp" ++#include "../memory/allocation.inline.hpp" ++#include "../../../os/linux/vm/jvm_linux.h" ++#include "../utilities/debug.hpp" ++#include "heapRedactor.hpp" ++#include "../utilities/debug.hpp" ++#ifdef TARGET_ARCH_x86 ++# include "bytes_x86.hpp" ++#endif ++#ifdef TARGET_ARCH_aarch64 ++# include "bytes_aarch64.hpp" ++#endif ++#ifdef TARGET_ARCH_sparc ++# include "bytes_sparc.hpp" ++#endif ++#ifdef TARGET_ARCH_zero ++# include "bytes_zero.hpp" ++#endif ++#ifdef TARGET_ARCH_arm ++# include "bytes_arm.hpp" ++#endif ++#ifdef TARGET_ARCH_ppc ++# include "bytes_ppc.hpp" ++#endif ++ ++const char* HeapRedactor::REDACT_UNKNOWN_STR = "UNKNOWN"; ++const char* HeapRedactor::REDACT_OFF_STR = "OFF"; ++const char* HeapRedactor::REDACT_NAMES_STR = "NAMES"; ++const char* HeapRedactor::REDACT_BASIC_STR = "BASIC"; ++const char* HeapRedactor::REDACT_DIYRULES_STR = "DIYRULES"; ++const char* HeapRedactor::REDACT_ANNOTATION_STR = "ANNOTATION"; ++const char* HeapRedactor::REDACT_FULL_STR = "FULL"; ++ ++HeapRedactor::HeapRedactor(outputStream* out) { ++ init_fields(); ++ _use_sys_params = true; ++ init(out); ++} ++ ++HeapRedactor::HeapRedactor(const char *redact_params_string, outputStream* out) { ++ init_fields(); ++ if (redact_params_string != NULL && strlen(redact_params_string) > 0) { ++ _use_sys_params = false; ++ parse_redact_params(redact_params_string); ++ } else { ++ _use_sys_params = true; ++ } ++ init(out); ++} ++ ++HeapRedactor::~HeapRedactor() { ++#ifdef LINUX ++ if (_redact_name_table != NULL) { ++ os::Linux::heap_dict_free(_redact_name_table,false); ++ _redact_name_table = NULL; ++ } ++ if (_redact_rules_table != NULL) { ++ os::Linux::heap_dict_free(_redact_rules_table, true); ++ _redact_rules_table = NULL; ++ } ++ if (_replace_value_table != NULL) { ++ os::Linux::heap_dict_free(_replace_value_table, false); ++ _replace_value_table = NULL; ++ } ++ if(_redact_class_field_table != NULL) { ++ os::Linux::heap_dict_free(_redact_class_field_table, true); ++ _redact_class_field_table = NULL; ++ } ++ if(_redact_record != NULL) { ++ os::Linux::heap_vector_free(_redact_record); ++ _redact_record = NULL; ++ } ++#endif ++ if (_file_name_map_list != NULL) { ++ FREE_C_HEAP_ARRAY(char, _file_name_map_list, mtInternal); ++ } ++ if (_name_map_list != NULL) { ++ FREE_C_HEAP_ARRAY(char, _name_map_list, mtInternal); ++ } ++ if (_redact_params.params_string != NULL) { ++ FREE_C_HEAP_ARRAY(char, _redact_params.params_string, mtInternal); ++ } ++ if(_annotation_class_path != NULL) { ++ FREE_C_HEAP_ARRAY(char, _annotation_class_path, mtInternal); ++ } ++} ++ ++void HeapRedactor::init_fields() { ++ _redact_level = REDACT_UNKNOWN; ++ _redact_name_table = NULL; ++ _redact_rules_table= NULL; ++ _replace_value_table = NULL; ++ _redact_class_field_table = NULL; ++ _file_name_map_list = NULL; ++ _name_map_list = NULL; ++ _redact_class_full_name = NULL; ++ _annotation_class_path = NULL; ++ _redact_record = NULL; ++ _redact_params.params_string = NULL; ++ _redact_params.heap_dump_redact = NULL; ++ _redact_params.redact_map = NULL; ++ _redact_params.redact_map_file = NULL; ++ _redact_params.annotation_class_path = NULL; ++ _redact_params.redact_password = NULL; ++} ++ ++void HeapRedactor::parse_redact_params(const char *redact_params_string) { ++ size_t length = strlen(redact_params_string); ++ char* buf = NEW_C_HEAP_ARRAY(char, length + 1, mtInternal); ++ _redact_params.params_string = buf; ++ strncpy(_redact_params.params_string, redact_params_string, length + 1); ++ size_t start = strlen("-HeapDumpRedact="); ++ _redact_params.heap_dump_redact = _redact_params.params_string + start; ++ char* map_pos = strstr(_redact_params.heap_dump_redact, ",RedactMap="); ++ char* file_pos = strstr(_redact_params.heap_dump_redact, ",RedactMapFile="); ++ char* class_path_pos = strstr(_redact_params.heap_dump_redact, ",RedactClassPath="); ++ char* redact_password_pos = strstr(_redact_params.heap_dump_redact, ",RedactPassword="); ++ ++ _redact_params.redact_map = parse_redact_child_param(map_pos, ",RedactMap=", ++ file_pos); ++ _redact_params.redact_map_file = parse_redact_child_param(file_pos, ",RedactMapFile=", ++ class_path_pos); ++ _redact_params.annotation_class_path = parse_redact_child_param(class_path_pos, ",RedactClassPath=", ++ redact_password_pos); ++ _redact_params.redact_password = parse_redact_child_param(redact_password_pos, ",RedactPassword=", ++ _redact_params.params_string + length); ++} ++ ++char* HeapRedactor::parse_redact_child_param(char *redact_params_sub_string, const char* redact_param_prefix, ++ const char* next_redact_param_prefix) { ++ char* pos = NULL; ++ if (redact_params_sub_string == NULL) { ++ pos = NULL; ++ } else { ++ *redact_params_sub_string = '\0'; ++ pos = redact_params_sub_string + strlen(redact_param_prefix); ++ if (pos == next_redact_param_prefix) { ++ pos = NULL; ++ } ++ } ++ return pos; ++} ++ ++bool HeapRedactor::check_launcher_heapdump_redact_support(const char *value) { ++ if (!strcmp(value, "=basic") || !strcmp(value, "=names") || !strcmp(value, "=off") ++ || !strcmp(value, "=diyrules") ||!strcmp(value, "=annotation") || !strcmp(value, "=full")) { ++ return true; ++ } ++ return false; ++} ++ ++void HeapRedactor::init(outputStream* out) { ++ /** -XX:+VerifyRedactPassword, ++ * if HeapDumpRedact is NULL , jmap operation can not open redact feature without password ++ * if HeapDumpRedact is not NULL, jmap operation can not change redact level without password ++ **/ ++ if(Arguments::get_heap_dump_redact_auth() == NULL) { ++ VerifyRedactPassword = false; ++ } ++ if(VerifyRedactPassword && !_use_sys_params) { ++ if(_redact_params.redact_password == NULL || ++ strcmp(_redact_params.redact_password, Arguments::get_heap_dump_redact_auth()) ) { ++ // no password or wrong password; ++ _use_sys_params = true; ++ if(out != NULL) { ++ out->print_cr("not correct password, use the default redact mode when stared"); ++ } ++ } ++ } ++ ++ if(_redact_params.redact_password != NULL) { ++ size_t password_Len = strlen(_redact_params.redact_password); ++ memset(_redact_params.redact_password, '\0', password_Len); ++ } ++ ++ if (_redact_level == REDACT_UNKNOWN) { ++ init_heapdump_redact_level(); ++ } ++ return; ++} ++ ++void HeapRedactor::init_redact_map() { ++ const char* map_param = NULL; ++ const char* map_file_param = NULL; ++ if (_use_sys_params) { ++ map_param = RedactMap; ++ map_file_param = RedactMapFile; ++ } else { ++ map_param = _redact_params.redact_map; ++ map_file_param = _redact_params.redact_map_file; ++ } ++ if (map_file_param != NULL) { ++ read_redact_map_from_file(map_file_param); ++ } ++ if (map_param != NULL) { ++ size_t length = strlen(map_param); ++ _name_map_list = NEW_C_HEAP_ARRAY(char, length + 1, mtInternal); ++ strncpy(_name_map_list, map_param, length + 1); ++ read_redact_map_dependon_mode(_name_map_list, _redact_level); ++ } ++} ++ ++void HeapRedactor::read_redact_map_dependon_mode(char* name_map_list, HeapDumpRedactLevel redact_level) { ++ if(redact_level == REDACT_DIYRULES) { ++ parse_redact_diy_rules(name_map_list); ++ } else { ++ parse_redact_map_string(name_map_list); ++ } ++} ++ ++void HeapRedactor::parse_redact_map_string(char *name_map_list) { ++#ifdef LINUX ++ size_t token_start = 0; ++ size_t step = 0; ++ size_t length = strlen(name_map_list); ++ ++ while (step < length) { ++ bool is_seperator = false; ++ if ((is_seperator = (name_map_list[step] == ',' || name_map_list[step] == ';' || name_map_list[step] == '\n' || ++ name_map_list[step] == ' ')) || ++ step == length - 1) { ++ if (is_seperator) { ++ name_map_list[step] = '\0'; ++ } else { ++ step++; ++ } ++ if (token_start < step) { ++ char *token = name_map_list + token_start; ++ size_t i = 0; ++ size_t token_length = strlen(token); ++ while (i < token_length && token[i] != ':') { ++ i++; ++ } ++ if (i < token_length - 1) { ++ token[i] = '\0'; ++ if((strlen(token) < INT_MAX) && (strlen(token + i + 1) < INT_MAX)) { ++ _redact_name_table = os::Linux::heap_dict_add(token, token + i + 1, _redact_name_table, 0); ++ } ++ } ++ } ++ token_start = step + 1; ++ } ++ step++; ++ } ++#endif ++} ++ ++void HeapRedactor::read_redact_map_from_file(const char *path) { ++ char base_path[JVM_MAXPATHLEN] = {'\0'}; ++ char buffer[MAX_MAP_FILE_LENGTH + 1] = {'\0'}; ++ if (path == NULL || path[0] == '\0') { ++ // RedactMapFile= not specified ++ } else { ++ if (strlen(path) >= JVM_MAXPATHLEN) { ++ warning("RedactMap File path is too long "); ++ return; ++ } ++ strncpy(base_path, path, sizeof(base_path)); ++ // check if the path is a directory (must exist) ++ int fd = open(base_path, O_RDONLY); ++ if (fd == -1) { ++ return; ++ } ++ size_t num_read = os::read(fd, (char *)buffer, MAX_MAP_FILE_LENGTH); ++ ++ _file_name_map_list = NEW_C_HEAP_ARRAY(char, num_read + 1, mtInternal); ++ strncpy(_file_name_map_list, buffer, num_read + 1); ++ ++ read_redact_map_dependon_mode(_file_name_map_list, _redact_level); ++ } ++} ++ ++void HeapRedactor::parse_redact_diy_rules(char* name_map_list) { ++ size_t token_start = 0; ++ size_t step = 0; ++ size_t length = strlen(name_map_list); ++ ++ while (step < length) { ++ bool is_seperator = false; ++ if ((is_seperator = (name_map_list[step] == ',' || name_map_list[step] == ';' || name_map_list[step] == '\n' || ++ name_map_list[step] == ' ')) || ++ step == length - 1) { ++ if (is_seperator) { ++ name_map_list[step] = '\0'; ++ } else { ++ step++; ++ } ++ ++ if (token_start >= step) { ++ // to reduce the depth of the method ++ token_start = step + 1; ++ continue; ++ } ++ ++ char *token = name_map_list + token_start; ++ parse_token(token); ++ token_start = step + 1; ++ } ++ step++; ++ } ++ // clear _redact_class_full_name, encase RedactMap has an unformatted value(without class name), ++ // will rewrite the last class's value_map ++ _redact_class_full_name = NULL; ++} ++ ++void HeapRedactor::parse_token(char* token) { ++#ifdef LINUX ++ size_t i = 0; ++ size_t token_length = strlen(token); ++ while (i < token_length && token[i] != ':') { ++ if(token[i] == '.' ) { ++ token[i] = '/'; ++ } ++ i++; ++ } ++ ++ void* _redact_rules_sub_table = _redact_class_full_name == NULL ? NULL : ++ os::Linux::heap_dict_lookup(_redact_class_full_name, _redact_rules_table, false); ++ if (i < token_length - 1 && _redact_rules_sub_table != NULL) { ++ token[i] = '\0'; ++ os::Linux::heap_dict_add(token, token + i + 1, _redact_rules_sub_table, 0); ++ } else if( i == token_length) { ++ _redact_class_full_name = token; ++ _redact_rules_sub_table = os::Linux::heap_dict_lookup(token, _redact_rules_table, false); ++ if (_redact_rules_sub_table == NULL) { ++ _redact_rules_sub_table = os::Linux::heap_dict_add(token, NULL, _redact_rules_sub_table, 0); ++ _redact_rules_table = os::Linux::heap_dict_add(token, _redact_rules_sub_table, _redact_rules_table, 0); ++ } ++ } ++#endif ++} ++ ++HeapDumpRedactLevel HeapRedactor::init_heapdump_redact_level() { ++ const char* redact_string = NULL; ++ if (_use_sys_params) { ++ redact_string = HeapDumpRedact; ++ } else { ++ redact_string = _redact_params.heap_dump_redact; ++ } ++ if (redact_string == NULL) { ++ _redact_level = REDACT_OFF; ++ } else { ++#ifdef LINUX ++ if (strcmp(redact_string, "basic") == 0) { ++ _redact_level = REDACT_BASIC; ++ } else if (strcmp(redact_string, "names") == 0) { ++ _redact_level = REDACT_NAMES; ++ init_redact_map(); ++ } else if (strcmp(redact_string, "full") == 0) { ++ _redact_level = REDACT_FULL; ++ init_redact_map(); ++ } else if (strcmp(redact_string, "diyrules") == 0) { ++ _redact_level = REDACT_DIYRULES; ++ init_redact_map(); ++ } else if (strcmp(redact_string, "annotation") == 0) { ++ _redact_level = REDACT_ANNOTATION; ++ init_class_path(); ++ if(_annotation_class_path == NULL) { ++ _redact_level = REDACT_OFF; ++ } ++ } else { ++ _redact_level = REDACT_OFF; ++ } ++#else ++ if (strcmp(redact_string, "basic") == 0) { ++ _redact_level = REDACT_BASIC; ++ } else if (strcmp(redact_string, "full") == 0) { ++ _redact_level = REDACT_BASIC; ++ } else { ++ _redact_level = REDACT_OFF; ++ } ++#endif ++ } ++ return _redact_level; ++} ++ ++void HeapRedactor::init_class_path() { ++ const char* class_path = NULL; ++ if (_use_sys_params) { ++ class_path = RedactClassPath; ++ } else { ++ class_path = _redact_params.annotation_class_path; ++ } ++ ++ if(class_path != NULL) { ++ size_t class_path_len = strlen(class_path); ++ _annotation_class_path = NEW_C_HEAP_ARRAY(char, class_path_len + 3, mtInternal); ++ _annotation_class_path[0] = 'L'; ++ strncpy(_annotation_class_path + 1, class_path, class_path_len + 1); ++ _annotation_class_path[class_path_len + 1] = ';'; ++ _annotation_class_path[class_path_len + 2] = '\0'; ++ } ++} ++ ++void HeapRedactor::insert_anonymous_value(void* key, void* value){ ++#ifdef LINUX ++ _replace_value_table = os::Linux::heap_dict_add(key, value, _replace_value_table, 1); ++#endif ++} ++ ++bool HeapRedactor::lookup_annotation_index_in_constant_pool(AnnotationArray* field_annotations, ConstantPool *cp, int &byte_i_ref) { ++ u2 num_annotations = 0; ++ bool has_anonymous_annotation = false; ++ ++ if ((byte_i_ref + 2) > field_annotations->length()) { ++ // not enough room for num_annotations field ++ return false; ++ } else { ++ num_annotations = Bytes::get_Java_u2((address) field_annotations->adr_at(byte_i_ref)); ++ } ++ ++ byte_i_ref += 2; ++ ++ for (int calc_num_annotations = 0; calc_num_annotations < num_annotations; calc_num_annotations++) { ++ ++ if ((byte_i_ref + 2 + 2) > field_annotations->length()) { ++ // not enough room for smallest annotation_struct ++ return false; ++ } ++ ++ // get constants pool index ++ address cp_index_addr = (address) field_annotations->adr_at(byte_i_ref); ++ byte_i_ref += 2; ++ u2 cp_index = Bytes::get_Java_u2(cp_index_addr); ++ if (cp_index >= cp->tags()->length()) { ++ return false; ++ } ++ Symbol *annotate_class_symbol = cp->symbol_at(cp_index); ++ char *annotate_class_name = annotate_class_symbol->as_C_string(); ++ ++ u2 num_element_value_pairs = Bytes::get_Java_u2((address) field_annotations->adr_at(byte_i_ref)); ++ byte_i_ref += 2; ++ if ((byte_i_ref + 2 + 1) > field_annotations->length()) { ++ // not enough room for smallest annotation_struct ++ return false; ++ } ++ ++ const char *annotation_class_path = get_annotation_class_path(); ++ has_anonymous_annotation = (strcmp(annotation_class_path, annotate_class_name) == 0); ++ if (has_anonymous_annotation) { ++ address element_name_addr = (address) field_annotations->adr_at(byte_i_ref); ++ byte_i_ref += 2; ++ u2 cp_name_index = Bytes::get_Java_u2(element_name_addr); ++ Symbol *element_name_symbol = cp->symbol_at(cp_name_index); ++ char *element_name = element_name_symbol->as_C_string(); ++ if(element_name == NULL || strcmp(element_name, "value")) { ++ // expected annotation has only one field "value" ++ return false; ++ } ++ // skip element tag ++ byte_i_ref++; ++ return true; ++ } ++ ++ int calc_num_element_value_pairs = 0; ++ // skip element_name_index ++ byte_i_ref += 2; ++ for (; calc_num_element_value_pairs < num_element_value_pairs; calc_num_element_value_pairs++) { ++ if (!recursion_cp_refs_in_element_value(field_annotations, byte_i_ref)) { ++ return false; ++ } ++ } ++ } ++ return false; ++} ++ ++bool HeapRedactor::recursion_cp_refs_in_annotation_struct( ++ AnnotationArray* annotations_typeArray, int &byte_i_ref) { ++ if ((byte_i_ref + 2 + 2) > annotations_typeArray->length()) { ++ // not enough room for smallest annotation_struct ++ return false; ++ } ++ ++ u2 type_index = Bytes::get_Java_u2((address)annotations_typeArray->adr_at(byte_i_ref)); ++ byte_i_ref += 2; ++ ++ u2 num_element_value_pairs = Bytes::get_Java_u2((address) annotations_typeArray->adr_at(byte_i_ref)); ++ byte_i_ref += 2; ++ ++ int calc_num_element_value_pairs = 0; ++ for (; calc_num_element_value_pairs < num_element_value_pairs; ++ calc_num_element_value_pairs++) { ++ if ((byte_i_ref + 2) > annotations_typeArray->length()) { ++ // not enough room for another element_name_index, let alone ++ // the rest of another component ++ // length() is too small for element_name_index ++ return false; ++ } ++ ++ u2 element_name_index = Bytes::get_Java_u2((address) annotations_typeArray->adr_at(byte_i_ref)); ++ byte_i_ref += 2; ++ ++ if (!recursion_cp_refs_in_element_value(annotations_typeArray, byte_i_ref)) { ++ // bad element_value ++ // propagate failure back to caller ++ return false; ++ } ++ } // end for each component ++ assert(num_element_value_pairs == calc_num_element_value_pairs, "sanity check"); ++ ++ return true; ++} // end recursion_cp_refs_in_annotation_struct() ++ ++bool HeapRedactor::recursion_cp_refs_in_element_value(AnnotationArray* field_annotations, int &byte_i_ref) { ++ if ((byte_i_ref + 1) > field_annotations->length()) { ++ // not enough room for a tag let alone the rest of an element_value ++ return false; ++ } ++ ++ u1 tag = field_annotations->at(byte_i_ref); ++ byte_i_ref++; ++ switch (tag) { ++ case JVM_SIGNATURE_BYTE: ++ case JVM_SIGNATURE_CHAR: ++ case JVM_SIGNATURE_DOUBLE: ++ case JVM_SIGNATURE_FLOAT: ++ case JVM_SIGNATURE_INT: ++ case JVM_SIGNATURE_LONG: ++ case JVM_SIGNATURE_SHORT: ++ case JVM_SIGNATURE_BOOLEAN: ++ case 's': ++ case 'c': ++ { ++ if ((byte_i_ref + 2) > field_annotations->length()) { ++ // length() is too small for a const_value_index ++ break; ++ } ++ byte_i_ref += 2; ++ } break; ++ case 'e': ++ { ++ if ((byte_i_ref + 4) > field_annotations->length()) { ++ // length() is too small for a enum_const_value ++ break; ++ } ++ byte_i_ref += 4; ++ } break; ++ ++ case '@': ++ // For the above tag value, value.attr_value is the right union ++ // field. This is a nested annotation. ++ if (!recursion_cp_refs_in_annotation_struct(field_annotations, byte_i_ref)) { ++ // propagate failure back to caller ++ return false; ++ } ++ break; ++ ++ case JVM_SIGNATURE_ARRAY: ++ { ++ if ((byte_i_ref + 2) > field_annotations->length()) { ++ // not enough room for a num_values field ++ return false; ++ } ++ ++ // For the above tag value, value.array_value is the right union ++ // field. This is an array of nested element_value. ++ u2 num_values = Bytes::get_Java_u2((address) field_annotations->adr_at(byte_i_ref)); ++ byte_i_ref += 2; ++ ++ int calc_num_values = 0; ++ for (; calc_num_values < num_values; calc_num_values++) { ++ if (!recursion_cp_refs_in_element_value(field_annotations, byte_i_ref)) { ++ // bad nested element_value ++ // propagate failure back to caller ++ return false; ++ } ++ } ++ } break; ++ ++ default: ++ // bad tag ++ return false; ++ } ++ ++ return true; ++} ++ ++bool HeapRedactor::record_typeArrayOop(typeArrayOop array) { ++ bool _inserted = false; ++#ifdef LINUX ++ _redact_record = os::Linux::heap_vector_add(array, _redact_record, _inserted); ++#endif ++ return _inserted; ++} ++ ++ ++void HeapRedactor::insert_class_field_value(void* class_key, void* field_key, void* value) { ++#ifdef LINUX ++ void* _redact_sub_table = os::Linux::heap_dict_lookup(class_key, _redact_class_field_table, false); ++ _redact_sub_table = os::Linux::heap_dict_add(field_key, value, _redact_sub_table, 1); ++ _redact_class_field_table = os::Linux::heap_dict_add(class_key, _redact_sub_table, _redact_class_field_table, 1); ++#endif ++} +diff --git a/hotspot/src/share/vm/services/heapRedactor.hpp b/hotspot/src/share/vm/services/heapRedactor.hpp +new file mode 100644 +index 000000000..06ffcc830 +--- /dev/null ++++ b/hotspot/src/share/vm/services/heapRedactor.hpp +@@ -0,0 +1,201 @@ ++/* ++ * 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. Huawei designates this ++ * particular file as subject to the "Classpath" exception as provided ++ * by Huawei 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 visit https://gitee.com/openeuler/bishengjdk-8 if you need additional ++ * information or have any questions. ++ */ ++ ++#ifndef LINUX_X86_64_NORMAL_SERVER_SLOWDEBUG_HEAPREDACTOR_HPP ++#define LINUX_X86_64_NORMAL_SERVER_SLOWDEBUG_HEAPREDACTOR_HPP ++#include "../memory/allocation.hpp" ++#include "oops/annotations.hpp" ++#include "oops/constantPool.hpp" ++#ifdef LINUX ++#include "os_linux.hpp" ++#endif ++ ++#define MAX_MAP_FILE_LENGTH 1024 ++ ++enum HeapDumpRedactLevel { ++ REDACT_UNKNOWN, ++ REDACT_OFF, ++ REDACT_NAMES, ++ REDACT_BASIC, ++ REDACT_DIYRULES, ++ REDACT_ANNOTATION, ++ REDACT_FULL ++}; ++ ++struct RedactParams { ++ char* params_string; ++ char* heap_dump_redact; ++ char* redact_map; ++ char* redact_map_file; ++ char* annotation_class_path; ++ char* redact_password; ++}; ++ ++class HeapRedactor : public StackObj { ++private: ++ HeapDumpRedactLevel _redact_level; ++ RedactParams _redact_params; ++ bool _use_sys_params; ++ void* _redact_name_table; ++ void* _redact_rules_table; ++ void* _replace_value_table; ++ void* _redact_class_field_table; ++ char* _file_name_map_list; ++ char* _name_map_list; ++ char* _annotation_class_path; ++ char* _redact_class_full_name; ++ void* _redact_record; ++ HeapDumpRedactLevel init_heapdump_redact_level(); ++ void read_redact_map_from_file(const char* path); ++ void read_redact_map_dependon_mode(char* name_map_list, HeapDumpRedactLevel redact_level); ++ void parse_redact_map_string(char* name_map_list); ++ void parse_redact_diy_rules(char* name_map_list); ++ void parse_token(char* token); ++ void parse_redact_params(const char *redact_params_string); ++ char* parse_redact_child_param(char *redact_params_sub_string, const char* redact_param_prefix, const char* next_redact_param_prefix); ++ void init(outputStream* out); ++ void init_fields(); ++ void init_redact_map(); ++ void init_class_path(); ++ ++public: ++ static const char* REDACT_UNKNOWN_STR; ++ static const char* REDACT_OFF_STR; ++ static const char* REDACT_NAMES_STR; ++ static const char* REDACT_BASIC_STR; ++ static const char* REDACT_DIYRULES_STR; ++ static const char* REDACT_ANNOTATION_STR; ++ static const char* REDACT_FULL_STR; ++ HeapRedactor(outputStream* out); ++ HeapRedactor(const char* redact_params, outputStream* out); ++ ~HeapRedactor(); ++ static bool check_launcher_heapdump_redact_support(const char* value); ++ HeapDumpRedactLevel redact_level() { ++ if(_redact_level == REDACT_UNKNOWN) { ++ _redact_level = init_heapdump_redact_level(); ++ } ++ return _redact_level; ++ } ++ ++ const char* get_redact_level_string() { ++#ifdef LINUX ++ switch (_redact_level) { ++ case REDACT_OFF: ++ return REDACT_OFF_STR; ++ case REDACT_NAMES: ++ return REDACT_NAMES_STR; ++ case REDACT_BASIC: ++ return REDACT_BASIC_STR; ++ case REDACT_DIYRULES: ++ return REDACT_DIYRULES_STR; ++ case REDACT_ANNOTATION: ++ return REDACT_ANNOTATION_STR; ++ case REDACT_FULL: ++ return REDACT_FULL_STR; ++ case REDACT_UNKNOWN: ++ default: ++ return REDACT_UNKNOWN_STR; ++ } ++#else ++ switch (_redact_level) { ++ case REDACT_OFF: ++ return REDACT_OFF_STR; ++ case REDACT_BASIC: ++ return REDACT_BASIC_STR; ++ case REDACT_UNKNOWN: ++ default: ++ return REDACT_UNKNOWN_STR; ++ } ++#endif ++ } ++ ++ char* lookup_redact_name(const void* name) const { ++ void* val = NULL; ++#ifdef LINUX ++ val = os::Linux::heap_dict_lookup(const_cast(name), _redact_name_table, false); ++#endif ++ if(val != NULL) { ++ return (char*)val; ++ } ++ return NULL; ++ } ++ ++ void* lookup_class_rules(const void* name) { ++ void* val = NULL; ++#ifdef LINUX ++ val = os::Linux::heap_dict_lookup(const_cast(name), _redact_rules_table, false); ++#endif ++ return val; ++ } ++ ++ void insert_class_field_value(void* class_key, void* field_key, void* value); ++ ++ void* lookup_class_value(void* key) { ++ void* val = NULL; ++#ifdef LINUX ++ val = os::Linux::heap_dict_lookup(key, _redact_class_field_table, false); ++#endif ++ return val; ++ } ++ ++ const char* get_annotation_class_path(){ ++ return _annotation_class_path; ++ } ++ ++ void insert_anonymous_value(void* key, void* value); ++ ++ template ++ T lookup_replace_value(void* key) { ++ void* val = NULL; ++#ifdef LINUX ++ val = os::Linux::heap_dict_lookup(key, _replace_value_table, true); ++#endif ++ if(val != NULL) { ++ return (T)val; ++ } ++ return NULL; ++ } ++ ++ void* lookup_value(void* key, void* heap_dict, bool deletable) { ++ void* val = NULL; ++#ifdef LINUX ++ val = os::Linux::heap_dict_lookup(key, heap_dict, deletable); ++#endif ++ return val; ++ } ++ ++ bool lookup_annotation_index_in_constant_pool(AnnotationArray* field_annotations, ConstantPool *cp, int &byte_i_ref); ++ bool recursion_cp_refs_in_element_value(AnnotationArray* field_annotations, int &byte_i_ref); ++ bool recursion_cp_refs_in_annotation_struct(AnnotationArray* field_annotations, int &byte_i_ref); ++ ++ bool record_typeArrayOop(typeArrayOop array); ++ void* get_vector_node_next(void* node, int &_cnt, void** &_items) { ++ void* val = NULL; ++#ifdef LINUX ++ val = os::Linux::heap_vector_get_next(_redact_record, node, _cnt, _items); ++#endif ++ return val; ++ } ++}; ++#endif // LINUX_X86_64_NORMAL_SERVER_SLOWDEBUG_HEAPREDACTOR_HPP +diff --git a/jdk/src/share/classes/sun/tools/jmap/JMap.java b/jdk/src/share/classes/sun/tools/jmap/JMap.java +index 2cb5a5c10..b184beb74 100644 +--- a/jdk/src/share/classes/sun/tools/jmap/JMap.java ++++ b/jdk/src/share/classes/sun/tools/jmap/JMap.java +@@ -25,10 +25,15 @@ + + package sun.tools.jmap; + ++import java.io.Console; ++import java.lang.reflect.Field; + import java.lang.reflect.Method; + import java.io.File; + import java.io.IOException; + import java.io.InputStream; ++import java.nio.charset.StandardCharsets; ++import java.security.MessageDigest; ++import java.util.Arrays; + + import com.sun.tools.attach.VirtualMachine; + import com.sun.tools.attach.AttachNotSupportedException; +@@ -163,7 +168,8 @@ public class JMap { + // -dump option needs to be handled in a special way + if (option.startsWith(DUMP_OPTION_PREFIX)) { + // first check that the option can be parsed +- String fn = parseDumpOptions(option); ++ RedactParams redactParams = new RedactParams(); ++ String fn = parseDumpOptions(option, redactParams); + if (fn == null) { + usage(1); + } +@@ -171,6 +177,11 @@ public class JMap { + // tool for heap dumping + tool = "sun.jvm.hotspot.tools.HeapDumper"; + ++ // HeapDump redact arguments ++ if (redactParams.isEnableRedact()) { ++ args = prepend(redactParams.toString(), args); ++ args = prepend("-r", args); ++ } + // HeapDumper -f + args = prepend(fn, args); + args = prepend("-f", args); +@@ -245,12 +256,18 @@ public class JMap { + } + + private static void dump(String pid, String options) throws IOException { ++ RedactParams redactParams = new RedactParams(); + // parse the options to get the dump filename +- String filename = parseDumpOptions(options); ++ String filename = parseDumpOptions(options,redactParams); + if (filename == null) { + usage(1); // invalid options or no filename + } + ++ String redactPassword = ",RedactPassword="; ++ if (options.contains("RedactPassword,") || options.contains(",RedactPassword")) { ++ // heap dump may need a password ++ redactPassword = getRedactPassword(); ++ } + // get the canonical path - important to avoid just passing + // a "heap.bin" and having the dump created in the target VM + // working directory rather than the directory where jmap +@@ -264,14 +281,77 @@ public class JMap { + System.out.println("Dumping heap to " + filename + " ..."); + InputStream in = ((HotSpotVirtualMachine)vm). + dumpHeap((Object)filename, +- (live ? LIVE_OBJECTS_OPTION : ALL_OBJECTS_OPTION)); ++ (live ? LIVE_OBJECTS_OPTION : ALL_OBJECTS_OPTION), ++ redactParams.isEnableRedact() ? redactParams.toDumpArgString() + redactPassword : ""); + drain(vm, in); + } + ++ private static String getRedactPassword() { ++ String redactPassword = ",RedactPassword="; ++ Console console = System.console(); ++ char[] passwords = null; ++ if (console == null) { ++ return redactPassword; ++ } ++ ++ try { ++ passwords = console.readPassword("redact authority password:"); ++ } catch (Exception e) { ++ } ++ if(passwords == null) { ++ return redactPassword; ++ } ++ ++ String password = new String(passwords); ++ Arrays.fill(passwords, '0'); ++ String passwordPattern = "^[0-9a-zA-Z!@#$]{1,9}$"; ++ if(!password.matches(passwordPattern)) { ++ return redactPassword; ++ } ++ ++ String digestStr = null; ++ byte[] passwordBytes = null; ++ char[] passwordValue = null; ++ try { ++ Field valueField = password.getClass().getDeclaredField("value"); ++ valueField.setAccessible(true); ++ passwordValue = (char[])valueField.get(password); ++ ++ passwordBytes= password.getBytes(StandardCharsets.UTF_8); ++ StringBuilder digestStrBuilder = new StringBuilder(); ++ MessageDigest messageDigest = MessageDigest.getInstance("SHA-256"); ++ byte[] digestBytes = messageDigest.digest(passwordBytes); ++ for(byte b : digestBytes) { ++ String hex = Integer.toHexString(0xff & b); ++ if(hex.length() == 1) { ++ digestStrBuilder.append('0'); ++ } ++ digestStrBuilder.append(hex); ++ } ++ digestStr = digestStrBuilder.toString(); ++ } catch (Exception e) { ++ }finally { ++ // clear all password ++ if(passwordBytes != null) { ++ Arrays.fill(passwordBytes, (byte) 0); ++ } ++ if(passwordValue != null) { ++ Arrays.fill(passwordValue, '0'); ++ } ++ } ++ ++ redactPassword += (digestStr == null ? "" : digestStr); ++ return redactPassword; ++ } ++ + // Parse the options to the -dump option. Valid options are format=b and + // file=. Returns if provided. Returns null if not + // provided, or invalid option. +- private static String parseDumpOptions(String arg) { ++ private static String parseDumpOptions(String arg){ ++ return parseDumpOptions(arg, null); ++ } ++ ++ private static String parseDumpOptions(String arg, RedactParams redactParams) { + assert arg.startsWith(DUMP_OPTION_PREFIX); + + String filename = null; +@@ -279,15 +359,16 @@ public class JMap { + // options are separated by comma (,) + String options[] = arg.substring(DUMP_OPTION_PREFIX.length()).split(","); + +- for (int i=0; i - check that is specified + if (option.startsWith("file=")) { + filename = option.substring(5); +@@ -295,13 +376,48 @@ public class JMap { + return null; + } + } else { ++ if (redactParams != null && initRedactParams(redactParams, option)) { ++ continue; ++ } + return null; // option not recognized + } + } + } ++ if (redactParams != null) { ++ if (redactParams.getHeapDumpRedact() == null) { ++ if (redactParams.getRedactMap() == null && redactParams.getRedactMapFile() == null ++ && redactParams.getRedactClassPath() == null) { ++ redactParams.setEnableRedact(false); ++ } else { ++ System.err.println("Error: HeapDumpRedact must be specified to enable heap-dump-redacting"); ++ usage(1); ++ } ++ } ++ } + return filename; + } + ++ private static boolean initRedactParams(RedactParams redactParams, String option) { ++ if (option.startsWith("HeapDumpRedact=")) { ++ if (!redactParams.setAndCheckHeapDumpRedact(option.substring("HeapDumpRedact=".length()))) { ++ usage(1); ++ } ++ return true; ++ } else if (option.startsWith("RedactMap=")) { ++ redactParams.setRedactMap(option.substring("RedactMap=".length())); ++ return true; ++ } else if (option.startsWith("RedactMapFile=")) { ++ redactParams.setRedactMapFile(option.substring("RedactMapFile=".length())); ++ return true; ++ } else if (option.startsWith("RedactClassPath")) { ++ redactParams.setRedactClassPath(option.substring("RedactClassPath=".length())); ++ return true; ++ } else { ++ // None matches ++ return false; ++ } ++ } ++ + private static boolean isDumpLiveObjects(String arg) { + // options are separated by comma (,) + String options[] = arg.substring(DUMP_OPTION_PREFIX.length()).split(","); +@@ -391,6 +507,14 @@ public class JMap { + System.err.println(" all objects in the heap are dumped."); + System.err.println(" format=b binary format"); + System.err.println(" file= dump heap to "); ++ System.err.println(" HeapDumpRedact= redact the heapdump"); ++ System.err.println(" information to remove sensitive data"); ++ System.err.println(" RedactMap= Redact the class and"); ++ System.err.println(" field names to other strings"); ++ System.err.println(" RedactMapFile= file path of the redact map"); ++ System.err.println(" RedactClassPath= full path of the redact annotation"); ++ System.err.println(" RedactPassword maybe redact feature has an authority, RedactPassword will wait for a password, "); ++ System.err.println(" without a correct password, heap dump with default redact level"); + System.err.println(" Example: jmap -dump:live,format=b,file=heap.bin "); + System.err.println(" -F force. Use with -dump: or -histo"); + System.err.println(" to force a heap dump or histogram when does not"); +@@ -413,4 +537,112 @@ public class JMap { + + System.exit(exit); + } ++ ++ public static class RedactParams { ++ private boolean enableRedact = false; ++ private String heapDumpRedact; ++ private String redactMap; ++ private String redactMapFile; ++ private String redactClassPath; ++ ++ public RedactParams() { ++ } ++ ++ public RedactParams(String heapDumpRedact, String redactMap, String redactMapFile, String redactClassPath) { ++ if (heapDumpRedact != null && checkLauncherHeapdumpRedactSupport(heapDumpRedact)) { ++ enableRedact = true; ++ } ++ this.heapDumpRedact = heapDumpRedact; ++ this.redactMap = redactMap; ++ this.redactMapFile = redactMapFile; ++ this.redactClassPath = redactClassPath; ++ } ++ ++ @Override ++ public String toString() { ++ StringBuilder builder = new StringBuilder(); ++ if (heapDumpRedact != null) { ++ builder.append("HeapDumpRedact="); ++ builder.append(heapDumpRedact); ++ builder.append(","); ++ } ++ if (redactMap != null) { ++ builder.append("RedactMap="); ++ builder.append(redactMap); ++ builder.append(","); ++ } ++ if (redactMapFile != null) { ++ builder.append("RedactMapFile="); ++ builder.append(redactMapFile); ++ builder.append(","); ++ } ++ if (redactClassPath != null) { ++ builder.append("RedactClassPath="); ++ builder.append(redactClassPath); ++ } ++ return builder.toString(); ++ } ++ ++ public String toDumpArgString() { ++ return "-HeapDumpRedact=" + (heapDumpRedact == null ? "off" : heapDumpRedact) + ++ ",RedactMap=" + (redactMap == null ? "" : redactMap) + ++ ",RedactMapFile=" + (redactMapFile == null ? "" : redactMapFile) + ++ ",RedactClassPath=" + (redactClassPath == null ? "" : redactClassPath); ++ } ++ ++ public static boolean checkLauncherHeapdumpRedactSupport(String value) { ++ String[] validValues = {"basic", "names", "full", "diyrules", "annotation", "off"}; ++ for (String validValue : validValues) { ++ if (validValue.equals(value)) { ++ return true; ++ } ++ } ++ return false; ++ } ++ ++ public boolean isEnableRedact() { ++ return enableRedact; ++ } ++ ++ public void setEnableRedact(boolean enableRedact) { ++ this.enableRedact = enableRedact; ++ } ++ ++ public String getHeapDumpRedact() { ++ return heapDumpRedact; ++ } ++ ++ public boolean setAndCheckHeapDumpRedact(String heapDumpRedact) { ++ if (!checkLauncherHeapdumpRedactSupport(heapDumpRedact)) { ++ return false; ++ } ++ this.heapDumpRedact = heapDumpRedact; ++ this.enableRedact = true; ++ return true; ++ } ++ ++ public String getRedactMap() { ++ return redactMap; ++ } ++ ++ public void setRedactMap(String redactMap) { ++ this.redactMap = redactMap; ++ } ++ ++ public String getRedactMapFile() { ++ return redactMapFile; ++ } ++ ++ public void setRedactMapFile(String redactMapFile) { ++ this.redactMapFile = redactMapFile; ++ } ++ ++ public String getRedactClassPath() { ++ return redactClassPath; ++ } ++ ++ public void setRedactClassPath(String redactClassPath) { ++ this.redactClassPath = redactClassPath; ++ } ++ } + } +-- +2.19.1 + diff --git a/openjdk-1.8.0.spec b/openjdk-1.8.0.spec index 3808e1a5c5f933cb15b00bb6f663c963aae3b9c1..e4135b2c1adf89475894a2c875c239677a1a76f6 100644 --- a/openjdk-1.8.0.spec +++ b/openjdk-1.8.0.spec @@ -619,7 +619,9 @@ exit 0 %{_jvmdir}/%{jredir -- %{?1}}/lib/%{archinstall}/libunpack.so %{_jvmdir}/%{jredir -- %{?1}}/lib/%{archinstall}/libverify.so %{_jvmdir}/%{jredir -- %{?1}}/lib/%{archinstall}/libzip.so +%ifnarch loongarch64 %{_jvmdir}/%{jredir -- %{?1}}/lib/%{archinstall}/libz.so +%endif %{_jvmdir}/%{jredir -- %{?1}}/lib/charsets.jar %{_jvmdir}/%{jredir -- %{?1}}/lib/classlist %{_jvmdir}/%{jredir -- %{?1}}/lib/content-types.properties @@ -943,7 +945,7 @@ Provides: java-%{javaver}-%{origin}-accessibility%{?1} = %{epoch}:%{version}-%{r Name: java-%{javaver}-%{origin} Version: %{javaver}.%{updatever}.%{buildver} -Release: 3 +Release: 11 # java-1.5.0-ibm from jpackage.org set Epoch to 1 for unknown reasons # and this change was brought into RHEL-4. java-1.5.0-ibm packages # also included the epoch in their virtual provides. This created a @@ -1205,10 +1207,10 @@ Patch301: fix-SUSE-x86_32-build-failure.patch Patch302: fix-the-issue-that-cert-of-geotrustglobalca-expired.patch # 8u372 -Patch303: 8074354-Make-CreateMinidumpOnCrash-a-new-name-and-av.patch -Patch304: jcmd-mnt-add-start-time-and-end-time.patch -Patch305: Fix-localtime_r-not-defined-on-windows.patch -Patch306: 8057743-process-Synchronize-exiting-of-threads-and-p.patch +Patch303: 8074354-Make-CreateMinidumpOnCrash-a-new-name-and-av.patch +Patch304: jcmd-mnt-add-start-time-and-end-time.patch +Patch305: Fix-localtime_r-not-defined-on-windows.patch +Patch306: 8057743-process-Synchronize-exiting-of-threads-and-p.patch Patch307: 8305541-C2-Div-Mod-nodes-without-zero-check-could-be.patch Patch308: 0002-8179498-attach-in-linux-should-be-relative-to-proc-p.patch Patch309: 0003-8187408-AbstractQueuedSynchronizer-wait-queue-corrup.patch @@ -1339,8 +1341,15 @@ Patch438: Huawei-Add-Aggressive-CDS.patch Patch439: Backport-8151845-Comment-in-globals.hpp-for-MetaspaceSize-is-.patch Patch440: Backport-8210706-G1-may-deadlock-when-starting-a-concurrent-c.patch Patch441: Backport-8318889-Backport-Important-Fixed-Issues-in-Later-Ver.patch + +#422 Patch442: Huawei-Keep-objects-when-remove-unshareable-info.patch -Patch443: The-fast-serialization-function-of-sun.rmi.transport.patch +Patch443: The-fast-serialization-function-of-sun.rmi.transport.patch +Patch445: Extending-the-IV-Length-Supported-by-KAEProvider-AES.patch +Patch446: 8137165-Tests-fail-in-SR_Handler-because-thread-is-n.patch +Patch447: heap-dump-redact-support.patch +Patch448: KAE-zip-support-streaming-data-decompression.patch +Patch449: Enhance-SIGBUS-and-rlimit-information-in-errlog.patch ############################################# # # Upstreamable patches @@ -1999,10 +2008,21 @@ pushd %{top_level_dir_name} %patch441 -p1 %patch442 -p1 %patch443 -p1 +%patch445 -p1 +%patch446 -p1 +%patch447 -p1 +%patch448 -p1 +%patch449 -p1 %endif %ifarch loongarch64 %patch4000 -p1 + +%patch308 -p1 +%patch310 -p1 +%patch311 -p1 +%patch312 -p1 + %endif %ifarch riscv64 @@ -2127,8 +2147,8 @@ bash ${top_srcdir_abs_path}/configure \ --with-update-version=%{updatever} \ --with-build-number=%{buildver} \ %ifnarch loongarch64 ppc64le - --with-company-name="Bisheng" \ - --with-vendor-name="Bisheng" \ + --with-company-name="BiSheng" \ + --with-vendor-name="BiSheng" \ %endif --with-vendor-url="https://openeuler.org/" \ --with-vendor-bug-url="https://gitee.com/src-openeuler/openjdk-1.8.0/issues/" \ @@ -2254,7 +2274,7 @@ done # Make sure gdb can do a backtrace based on line numbers on libjvm.so # javaCalls.cpp:58 should map to: -# http://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/ff3b27e6bcc2/src/share/vm/runtime/javaCalls.cpp#l58 +# http://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/ff3b27e6bcc2/src/share/vm/runtime/javaCalls.cpp#l58 # Using line number 1 might cause build problems. %ifnarch loongarch64 gdb -q "$JAVA_HOME/bin/java" < -1:1.8.0.422-b05.3 +* Fri Sep 6 2024 Benshuai5D -1:1.8.0.422-b05.11 +- add Enhance-SIGBUS-and-rlimit-information-in-errlog.patch + +* Wed Sep 4 2024 neu-mobi -1:1.8.0.422-b05.10 +- Support KAE Zip +- rename Bisheng to BiSheng + +* Mon Sep 2 2024 Dingli Zhang -1:1.8.0.422-b05.9 - Fix build error on riscv64 because of patch438 -* Thu Aug 29 2024 Dingli Zhang -1:1.8.0.422-b05.2 +* Thu Aug 29 2024 Dingli Zhang -1:1.8.0.422-b05.8 - Fix build on riscv64 in prep stage for 8u422 -* Fri Tue 26 2024 benshuai5D -1:1.8.0.422-b05.1 +* Tue Aug 20 2024 wuyafang -1:1.8.0.422-b05.7 +- modified add-Fix-aarch64-runtime-thread-signal-transfer-bug.patch + +* Wed Aug 7 2024 songliyang -1:1.8.0.422-b05.6 +- let support-KAE-zip.patch not install libz.so on loongarch64 +- update LoongArch64 port to 8u422 +- fix changelog date error + +* Tue Aug 6 2024 benshuai5D -1:1.8.0.422-b05.5 +- modified add-Fix-aarch64-runtime-thread-signal-transfer-bug.patch + +* Sat Aug 3 2024 kuenking111 -1:1.8.0.422-b05.4 +- Add 8137165-Tests-fail-in-SR_Handler-because-thread-is-n.patch +- Extending-the-IV-Length-Supported-by-KAEProvider-AES.patch + +* Tue Jul 30 2024 benshuai5D -1:1.8.0.422-b05.3 +- Fix-the-ActiveProcessorCount-function-of-the-spark-s.patch + +* Tue Jul 30 2024 Xiang Gao - 1:1.8.0.422-b05.2 +- Backport 8178498,8193710,8196743,8284330, serviceability tools support containers on loongarch + +* Fri Jul 26 2024 benshuai5D -1:1.8.0.422-b05.1 - Add The-fast-serialization-function-of-sun.rmi.transport.patch -* Thu Tue 16 2024 Autistic_boyya -1:1.8.0.422-b05.rolling +* Tue Jul 16 2024 Autistic_boyya -1:1.8.0.422-b05.rolling - modified 8014628-Support-AES-Encryption-with-HMAC-SHA2-for-Ke.patch - modified 8136577_Make_AbortVMOnException_available_in_product_builds.patch - modified Dynamic-CDS-Archive.patch @@ -2937,10 +2985,10 @@ cjc.mainProgram(args) -- the returns from copy_jdk_configs.lua should not affect - 0054-Fix-jmap-heapdump-symbols-when-the-class-is-loaded-f.patch - 0055-Fix-CodelistTest.java-Failed-to-Execute-CodelistTest.patch -* Fri May 11 2023 crash888 - 1:1.8.0.372-b07.1 -- modified Fix-the-crash-that-occurs-when-the-process-exits-due.patch +* Thu May 11 2023 crash888 - 1:1.8.0.372-b07.1 +- modified Fix-the-crash-that-occurs-when-the-process-exits-due.patch -* Fri May 6 2023 crash888 - 1:1.8.0.372-b07.0 +* Sat May 6 2023 crash888 - 1:1.8.0.372-b07.0 - deleted Add-ability-to-configure-third-port-for-remote-JMX.patch - deleted 8287109-Distrust-failed-with-CertificateExpired.patch - deleted Huawei-fix-windows-build-Dynamic-CDS-failure.patch @@ -2956,10 +3004,10 @@ cjc.mainProgram(args) -- the returns from copy_jdk_configs.lua should not affect - modified add-missing-test-case.patch - modified fix-the-issue-that-cert-of-geotrustglobalca-expired.patch - modified fix_X509TrustManagerImpl_symantec_distrust.patch -- add 8074354-Make-CreateMinidumpOnCrash-a-new-name-and-av.patch -- add jcmd-mnt-add-start-time-and-end-time.patch -- add Fix-localtime_r-not-defined-on-windows.patch -- add 8057743-process-Synchronize-exiting-of-threads-and-p.patch +- add 8074354-Make-CreateMinidumpOnCrash-a-new-name-and-av.patch +- add jcmd-mnt-add-start-time-and-end-time.patch +- add Fix-localtime_r-not-defined-on-windows.patch +- add 8057743-process-Synchronize-exiting-of-threads-and-p.patch - add 8305541-C2-Div-Mod-nodes-without-zero-check-could-be.patch - upgrade to jdk8u372-b07 @@ -3163,7 +3211,7 @@ cjc.mainProgram(args) -- the returns from copy_jdk_configs.lua should not affect - modified implementation_of_Blas_hotspot_function_in_Intrinsics.patch * Tue Feb 15 2022 eapen - 1:1.8.0.322-b06.1 -- fix makes failure when gcc version is lower than 8 +- fix makes failure when gcc version is lower than 8 * Thu Feb 10 2022 eapen - 1:1.8.0.322-b06.0 - upgrade to 8u322-b06(ga) @@ -3439,7 +3487,7 @@ cjc.mainProgram(args) -- the returns from copy_jdk_configs.lua should not affect * Tue Nov 10 2020 ow_wo - 1:1.8.0.272-b10.6 - add 8236512-PKCS11-Connection-closed-after-Cipher.doFinal-and-NoPadding.patch -- add 8250861-Crash-in-MinINode-Ideal-PhaseGVN-bool.patch +- add 8250861-Crash-in-MinINode-Ideal-PhaseGVN-bool.patch * Mon Nov 09 2020 ow_wo - 1:1.8.0.272-b10.5 - add 8223940-Private-key-not-supported-by-chosen-signature.patch @@ -3517,7 +3565,7 @@ cjc.mainProgram(args) -- the returns from copy_jdk_configs.lua should not affect - Add Ddot-intrinsic-implement.patch - Add 8234003-Improve-IndexSet-iteration.patch - Add 8220159-Optimize-various-RegMask-operations-by-introducing-watermarks.patch -- Remove prohibition-of-irreducible-loop-in-mergers.patch +- Remove prohibition-of-irreducible-loop-in-mergers.patch * Tue Aug 25 2020 noah - 1:1.8.0.265-b10.0 - Update to aarch64-shenandoah-jdk8u-8u265-b01