From e1b5fb640510d476884daa9cb3d769881000c05b Mon Sep 17 00:00:00 2001 From: wuyafang <791828096@qq.com> Date: Wed, 4 Sep 2024 17:11:13 +0800 Subject: [PATCH] Support KAE Zip --- ...support-streaming-data-decompression.patch | 691 +++ heap-dump-redact-support.patch | 3904 +++++++++++++++++ openjdk-1.8.0.spec | 14 +- 3 files changed, 4606 insertions(+), 3 deletions(-) create mode 100644 KAE-zip-support-streaming-data-decompression.patch create mode 100644 heap-dump-redact-support.patch diff --git a/KAE-zip-support-streaming-data-decompression.patch b/KAE-zip-support-streaming-data-decompression.patch new file mode 100644 index 0000000..4e01111 --- /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/heap-dump-redact-support.patch b/heap-dump-redact-support.patch new file mode 100644 index 0000000..c32d212 --- /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 0749dc2..4251dc6 100644 --- a/openjdk-1.8.0.spec +++ b/openjdk-1.8.0.spec @@ -945,7 +945,7 @@ Provides: java-%{javaver}-%{origin}-accessibility%{?1} = %{epoch}:%{version}-%{r Name: java-%{javaver}-%{origin} Version: %{javaver}.%{updatever}.%{buildver} -Release: 9 +Release: 10 # 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 @@ -1347,6 +1347,8 @@ Patch442: Huawei-Keep-objects-when-remove-unshareable-info.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 ############################################# # # Upstreamable patches @@ -2007,6 +2009,8 @@ pushd %{top_level_dir_name} %patch443 -p1 %patch445 -p1 %patch446 -p1 +%patch447 -p1 +%patch448 -p1 %endif %ifarch loongarch64 @@ -2141,8 +2145,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/" \ @@ -2672,6 +2676,10 @@ cjc.mainProgram(args) -- the returns from copy_jdk_configs.lua should not affect %endif %changelog +* 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 -- Gitee