Projects
openEuler:24.03:SP1:Everything
openjdk-1.8.0
_service:tar_scm:heap-dump-redact-support.patch
Sign Up
Log In
Username
Password
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File _service:tar_scm:heap-dump-redact-support.patch of Package openjdk-1.8.0
--- .../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
Locations
Projects
Search
Status Monitor
Help
Open Build Service
OBS Manuals
API Documentation
OBS Portal
Reporting a Bug
Contact
Mailing List
Forums
Chat (IRC)
Twitter
Open Build Service (OBS)
is an
openSUSE project
.
浙ICP备2022010568号-2