openjdk-17/heap-dump-redact-support.patch

4102 lines
169 KiB
Diff
Raw Normal View History

2024-11-18 16:55:21 +08:00
---
make/hotspot/lib/JvmFlags.gmk | 6 +
make/hotspot/lib/JvmMapfile.gmk | 6 +
src/hotspot/os/linux/os_linux.cpp | 70 ++
src/hotspot/os/linux/os_linux.hpp | 79 +++
.../classfile/systemDictionaryShared.cpp | 67 +-
.../classfile/systemDictionaryShared.hpp | 1 -
src/hotspot/share/jbooster/lazyAot.cpp | 63 +-
src/hotspot/share/oops/annotations.hpp | 1 +
src/hotspot/share/oops/method.hpp | 6 +-
src/hotspot/share/runtime/arguments.cpp | 26 +
src/hotspot/share/runtime/arguments.hpp | 5 +
src/hotspot/share/runtime/globals.hpp | 18 +
src/hotspot/share/runtime/handles.hpp | 3 +
src/hotspot/share/runtime/vmStructs.cpp | 15 +-
src/hotspot/share/services/heapDumper.cpp | 632 ++++++++++++++++-
src/hotspot/share/services/heapRedactor.cpp | 649 ++++++++++++++++++
src/hotspot/share/services/heapRedactor.hpp | 204 ++++++
src/hotspot/share/services/writeableFlags.cpp | 7 +
src/hotspot/share/utilities/growableArray.hpp | 7 +
.../share/classes/sun/jvm/hotspot/HSDB.java | 17 +-
.../classes/sun/jvm/hotspot/SALauncher.java | 38 +-
.../sun/jvm/hotspot/oops/Annotation.java | 70 ++
.../classes/sun/jvm/hotspot/oops/Field.java | 10 +
.../sun/jvm/hotspot/oops/InstanceKlass.java | 7 +
.../classes/sun/jvm/hotspot/tools/JMap.java | 27 +-
.../hotspot/utilities/AnnotationArray2D.java | 62 ++
.../hotspot/utilities/HeapHprofBinWriter.java | 346 +++++++++-
.../jvm/hotspot/utilities/HeapRedactor.java | 446 ++++++++++++
.../share/classes/sun/tools/jmap/JMap.java | 204 +++++-
29 files changed, 2978 insertions(+), 114 deletions(-)
create mode 100644 src/hotspot/share/services/heapRedactor.cpp
create mode 100644 src/hotspot/share/services/heapRedactor.hpp
create mode 100644 src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Annotation.java
create mode 100644 src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/AnnotationArray2D.java
create mode 100644 src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/HeapRedactor.java
diff --git a/make/hotspot/lib/JvmFlags.gmk b/make/hotspot/lib/JvmFlags.gmk
index 0c292ad86..524f6f56d 100644
--- a/make/hotspot/lib/JvmFlags.gmk
+++ b/make/hotspot/lib/JvmFlags.gmk
@@ -41,6 +41,12 @@ JVM_SRC_DIRS += $(call uniq, $(wildcard $(foreach d, $(JVM_SRC_ROOTS), \
$(JVM_VARIANT_OUTPUTDIR)/gensrc
#
+JVM_KUNPENG_PLUGIN_DIR := $(call FindSrcDirsForLib, java.base, jplugin)
+JVM_KUNPENG_PLUGIN_SRC := $(JVM_KUNPENG_PLUGIN_DIR)/feature
+ifeq ($(wildcard $(JVM_KUNPENG_PLUGIN_SRC)), $(JVM_KUNPENG_PLUGIN_SRC))
+ JVM_SRC_DIRS += $(JVM_KUNPENG_PLUGIN_SRC)
+endif
+
JVM_CFLAGS_INCLUDES += \
$(patsubst %,-I%,$(JVM_SRC_DIRS)) \
-I$(TOPDIR)/src/hotspot/share/precompiled \
diff --git a/make/hotspot/lib/JvmMapfile.gmk b/make/hotspot/lib/JvmMapfile.gmk
index 5cba93178..4b76377c1 100644
--- a/make/hotspot/lib/JvmMapfile.gmk
+++ b/make/hotspot/lib/JvmMapfile.gmk
@@ -48,6 +48,12 @@ ifneq ($(findstring debug, $(DEBUG_LEVEL)), )
endif
endif
+JVM_KUNPENG_PLUGIN_DIR := $(call FindSrcDirsForLib, java.base, jplugin)
+JVM_KUNPENG_PLUGIN_SYMBOLS_SRC := $(JVM_KUNPENG_PLUGIN_DIR)/make/hotspot-symbols
+ifeq ($(wildcard $(JVM_KUNPENG_PLUGIN_SYMBOLS_SRC)), $(JVM_KUNPENG_PLUGIN_SYMBOLS_SRC))
+ SYMBOLS_SRC += $(JVM_KUNPENG_PLUGIN_SYMBOLS_SRC)/symbols-plugin
+endif
+
################################################################################
# Create a dynamic list of symbols from the built object files. This is highly
# platform dependent.
diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp
index 48c812313..a898b2077 100644
--- a/src/hotspot/os/linux/os_linux.cpp
+++ b/src/hotspot/os/linux/os_linux.cpp
@@ -4593,6 +4593,74 @@ void os::Linux::numa_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;
+#if INCLUDE_AGGRESSIVE_CDS
+os::Linux::jboosterAggressiveCDS_do_t os::Linux::_jboosterAggressiveCDS_do;
+#endif // INCLUDE_AGGRESSIVE_CDS
+#if INCLUDE_JBOOSTER
+os::Linux::jboosterLazyAOT_do_t os::Linux::_jboosterLazyAOT_do;
+#endif // INCLUDE_JBOOSTER
+
+void os::Linux::load_plugin_library() {
+
+#if INCLUDE_AGGRESSIVE_CDS
+ _jboosterAggressiveCDS_do = CAST_TO_FN_PTR(jboosterAggressiveCDS_do_t, dlsym(RTLD_DEFAULT, "JBoosterAggressiveCDS_DO"));
+#endif // INCLUDE_AGGRESSIVE_CDS
+#if INCLUDE_JBOOSTER
+ _jboosterLazyAOT_do = CAST_TO_FN_PTR(jboosterLazyAOT_do_t, dlsym(RTLD_DEFAULT, "JBoosterLazyAOT_DO"));
+#endif // INCLUDE_JBOOSTER
+
+ _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_locate_lib(path, sizeof(path), Arguments::get_dll_dir(), "jvm17_kunpeng") ||
+ os::dll_locate_lib(path, sizeof(path), "/usr/lib64", "jvm17_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"));
+ }
+#if INCLUDE_AGGRESSIVE_CDS
+ if (_jboosterAggressiveCDS_do == NULL) {
+ _jboosterAggressiveCDS_do = CAST_TO_FN_PTR(jboosterAggressiveCDS_do_t, dlsym(handle, "JBoosterAggressiveCDS_DO"));
+ }
+#endif // INCLUDE_AGGRESSIVE_CDS
+#if INCLUDE_JBOOSTER
+ if (_jboosterLazyAOT_do == NULL) {
+ _jboosterLazyAOT_do = CAST_TO_FN_PTR(jboosterLazyAOT_do_t, dlsym(handle, "JBoosterLazyAOT_DO"));
+ }
+#endif // INCLUDE_JBOOSTER
+ }
+}
+
// this is called _after_ the global arguments have been parsed
jint os::init_2(void) {
@@ -4635,6 +4703,8 @@ jint os::init_2(void) {
init_adjust_stacksize_for_guard_pages();
#endif
+ Linux::load_plugin_library();
+
if (UseNUMA || UseNUMAInterleaving) {
Linux::numa_init();
}
diff --git a/src/hotspot/os/linux/os_linux.hpp b/src/hotspot/os/linux/os_linux.hpp
index dc83208f6..2afecada2 100644
--- a/src/hotspot/os/linux/os_linux.hpp
+++ b/src/hotspot/os/linux/os_linux.hpp
@@ -138,6 +138,7 @@ class Linux {
static const char *libc_version() { return _libc_version; }
static const char *libpthread_version() { return _libpthread_version; }
+ static void load_plugin_library();
static void libpthread_init();
static void sched_getcpu_init();
static bool libnuma_init();
@@ -214,6 +215,26 @@ class Linux {
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;
+#if INCLUDE_AGGRESSIVE_CDS
+ typedef void (*jboosterAggressiveCDS_do_t)(uintptr_t related_data[], address ik, address class_loader, address pd, address thread);
+ static jboosterAggressiveCDS_do_t _jboosterAggressiveCDS_do;
+#endif // INCLUDE_AGGRESSIVE_CDS
+#if INCLUDE_JBOOSTER
+ typedef void (*jboosterLazyAOT_do_t)(int data_layout[], address methods, address tc_method_array, address nc_method_array, address klasses);
+ static jboosterLazyAOT_do_t _jboosterLazyAOT_do;
+#endif // INCLUDE_JBOOSTER
static sched_getcpu_func_t _sched_getcpu;
static numa_node_to_cpus_func_t _numa_node_to_cpus;
static numa_node_to_cpus_v2_func_t _numa_node_to_cpus_v2;
@@ -425,6 +446,64 @@ class Linux {
static const GrowableArray<int>* numa_nindex_to_node() {
return _nindex_to_node;
}
+
+ 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);
+ }
+ }
+
+#if INCLUDE_AGGRESSIVE_CDS
+ static void jboosterAggressiveCDS_do(uintptr_t related_data[], address ik, address class_loader, address pd, address thread) {
+ if (_jboosterAggressiveCDS_do != NULL) {
+ _jboosterAggressiveCDS_do(related_data, ik, class_loader, pd, thread);
+ }
+ }
+#endif // INCLUDE_AGGRESSIVE_CDS
+#if INCLUDE_JBOOSTER
+ static bool is_jboosterLazyAOT_do_valid() {
+ return _jboosterLazyAOT_do != NULL;
+ }
+ static void jboosterLazyAOT_do(int data_layout[], address methods, address tc_method_array, address nc_method_array, address klasses) {
+ if (_jboosterLazyAOT_do != NULL) {
+ _jboosterLazyAOT_do(data_layout, methods, tc_method_array, nc_method_array, klasses);
+ }
+ }
+#endif // INCLUDE_JBOOSTER
};
#endif // OS_LINUX_OS_LINUX_HPP
diff --git a/src/hotspot/share/classfile/systemDictionaryShared.cpp b/src/hotspot/share/classfile/systemDictionaryShared.cpp
index 163b581fb..c15e8f4df 100644
--- a/src/hotspot/share/classfile/systemDictionaryShared.cpp
+++ b/src/hotspot/share/classfile/systemDictionaryShared.cpp
@@ -860,24 +860,24 @@ public:
return NULL;
}
-
// check timestamp in the load time when UseAggressiveCDS.
// regular_file(*.class): need to check timestamp.
// jar_file(*.jar): no need to check timestamp here,already checked
// somewhere else, see SharedClassPathEntry::validate.
// other_file: not supported when UseAggressiveCDS.
- bool check_classfile_timestamp(char* url_string, TRAPS) {
+ static bool check_classfile_timestamp(RunTimeSharedClassInfo* info, TRAPS) {
+ char* url_string = (char*)(info->_url_string->data);
if (SystemDictionaryShared::is_regular_file(url_string)) {
ResourceMark rm(THREAD);
char* dir = SystemDictionaryShared::get_filedir(url_string);
if (dir == NULL) {
return false;
}
- int64_t timestamp = SystemDictionaryShared::get_timestamp(dir, _klass->name());
- if (timestamp != _classfile_timestamp) {
+ int64_t timestamp = SystemDictionaryShared::get_timestamp(dir, info->_klass->name());
+ if (timestamp != info->_classfile_timestamp) {
log_trace(cds, aggressive)("%s, timestamp mismatch: " INT64_FORMAT " -> " INT64_FORMAT,
- _klass->name()->as_C_string(),
- _classfile_timestamp, timestamp);
+ info->_klass->name()->as_C_string(),
+ info->_classfile_timestamp, timestamp);
return false;
}
} else if (!SystemDictionaryShared::is_jar_file(url_string)) {
@@ -887,28 +887,19 @@ public:
return true;
}
- Handle get_protection_domain(Handle class_loader, TRAPS) {
- if (_url_string == NULL) {
- return Handle();
- }
- char* data_ptr = (char*)(_url_string->data);
-
- if (CheckClassFileTimeStamp) {
- if (!check_classfile_timestamp(data_ptr, THREAD)) {
- return Handle();
- }
- }
-
+ static oop* get_protection_domain(RunTimeSharedClassInfo* info, Handle* class_loader, TRAPS) {
+ assert(info->_url_string != NULL, "sanity");
+ char* data_ptr = (char*)(info->_url_string->data);
Handle url_string = java_lang_String::create_from_str(data_ptr, THREAD);
JavaValue result(T_OBJECT);
JavaCalls::call_virtual(&result,
- class_loader,
- class_loader->klass(),
+ *class_loader,
+ (*class_loader)->klass(),
vmSymbols::getProtectionDomainByURLString_name(),
vmSymbols::getProtectionDomainByURLString_signature(),
url_string, THREAD);
if (!HAS_PENDING_EXCEPTION) {
- return Handle(THREAD, result.get_oop());
+ return Handle(THREAD, result.get_oop()).raw_value();
} else {
LogTarget(Warning, cds, aggressive) lt;
if (lt.is_enabled()) {
@@ -916,7 +907,7 @@ public:
}
DebugUtils::clear_java_exception_and_print_stack_trace(lt, THREAD);
}
- return Handle();
+ return NULL;
}
#endif // INCLUDE_AGGRESSIVE_CDS
};
@@ -2915,12 +2906,6 @@ int64_t SystemDictionaryShared::get_timestamp(char* dir, Symbol* class_name) {
return 0;
}
-Handle SystemDictionaryShared::get_protection_domain(InstanceKlass* k, Handle class_loader, TRAPS) {
- assert(UseAggressiveCDS, "sanity");
- RunTimeSharedClassInfo* info = RunTimeSharedClassInfo::get_for(k);
- return info->get_protection_domain(class_loader, CHECK_NH);
-}
-
void SystemDictionaryShared::set_url_string(InstanceKlass* k, char* string_value) {
assert(UseAggressiveCDS, "sanity");
Arguments::assert_is_dumping_archive();
@@ -2953,6 +2938,20 @@ void SystemDictionaryShared::set_classfile_timestamp(InstanceKlass* k, int64_t c
}
}
+uintptr_t related_data[] = {
+ (uintptr_t)in_bytes(byte_offset_of(RunTimeSharedClassInfo, _klass)),
+ (uintptr_t)in_bytes(byte_offset_of(RunTimeSharedClassInfo, _shared_class_file)),
+ (uintptr_t)in_bytes(byte_offset_of(RunTimeSharedClassInfo, _url_string)),
+ (uintptr_t)in_bytes(byte_offset_of(RunTimeSharedClassInfo, _classfile_timestamp)),
+ (uintptr_t)in_bytes(Handle::handle_offset()),
+ (uintptr_t)RunTimeSharedClassInfo::get_for,
+ (uintptr_t)RunTimeSharedClassInfo::set_for,
+ (uintptr_t)RunTimeSharedClassInfo::EQUALS,
+ (uintptr_t)RunTimeSharedClassInfo::check_classfile_timestamp,
+ (uintptr_t)RunTimeSharedClassInfo::get_protection_domain,
+ (uintptr_t)CheckClassFileTimeStamp
+};
+
InstanceKlass* SystemDictionaryShared::lookup_trusted_share_class(Symbol* class_name,
Handle class_loader,
TRAPS) {
@@ -2978,6 +2977,7 @@ InstanceKlass* SystemDictionaryShared::lookup_trusted_share_class(Symbol* class_
}
}
+ HandleMark hm(THREAD);
Handle lock = get_loader_lock_or_null(class_loader);
ObjectLocker ol(lock, THREAD);
@@ -2997,12 +2997,13 @@ InstanceKlass* SystemDictionaryShared::lookup_trusted_share_class(Symbol* class_
return NULL;
}
- Handle protection_domain = SystemDictionaryShared::get_protection_domain(record->_klass, class_loader, CHECK_NULL);
+ Handle protection_domain;
+ os::Linux::jboosterAggressiveCDS_do(related_data, (address)record->_klass,
+ (address)&class_loader,
+ (address)&protection_domain,
+ (address)THREAD);
+
if (protection_domain.is_null()) {
- // The protection_domain is rebuilt based on the RunTimeSharedClassInfo::_url_string.
- // We lookup the URL of _url_string from the URLClassPath of the URLClassLoader.
- // The URLClassPath returns null if _url_string is not in its url array, which also
- // means that this class was not defined by this class loader at dump run.
return NULL;
}
diff --git a/src/hotspot/share/classfile/systemDictionaryShared.hpp b/src/hotspot/share/classfile/systemDictionaryShared.hpp
index 6dafba669..04b8d59fc 100644
--- a/src/hotspot/share/classfile/systemDictionaryShared.hpp
+++ b/src/hotspot/share/classfile/systemDictionaryShared.hpp
@@ -385,7 +385,6 @@ public:
static ClassFileStream* get_shared_class_file_stream(InstanceKlass* k);
static ClassFileStream* get_byte_code_from_cache(Symbol* class_name, Handle class_loader, TRAPS);
static void set_shared_class_file(InstanceKlass* k, ClassFileStream* cfs);
- static Handle get_protection_domain(InstanceKlass* k, Handle class_loader, TRAPS);
static void set_url_string(InstanceKlass* k, char* string_value);
static void save_timestamp(InstanceKlass* k, char* string_value);
static void set_classfile_timestamp(InstanceKlass* k, int64_t classfile_timestamp);
diff --git a/src/hotspot/share/jbooster/lazyAot.cpp b/src/hotspot/share/jbooster/lazyAot.cpp
index d36bfe671..410d575e9 100644
--- a/src/hotspot/share/jbooster/lazyAot.cpp
+++ b/src/hotspot/share/jbooster/lazyAot.cpp
@@ -317,6 +317,40 @@ bool LazyAOT::can_be_compiled(ClassLoaderData* cld) {
return true;
}
+int data_layout[] = {
+ in_bytes(Method::const_offset()),
+ in_bytes(Method::access_flags_offset()),
+ in_bytes(Method::flags_offset()),
+ in_bytes(Method::from_compiled_offset()),
+ in_bytes(Method::code_offset()),
+ Method::method_data_offset_in_bytes(),
+ in_bytes(Method::method_counters_offset()),
+ in_bytes(Method::native_function_offset()),
+ in_bytes(Method::from_interpreted_offset()),
+ in_bytes(Method::interpreter_entry_offset()),
+ in_bytes(Method::signature_handler_offset()),
+ in_bytes(Method::itable_index_offset()),
+ (int)Method::last_method_flags(),
+
+ in_bytes(ConstMethod::constants_offset()),
+ in_bytes(ConstMethod::max_stack_offset()),
+ in_bytes(ConstMethod::size_of_locals_offset()),
+ in_bytes(ConstMethod::size_of_parameters_offset()),
+ in_bytes(ConstMethod::result_type_offset()),
+
+ ConstantPool::tags_offset_in_bytes(),
+ ConstantPool::cache_offset_in_bytes(),
+ ConstantPool::pool_holder_offset_in_bytes(),
+ ConstantPool::resolved_klasses_offset_in_bytes(),
+
+ Array<Method*>::length_offset_in_bytes(),
+ Array<Method*>::base_offset_in_bytes(),
+
+ in_bytes(GrowableArrayBase::len_offset()),
+ in_bytes(GrowableArrayBase::max_offset()),
+ in_bytes(GrowableArrayView<address>::data_offset())
+};
+
class KlassGetAllInstanceKlassesClosure: public KlassClosure {
GrowableArray<InstanceKlass*>* _klasses;
GrowableArray<Method*>* _methods_to_compile;
@@ -350,26 +384,17 @@ public:
// Maybe we should add "if (!ik->is_initialized()) return;".
if (!LazyAOT::can_be_compiled(ik, /* check_cld */ false)) return;
- bool should_append_klass = false;
Array<Method*>* methods = ik->methods();
- int len = methods->length();
- for (int i = 0; i < len; i++) {
- Method* m = methods->at(i);
-
- bool should_compile = m->has_compiled_code();
-
- if (should_compile) {
- _methods_to_compile->append(m);
- should_append_klass = true;
- }
-
- if (m->is_rewrite_invokehandle()) {
- _methods_not_compile->append(m);
- }
- }
-
- if (should_append_klass) {
- _klasses->append(ik);
+ if (methods->length() == 0) return;
+ if (os::Linux::is_jboosterLazyAOT_do_valid()) {
+ int current_tc_count = _methods_to_compile->length();
+ int current_nc_count = _methods_not_compile->length();
+ int current_k_count = _klasses->length();
+ _methods_to_compile->at_put_grow(current_tc_count + methods->length() - 1, (Method*)(uintptr_t)current_tc_count, nullptr);
+ _methods_not_compile->at_put_grow(current_nc_count + methods->length() - 1, (Method*)(uintptr_t)current_nc_count, nullptr);
+ _klasses->at_put_grow(current_k_count, (InstanceKlass*)(uintptr_t)current_k_count, nullptr);
+ os::Linux::jboosterLazyAOT_do(data_layout, (address)methods, (address)_methods_to_compile,
+ (address)_methods_not_compile, (address)_klasses);
}
}
};
diff --git a/src/hotspot/share/oops/annotations.hpp b/src/hotspot/share/oops/annotations.hpp
index e650b5f55..bc84e41f0 100644
--- a/src/hotspot/share/oops/annotations.hpp
+++ b/src/hotspot/share/oops/annotations.hpp
@@ -41,6 +41,7 @@ typedef Array<u1> AnnotationArray;
// a type_annotation instance.
class Annotations: public MetaspaceObj {
+ friend class VMStructs;
friend class JVMCIVMStructs;
// If you add a new field that points to any metaspace object, you
diff --git a/src/hotspot/share/oops/method.hpp b/src/hotspot/share/oops/method.hpp
index 1155f33a4..a93948285 100644
--- a/src/hotspot/share/oops/method.hpp
+++ b/src/hotspot/share/oops/method.hpp
@@ -93,7 +93,8 @@ class Method : public Metadata {
_scoped = 1 << 7
#if INCLUDE_JBOOSTER
,
- _rewrite_invokehandle = 1 << 8
+ _rewrite_invokehandle = 1 << 8, /* always be the last */
+ _last_method_flags = _rewrite_invokehandle
#endif // INCLUDE_JBOOSTER
};
mutable u2 _flags;
@@ -1017,6 +1018,9 @@ public:
#if INCLUDE_JBOOSTER
public:
+ static ByteSize flags_offset() { return byte_offset_of(Method, _flags); }
+ static u2 last_method_flags() { return _last_method_flags; }
+
bool is_rewrite_invokehandle() {
return (_flags & _rewrite_invokehandle) != 0;
}
diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp
index 9ef0a0db2..42b4f90f1 100644
--- a/src/hotspot/share/runtime/arguments.cpp
+++ b/src/hotspot/share/runtime/arguments.cpp
@@ -54,6 +54,7 @@
#include "runtime/vm_version.hpp"
#include "services/management.hpp"
#include "services/nmtCommon.hpp"
+#include "services/heapRedactor.hpp"
#include "utilities/align.hpp"
#include "utilities/defaultStream.hpp"
#include "utilities/macros.hpp"
@@ -120,6 +121,8 @@ bool Arguments::_has_jimage = false;
char* Arguments::_ext_dirs = NULL;
+char* Arguments::_heap_dump_redact_auth = NULL;
+
bool PathString::set_value(const char *value) {
if (_value != NULL) {
FreeHeap(_value);
@@ -3734,6 +3737,29 @@ jint Arguments::match_special_option_and_act(const JavaVMInitArgs* args,
vm_exit(0);
}
+ if (match_option(option, "-XX:HeapDumpRedact", &tail)) {
+ // HeapDumpRedact arguments.
+ 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, mtArguments);
+ 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")) {
JVMFlag::printFlags(tty, true);
diff --git a/src/hotspot/share/runtime/arguments.hpp b/src/hotspot/share/runtime/arguments.hpp
index cb2a04a2d..6b9759906 100644
--- a/src/hotspot/share/runtime/arguments.hpp
+++ b/src/hotspot/share/runtime/arguments.hpp
@@ -468,6 +468,8 @@ class Arguments : AllStatic {
char** base_archive_path,
char** top_archive_path) NOT_CDS_RETURN;
+ static char* _heap_dump_redact_auth;
+
public:
// Parses the arguments, first phase
static jint parse(const JavaVMInitArgs* args);
@@ -553,6 +555,9 @@ class Arguments : AllStatic {
// Java launcher properties
static void process_sun_java_launcher_properties(JavaVMInitArgs* args);
+ // heap dump redact password
+ static const char* get_heap_dump_redact_auth() { return _heap_dump_redact_auth; }
+
// System properties
static void init_system_properties();
diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp
index 27695a3f5..a1d787de6 100644
--- a/src/hotspot/share/runtime/globals.hpp
+++ b/src/hotspot/share/runtime/globals.hpp
@@ -549,6 +549,24 @@ const intx ObjectAlignmentInBytes = 8;
"compression. Otherwise the level must be between 1 and 9.") \
range(0, 9) \
\
+ product(ccstr, HeapDumpRedact, NULL, MANAGEABLE, \
+ "Redact the heapdump information to remove sensitive data") \
+ \
+ product(ccstr, RedactMap, NULL, MANAGEABLE, \
+ "Redact the class and field names to other strings") \
+ \
+ product(ccstr, RedactMapFile, NULL, MANAGEABLE, \
+ "File path of the Redact Map") \
+ \
+ product(ccstr, RedactClassPath, NULL, MANAGEABLE, \
+ "full path of the Redact Annotation") \
+ \
+ product(bool, VerifyRedactPassword, false, \
+ "verify authority for operating heapDump redact feature") \
+ \
+ product(ccstr, RedactPassword, NULL, \
+ "authority for operating heapDump redact feature") \
+ \
product(ccstr, NativeMemoryTracking, DEBUG_ONLY("summary") NOT_DEBUG("off"), \
"Native memory tracking options") \
\
diff --git a/src/hotspot/share/runtime/handles.hpp b/src/hotspot/share/runtime/handles.hpp
index 48c41bd43..cecb191e9 100644
--- a/src/hotspot/share/runtime/handles.hpp
+++ b/src/hotspot/share/runtime/handles.hpp
@@ -100,6 +100,9 @@ class Handle {
// since duplicates is only valid as long as original handle is alive.
oop* raw_value() const { return _handle; }
static oop raw_resolve(oop *handle) { return handle == NULL ? (oop)NULL : *handle; }
+#if INCLUDE_JBOOSTER
+ static ByteSize handle_offset() { return byte_offset_of(Handle, _handle); }
+#endif // INCLUDE_JBOOSTER
};
// Specific Handles for different oop types
diff --git a/src/hotspot/share/runtime/vmStructs.cpp b/src/hotspot/share/runtime/vmStructs.cpp
index ef2a366f7..22d2a7c47 100644
--- a/src/hotspot/share/runtime/vmStructs.cpp
+++ b/src/hotspot/share/runtime/vmStructs.cpp
@@ -332,7 +332,11 @@ typedef HashtableEntry<InstanceKlass*, mtClass> KlassHashtableEntry;
nonstatic_field(Symbol, _body[0], u1) \
nonstatic_field(TypeArrayKlass, _max_length, jint) \
nonstatic_field(OopHandle, _obj, oop*) \
- \
+ 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 */ \
/***********************/ \
@@ -509,7 +513,9 @@ typedef HashtableEntry<InstanceKlass*, mtClass> KlassHashtableEntry;
\
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 */ \
/*******************/ \
@@ -1056,7 +1062,8 @@ typedef HashtableEntry<InstanceKlass*, mtClass> KlassHashtableEntry;
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 */ \
/*********************************/ \
@@ -1261,6 +1268,7 @@ typedef HashtableEntry<InstanceKlass*, mtClass> KlassHashtableEntry;
declare_type(Method, Metadata) \
declare_type(MethodCounters, MetaspaceObj) \
declare_type(ConstMethod, MetaspaceObj) \
+ declare_type(Annotations, MetaspaceObj) \
\
declare_toplevel_type(MethodData::CompilerCounters) \
\
@@ -1966,6 +1974,7 @@ typedef HashtableEntry<InstanceKlass*, mtClass> KlassHashtableEntry;
declare_type(Array<u2>, MetaspaceObj) \
declare_type(Array<Klass*>, MetaspaceObj) \
declare_type(Array<Method*>, MetaspaceObj) \
+ declare_type(Array<Array<u1>*>, MetaspaceObj) \
\
declare_toplevel_type(BitMap) \
declare_type(BitMapView, BitMap) \
diff --git a/src/hotspot/share/services/heapDumper.cpp b/src/hotspot/share/services/heapDumper.cpp
index 57b8100b0..dcf6bf85b 100644
--- a/src/hotspot/share/services/heapDumper.cpp
+++ b/src/hotspot/share/services/heapDumper.cpp
@@ -53,11 +53,13 @@
#include "runtime/vframe.hpp"
#include "runtime/vmThread.hpp"
#include "runtime/vmOperations.hpp"
+#include "runtime/fieldDescriptor.inline.hpp"
#include "services/heapDumper.hpp"
#include "services/heapDumperCompression.hpp"
#include "services/threadService.hpp"
#include "utilities/macros.hpp"
#include "utilities/ostream.hpp"
+#include "services/heapRedactor.hpp"
/*
* HPROF binary format - description copied from:
@@ -414,6 +416,8 @@ class DumpWriter : public StackObj {
// Returns true if we have enough room in the buffer for 'len' bytes.
bool can_write_fast(size_t len);
+ HeapRedactor* redactor;
+
public:
// Takes ownership of the writer and compressor.
DumpWriter(AbstractWriter* writer, AbstractCompressor* compressor);
@@ -427,6 +431,7 @@ class DumpWriter : public StackObj {
// writer functions
void write_raw(void* s, size_t len);
+ void write_zero_raw(void* s, size_t len);
void write_u1(u1 x);
void write_u2(u2 x);
void write_u4(u4 x);
@@ -443,10 +448,16 @@ class DumpWriter : public StackObj {
// Finishes the current dump segment if not already finished.
void finish_dump_segment();
- // Called by threads used for parallel writing.
- void writer_loop() { _backend.thread_loop(); }
- // Called when finished to release the threads.
- void deactivate() { flush(); _backend.deactivate(); }
+ // remove sensitive data from heapdump information
+
+ void setHeapRedactor(HeapRedactor* value);
+ HeapRedactor* heapRedactor();
+ HeapDumpRedactLevel getHeapDumpRedactLevel();
+
+ // Called by threads used for parallel writing.
+ void writer_loop() { _backend.thread_loop(); }
+ // Called when finished to release the threads.
+ void deactivate() { flush(); _backend.deactivate(); }
};
// Check for error after constructing the object and destroy it in case of an error.
@@ -463,6 +474,21 @@ DumpWriter::~DumpWriter() {
flush();
}
+void DumpWriter::setHeapRedactor(HeapRedactor* value) {
+ redactor = value;
+}
+
+HeapRedactor* DumpWriter::heapRedactor() {
+ return redactor;
+}
+
+HeapDumpRedactLevel DumpWriter::getHeapDumpRedactLevel() {
+ if(redactor==NULL){
+ return REDACT_OFF;
+ }
+ return redactor->redact_level();
+}
+
void DumpWriter::write_fast(void* s, size_t len) {
assert(!_in_dump_segment || (_sub_record_left >= len), "sub-record too large");
assert(buffer_size() - position() >= len, "Must fit");
@@ -498,6 +524,27 @@ void DumpWriter::write_raw(void* s, size_t len) {
set_position(position() + len);
}
+void DumpWriter::write_zero_raw(void* s, size_t len) {
+ assert(!_in_dump_segment || (_sub_record_left >= len), "sub-record too large");
+ debug_only(_sub_record_left -= len);
+
+ // flush buffer to make room.
+ while (len > buffer_size() - position()) {
+ assert(!_in_dump_segment || _is_huge_sub_record,
+ "Cannot overflow in non-huge sub-record.");
+
+ size_t to_write = buffer_size() - position();
+ memset(buffer() + position(), 0, to_write);
+ s = (void*) ((char*) s + to_write);
+ len -= to_write;
+ set_position(position() + to_write);
+ flush();
+ }
+
+ memset(buffer() + position(), 0, len);
+ set_position(position() + len);
+}
+
// flush any buffered bytes to the file
void DumpWriter::flush() {
_backend.get_new_buffer(&_buffer, &_pos, &_size);
@@ -615,6 +662,9 @@ void DumpWriter::end_sub_record() {
debug_only(_sub_record_ended = true);
}
+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 {
@@ -645,15 +695,27 @@ 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 redact values of the instance fields of the given object
+ static void dump_instance_redact_fields(DumpWriter* writer, oop o, void* replace_value_table);
// get the count of the instance fields for a given class
static u2 get_instance_fields_count(InstanceKlass* ik);
// 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 for a given class
+ static void dump_instance_annotation_field_descriptors(DumpWriter* writer, Klass* k);
+ // dumps the definition of the instance fields 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);
@@ -662,11 +724,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 void 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);
// fixes up the current dump record and writes HPROF_HEAP_DUMP_END record
static void end_of_dump(DumpWriter* writer);
@@ -928,14 +994,47 @@ void DumperSupport::dump_static_fields(DumpWriter* writer, Klass* k) {
// dump the raw values of the instance fields of the given object
void DumperSupport::dump_instance_fields(DumpWriter* writer, oop o) {
- InstanceKlass* ik = InstanceKlass::cast(o->klass());
+ InstanceKlass* ik = InstanceKlass::cast(o->klass());
- for (FieldStream fld(ik, false, false); !fld.eos(); fld.next()) {
- if (!fld.access_flags().is_static()) {
- Symbol* sig = fld.signature();
- dump_field_value(writer, sig->char_at(0), o, fld.offset());
+ for (FieldStream fld(ik, false, false); !fld.eos(); fld.next()) {
+ if (!fld.access_flags().is_static()) {
+ Symbol* sig = fld.signature();
+ dump_field_value(writer, sig->char_at(0), o, fld.offset());
+ }
+ }
+}
+
+// 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();
+ char type = sig->char_at(0);
+
+ 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 = o->obj_field_access<ON_UNKNOWN_OOP_REF | AS_NO_KEEPALIVE>(fld.offset());
+ 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;
+ }
+ dump_field_value(writer, type, o, fld.offset());
}
- }
}
// dumps the definition of the instance fields for a given class
@@ -964,6 +1063,97 @@ 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) {
+ ResourceMark rm;
+ InstanceKlass* ik = InstanceKlass::cast(k);
+ Symbol *class_name_symbol = ik->name();
+ bool in_exclude_package = false;
+ if (Symbol::is_valid(class_name_symbol)) {
+ 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) {
+ DumperSupport::dump_instance_field_descriptors(writer, k);
+ return;
+ }
+
+ address obj_adr = (address)((uintptr_t)class_name_symbol);
+ // dump the field descriptors
+ for (FieldStream fld(ik, 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) {
+ ResourceMark rm;
+ InstanceKlass* ik = InstanceKlass::cast(k);
+ Symbol *class_name_symbol = ik->name();
+ void* redact_class_table = NULL;
+ bool has_diyrules = false;
+ if (Symbol::is_valid(class_name_symbol)) {
+ 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) {
+ DumperSupport::dump_instance_field_descriptors(writer, k);
+ return;
+ }
+
+ address obj_adr = (address) ((uintptr_t) class_name_symbol);
+ // dump the field descriptors
+ for (FieldStream fld(ik, 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) {
InstanceKlass* ik = InstanceKlass::cast(o->klass());
@@ -986,9 +1176,102 @@ void DumperSupport::dump_instance(DumpWriter* writer, oop o) {
writer->end_sub_record();
}
+// creates HPROF_GC_INSTANCE_REDACT_DUMP record for the given object
+void DumperSupport::dump_redact_instance(DumpWriter* writer, oop o) {
+ InstanceKlass* ik = InstanceKlass::cast(o->klass());
+ u4 is = instance_size(ik);
+ u4 size = 1 + sizeof(address) + 4 + sizeof(address) + 4 + is;
+
+ writer->start_sub_record(HPROF_GC_INSTANCE_DUMP, size);
+ writer->write_objectID(o);
+ writer->write_u4(STACK_TRACE_ID);
+
+ // class ID
+ writer->write_classID(ik);
+
+ // number of bytes that follow
+ writer->write_u4(is);
+
+ // field values
+ void* replace_value_table = NULL;
+ InstanceKlass* java_super = ik;
+ 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);
+ }
+
+ writer->end_sub_record();
+}
+
+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 = TypeArrayKlass::cast(array->klass())->element_type();
+ if(type != T_BYTE) {
+ return false;
+ }
+
+ // 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) {
+ return writer->heapRedactor()->record_typeArrayOop(array);
+ }
+
+ 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->start_sub_record(HPROF_GC_PRIM_ARRAY_DUMP, size);
+ writer->write_objectID(array);
+ writer->write_u4(STACK_TRACE_ID);
+ writer->write_u4(length);
+ writer->write_u1(HPROF_BYTE);
+
+ // nothing to copy
+ if (length == 0) {
+ writer->end_sub_record();
+ return true;
+ }
+
+ writer->write_raw(anonymous_value, char_length);
+ writer->end_sub_record();
+ 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) {
InstanceKlass* ik = InstanceKlass::cast(k);
// We can safepoint and do a heap dump at a point where we have a Klass,
@@ -1038,7 +1321,7 @@ void DumperSupport::dump_class_and_array_classes(DumpWriter* writer, Klass* k) {
// description of instance fields
writer->write_u2(instance_fields_count);
- dump_instance_field_descriptors(writer, ik);
+ fn(writer, ik);
writer->end_sub_record();
@@ -1112,12 +1395,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) {
type_size = sizeof(address);
@@ -1266,6 +1548,101 @@ void DumperSupport::dump_prim_array(DumpWriter* writer, typeArrayOop array) {
writer->end_sub_record();
}
+// 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;
+ u4 size = header_size + length_in_bytes;
+
+ writer->start_sub_record(HPROF_GC_PRIM_ARRAY_DUMP, size);
+ 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) {
+ writer->end_sub_record();
+ return;
+ }
+
+ // If the byte ordering is big endian then we can copy most types directly
+
+ switch (type) {
+ case T_INT : {
+ writer->write_zero_raw((void*)(array->int_at_addr(0)), length_in_bytes);
+ break;
+ }
+ case T_BYTE : {
+ writer->write_zero_raw((void*)(array->byte_at_addr(0)), length_in_bytes);
+ break;
+ }
+ case T_CHAR : {
+ writer->write_zero_raw((void*)(array->char_at_addr(0)), length_in_bytes);
+ break;
+ }
+ case T_SHORT : {
+ if (Endian::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 (Endian::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 (Endian::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();
+ }
+
+ writer->end_sub_record();
+}
+
+// creates HPROF_GC_PRIM_ARRAY_DUMP redact record for the given type array
+void DumperSupport::redact_replace_dump_prim_array(CALL_DO_LOOKUP_REPLACE_VALUE fn, DumpWriter *writer, typeArrayOop array) {
+ if(dump_replace_value(fn, writer, array)) {
+ return;
+ }
+
+ DumperSupport::dump_prim_array(writer, array);
+}
+
// create a HPROF_FRAME record of the given Method* and bci
void DumperSupport::dump_stack_frame(DumpWriter* writer,
int frame_serial_num,
@@ -1315,6 +1692,39 @@ 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((level == REDACT_NAMES || level == REDACT_FULL) &&
+ (redact_field = writer()->heapRedactor()->lookup_redact_name(s)) != NULL){
+ len = (int)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 {
@@ -1409,12 +1819,14 @@ class VM_HeapDumper;
class HeapObjectDumper : public ObjectClosure {
private:
DumpWriter* _writer;
+ CALL_DUMP_PRIM_ARRAY _redact_dump_prim_array;
DumpWriter* writer() { return _writer; }
public:
- HeapObjectDumper(DumpWriter* writer) {
+ HeapObjectDumper(DumpWriter* writer, CALL_DUMP_PRIM_ARRAY fn = DumperSupport::dump_prim_array) {
_writer = writer;
+ _redact_dump_prim_array = fn;
}
// called for each object in the heap
@@ -1442,7 +1854,54 @@ void HeapObjectDumper::do_object(oop o) {
DumperSupport::dump_object_array(writer(), objArrayOop(o));
} 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));
+ }
+}
+
+void DumperSupport::redact_dump_prim_array(CALL_DUMP_PRIM_ARRAY fn, DumpWriter* dumpWriter, typeArrayOop o){
+ fn(dumpWriter, o);
+}
+
+class HeapObjectRedactDumper : public ObjectClosure {
+private:
+ DumpWriter* _writer;
+
+ DumpWriter* writer() { return _writer; }
+
+ CALL_DO_LOOKUP_REPLACE_VALUE _do_lookup_replace_value;
+
+public:
+ HeapObjectRedactDumper(DumpWriter* writer, CALL_DO_LOOKUP_REPLACE_VALUE do_lookup_replace_value) {
+ _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) {
+ // skip classes as these emitted as HPROF_GC_CLASS_DUMP records
+ if (o->klass() == vmClasses::Class_klass()) {
+ if (!java_lang_Class::is_primitive(o)) {
+ return;
+ }
+ }
+
+ if (DumperSupport::mask_dormant_archived_object(o) == NULL) {
+ log_debug(cds, heap)("skipped dormant archived object " INTPTR_FORMAT " (%s)", p2i(o), o->klass()->external_name());
+ return;
+ }
+
+ if (o->is_instance()) {
+ // create a HPROF_GC_INSTANCE record for each object
+ DumperSupport::dump_redact_instance(writer(), o);
+ } else if (o->is_objArray()) {
+ // create a HPROF_GC_OBJ_ARRAY_DUMP record for each object array
+ DumperSupport::dump_object_array(writer(), objArrayOop(o));
+ } else if (o->is_typeArray()) {
+ // create a HPROF_GC_PRIM_ARRAY_DUMP record for each type array
+ DumperSupport::redact_replace_dump_prim_array(_do_lookup_replace_value, writer(), typeArrayOop(o));
}
}
@@ -1459,6 +1918,8 @@ class VM_HeapDumper : public VM_GC_Operation, public AbstractGangTask {
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; }
@@ -1498,6 +1959,9 @@ class VM_HeapDumper : public VM_GC_Operation, public AbstractGangTask {
// 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 */,
@@ -1510,6 +1974,14 @@ class VM_HeapDumper : public VM_GC_Operation, public AbstractGangTask {
_klass_map = new (ResourceObj::C_HEAP, mtServiceability) GrowableArray<Klass*>(INITIAL_CLASS_COUNT, mtServiceability);
_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
@@ -1541,6 +2013,7 @@ class VM_HeapDumper : public VM_GC_Operation, public AbstractGangTask {
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;
@@ -1591,7 +2064,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->is_instance_klass()) {
- DumperSupport::dump_class_and_array_classes(writer(), k);
+ DumperSupport::dump_class_and_array_classes(_dump_instance_fields_descriptors, writer(), k);
}
}
@@ -1796,8 +2269,14 @@ void VM_HeapDumper::work(uint worker_id) {
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
{
@@ -1823,8 +2302,25 @@ void VM_HeapDumper::work(uint worker_id) {
// segment is started.
// The HPROF_GC_CLASS_DUMP and HPROF_GC_INSTANCE_DUMP are the vast bulk
// of the heap dump.
- HeapObjectDumper obj_dumper(writer());
- Universe::heap()->object_iterate(&obj_dumper);
+ if(writer()->heapRedactor() != NULL && (writer()->heapRedactor()->redact_level() == REDACT_BASIC ||
+ writer()->heapRedactor()->redact_level() == REDACT_FULL)) {
+ HeapObjectDumper obj_dumper(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(writer(), DumperSupport::do_lookup_replace_value_with_symbol);
+ Universe::heap()->object_iterate(&obj_dumper);
+ } else if(writer()->heapRedactor() != NULL && writer()->heapRedactor()->redact_level() == REDACT_DIYRULES) {
+ HeapObjectRedactDumper obj_dumper(writer(), DumperSupport::do_lookup_replace_value_with_char);
+ Universe::heap()->object_iterate(&obj_dumper);
+ } else {
+ HeapObjectDumper obj_dumper(writer());
+ Universe::heap()->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();
@@ -1904,13 +2400,84 @@ void VM_HeapDumper::dump_stack_traces() {
}
}
+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_BYTE;
+ 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 = (typeArrayOopDesc*)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()->start_sub_record(HPROF_GC_PRIM_ARRAY_DUMP, size);
+ writer()->write_objectID(array);
+ writer()->write_u4(STACK_TRACE_ID);
+ writer()->write_u4(length);
+ writer()->write_u1(HPROF_BYTE);
+
+ // nothing to copy
+ if (length == 0) {
+ writer()->end_sub_record();
+ continue;
+ }
+ if(anonymous_value != NULL){
+ writer()->write_raw(anonymous_value, length);
+ } else {
+ writer()->write_raw((void *) (array->byte_at_addr(0)), length_in_bytes);
+ }
+ writer()->end_sub_record();
+ }
+ // 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.
int HeapDumper::dump(const char* path, outputStream* out, int compression, bool overwrite) {
assert(path != NULL && strlen(path) > 0, "path missing");
+ const char* path_and_anonymous = path;
+ const char* redact_params = NULL;
+ // parse args[0] to get real path and redact_params
+ const char* split_char = strstr(path_and_anonymous, ";");
+ size_t path_length = (split_char == NULL) ? strlen(path_and_anonymous) : (unsigned long)(split_char - path_and_anonymous);
+ size_t path_and_anonymous_length = strlen(path_and_anonymous);
+ char* _path = NEW_C_HEAP_ARRAY(char, path_and_anonymous_length + 1, mtInternal);
+ strncpy(_path, path_and_anonymous, path_and_anonymous_length + 1);
+ _path[path_length] = '\0';
+ if (split_char != NULL) {
+ redact_params = split_char + 1;
+ }
+
// print message in interactive case
if (out != NULL) {
- out->print_cr("Dumping heap to %s ...", path);
+ out->print_cr("Dumping heap to %s ...", _path);
timer()->start();
}
@@ -1928,17 +2495,26 @@ int HeapDumper::dump(const char* path, outputStream* out, int compression, bool
}
}
- DumpWriter writer(new (std::nothrow) FileWriter(path, overwrite), compressor);
+ HeapRedactor heapRedactor(redact_params, out);
+ DumpWriter writer(new (std::nothrow) FileWriter(_path, overwrite), compressor);
+ 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.error() != NULL) {
set_error(writer.error());
if (out != NULL) {
- out->print_cr("Unable to create %s: %s", path,
- (error() != NULL) ? error() : "reason unknown");
+ out->print_cr("Unable to create %s: %s", _path,
+ (error() != NULL) ? error() : "reason unknown");
}
+ FREE_C_HEAP_ARRAY(char, _path);
return -1;
}
+ FREE_C_HEAP_ARRAY(char, _path);
// generate the dump
VM_HeapDumper dumper(&writer, _gc_before_heap_dump, _oome);
if (Thread::current()->is_VM_thread()) {
diff --git a/src/hotspot/share/services/heapRedactor.cpp b/src/hotspot/share/services/heapRedactor.cpp
new file mode 100644
index 000000000..0e7b0a97c
--- /dev/null
+++ b/src/hotspot/share/services/heapRedactor.cpp
@@ -0,0 +1,649 @@
+/*
+ * Copyright (c) 2020, 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 "jvm_md.h"
+#include "utilities/debug.hpp"
+#include "services/heapRedactor.hpp"
+#include "logging/log.hpp"
+#include "logging/logLevel.hpp"
+
+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_ANNOTATION_STR = "ANNOTATION";
+const char* HeapRedactor::REDACT_DIYRULES_STR = "DIYRULES";
+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(_annotation_value_table != NULL) {
+ os::Linux::heap_dict_free(_annotation_value_table, false);
+ _annotation_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(_name_map_list != NULL){
+ FREE_C_HEAP_ARRAY(char, _name_map_list);
+ }
+ if(_file_name_map_list != NULL){
+ FREE_C_HEAP_ARRAY(char, _file_name_map_list);
+ }
+ if(_annotation_class_path != NULL) {
+ FREE_C_HEAP_ARRAY(char, _annotation_class_path);
+ }
+ _file_name_map_list = NULL;
+ _name_map_list = NULL;
+ _annotation_class_path = NULL;
+}
+
+void HeapRedactor::init_fields() {
+ _redact_level = REDACT_UNKNOWN;
+ _redact_name_table = NULL;
+ _redact_rules_table= NULL;
+ _annotation_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) {
+ // to reduce the depth of the method
+ token_start = step + 1;
+ continue;
+ }
+
+ 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';
+ _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)
+ FILE* fm = NULL;
+ fm = fopen(base_path, "r");
+ if (fm == NULL) {
+ return;
+ }
+ size_t num_read = fread((char *)buffer, 1, MAX_MAP_FILE_LENGTH, fm);
+ if((int)num_read != -1){
+ _file_name_map_list = NEW_C_HEAP_ARRAY(char, (size_t)num_read + 1, mtInternal);
+ strncpy(_file_name_map_list, buffer, num_read + 1);
+ read_redact_map_dependon_mode(_file_name_map_list, _redact_level);
+ }
+
+ fclose(fm);
+ }
+}
+
+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
+
+ }
+
+ if (_redact_params.params_string != NULL) {
+ FREE_C_HEAP_ARRAY(char, _redact_params.params_string);
+ }
+ 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
+ _annotation_value_table = os::Linux::heap_dict_add(key, value, _annotation_value_table, 1);
+#endif
+}
+
+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
+ if (log_is_enabled(Debug, cds, heap)) {
+ log_debug(cds, heap)("length() is too small for 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;
+
+ if (log_is_enabled(Debug, cds, heap)) {
+ log_debug(cds, heap)("type_index=%d num_element_value_pairs=%d", type_index, num_element_value_pairs);
+ }
+
+ 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
+ log_debug(cds, heap)("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;
+
+ log_debug(cds, heap)("element_name_index=%d", element_name_index);
+
+ if (!recursion_cp_refs_in_element_value(annotations_typeArray, byte_i_ref)) {
+ log_debug(cds, heap)("bad element_value at %d", calc_num_element_value_pairs);
+ // 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::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
+ log_debug(cds, heap)("length() is too small 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
+ log_debug(cds, heap)("length() is too small for 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);
+ Symbol *annotate_class_symbol = cp->symbol_at(cp_index);
+ if (!Symbol::is_valid(annotate_class_symbol)) {
+ return false;
+ }
+ 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
+ log_debug(cds, heap)("length() is too small for element_name_index");
+ 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_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
+ if (log_is_enabled(Debug, cds, heap)) {
+ log_debug(cds, heap)("length() is too small for a tag");
+ }
+ 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()) {
+ if (log_is_enabled(Debug, cds, heap)) {
+ log_debug(cds, heap)("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()) {
+ if (log_is_enabled(Debug, cds, heap)) {
+ log_debug(cds, heap)("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
+ if (log_is_enabled(Debug, cds, heap)) {
+ log_debug(cds, heap)("length() is too small 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;
+ if (log_is_enabled(Debug, cds, heap)) {
+ log_debug(cds, heap)("num_values=%d", num_values);
+ }
+
+ 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)) {
+ if (log_is_enabled(Debug, cds, heap)) {
+ log_debug(cds, heap)("bad nested element_value at %d", calc_num_values);
+ }
+ // propagate failure back to caller
+ return false;
+ }
+ }
+ } break;
+
+ default:
+ if (log_is_enabled(Debug, cds, heap)) {
+ log_debug(cds, heap)("bad tag=0x%x", 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/src/hotspot/share/services/heapRedactor.hpp b/src/hotspot/share/services/heapRedactor.hpp
new file mode 100644
index 000000000..790430507
--- /dev/null
+++ b/src/hotspot/share/services/heapRedactor.hpp
@@ -0,0 +1,204 @@
+/*
+ * Copyright (c) 2020, 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 SHARE_SERVICES_HEAPREDACTOR_HPP
+#define SHARE_SERVICES_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 {
+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() const {
+#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) {
+ 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;
+ }
+
+ 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, _annotation_value_table, true);
+#endif
+ if(val != NULL) {
+ return (T)val;
+ }
+ return NULL;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+private:
+ HeapDumpRedactLevel _redact_level;
+ RedactParams _redact_params;
+ bool _use_sys_params;
+ void* _redact_name_table;
+ void* _redact_rules_table;
+ void* _annotation_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();
+
+};
+#endif // SHARE_SERVICES_HEAPREDACTOR_HPP
\ No newline at end of file
diff --git a/src/hotspot/share/services/writeableFlags.cpp b/src/hotspot/share/services/writeableFlags.cpp
index f4a10efa4..3524371c2 100644
--- a/src/hotspot/share/services/writeableFlags.cpp
+++ b/src/hotspot/share/services/writeableFlags.cpp
@@ -235,6 +235,13 @@ JVMFlag::Error WriteableFlags::set_flag(const char* name, const void* value, JVM
JVMFlag* f = JVMFlag::find_flag(name);
if (f) {
+ if(VerifyRedactPassword) {
+ if(strcmp(name, "HeapDumpRedact") == 0 || strcmp(name, "RedactMap") == 0 || strcmp(name, "RedactMapFile") == 0
+ || strcmp(name, "RedactClassPath") == 0) {
+ err_msg.print("has no authority to reset redact params");
+ return JVMFlag::NON_WRITABLE;
+ }
+ }
// only writeable flags are allowed to be set
if (f->is_writeable()) {
return setter(f, value, origin, err_msg);
diff --git a/src/hotspot/share/utilities/growableArray.hpp b/src/hotspot/share/utilities/growableArray.hpp
index 90ec80154..fa078a349 100644
--- a/src/hotspot/share/utilities/growableArray.hpp
+++ b/src/hotspot/share/utilities/growableArray.hpp
@@ -99,6 +99,10 @@ public:
assert(length <= _len,"cannot increase length");
_len = length;
}
+#if INCLUDE_JBOOSTER
+ static ByteSize len_offset() { return byte_offset_of(GrowableArrayBase, _len); }
+ static ByteSize max_offset() { return byte_offset_of(GrowableArrayBase, _max); }
+#endif // INCLUDE_JBOOSTER
};
template <typename E> class GrowableArrayIterator;
@@ -125,6 +129,9 @@ protected:
public:
const static GrowableArrayView EMPTY;
+#if INCLUDE_JBOOSTER
+ static ByteSize data_offset() { return byte_offset_of(GrowableArrayView, _data); }
+#endif // INCLUDE_JBOOSTER
bool operator==(const GrowableArrayView& rhs) const {
if (_len != rhs._len)
diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/HSDB.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/HSDB.java
index 02b665123..5224cd3b9 100644
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/HSDB.java
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/HSDB.java
@@ -41,14 +41,15 @@ import sun.jvm.hotspot.gc.g1.*;
import sun.jvm.hotspot.gc.z.*;
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.Annotation;
import sun.jvm.hotspot.ui.tree.*;
import sun.jvm.hotspot.ui.classbrowser.*;
import sun.jvm.hotspot.utilities.*;
import sun.jvm.hotspot.utilities.Observable;
import sun.jvm.hotspot.utilities.Observer;
+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
@@ -994,7 +995,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 {
@@ -1004,14 +1005,14 @@ 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));
}
// 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();
@@ -1023,7 +1024,7 @@ public class HSDB implements ObjectHistogramPanel.Listener, SAListener {
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()));
}
@@ -1032,9 +1033,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();
@@ -1150,7 +1151,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/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/SALauncher.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/SALauncher.java
index b884058a3..291e483e0 100644
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/SALauncher.java
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/SALauncher.java
@@ -130,6 +130,10 @@ public class SALauncher {
System.out.println(" --histo To print histogram of java object heap.");
System.out.println(" --clstats To print class loader statistics.");
System.out.println(" --finalizerinfo To print information on objects awaiting finalization.");
+ System.out.println(" --HeapDumpRedact <basic|names|full|annotation||off> redact the heapdump information to remove sensitive data.");
+ System.out.println(" --RedactMap <name1:value1;name2:value2;...> Redact the class and field names to other strings.");
+ System.out.println(" --RedactMapFile <file> file path of the redact map.");
+ System.err.println(" --RedactClassPath <classpath> full path of the redact annotation");
return commonHelpWithConnect("jmap");
}
@@ -302,8 +306,8 @@ public class SALauncher {
jstack.runWithArgs(buildAttachArgs(newArgMap, false));
}
- private static void runJMAP(String[] oldArgs) {
- Map<String, String> longOptsMap = Map.ofEntries(
+ private static Map<String, String> getLongOptsMap() {
+ return Map.ofEntries(
Map.entry("exe=", "exe"),
Map.entry("core=", "core"),
Map.entry("pid=", "pid"),
@@ -314,13 +318,25 @@ public class SALauncher {
Map.entry("gz=", "gz"),
Map.entry("histo", "-histo"),
Map.entry("clstats", "-clstats"),
- Map.entry("finalizerinfo", "-finalizerinfo"));
+ Map.entry("finalizerinfo", "-finalizerinfo"),
+ Map.entry("HeapDumpRedact=", "HeapDumpRedact"),
+ Map.entry("RedactMap=", "RedactMap"),
+ Map.entry("RedactMapFile=", "RedactMapFile"),
+ Map.entry("RedactClassPath=", "RedactClassPath"));
+ }
+
+ private static void runJMAP(String[] oldArgs) {
+ Map<String, String> longOptsMap = getLongOptsMap();
Map<String, String> newArgMap = parseOptions(oldArgs, longOptsMap);
boolean requestHeapdump = newArgMap.containsKey("binaryheap");
String dumpfile = newArgMap.get("dumpfile");
String gzLevel = newArgMap.get("gz");
String command = "-heap:format=b";
+ String heapDumpRedact = newArgMap.get("HeapDumpRedact");
+ String redactMap = newArgMap.get("RedactMap");
+ String redactMapFile = newArgMap.get("RedactMapFile");
+ String redactClassPath = newArgMap.get("RedactClassPath");
if (!requestHeapdump && (dumpfile != null)) {
throw new IllegalArgumentException("Unexpected argument: dumpfile");
}
@@ -331,12 +347,28 @@ public class SALauncher {
if (dumpfile != null) {
command += ",file=" + dumpfile;
}
+ if (heapDumpRedact != null) {
+ command += ",HeapDumpRedact=" + heapDumpRedact;
+ }
+ if (redactMap != null) {
+ command += ",RedactMap=" + redactMap;
+ }
+ if (redactMapFile != null) {
+ command += ",RedactMapFile=" + redactMapFile;
+ }
+ if (redactClassPath != null) {
+ command += ",RedactClassPath=" + redactClassPath;
+ }
newArgMap.put(command, null);
}
newArgMap.remove("binaryheap");
newArgMap.remove("dumpfile");
newArgMap.remove("gz");
+ newArgMap.remove("HeapDumpRedact");
+ newArgMap.remove("RedactMap");
+ newArgMap.remove("RedactMapFile");
+ newArgMap.remove("RedactClassPath");
JMap.main(buildAttachArgs(newArgMap, false));
}
diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Annotation.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Annotation.java
new file mode 100644
index 000000000..643de2b60
--- /dev/null
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Annotation.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2000, 2020, 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 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;
+import sun.jvm.hotspot.utilities.Observable;
+import sun.jvm.hotspot.utilities.Observer;
+
+// 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);
+ }
+}
diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Field.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Field.java
index ee5cf63fe..6002e42fd 100644
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Field.java
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Field.java
@@ -68,6 +68,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; }
@@ -115,6 +117,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/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/InstanceKlass.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/InstanceKlass.java
index 268cedc41..03d6408bb 100644
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/InstanceKlass.java
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/InstanceKlass.java
@@ -89,6 +89,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);
sourceDebugExtension = type.getAddressField("_source_debug_extension");
innerClasses = type.getAddressField("_inner_classes");
@@ -168,6 +169,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 sourceDebugExtension;
private static AddressField innerClasses;
@@ -918,6 +920,11 @@ public class InstanceKlass extends Klass {
return (IntArray) VMObjectFactory.newObject(IntArray.class, addr);
}
+ public Annotation getAnnotation() {
+ Address addr = getAddress().getAddressAt(annotate.getOffset());
+ return VMObjectFactory.newObject(Annotation.class, addr);
+ }
+
public U2Array getFields() {
Address addr = getAddress().getAddressAt(fields.getOffset());
return (U2Array) VMObjectFactory.newObject(U2Array.class, addr);
diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/JMap.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/JMap.java
index 4c80ecd6c..e52cd1fb1 100644
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/JMap.java
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/JMap.java
@@ -78,6 +78,7 @@ public class JMap extends Tool {
private static String dumpfile = "heap.bin";
private static int gzLevel = 0;
+ private static HeapRedactor heapRedactor;
public void run() {
Tool tool = null;
@@ -123,6 +124,7 @@ public class JMap extends Tool {
public static void main(String[] args) {
int mode = MODE_PMAP;
+ HeapRedactor.RedactParams redactParams = new HeapRedactor.RedactParams();
if (args.length > 1 ) {
String modeFlag = args[0];
boolean copyArgs = true;
@@ -177,6 +179,16 @@ public class JMap extends Tool {
System.err.println("compression level out of range (1-9): " + level);
System.exit(1);
}
+ } else if (keyValue[0].equals("HeapDumpRedact")) {
+ if (!redactParams.setAndCheckHeapDumpRedact(keyValue[1])) {
+ System.exit(1);
+ }
+ } else if (keyValue[0].equals("RedactMap")) {
+ redactParams.setRedactMap(keyValue[1]);
+ } else if (keyValue[0].equals("RedactMapFile")) {
+ redactParams.setRedactMapFile(keyValue[1]);
+ } else if (keyValue[0].equals("RedactClassPath")) {
+ redactParams.setRedactClassPath(keyValue[1]);
} else {
System.err.println("unknown option:" + keyValue[0]);
@@ -189,12 +201,24 @@ public class JMap extends Tool {
}
}
+ 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");
+ copyArgs = false;
+ System.exit(1);
+ }
+ }
+
if (copyArgs) {
String[] newArgs = new String[args.length - 1];
for (int i = 0; i < newArgs.length; i++) {
newArgs[i] = args[i + 1];
}
args = newArgs;
+ heapRedactor = new HeapRedactor(redactParams);
}
}
@@ -204,7 +228,7 @@ public class JMap extends Tool {
public boolean writeHeapHprofBin(String fileName, int gzLevel) {
try {
- HeapGraphWriter hgw;
+ HeapHprofBinWriter hgw;
if (gzLevel == 0) {
hgw = new HeapHprofBinWriter();
} else if (gzLevel >=1 && gzLevel <= 9) {
@@ -213,6 +237,7 @@ public class JMap extends Tool {
System.err.println("Illegal compression level: " + gzLevel);
return false;
}
+ hgw.setHeapRedactor(heapRedactor);
hgw.write(fileName);
System.out.println("heap written to " + fileName);
return true;
diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/AnnotationArray2D.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/AnnotationArray2D.java
new file mode 100644
index 000000000..a93188567
--- /dev/null
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/AnnotationArray2D.java
@@ -0,0 +1,62 @@
+/*
+ * 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.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;
+ }
+}
diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/HeapHprofBinWriter.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/HeapHprofBinWriter.java
index c6c59fae8..e73b6f9a3 100644
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/HeapHprofBinWriter.java
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/HeapHprofBinWriter.java
@@ -386,6 +386,62 @@ public class HeapHprofBinWriter extends AbstractHeapGraphWriter {
private static final long MAX_U4_VALUE = 0xFFFFFFFFL;
int serialNum = 1;
+ // 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 Optional<String> lookupRedactName(String name){
+ return heapRedactor == null ? Optional.empty() : heapRedactor.lookupRedactName(name);
+ }
+
+ private void resetRedactParams(){
+ Optional<String> redactOption = getVMRedactParameter("HeapDumpRedact");
+ String redactStr= redactOption.isPresent() ? redactOption.get() : null;
+ if(redactStr!=null && !redactStr.isEmpty()){
+ HeapRedactor.RedactParams redactParams = new HeapRedactor.RedactParams();
+ if(HeapRedactor.REDACT_ANNOTATION_OPTION.equals(redactStr)){
+ Optional<String> classPathOption = getVMRedactParameter("RedactClassPath");
+ String classPathStr = classPathOption.isPresent() ? classPathOption.get() : null;
+ redactStr = classPathOption.isPresent() ? redactStr : HeapRedactor.REDACT_OFF_OPTION;
+ redactParams.setRedactClassPath(classPathStr);
+ } else {
+ Optional<String> redactMapOption = getVMRedactParameter("RedactMap");
+ String redactMapStr = redactMapOption.isPresent() ? redactMapOption.get() : null;
+ redactParams.setRedactMap(redactMapStr);
+ Optional<String> redactMapFileOption = getVMRedactParameter("RedactMapFile");
+ String redactMapFileStr = redactMapFileOption.isPresent() ? redactMapFileOption.get() : null;
+ redactParams.setRedactMapFile(redactMapFileStr);
+ }
+ if(!redactParams.setAndCheckHeapDumpRedact(redactStr)){
+ redactParams.setAndCheckHeapDumpRedact(HeapRedactor.REDACT_OFF_OPTION);
+ }
+ setHeapRedactor(new HeapRedactor(redactParams));
+ }
+ }
+
+ private Optional<String> getVMRedactParameter(String name) {
+ VM vm = VM.getVM();
+ VM.Flag flag = vm.getCommandLineFlag(name);
+ if(flag == null){
+ return Optional.empty();
+ }
+ return Optional.ofNullable(flag.getCcstr());
+ }
+
public HeapHprofBinWriter() {
this.KlassMap = new ArrayList<Klass>();
this.names = new HashSet<Symbol>();
@@ -401,6 +457,10 @@ public class HeapHprofBinWriter extends AbstractHeapGraphWriter {
public synchronized void write(String fileName) throws IOException {
VM vm = VM.getVM();
+ if(getHeapDumpRedactLevel() == HeapRedactor.HeapDumpRedactLevel.REDACT_UNKNOWN) {
+ resetRedactParams();
+ }
+
// Check whether we should dump the heap as segments
useSegmentedHeapDump = isCompression() ||
(vm.getUniverse().heap().used() > HPROF_SEGMENTED_HEAP_DUMP_THRESHOLD);
@@ -468,6 +528,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();
@@ -638,6 +701,68 @@ 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_BYTE) {
+ 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();
+ }
+
+ int headerSize = getArrayHeaderSize(false);
+ final String typeName = tak.getElementTypeName();
+ final long typeSize = getSizeForType(type);
+ final int length = calculateArrayMaxLength(expectLength,
+ headerSize,
+ typeSize,
+ typeName);
+ out.writeByte((byte) HPROF_GC_PRIM_ARRAY_DUMP);
+ writeObjectID(array);
+ out.writeInt(DUMMY_STACK_TRACE_ID);
+ out.writeInt(length);
+ out.writeByte((byte) type);
+
+ if (annotateValue != null) {
+ for(int index = 0; index < expectLength; index++) {
+ out.writeByte(annotateValue.charAt(index));
+ }
+ } else {
+ for (int index = 0; index < length; index++) {
+ long offset = BYTE_BASE_OFFSET + index * BYTE_SIZE;
+ out.writeByte(array.getHandle().getJByteAt(offset));
+ }
+ }
+ }
+
+ 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.
@@ -865,9 +990,18 @@ public class HeapHprofBinWriter extends AbstractHeapGraphWriter {
}
protected void writePrimitiveArray(TypeArray array) throws IOException {
+ HeapRedactor.HeapDumpRedactLevel level = getHeapDumpRedactLevel();
+
int headerSize = getArrayHeaderSize(false);
TypeArrayKlass tak = (TypeArrayKlass) array.getKlass();
final int type = (int) tak.getElementType();
+
+ if(type == TypeArrayKlass.T_BYTE && (level == HeapRedactor.HeapDumpRedactLevel.REDACT_ANNOTATION
+ || level == HeapRedactor.HeapDumpRedactLevel.REDACT_DIYRULES)) {
+ heapRedactor.recordTypeArray(array);
+ return;
+ }
+
final String typeName = tak.getElementTypeName();
final long typeSize = getSizeForType(type);
final int length = calculateArrayMaxLength(array.getLength(),
@@ -879,12 +1013,16 @@ public class HeapHprofBinWriter extends AbstractHeapGraphWriter {
out.writeInt(DUMMY_STACK_TRACE_ID);
out.writeInt(length);
out.writeByte((byte) type);
+
+ boolean shouldRedact = ( level == HeapRedactor.HeapDumpRedactLevel.REDACT_BASIC
+ || level == HeapRedactor.HeapDumpRedactLevel.REDACT_FULL);
+
switch (type) {
case TypeArrayKlass.T_BOOLEAN:
writeBooleanArray(array, length);
break;
case TypeArrayKlass.T_CHAR:
- writeCharArray(array, length);
+ writeCharArray(array, length, shouldRedact);
break;
case TypeArrayKlass.T_FLOAT:
writeFloatArray(array, length);
@@ -893,13 +1031,13 @@ public class HeapHprofBinWriter extends AbstractHeapGraphWriter {
writeDoubleArray(array, length);
break;
case TypeArrayKlass.T_BYTE:
- writeByteArray(array, length);
+ writeByteArray(array, length, shouldRedact);
break;
case TypeArrayKlass.T_SHORT:
writeShortArray(array, length);
break;
case TypeArrayKlass.T_INT:
- writeIntArray(array, length);
+ writeIntArray(array, length, shouldRedact);
break;
case TypeArrayKlass.T_LONG:
writeLongArray(array, length);
@@ -917,10 +1055,16 @@ public class HeapHprofBinWriter extends AbstractHeapGraphWriter {
}
}
- private void writeByteArray(TypeArray array, int length) throws IOException {
- for (int index = 0; index < length; index++) {
- long offset = BYTE_BASE_OFFSET + index * BYTE_SIZE;
- out.writeByte(array.getHandle().getJByteAt(offset));
+ private void writeByteArray(TypeArray array, int length, boolean shouldRedact) throws IOException {
+ if (shouldRedact) {
+ for(int index = 0; index < length; index++) {
+ out.writeByte(0);
+ }
+ } else {
+ for (int index = 0; index < length; index++) {
+ long offset = BYTE_BASE_OFFSET + index * BYTE_SIZE;
+ out.writeByte(array.getHandle().getJByteAt(offset));
+ }
}
}
@@ -931,10 +1075,16 @@ public class HeapHprofBinWriter extends AbstractHeapGraphWriter {
}
}
- private void writeIntArray(TypeArray array, int length) throws IOException {
- for (int index = 0; index < length; index++) {
- long offset = INT_BASE_OFFSET + index * INT_SIZE;
- out.writeInt(array.getHandle().getJIntAt(offset));
+ private void writeIntArray(TypeArray array, int length, boolean shouldRedact) throws IOException {
+ if (shouldRedact) {
+ for(int index = 0; index < length; index++) {
+ out.writeInt(0);
+ }
+ } else {
+ for (int index = 0; index < length; index++) {
+ long offset = INT_BASE_OFFSET + index * INT_SIZE;
+ out.writeInt(array.getHandle().getJIntAt(offset));
+ }
}
}
@@ -945,10 +1095,16 @@ public class HeapHprofBinWriter extends AbstractHeapGraphWriter {
}
}
- private void writeCharArray(TypeArray array, int length) throws IOException {
- for (int index = 0; index < length; index++) {
- long offset = CHAR_BASE_OFFSET + index * CHAR_SIZE;
- out.writeChar(array.getHandle().getJCharAt(offset));
+ private void writeCharArray(TypeArray array, int length, boolean shouldRedact) throws IOException {
+ if (shouldRedact) {
+ for(int index = 0; index < length; index++) {
+ out.writeChar(0);
+ }
+ } else {
+ for (int index = 0; index < length; index++) {
+ long offset = CHAR_BASE_OFFSET + index * CHAR_SIZE;
+ out.writeChar(array.getHandle().getJCharAt(offset));
+ }
}
}
@@ -990,6 +1146,16 @@ public class HeapHprofBinWriter extends AbstractHeapGraphWriter {
for (Iterator<Field> itr = fields.iterator(); itr.hasNext();) {
writeField(itr.next(), instance);
}
+
+ // 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
@@ -1012,6 +1178,131 @@ 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.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 {
+ Symbol filedNameSymbol = field.getName();
+ if(filedNameSymbol == null) {
+ continue;
+ }
+
+ String filedName = filedNameSymbol.asString();
+ 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:
@@ -1128,7 +1419,18 @@ public class HeapHprofBinWriter extends AbstractHeapGraphWriter {
// If name is already written don't write it again.
if (names.add(sym)) {
if(sym != null) {
- 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){
+ Optional<String> redactFiled = lookupRedactName(symbolStr);
+ buf = redactFiled.isPresent() ? redactFiled.get().getBytes("UTF-8") : null;
+ }
+ if(buf == null){
+ buf = symbolStr.getBytes("UTF-8");
+ }
writeHeader(HPROF_UTF8, buf.length + OBJ_ID_SIZE);
writeSymbolID(sym);
out.write(buf);
@@ -1208,11 +1510,23 @@ public class HeapHprofBinWriter extends AbstractHeapGraphWriter {
List<Field> res = new ArrayList<>();
while (klass != null) {
List<Field> 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<Field> itr = curFields.iterator(); itr.hasNext();) {
Field f = itr.next();
if (! f.isStatic()) {
res.add(f);
+ // record annotation for class Field
+ if(hasAnnotations) {
+ f.setFieldAnnotations(fieldsAnnotations.getAt(fieldIndex));
+ }
}
+ fieldIndex++;
}
klass = (InstanceKlass) klass.getSuper();
}
diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/HeapRedactor.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/HeapRedactor.java
new file mode 100644
index 000000000..c2a916617
--- /dev/null
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/HeapRedactor.java
@@ -0,0 +1,446 @@
+/*
+ * 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.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Locale;
+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 Optional<String> lookupRedactName(String name){
+ return Optional.ofNullable(redactNameTable == null ? null : 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.ofNullable(redactClassTable == null ? null: redactClassTable.get(key));
+ }
+
+ private HeapDumpRedactLevel initHeapdumpRedactLevel(String options) {
+ RedactParams customizedParams = parseRedactOptions(options);
+
+ if (customizedParams.isEnableRedact() || this.redactParams == null) {
+ this.redactParams = customizedParams;
+ }
+
+ if (redactParams.heapDumpRedact == null) {
+ redactLevel = HeapDumpRedactLevel.REDACT_UNKNOWN;
+ } 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 {
+ continue;
+ }
+ }
+ }
+ 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
+ return;
+ } 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;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/jdk.jcmd/share/classes/sun/tools/jmap/JMap.java b/src/jdk.jcmd/share/classes/sun/tools/jmap/JMap.java
index c59da64e6..ef4ea7152 100644
--- a/src/jdk.jcmd/share/classes/sun/tools/jmap/JMap.java
+++ b/src/jdk.jcmd/share/classes/sun/tools/jmap/JMap.java
@@ -25,11 +25,17 @@
package sun.tools.jmap;
+import java.io.Console;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+import java.security.MessageDigest;
+import java.util.Arrays;
import java.util.Collection;
+import java.util.regex.Pattern;
import com.sun.tools.attach.VirtualMachine;
import com.sun.tools.attach.VirtualMachineDescriptor;
@@ -210,6 +216,8 @@ public class JMap {
String filename = null;
String liveopt = "-all";
String compress_level = null;
+ RedactParams redactParams = new RedactParams();
+ String redactPassword = ",RedactPassword=";
for (int i = 0; i < subopts.length; i++) {
String subopt = subopts[i];
@@ -231,6 +239,18 @@ public class JMap {
System.err.println("Fail: no number provided in option: '" + subopt + "'");
usage(1);
}
+ } else if (subopt.startsWith("HeapDumpRedact=")) {
+ if (!redactParams.setAndCheckHeapDumpRedact(subopt.substring("HeapDumpRedact=".length()))) {
+ usage(1);
+ }
+ } else if (subopt.startsWith("RedactMap=")) {
+ redactParams.setRedactMap(subopt.substring("RedactMap=".length()));
+ } else if (subopt.startsWith("RedactMapFile=")) {
+ redactParams.setRedactMapFile(subopt.substring("RedactMapFile=".length()));
+ } else if (subopt.startsWith("RedactClassPath")) {
+ redactParams.setRedactClassPath(subopt.substring("RedactClassPath=".length()));
+ } else if (subopt.startsWith("RedactPassword")) {
+ redactPassword = getRedactPassword();
} else {
System.err.println("Fail: invalid option: '" + subopt + "'");
usage(1);
@@ -242,10 +262,78 @@ public class JMap {
usage(1);
}
+ checkRedactParams(redactParams);
+
System.out.flush();
// dumpHeap is not the same as jcmd GC.heap_dump
- executeCommandForPid(pid, "dumpheap", filename, liveopt, compress_level);
+ String heapDumpRedactParams = redactParams.isEnableRedact() ? ";" + redactParams.toDumpArgString() + redactPassword : "";
+ executeCommandForPid(pid, "dumpheap", filename + heapDumpRedactParams, liveopt, compress_level);
+ }
+
+ private static void checkRedactParams(RedactParams redactParams) {
+ if (redactParams.getHeapDumpRedact() == null) {
+ if (redactParams.getRedactMap() == null && redactParams.getRedactMapFile() == null) {
+ redactParams.setEnableRedact(false);
+ } else {
+ System.err.println("Error: HeapDumpRedact must be specified to enable heap-dump-redacting");
+ usage(1);
+ }
+ }
+ }
+
+ private static String getRedactPassword() {
+ String redactPassword = ",RedactPassword=";
+ // heap dump may need a password
+ 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 digestStr = null;
+ byte[] passwordBytes = null;
+ try {
+ CharBuffer cb = CharBuffer.wrap(passwords);
+ String passwordPattern = "^[0-9a-zA-Z!@#$]{1,9}$";
+ if(!Pattern.matches(passwordPattern, cb)) {
+ return redactPassword;
+ }
+ Charset cs = Charset.forName("UTF-8");
+ passwordBytes= cs.encode(cb).array();
+
+ 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(passwords != null) {
+ Arrays.fill(passwords, '0');
+ }
+ if(passwordBytes != null) {
+ Arrays.fill(passwordBytes, (byte) 0);
+ }
+ }
+
+ redactPassword += (digestStr == null ? "" : digestStr);
+ return redactPassword;
}
private static void checkForUnsupportedOptions(String[] args) {
@@ -312,6 +400,12 @@ public class JMap {
System.err.println(" file=<file> dump heap to <file>");
System.err.println(" gz=<number> If specified, the heap dump is written in gzipped format using the given compression level.");
System.err.println(" 1 (recommended) is the fastest, 9 the strongest compression.");
+ System.err.println(" HeapDumpRedact=<basic|names|full|diyrules|annotation|off> redact the heapdump information to remove sensitive data,");
+ System.err.println(" RedactMap=<name1:value1;name2:value2;...> Redact the class and 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, will wait for a password, ");
+ System.err.println(" without a correct password, heap dump with default redact level");
System.err.println("");
System.err.println(" Example: jmap -dump:live,format=b,file=heap.bin <pid>");
System.err.println("");
@@ -327,4 +421,112 @@ public class JMap {
System.err.println(" Example: jmap -histo:live,file=/tmp/histo.data <pid>");
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