3905 lines
158 KiB
Diff
3905 lines
158 KiB
Diff
---
|
|
.../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
|
|
|