代码拉取完成,页面将自动刷新
同步操作将从 src-openEuler/openjdk-1.8.0 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
---
.../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 <file> <args...>
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<u1>*");
+
+ Type type = db.lookupType("Array<Array<u1>*>");
+ 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<TypeArray> 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<String> 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<Field> 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<String> 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<String> getAnonymousValue(U1Array fieldAnnotations, ConstantPool cp) {
+ Optional<String> 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<Field> fields, Instance instance) {
+ Klass klass = instance.getKlass();
+ boolean diyRulesFlag = false;
+ Symbol classNameSymbol = klass.getName();
+ Map<String, String> redactRulesMap = null;
+ if(classNameSymbol != null) {
+ String className = classNameSymbol.asString();
+ Optional<Map<String, String>> redactRulesMapOptional = className == null ? Optional.<Map<String, String>>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<Field> 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<String, String> redactNameTable;
+ private Map<String, Map<String, String>> redactClassTable;
+ private String redactClassFullName = null;
+ private Map<Long, String> 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<TypeArray> 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<String> lookupRedactAnnotationValue(Long addr){
+ return Optional.ofNullable(redactValueTable == null ? null : redactValueTable.get(addr));
+ }
+
+ public String getRedactAnnotationClassPath(){
+ return redactParams.getRedactClassPath();
+ }
+
+ public Optional<Map<String, String>> getRedactRulesTable(String key) {
+ return Optional.<Map<String, String>>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<TypeArray> 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=<file> 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<String, String> 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<TypeArray> typeArrayList;
+ private RedactVectorNode next;
+ private int currentIndex;
+
+ public List<TypeArray> getTypeArrayList() {
+ return typeArrayList;
+ }
+
+ public void setTypeArrayList(List<TypeArray> 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_<arch>.[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<u1> 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<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<const char*, Symbol*, symbol_equals_compact_hasht
nonstatic_field(Symbol, _length, unsigned short) \
unchecked_nonstatic_field(Symbol, _body, sizeof(jbyte)) /* NOTE: no type */ \
nonstatic_field(TypeArrayKlass, _max_length, int) \
+ nonstatic_field(Annotations, _class_annotations, Array<u1>*) \
+ nonstatic_field(Annotations, _class_type_annotations, Array<u1>*) \
+ nonstatic_field(Annotations, _fields_annotations, Array<Array<u1>*>*) \
+ nonstatic_field(Annotations, _fields_type_annotations, Array<Array<u1>*>*) \
\
/***********************/ \
/* Constant Pool Cache */ \
@@ -767,6 +771,8 @@ typedef OffsetCompactHashtable<const char*, Symbol*, symbol_equals_compact_hasht
\
nonstatic_field(Array<Klass*>, _length, int) \
nonstatic_field(Array<Klass*>, _data[0], Klass*) \
+ nonstatic_field(Array<Array<u1>*>, _length, int) \
+ nonstatic_field(Array<Array<u1>*>, _data[0], Array<u1>*) \
\
/*******************/ \
/* GrowableArrays */ \
@@ -1264,6 +1270,7 @@ typedef OffsetCompactHashtable<const char*, Symbol*, symbol_equals_compact_hasht
unchecked_nonstatic_field(Array<u2>, _data, sizeof(u2)) \
unchecked_nonstatic_field(Array<Method*>, _data, sizeof(Method*)) \
unchecked_nonstatic_field(Array<Klass*>, _data, sizeof(Klass*)) \
+ unchecked_nonstatic_field(Array<Array<u1>*>, _data, sizeof(Array<u1>*)) \
\
/*********************************/ \
/* java_lang_Class fields */ \
@@ -1460,6 +1467,7 @@ typedef OffsetCompactHashtable<const char*, Symbol*, symbol_equals_compact_hasht
declare_type(Method, Metadata) \
declare_type(MethodCounters, MetaspaceObj) \
declare_type(ConstMethod, MetaspaceObj) \
+ declare_type(Annotations, MetaspaceObj) \
\
declare_toplevel_type(vtableEntry) \
\
@@ -2131,6 +2139,7 @@ typedef OffsetCompactHashtable<const char*, Symbol*, symbol_equals_compact_hasht
declare_type(Array<u2>, MetaspaceObj) \
declare_type(Array<Klass*>, MetaspaceObj) \
declare_type(Array<Method*>, MetaspaceObj) \
+ declare_type(Array<Array<u1>*>, 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=<heapDumpRedactLevel>,RedactMap=<redactMap>,RedactMapFile=<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<address>(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<address>(array);
+ Symbol* anonymous_value_symbol = writer->heapRedactor()->lookup_replace_value<Symbol*>(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<address>(array);
+ char* anonymous_value = writer->heapRedactor()->lookup_replace_value<char*>(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<Klass*>(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=<file> 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<void*>(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<void*>(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<typename T>
+ 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 <file>
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=<file>. Returns <file> if provided. Returns null if <file> 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<options.length; i++) {
+ for (int i = 0; i < options.length; i++) {
String option = options[i];
if (option.equals("format=b")) {
// ignore format (not needed at this time)
} else if (option.equals("live")) {
// a valid suboption
+ } else if (option.equals("RedactPassword")) {
+ // ignore this option, just suit the parse rule
} else {
-
// file=<file> - check that <file> 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=<file> dump heap to <file>");
+ System.err.println(" HeapDumpRedact=<basic|names|full|diyrules|annotation|off> redact the heapdump");
+ System.err.println(" information to remove sensitive data");
+ System.err.println(" RedactMap=<name1:value1;name2:value2;...> Redact the class and");
+ System.err.println(" field names to other strings");
+ System.err.println(" RedactMapFile=<file> file path of the redact map");
+ System.err.println(" RedactClassPath=<classpath> 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 <pid>");
System.err.println(" -F force. Use with -dump:<dump-options> <pid> or -histo");
System.err.println(" to force a heap dump or histogram when <pid> 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
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。