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