--- 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* 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::length_offset_in_bytes(), + Array::base_offset_in_bytes(), + + in_bytes(GrowableArrayBase::len_offset()), + in_bytes(GrowableArrayBase::max_offset()), + in_bytes(GrowableArrayView
::data_offset()) +}; + class KlassGetAllInstanceKlassesClosure: public KlassClosure { GrowableArray* _klasses; GrowableArray* _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* 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 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 KlassHashtableEntry; nonstatic_field(Symbol, _body[0], u1) \ nonstatic_field(TypeArrayKlass, _max_length, jint) \ nonstatic_field(OopHandle, _obj, oop*) \ - \ + nonstatic_field(Annotations, _class_annotations, Array*) \ + nonstatic_field(Annotations, _class_type_annotations, Array*) \ + nonstatic_field(Annotations, _fields_annotations, Array*>*) \ + nonstatic_field(Annotations, _fields_type_annotations, Array*>*) \ + \ /***********************/ \ /* Constant Pool Cache */ \ /***********************/ \ @@ -509,7 +513,9 @@ typedef HashtableEntry KlassHashtableEntry; \ nonstatic_field(Array, _length, int) \ nonstatic_field(Array, _data[0], Klass*) \ - \ + nonstatic_field(Array*>, _length, int) \ + nonstatic_field(Array*>, _data[0], Array*) \ + \ /*******************/ \ /* GrowableArrays */ \ /*******************/ \ @@ -1056,7 +1062,8 @@ typedef HashtableEntry KlassHashtableEntry; unchecked_nonstatic_field(Array, _data, sizeof(u2)) \ unchecked_nonstatic_field(Array, _data, sizeof(Method*)) \ unchecked_nonstatic_field(Array, _data, sizeof(Klass*)) \ - \ + unchecked_nonstatic_field(Array*>, _data, sizeof(Array*)) \ + \ /*********************************/ \ /* java_lang_Class fields */ \ /*********************************/ \ @@ -1261,6 +1268,7 @@ typedef HashtableEntry 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 KlassHashtableEntry; declare_type(Array, MetaspaceObj) \ declare_type(Array, MetaspaceObj) \ declare_type(Array, MetaspaceObj) \ + declare_type(Array*>, 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(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
(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
(array); + Symbol* anonymous_value_symbol = writer->heapRedactor()->lookup_replace_value(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
(array); + char* anonymous_value = writer->heapRedactor()->lookup_replace_value(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(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= 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(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(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 + 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 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 redact the heapdump information to remove sensitive data."); + System.out.println(" --RedactMap Redact the class and field names to other strings."); + System.out.println(" --RedactMapFile file path of the redact map."); + System.err.println(" --RedactClassPath 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 longOptsMap = Map.ofEntries( + private static Map 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 longOptsMap = getLongOptsMap(); Map 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*"); + + Type type = db.lookupType("Array*>"); + dataFieldOffset = type.getAddressField("_data").getOffset(); + } + + private static long dataFieldOffset; + protected static Type elemType; + + public AnnotationArray2D(Address addr) { + super(addr, dataFieldOffset); + } + + public U1Array getAt(int i) { + return new U1Array(getAddressAt(i)); + } + public Type getElemType() { + return elemType; + } +} 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 lookupRedactName(String name){ + return heapRedactor == null ? Optional.empty() : heapRedactor.lookupRedactName(name); + } + + private void resetRedactParams(){ + Optional 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 classPathOption = getVMRedactParameter("RedactClassPath"); + String classPathStr = classPathOption.isPresent() ? classPathOption.get() : null; + redactStr = classPathOption.isPresent() ? redactStr : HeapRedactor.REDACT_OFF_OPTION; + redactParams.setRedactClassPath(classPathStr); + } else { + Optional redactMapOption = getVMRedactParameter("RedactMap"); + String redactMapStr = redactMapOption.isPresent() ? redactMapOption.get() : null; + redactParams.setRedactMap(redactMapStr); + Optional 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 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(); this.names = new HashSet(); @@ -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 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 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 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 fields, Instance instance) { + Klass klass = instance.getKlass(); + boolean inJavaPackage = false; + Symbol classNameSymbol = klass.getName(); + if(classNameSymbol != null) { + String className = classNameSymbol.asString(); + inJavaPackage = (className != null && className.startsWith("java/")); + } + if(inJavaPackage){ + return; + } + for (Field field : fields) { + Symbol fieldSignature = field.getSignature(); + if(fieldSignature == null || fieldSignature.asString() == null + || !"Ljava/lang/String;".equals(fieldSignature.asString())) { + continue; + } + try { + InstanceKlass fieldHolder = field.getFieldHolder(); + U1Array fieldAnnotations = field.getFieldAnnotations(); + Optional anonymousValueOption = getAnonymousValue(fieldAnnotations, fieldHolder.getConstants()); + if(!anonymousValueOption.isPresent()) { + continue; + } + long address = getStringFieldAddress(field, instance); + if(address > 0L) { + heapRedactor.recordRedactAnnotationValue(address, anonymousValueOption.get()); + } + } catch (Exception e) { + } + } + } + + private Optional getAnonymousValue(U1Array fieldAnnotations, ConstantPool cp) { + Optional anonymousValueOption = Optional.empty(); + if (fieldAnnotations.getAddress() == null) { + return anonymousValueOption; + } + + int fieldAnnotationsTagsLen = fieldAnnotations.length(); + boolean isAnonymousAnnotation = false; + int annotationStart = 0; + int annotationEnd = 0; + for (int j = 0; j < fieldAnnotationsTagsLen; j++) { + int cpIndex = fieldAnnotations.at(j); + if (cpIndex >= cp.getLength() || cpIndex < 0) { + continue; + } + byte cpConstType = cp.getTags().at(cpIndex); + if (cpConstType == ConstantPool.JVM_CONSTANT_Utf8) { + annotationStart += (isAnonymousAnnotation ? 0 : 1); + annotationEnd++; + Symbol symbol = cp.getSymbolAt(cpIndex); + if (symbol.asString() == null || symbol.asString().isEmpty()) { + continue; + } + if (symbol.asString().equals("L" + heapRedactor.getRedactAnnotationClassPath() + ";")) { + isAnonymousAnnotation = true; + } + if(annotationEnd - annotationStart == 1 && !"value".equals(symbol.asString())) { + break; + } + if(annotationEnd - annotationStart == 2) { + anonymousValueOption = Optional.ofNullable(cp.getSymbolAt(cpIndex).asString()); + break; + } + } + } + return anonymousValueOption; + } + + private void recordDiyRulesValueMap(List fields, Instance instance) { + Klass klass = instance.getKlass(); + boolean diyRulesFlag = false; + Symbol classNameSymbol = klass.getName(); + Map redactRulesMap = null; + if(classNameSymbol != null) { + String className = classNameSymbol.asString(); + Optional> 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 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 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 res = new ArrayList<>(); while (klass != null) { List curFields = klass.getImmediateFields(); + Annotation annotation = klass.getAnnotation(); + AnnotationArray2D fieldsAnnotations = (annotation == null) ? null : annotation.getFieldsAnnotations(); + boolean hasAnnotations = false; + if(fieldsAnnotations != null && fieldsAnnotations.getAddress() != null) { + hasAnnotations = true; + } + int fieldIndex = 0; for (Iterator itr = curFields.iterator(); itr.hasNext();) { Field f = 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 redactNameTable; + private Map> redactClassTable; + private String redactClassFullName = null; + private Map 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 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 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 lookupRedactAnnotationValue(Long addr){ + return Optional.ofNullable(redactValueTable == null ? null : redactValueTable.get(addr)); + } + + public String getRedactAnnotationClassPath(){ + return redactParams.getRedactClassPath(); + } + + public Optional> 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 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= 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 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 typeArrayList; + private RedactVectorNode next; + private int currentIndex; + + public List getTypeArrayList() { + return typeArrayList; + } + + public void setTypeArrayList(List 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= dump heap to "); System.err.println(" gz= 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= redact the heapdump information to remove sensitive data,"); + System.err.println(" RedactMap= Redact the class and field names to other strings"); + System.err.println(" RedactMapFile= file path of the redact map"); + System.err.println(" RedactClassPath= 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 "); System.err.println(""); @@ -327,4 +421,112 @@ public class JMap { System.err.println(" Example: jmap -histo:live,file=/tmp/histo.data "); 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