--- old/src/hotspot/share/classfile/classLoaderData.cpp 2019-09-26 22:29:51.498950906 +0800 +++ new/src/hotspot/share/classfile/classLoaderData.cpp 2019-09-26 22:29:51.350945752 +0800 @@ -1276,7 +1276,7 @@ } } -void ClassLoaderDataGraph::print_dictionary_statistics(outputStream* st) { +void ClassLoaderDataGraph::print_table_statistics(outputStream* st) { FOR_ALL_DICTIONARY(cld) { ResourceMark rm; stringStream tempst; --- old/src/hotspot/share/classfile/classLoaderData.hpp 2019-09-26 22:29:52.178974587 +0800 +++ new/src/hotspot/share/classfile/classLoaderData.hpp 2019-09-26 22:29:52.035969607 +0800 @@ -133,7 +133,7 @@ static void verify_dictionary(); static void print_dictionary(outputStream* st); - static void print_dictionary_statistics(outputStream* st); + static void print_table_statistics(outputStream* st); // CMS support. static void remember_new_clds(bool remember) { _saved_head = (remember ? _head : NULL); } --- old/src/hotspot/share/classfile/stringTable.cpp 2019-09-26 22:29:52.858998269 +0800 +++ new/src/hotspot/share/classfile/stringTable.cpp 2019-09-26 22:29:52.711993150 +0800 @@ -636,6 +636,13 @@ }; }; +TableStatistics StringTable::get_table_statistics() { + static TableStatistics ts; + SizeFunc sz; + ts = _local_table->statistics_get(Thread::current(), sz, ts); + return ts; +} + void StringTable::print_table_statistics(outputStream* st, const char* table_name) { SizeFunc sz; --- old/src/hotspot/share/classfile/stringTable.hpp 2019-09-26 22:29:53.534021776 +0800 +++ new/src/hotspot/share/classfile/stringTable.hpp 2019-09-26 22:29:53.387016657 +0800 @@ -101,6 +101,7 @@ // The string table static StringTable* the_table() { return _the_table; } size_t table_size(Thread* thread = NULL); + TableStatistics get_table_statistics(); static OopStorage* weak_storage() { return the_table()->_weak_handles; } --- old/src/hotspot/share/classfile/symbolTable.cpp 2019-09-26 22:29:54.204045110 +0800 +++ new/src/hotspot/share/classfile/symbolTable.cpp 2019-09-26 22:29:54.058040025 +0800 @@ -452,6 +452,10 @@ return table->basic_add(index, (u1*)name, (int)strlen(name), hash, false, THREAD); } +TableStatistics SymbolTable::get_table_statistics() { + return the_table()->statistics_calculate(); +} + Symbol* SymbolTable::basic_add(int index_arg, u1 *name, int len, unsigned int hashValue_arg, bool c_heap, TRAPS) { assert(!Universe::heap()->is_in_reserved(name), --- old/src/hotspot/share/classfile/symbolTable.hpp 2019-09-26 22:29:54.904069488 +0800 +++ new/src/hotspot/share/classfile/symbolTable.hpp 2019-09-26 22:29:54.757064368 +0800 @@ -168,6 +168,7 @@ // The symbol table static SymbolTable* the_table() { return _the_table; } + TableStatistics get_table_statistics(); // Size of one bucket in the string table. Used when checking for rollover. static uint bucket_size() { return sizeof(HashtableBucket); } --- old/src/hotspot/share/classfile/systemDictionary.cpp 2019-09-26 22:29:55.572092752 +0800 +++ new/src/hotspot/share/classfile/systemDictionary.cpp 2019-09-26 22:29:55.423087562 +0800 @@ -2936,13 +2936,25 @@ if (shared_dictionary() != NULL) { shared_dictionary()->print_table_statistics(st, "Shared Dictionary"); } - ClassLoaderDataGraph::print_dictionary_statistics(st); + ClassLoaderDataGraph::print_table_statistics(st); placeholders()->print_table_statistics(st, "Placeholder Table"); constraints()->print_table_statistics(st, "LoaderConstraints Table"); - _pd_cache_table->print_table_statistics(st, "ProtectionDomainCache Table"); + pd_cache_table()->print_table_statistics(st, "ProtectionDomainCache Table"); } } +TableStatistics SystemDictionary::placeholders_statistics() { + return placeholders()->statistics_calculate(); +} + +TableStatistics SystemDictionary::loader_constraints_statistics() { + return constraints()->statistics_calculate(); +} + +TableStatistics SystemDictionary::protection_domain_cache_statistics() { + return pd_cache_table()->statistics_calculate(); +} + // Utility for dumping dictionaries. SystemDictionaryDCmd::SystemDictionaryDCmd(outputStream* output, bool heap) : DCmdWithParser(output, heap), --- old/src/hotspot/share/classfile/systemDictionary.hpp 2019-09-26 22:29:56.266116920 +0800 +++ new/src/hotspot/share/classfile/systemDictionary.hpp 2019-09-26 22:29:56.120111836 +0800 @@ -376,6 +376,9 @@ // System loader lock static oop system_loader_lock() { return _system_loader_lock_obj; } + // Protection Domain Table + static ProtectionDomainCacheTable* pd_cache_table() { return _pd_cache_table; } + public: // Sharing support. static void reorder_dictionary_for_sharing() NOT_CDS_RETURN; @@ -730,6 +733,11 @@ static oop _java_platform_loader; static bool _has_checkPackageAccess; + +public: + static TableStatistics placeholders_statistics(); + static TableStatistics loader_constraints_statistics(); + static TableStatistics protection_domain_cache_statistics(); }; #endif // SHARE_VM_CLASSFILE_SYSTEMDICTIONARY_HPP --- old/src/hotspot/share/jfr/metadata/metadata.xml 2019-09-26 22:29:56.959141055 +0800 +++ new/src/hotspot/share/jfr/metadata/metadata.xml 2019-09-26 22:29:56.811135901 +0800 @@ -695,6 +695,66 @@ description="Total size of all allocated metaspace blocks for anonymous classes (each chunk has several blocks)" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + --- old/src/hotspot/share/jfr/periodic/jfrPeriodic.cpp 2019-09-26 22:29:57.647165015 +0800 +++ new/src/hotspot/share/jfr/periodic/jfrPeriodic.cpp 2019-09-26 22:29:57.500159896 +0800 @@ -26,6 +26,9 @@ #include "jvm.h" #include "classfile/classLoaderStats.hpp" #include "classfile/javaClasses.hpp" +#include "classfile/stringTable.hpp" +#include "classfile/symbolTable.hpp" +#include "classfile/systemDictionary.hpp" #include "code/codeCache.hpp" #include "compiler/compileBroker.hpp" #include "gc/g1/g1HeapRegionEventSender.hpp" @@ -511,6 +514,46 @@ VMThread::execute(&op); } +template +static void emit_table_statistics(TableStatistics statistics) { + EVENT event; + event.set_bucketCount(statistics._number_of_buckets); + event.set_entryCount(statistics._number_of_entries); + event.set_totalFootprint(statistics._total_footprint); + event.set_bucketCountMaximum(statistics._maximum_bucket_size); + event.set_bucketCountAverage(statistics._average_bucket_size); + event.set_bucketCountVariance(statistics._variance_of_bucket_size); + event.set_bucketCountStandardDeviation(statistics._stddev_of_bucket_size); + event.set_insertionRate(statistics._add_rate); + event.set_removalRate(statistics._remove_rate); + event.commit(); +} + +TRACE_REQUEST_FUNC(SymbolTableStatistics) { + TableStatistics statistics = SymbolTable::the_table()->get_table_statistics(); + emit_table_statistics(statistics); +} + +TRACE_REQUEST_FUNC(StringTableStatistics) { + TableStatistics statistics = StringTable::the_table()->get_table_statistics(); + emit_table_statistics(statistics); +} + +TRACE_REQUEST_FUNC(PlaceholderTableStatistics) { + TableStatistics statistics = SystemDictionary::placeholders_statistics(); + emit_table_statistics(statistics); +} + +TRACE_REQUEST_FUNC(LoaderConstraintsTableStatistics) { + TableStatistics statistics = SystemDictionary::loader_constraints_statistics(); + emit_table_statistics(statistics); +} + +TRACE_REQUEST_FUNC(ProtectionDomainCacheTableStatistics) { + TableStatistics statistics = SystemDictionary::protection_domain_cache_statistics(); + emit_table_statistics(statistics); +} + TRACE_REQUEST_FUNC(CompilerStatistics) { EventCompilerStatistics event; event.set_compileCount(CompileBroker::get_total_compile_count()); --- old/src/hotspot/share/memory/allocation.hpp 2019-09-26 22:29:58.319188418 +0800 +++ new/src/hotspot/share/memory/allocation.hpp 2019-09-26 22:29:58.174183368 +0800 @@ -131,6 +131,7 @@ mtTest, // Test type for verifying NMT mtTracing, // memory used for Tracing mtLogging, // memory for logging + mtStatistics, // memory for statistics mtArguments, // memory for argument processing mtModule, // memory for module processing mtSynchronizer, // memory for synchronization primitives --- old/src/hotspot/share/utilities/concurrentHashTable.hpp 2019-09-26 22:29:58.999212099 +0800 +++ new/src/hotspot/share/utilities/concurrentHashTable.hpp 2019-09-26 22:29:58.849206876 +0800 @@ -25,6 +25,8 @@ #ifndef SHARE_UTILITIES_CONCURRENT_HASH_TABLE_HPP #define SHARE_UTILITIES_CONCURRENT_HASH_TABLE_HPP +#include "utilities/tableStatistics.hpp" + // A mostly concurrent-hash-table where the read-side is wait-free, inserts are // CAS and deletes mutual exclude each other on per bucket-basis. VALUE is the // type kept inside each Node and CONFIG contains hash and allocation methods. @@ -373,6 +375,7 @@ size_t grow_hint = DEFAULT_GROW_HINT); ~ConcurrentHashTable(); + TableRateStatistics _stats_rate; size_t get_size_log2(Thread* thread); size_t get_node_size() const { return sizeof(Node); } @@ -478,6 +481,15 @@ template void bulk_delete(Thread* thread, EVALUATE_FUNC& eval_f, DELETE_FUNC& del_f); + // Calcuate statistics. Item sizes are calculated with VALUE_SIZE_FUNC. + template + TableStatistics statistics_calculate(Thread* thread, VALUE_SIZE_FUNC& vs_f); + + // Gets statistics if available, if not return old one. Item sizes are calculated with + // VALUE_SIZE_FUNC. + template + TableStatistics statistics_get(Thread* thread, VALUE_SIZE_FUNC& vs_f, TableStatistics old); + // Writes statistics to the outputStream. Item sizes are calculated with // VALUE_SIZE_FUNC. template --- old/src/hotspot/share/utilities/concurrentHashTable.inline.hpp 2019-09-26 22:29:59.680235816 +0800 +++ new/src/hotspot/share/utilities/concurrentHashTable.inline.hpp 2019-09-26 22:29:59.533230697 +0800 @@ -480,6 +480,7 @@ GlobalCounter::write_synchronize(); delete_f(rem_n->value()); Node::destroy_node(rem_n); + JFR_ONLY(_stats_rate.remove();) return true; } @@ -528,6 +529,7 @@ for (size_t node_it = 0; node_it < nd; node_it++) { del_f(ndel[node_it]->value()); Node::destroy_node(ndel[node_it]); + JFR_ONLY(_stats_rate.remove();) DEBUG_ONLY(ndel[node_it] = (Node*)POISON_PTR;) } GlobalCounter::critical_section_begin(thread); @@ -564,6 +566,7 @@ GlobalCounter::write_synchronize(); for (size_t node_it = 0; node_it < dels; node_it++) { Node::destroy_node(ndel[node_it]); + JFR_ONLY(_stats_rate.remove();) DEBUG_ONLY(ndel[node_it] = (Node*)POISON_PTR;) } } @@ -898,6 +901,7 @@ new_node->set_next(first_at_start); } if (bucket->cas_first(new_node, first_at_start)) { + JFR_ONLY(_stats_rate.add();) callback(true, new_node->value()); new_node = NULL; ret = true; @@ -1004,6 +1008,7 @@ _size_limit_reached(false), _resize_lock_owner(NULL), _invisible_epoch(0) { + _stats_rate = TableRateStatistics(); _resize_lock = new Mutex(Mutex::leaf, "ConcurrentHashTable", false, Monitor::_safepoint_check_never); @@ -1087,6 +1092,7 @@ if (!bucket->cas_first(new_node, bucket->first())) { assert(false, "bad"); } + JFR_ONLY(_stats_rate.add();) return true; } @@ -1141,24 +1147,18 @@ template template -inline void ConcurrentHashTable:: - statistics_to(Thread* thread, VALUE_SIZE_FUNC& vs_f, - outputStream* st, const char* table_name) +inline TableStatistics ConcurrentHashTable:: + statistics_calculate(Thread* thread, VALUE_SIZE_FUNC& vs_f) { NumberSeq summary; size_t literal_bytes = 0; - if (!try_resize_lock(thread)) { - st->print_cr("statistics unavailable at this moment"); - return; - } - InternalTable* table = get_table(); for (size_t bucket_it = 0; bucket_it < table->_size; bucket_it++) { ScopedCS cs(thread, this); size_t count = 0; Bucket* bucket = table->get_bucket(bucket_it); if (bucket->have_redirect() || bucket->is_locked()) { - continue; + continue; } Node* current_node = bucket->first(); while (current_node != NULL) { @@ -1169,37 +1169,39 @@ summary.add((double)count); } - double num_buckets = summary.num(); - double num_entries = summary.sum(); + return TableStatistics(_stats_rate, summary, literal_bytes, sizeof(Bucket), sizeof(Node)); +} + +template +template +inline TableStatistics ConcurrentHashTable:: + statistics_get(Thread* thread, VALUE_SIZE_FUNC& vs_f, TableStatistics old) +{ + if (!try_resize_lock(thread)) { + return old; + } + + TableStatistics ts = statistics_calculate(thread, vs_f); + unlock_resize_lock(thread); + + return ts; +} + +template +template +inline void ConcurrentHashTable:: + statistics_to(Thread* thread, VALUE_SIZE_FUNC& vs_f, + outputStream* st, const char* table_name) +{ + if (!try_resize_lock(thread)) { + st->print_cr("statistics unavailable at this moment"); + return; + } - size_t bucket_bytes = num_buckets * sizeof(Bucket); - size_t entry_bytes = num_entries * sizeof(Node); - size_t total_bytes = literal_bytes + bucket_bytes + entry_bytes; - - size_t bucket_size = (num_buckets <= 0) ? 0 : (bucket_bytes / num_buckets); - size_t entry_size = (num_entries <= 0) ? 0 : (entry_bytes / num_entries); - - st->print_cr("%s statistics:", table_name); - st->print_cr("Number of buckets : %9" PRIuPTR " = %9" PRIuPTR - " bytes, each " SIZE_FORMAT, - (size_t)num_buckets, bucket_bytes, bucket_size); - st->print_cr("Number of entries : %9" PRIuPTR " = %9" PRIuPTR - " bytes, each " SIZE_FORMAT, - (size_t)num_entries, entry_bytes, entry_size); - if (literal_bytes != 0) { - double literal_avg = (num_entries <= 0) ? 0 : (literal_bytes / num_entries); - st->print_cr("Number of literals : %9" PRIuPTR " = %9" PRIuPTR - " bytes, avg %7.3f", - (size_t)num_entries, literal_bytes, literal_avg); - } - st->print_cr("Total footprsize_t : %9s = %9" PRIuPTR " bytes", "" - , total_bytes); - st->print_cr("Average bucket size : %9.3f", summary.avg()); - st->print_cr("Variance of bucket size : %9.3f", summary.variance()); - st->print_cr("Std. dev. of bucket size: %9.3f", summary.sd()); - st->print_cr("Maximum bucket size : %9" PRIuPTR, - (size_t)summary.maximum()); + TableStatistics ts = statistics_calculate(thread, vs_f); unlock_resize_lock(thread); + + ts.print(st, table_name); } template --- old/src/hotspot/share/utilities/hashtable.cpp 2019-09-26 22:30:00.363259602 +0800 +++ new/src/hotspot/share/utilities/hashtable.cpp 2019-09-26 22:30:00.215254448 +0800 @@ -310,18 +310,7 @@ return true; } -// Dump footprint and bucket length statistics -// -// Note: if you create a new subclass of Hashtable, you will need to -// add a new function static int literal_size(MyNewType lit) -// because I can't get template int literal_size(T) to pick the specializations for Symbol and oop. -// -// The StringTable and SymbolTable dumping print how much footprint is used by the String and Symbol -// literals. - -template void Hashtable::print_table_statistics(outputStream* st, - const char *table_name, - T (*literal_load_barrier)(HashtableEntry*)) { +template TableStatistics Hashtable::statistics_calculate(T (*literal_load_barrier)(HashtableEntry*)) { NumberSeq summary; int literal_bytes = 0; for (int i = 0; i < this->table_size(); ++i) { @@ -334,28 +323,19 @@ } summary.add((double)count); } - double num_buckets = summary.num(); - double num_entries = summary.sum(); - - int bucket_bytes = (int)num_buckets * sizeof(HashtableBucket); - int entry_bytes = (int)num_entries * sizeof(HashtableEntry); - int total_bytes = literal_bytes + bucket_bytes + entry_bytes; - - int bucket_size = (num_buckets <= 0) ? 0 : (bucket_bytes / num_buckets); - int entry_size = (num_entries <= 0) ? 0 : (entry_bytes / num_entries); + return TableStatistics(this->_stats_rate, summary, literal_bytes, sizeof(HashtableBucket), sizeof(HashtableEntry)); +} - st->print_cr("%s statistics:", table_name); - st->print_cr("Number of buckets : %9d = %9d bytes, each %d", (int)num_buckets, bucket_bytes, bucket_size); - st->print_cr("Number of entries : %9d = %9d bytes, each %d", (int)num_entries, entry_bytes, entry_size); - if (literal_bytes != 0) { - double literal_avg = (num_entries <= 0) ? 0 : (literal_bytes / num_entries); - st->print_cr("Number of literals : %9d = %9d bytes, avg %7.3f", (int)num_entries, literal_bytes, literal_avg); - } - st->print_cr("Total footprint : %9s = %9d bytes", "", total_bytes); - st->print_cr("Average bucket size : %9.3f", summary.avg()); - st->print_cr("Variance of bucket size : %9.3f", summary.variance()); - st->print_cr("Std. dev. of bucket size: %9.3f", summary.sd()); - st->print_cr("Maximum bucket size : %9d", (int)summary.maximum()); +// Dump footprint and bucket length statistics +// +// Note: if you create a new subclass of Hashtable, you will need to +// add a new function static int literal_size(MyNewType lit) +// because I can't get template int literal_size(T) to pick the specializations for Symbol and oop. +template void Hashtable::print_table_statistics(outputStream* st, + const char *table_name, + T (*literal_load_barrier)(HashtableEntry*)) { + TableStatistics ts = statistics_calculate(literal_load_barrier); + ts.print(st, table_name); } --- old/src/hotspot/share/utilities/hashtable.hpp 2019-09-26 22:30:01.046283388 +0800 +++ new/src/hotspot/share/utilities/hashtable.hpp 2019-09-26 22:30:00.899278269 +0800 @@ -30,6 +30,7 @@ #include "oops/oop.hpp" #include "oops/symbol.hpp" #include "runtime/handles.hpp" +#include "utilities/tableStatistics.hpp" // This is a generic hashtable, designed to be used for the symbol // and string tables. @@ -172,6 +173,8 @@ protected: + TableRateStatistics _stats_rate; + void initialize(int table_size, int entry_size, int number_of_entries); // Accessor @@ -265,6 +268,7 @@ return this->hash_to_index(compute_hash(name)); } + TableStatistics statistics_calculate(T (*literal_load_barrier)(HashtableEntry*) = NULL); void print_table_statistics(outputStream* st, const char *table_name, T (*literal_load_barrier)(HashtableEntry*) = NULL); protected: --- old/src/hotspot/share/utilities/hashtable.inline.hpp 2019-09-26 22:30:01.730307209 +0800 +++ new/src/hotspot/share/utilities/hashtable.inline.hpp 2019-09-26 22:30:01.580301985 +0800 @@ -43,6 +43,7 @@ for (int index = 0; index < _table_size; index++) { _buckets[index].clear(); } + _stats_rate = TableRateStatistics(); } @@ -52,6 +53,7 @@ // Called on startup, no locking needed initialize(table_size, entry_size, number_of_entries); _buckets = buckets; + _stats_rate = TableRateStatistics(); } @@ -93,6 +95,11 @@ template inline void BasicHashtable::set_entry(int index, BasicHashtableEntry* entry) { _buckets[index].set_entry(entry); + if (entry != NULL) { + JFR_ONLY(_stats_rate.add();) + } else { + JFR_ONLY(_stats_rate.remove();) + } } @@ -100,12 +107,14 @@ entry->set_next(bucket(index)); _buckets[index].set_entry(entry); ++_number_of_entries; + JFR_ONLY(_stats_rate.add();) } template inline void BasicHashtable::free_entry(BasicHashtableEntry* entry) { entry->set_next(_free_list); _free_list = entry; --_number_of_entries; + JFR_ONLY(_stats_rate.remove();) } #endif // SHARE_VM_UTILITIES_HASHTABLE_INLINE_HPP --- old/src/jdk.jfr/share/conf/jfr/default.jfc 2019-09-26 22:30:02.413330995 +0800 +++ new/src/jdk.jfr/share/conf/jfr/default.jfc 2019-09-26 22:30:02.267325910 +0800 @@ -27,6 +27,31 @@ 1000 ms + + true + 10 s + + + + true + 10 s + + + + true + 10 s + + + + true + 10 s + + + + true + 10 s + + true --- old/src/jdk.jfr/share/conf/jfr/profile.jfc 2019-09-26 22:30:03.092354642 +0800 +++ new/src/jdk.jfr/share/conf/jfr/profile.jfc 2019-09-26 22:30:02.946349557 +0800 @@ -27,6 +27,31 @@ 1000 ms + + true + 10 s + + + + true + 10 s + + + + true + 10 s + + + + true + 10 s + + + + true + 10 s + + true --- old/test/lib/jdk/test/lib/jfr/EventNames.java 2019-09-26 22:30:03.774378393 +0800 +++ new/test/lib/jdk/test/lib/jfr/EventNames.java 2019-09-26 22:30:03.627373273 +0800 @@ -86,6 +86,11 @@ public final static String BiasedLockRevocation = PREFIX + "BiasedLockRevocation"; public final static String BiasedLockSelfRevocation = PREFIX + "BiasedLockSelfRevocation"; public final static String BiasedLockClassRevocation = PREFIX + "BiasedLockClassRevocation"; + public final static String SymbolTableStatistics = PREFIX + "SymbolTableStatistics"; + public final static String StringTableStatistics = PREFIX + "StringTableStatistics"; + public final static String PlaceholderTableStatistics = PREFIX + "PlaceholderTableStatistics"; + public final static String LoaderConstraintsTableStatistics = PREFIX + "LoaderConstraintsTableStatistics"; + public final static String ProtectionDomainCacheTableStatistics = PREFIX + "ProtectionDomainCacheTableStatistics"; // GC public final static String GCHeapSummary = PREFIX + "GCHeapSummary"; --- /dev/null 2019-03-13 11:16:31.764306451 +0800 +++ new/src/hotspot/share/utilities/tableStatistics.cpp 2019-09-26 22:30:04.307396955 +0800 @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * 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. + * + */ + +#include "precompiled.hpp" +#include "jfr/jfr.hpp" +#include "runtime/atomic.hpp" +#include "runtime/os.hpp" +#include "utilities/debug.hpp" +#include "utilities/tableStatistics.hpp" + +TableRateStatistics::TableRateStatistics() : + _added_items(0), _removed_items(0), + _time_stamp(0), _seconds_stamp(0), + _added_items_stamp(0), _added_items_stamp_prev(0), + _removed_items_stamp(0), _removed_items_stamp_prev(0) {} + +TableRateStatistics::~TableRateStatistics() { }; + +void TableRateStatistics::add() { + if (Jfr::is_recording()) { + Atomic::inc(&_added_items); + } +} + +void TableRateStatistics::remove() { + if (Jfr::is_recording()) { + Atomic::inc(&_removed_items); + } +} + +void TableRateStatistics::stamp() { + jlong now = os::javaTimeNanos(); + + _added_items_stamp_prev = _added_items_stamp; + _removed_items_stamp_prev = _removed_items_stamp; + + _added_items_stamp = _added_items; + _removed_items_stamp = _removed_items; + + if (_time_stamp == 0) { + _time_stamp = now - 1000000000; + } + jlong diff = (now - _time_stamp); + _seconds_stamp = (float)diff / 1000000000.0; + _time_stamp = now; +} + +float TableRateStatistics::get_add_rate() { + return (float)((_added_items_stamp - _added_items_stamp_prev) / _seconds_stamp); +} + +float TableRateStatistics::get_remove_rate() { + return (float)((_removed_items_stamp - _removed_items_stamp_prev) / _seconds_stamp); +} + +TableStatistics::TableStatistics() : + _literal_bytes(0), + _number_of_buckets(0), _number_of_entries(0), + _maximum_bucket_size(0), _average_bucket_size(0), + _variance_of_bucket_size(0), _stddev_of_bucket_size(0), + _bucket_bytes(0), _entry_bytes(0), _total_footprint(0), + _bucket_size(0), _entry_size(0), + _add_rate(0), _remove_rate(0) { +} + +TableStatistics::TableStatistics(TableRateStatistics& rate_stats, NumberSeq summary, size_t literal_bytes, size_t bucket_bytes, size_t node_bytes) : + _literal_bytes(literal_bytes), + _number_of_buckets(0), _number_of_entries(0), + _maximum_bucket_size(0), _average_bucket_size(0), + _variance_of_bucket_size(0), _stddev_of_bucket_size(0), + _bucket_bytes(0), _entry_bytes(0), _total_footprint(0), + _bucket_size(0), _entry_size(0), + _add_rate(0), _remove_rate(0) { + + _number_of_buckets = summary.num(); + _number_of_entries = summary.sum(); + + _maximum_bucket_size = summary.maximum(); + _average_bucket_size = summary.avg(); + _variance_of_bucket_size = summary.variance(); + _stddev_of_bucket_size = summary.sd(); + + _bucket_bytes = _number_of_buckets * bucket_bytes; + _entry_bytes = _number_of_entries * node_bytes; + _total_footprint = _literal_bytes + _bucket_bytes + _entry_bytes; + + _bucket_size = (_number_of_buckets <= 0) ? 0 : (_bucket_bytes / _number_of_buckets); + _entry_size = (_number_of_entries <= 0) ? 0 : (_entry_bytes / _number_of_entries); + + if (Jfr::is_recording()) { + rate_stats.stamp(); + _add_rate = rate_stats.get_add_rate(); + _remove_rate = rate_stats.get_remove_rate(); + } +} + +TableStatistics::~TableStatistics() { } + +void TableStatistics::print(outputStream* st, const char *table_name) { + st->print_cr("%s statistics:", table_name); + st->print_cr("Number of buckets : %9" PRIuPTR " = %9" PRIuPTR + " bytes, each " SIZE_FORMAT, + _number_of_buckets, _bucket_bytes, _bucket_size); + st->print_cr("Number of entries : %9" PRIuPTR " = %9" PRIuPTR + " bytes, each " SIZE_FORMAT, + _number_of_entries, _entry_bytes, _entry_size); + if (_literal_bytes != 0) { + float literal_avg = (_number_of_entries <= 0) ? 0 : (_literal_bytes / _number_of_entries); + st->print_cr("Number of literals : %9" PRIuPTR " = %9" PRIuPTR + " bytes, avg %7.3f", + _number_of_entries, _literal_bytes, literal_avg); + } + st->print_cr("Total footprint : %9s = %9" PRIuPTR " bytes", "", _total_footprint); + st->print_cr("Average bucket size : %9.3f", _average_bucket_size); + st->print_cr("Variance of bucket size : %9.3f", _variance_of_bucket_size); + st->print_cr("Std. dev. of bucket size: %9.3f", _stddev_of_bucket_size); + st->print_cr("Maximum bucket size : %9" PRIuPTR, _maximum_bucket_size); +} + --- /dev/null 2019-03-13 11:16:31.764306451 +0800 +++ new/src/hotspot/share/utilities/tableStatistics.hpp 2019-09-26 22:30:05.022421855 +0800 @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * 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. + * + */ + +#ifndef SHARE_UTILITIES_TABLE_STATISTICS_HPP +#define SHARE_UTILITIES_TABLE_STATISTICS_HPP + +#include "memory/allocation.hpp" +#include "utilities/debug.hpp" +#include "utilities/globalDefinitions.hpp" +#include "utilities/numberSeq.hpp" + +class TableRateStatistics : CHeapObj { + + friend class TableStatistics; + +private: + volatile size_t _added_items; + volatile size_t _removed_items; + + jlong _time_stamp; + double _seconds_stamp; + size_t _added_items_stamp; + size_t _added_items_stamp_prev; + size_t _removed_items_stamp; + size_t _removed_items_stamp_prev; + +public: + TableRateStatistics(); + ~TableRateStatistics(); + + void add(); + void remove(); + +protected: + void stamp(); + float get_add_rate(); + float get_remove_rate(); +}; + +class TableStatistics : CHeapObj { + +public: + size_t _literal_bytes; + + size_t _number_of_buckets; + size_t _number_of_entries; + + size_t _maximum_bucket_size; + float _average_bucket_size; + float _variance_of_bucket_size; + float _stddev_of_bucket_size; + + size_t _bucket_bytes; + size_t _entry_bytes; + size_t _total_footprint; + + size_t _bucket_size; + size_t _entry_size; + + float _add_rate; + float _remove_rate; + + TableStatistics(); + TableStatistics(TableRateStatistics& rate_stats, NumberSeq summary, size_t literal_bytes, size_t bucket_bytes, size_t node_bytes); + ~TableStatistics(); + + void print(outputStream* st, const char *table_name); +}; + +#endif // SHARE_UTILITIES_TABLE_STATISTICS_HPP --- /dev/null 2019-03-13 11:16:31.764306451 +0800 +++ new/test/jdk/jdk/jfr/event/runtime/TestTableStatisticsEvent.java 2019-09-26 22:30:05.764447696 +0800 @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle 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 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 jdk.jfr.event.runtime; + +import java.util.List; +import java.util.stream.Collectors; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedClass; +import jdk.jfr.consumer.RecordedClassLoader; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @key jfr + * @requires vm.hasJFR + * @library /test/lib /test/jdk + * @build jdk.jfr.event.runtime.TestClasses + * @run main/othervm jdk.jfr.event.runtime.TestTableStatisticsEvent + * @bug 8185525 + */ +public final class TestTableStatisticsEvent { + + public static void main(String[] args) throws Throwable { + try (Recording recording = new Recording()) { + recording.enable(EventNames.SymbolTableStatistics); + recording.enable(EventNames.StringTableStatistics); + recording.enable(EventNames.PlaceholderTableStatistics); + recording.enable(EventNames.LoaderConstraintsTableStatistics); + recording.enable(EventNames.ProtectionDomainCacheTableStatistics); + recording.start(); + recording.stop(); + + List events = Events.fromRecording(recording); + verifyTable(events, EventNames.SymbolTableStatistics); + verifyTable(events, EventNames.StringTableStatistics); + verifyTable(events, EventNames.PlaceholderTableStatistics); + verifyTable(events, EventNames.LoaderConstraintsTableStatistics); + verifyTable(events, EventNames.ProtectionDomainCacheTableStatistics); + } + } + + private static void verifyTable(List allEvents, String eventName) throws Exception { + List eventsForTable = allEvents.stream().filter(e -> e.getEventType().getName().equals(eventName)).collect(Collectors.toList()); + if (eventsForTable.isEmpty()) { + throw new Exception("No events for " + eventName); + } + for (RecordedEvent event : eventsForTable) { + Events.assertField(event, "bucketCount").atLeast(0L); + long entryCount = Events.assertField(event, "entryCount").atLeast(0L).getValue(); + Events.assertField(event, "totalFootprint").atLeast(0L); + float averageBucketCount = Events.assertField(event, "bucketCountAverage").atLeast(0.0f).getValue(); + Events.assertField(event, "bucketCountMaximum").atLeast((long)averageBucketCount); + Events.assertField(event, "bucketCountVariance").atLeast(0.0f); + Events.assertField(event, "bucketCountStandardDeviation").atLeast(0.0f); + float insertionRate = Events.assertField(event, "insertionRate").atLeast(0.0f).getValue(); + float removalRate = Events.assertField(event, "removalRate").atLeast(0.0f).getValue(); + if ((insertionRate > 0.0f) && (insertionRate > removalRate)) { + Asserts.assertGreaterThan(entryCount, 0L, "Entries marked as added, but no entries found for " + eventName); + } + } + } +}