652 lines
28 KiB
Diff
652 lines
28 KiB
Diff
|
|
Date: Fri, 9 Jun 2023 12:13:26 +0800
|
||
|
|
Subject: [PATCH 47/59] 8025692: Log what methods are touched at run-time
|
||
|
|
|
||
|
|
Bug url: https://bugs.openjdk.org/browse/JDK-8025692
|
||
|
|
---
|
||
|
|
.../vm/templateInterpreter_aarch64.cpp | 4 +-
|
||
|
|
hotspot/src/cpu/ppc/vm/interp_masm_ppc_64.cpp | 2 +-
|
||
|
|
.../cpu/ppc/vm/templateInterpreter_ppc.cpp | 4 +-
|
||
|
|
.../src/cpu/sparc/vm/interp_masm_sparc.cpp | 2 +-
|
||
|
|
.../sparc/vm/templateInterpreter_sparc.cpp | 4 +-
|
||
|
|
.../cpu/x86/vm/templateInterpreter_x86_32.cpp | 4 +-
|
||
|
|
.../cpu/x86/vm/templateInterpreter_x86_64.cpp | 4 +-
|
||
|
|
hotspot/src/share/vm/ci/ciMethod.cpp | 3 +
|
||
|
|
hotspot/src/share/vm/oops/method.cpp | 84 +++++++++++
|
||
|
|
hotspot/src/share/vm/oops/method.hpp | 2 +
|
||
|
|
hotspot/src/share/vm/runtime/globals.hpp | 6 +
|
||
|
|
hotspot/src/share/vm/runtime/java.cpp | 8 ++
|
||
|
|
hotspot/src/share/vm/runtime/mutexLocker.cpp | 2 +
|
||
|
|
hotspot/src/share/vm/runtime/mutexLocker.hpp | 1 +
|
||
|
|
.../src/share/vm/runtime/vm_operations.hpp | 1 +
|
||
|
|
.../share/vm/services/diagnosticCommand.cpp | 33 +++++
|
||
|
|
.../share/vm/services/diagnosticCommand.hpp | 17 +++
|
||
|
|
.../CommandLine/PrintTouchedMethods.java | 134 ++++++++++++++++++
|
||
|
|
.../CommandLine/TestLogTouchedMethods.java | 35 +++++
|
||
|
|
19 files changed, 338 insertions(+), 12 deletions(-)
|
||
|
|
create mode 100644 hotspot/test/runtime/CommandLine/PrintTouchedMethods.java
|
||
|
|
create mode 100644 hotspot/test/runtime/CommandLine/TestLogTouchedMethods.java
|
||
|
|
|
||
|
|
diff --git a/hotspot/src/cpu/aarch64/vm/templateInterpreter_aarch64.cpp b/hotspot/src/cpu/aarch64/vm/templateInterpreter_aarch64.cpp
|
||
|
|
index a5a91e5f3..28b84cb51 100644
|
||
|
|
--- a/hotspot/src/cpu/aarch64/vm/templateInterpreter_aarch64.cpp
|
||
|
|
+++ b/hotspot/src/cpu/aarch64/vm/templateInterpreter_aarch64.cpp
|
||
|
|
@@ -1135,7 +1135,7 @@ void InterpreterGenerator::bang_stack_shadow_pages(bool native_call) {
|
||
|
|
// native method than the typical interpreter frame setup.
|
||
|
|
address InterpreterGenerator::generate_native_entry(bool synchronized) {
|
||
|
|
// determine code generation flags
|
||
|
|
- bool inc_counter = UseCompiler || CountCompiledCalls;
|
||
|
|
+ bool inc_counter = UseCompiler || CountCompiledCalls || LogTouchedMethods;
|
||
|
|
|
||
|
|
// r1: Method*
|
||
|
|
// rscratch1: sender sp
|
||
|
|
@@ -1591,7 +1591,7 @@ address InterpreterGenerator::generate_native_entry(bool synchronized) {
|
||
|
|
//
|
||
|
|
address InterpreterGenerator::generate_normal_entry(bool synchronized) {
|
||
|
|
// determine code generation flags
|
||
|
|
- bool inc_counter = UseCompiler || CountCompiledCalls;
|
||
|
|
+ bool inc_counter = UseCompiler || CountCompiledCalls || LogTouchedMethods;
|
||
|
|
|
||
|
|
// rscratch1: sender sp
|
||
|
|
address entry_point = __ pc();
|
||
|
|
diff --git a/hotspot/src/cpu/ppc/vm/interp_masm_ppc_64.cpp b/hotspot/src/cpu/ppc/vm/interp_masm_ppc_64.cpp
|
||
|
|
index 463f9da92..594905ef7 100644
|
||
|
|
--- a/hotspot/src/cpu/ppc/vm/interp_masm_ppc_64.cpp
|
||
|
|
+++ b/hotspot/src/cpu/ppc/vm/interp_masm_ppc_64.cpp
|
||
|
|
@@ -2226,7 +2226,7 @@ void InterpreterMacroAssembler::get_method_counters(Register method,
|
||
|
|
}
|
||
|
|
|
||
|
|
void InterpreterMacroAssembler::increment_invocation_counter(Register Rcounters, Register iv_be_count, Register Rtmp_r0) {
|
||
|
|
- assert(UseCompiler, "incrementing must be useful");
|
||
|
|
+ assert(UseCompiler || LogTouchedMethods, "incrementing must be useful");
|
||
|
|
Register invocation_count = iv_be_count;
|
||
|
|
Register backedge_count = Rtmp_r0;
|
||
|
|
int delta = InvocationCounter::count_increment;
|
||
|
|
diff --git a/hotspot/src/cpu/ppc/vm/templateInterpreter_ppc.cpp b/hotspot/src/cpu/ppc/vm/templateInterpreter_ppc.cpp
|
||
|
|
index 0fb934166..4e2cec39b 100644
|
||
|
|
--- a/hotspot/src/cpu/ppc/vm/templateInterpreter_ppc.cpp
|
||
|
|
+++ b/hotspot/src/cpu/ppc/vm/templateInterpreter_ppc.cpp
|
||
|
|
@@ -726,7 +726,7 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) {
|
||
|
|
|
||
|
|
address entry = __ pc();
|
||
|
|
|
||
|
|
- const bool inc_counter = UseCompiler || CountCompiledCalls;
|
||
|
|
+ const bool inc_counter = UseCompiler || CountCompiledCalls || LogTouchedMethods;
|
||
|
|
|
||
|
|
// -----------------------------------------------------------------------------
|
||
|
|
// Allocate a new frame that represents the native callee (i2n frame).
|
||
|
|
@@ -1176,7 +1176,7 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) {
|
||
|
|
// Generic interpreted method entry to (asm) interpreter.
|
||
|
|
//
|
||
|
|
address TemplateInterpreterGenerator::generate_normal_entry(bool synchronized) {
|
||
|
|
- bool inc_counter = UseCompiler || CountCompiledCalls;
|
||
|
|
+ bool inc_counter = UseCompiler || CountCompiledCalls || LogTouchedMethods;
|
||
|
|
address entry = __ pc();
|
||
|
|
// Generate the code to allocate the interpreter stack frame.
|
||
|
|
Register Rsize_of_parameters = R4_ARG2, // Written by generate_fixed_frame.
|
||
|
|
diff --git a/hotspot/src/cpu/sparc/vm/interp_masm_sparc.cpp b/hotspot/src/cpu/sparc/vm/interp_masm_sparc.cpp
|
||
|
|
index 66bcfcaf1..83d54d731 100644
|
||
|
|
--- a/hotspot/src/cpu/sparc/vm/interp_masm_sparc.cpp
|
||
|
|
+++ b/hotspot/src/cpu/sparc/vm/interp_masm_sparc.cpp
|
||
|
|
@@ -2362,7 +2362,7 @@ void InterpreterMacroAssembler::get_method_counters(Register method,
|
||
|
|
}
|
||
|
|
|
||
|
|
void InterpreterMacroAssembler::increment_invocation_counter( Register Rcounters, Register Rtmp, Register Rtmp2 ) {
|
||
|
|
- assert(UseCompiler, "incrementing must be useful");
|
||
|
|
+ assert(UseCompiler || LogTouchedMethods, "incrementing must be useful");
|
||
|
|
assert_different_registers(Rcounters, Rtmp, Rtmp2);
|
||
|
|
|
||
|
|
Address inv_counter(Rcounters, MethodCounters::invocation_counter_offset() +
|
||
|
|
diff --git a/hotspot/src/cpu/sparc/vm/templateInterpreter_sparc.cpp b/hotspot/src/cpu/sparc/vm/templateInterpreter_sparc.cpp
|
||
|
|
index 540f9b287..9526866bd 100644
|
||
|
|
--- a/hotspot/src/cpu/sparc/vm/templateInterpreter_sparc.cpp
|
||
|
|
+++ b/hotspot/src/cpu/sparc/vm/templateInterpreter_sparc.cpp
|
||
|
|
@@ -823,7 +823,7 @@ address InterpreterGenerator::generate_native_entry(bool synchronized) {
|
||
|
|
// the following temporary registers are used during frame creation
|
||
|
|
const Register Gtmp1 = G3_scratch ;
|
||
|
|
const Register Gtmp2 = G1_scratch;
|
||
|
|
- bool inc_counter = UseCompiler || CountCompiledCalls;
|
||
|
|
+ bool inc_counter = UseCompiler || CountCompiledCalls || LogTouchedMethods;
|
||
|
|
|
||
|
|
// make sure registers are different!
|
||
|
|
assert_different_registers(G2_thread, G5_method, Gargs, Gtmp1, Gtmp2);
|
||
|
|
@@ -1261,7 +1261,7 @@ address InterpreterGenerator::generate_native_entry(bool synchronized) {
|
||
|
|
address InterpreterGenerator::generate_normal_entry(bool synchronized) {
|
||
|
|
address entry = __ pc();
|
||
|
|
|
||
|
|
- bool inc_counter = UseCompiler || CountCompiledCalls;
|
||
|
|
+ bool inc_counter = UseCompiler || CountCompiledCalls || LogTouchedMethods;
|
||
|
|
|
||
|
|
// the following temporary registers are used during frame creation
|
||
|
|
const Register Gtmp1 = G3_scratch ;
|
||
|
|
diff --git a/hotspot/src/cpu/x86/vm/templateInterpreter_x86_32.cpp b/hotspot/src/cpu/x86/vm/templateInterpreter_x86_32.cpp
|
||
|
|
index b7c515031..662d82222 100644
|
||
|
|
--- a/hotspot/src/cpu/x86/vm/templateInterpreter_x86_32.cpp
|
||
|
|
+++ b/hotspot/src/cpu/x86/vm/templateInterpreter_x86_32.cpp
|
||
|
|
@@ -990,7 +990,7 @@ address InterpreterGenerator::generate_CRC32_updateBytes_entry(AbstractInterpret
|
||
|
|
|
||
|
|
address InterpreterGenerator::generate_native_entry(bool synchronized) {
|
||
|
|
// determine code generation flags
|
||
|
|
- bool inc_counter = UseCompiler || CountCompiledCalls;
|
||
|
|
+ bool inc_counter = UseCompiler || CountCompiledCalls || LogTouchedMethods;
|
||
|
|
|
||
|
|
// rbx,: Method*
|
||
|
|
// rsi: sender sp
|
||
|
|
@@ -1405,7 +1405,7 @@ address InterpreterGenerator::generate_native_entry(bool synchronized) {
|
||
|
|
//
|
||
|
|
address InterpreterGenerator::generate_normal_entry(bool synchronized) {
|
||
|
|
// determine code generation flags
|
||
|
|
- bool inc_counter = UseCompiler || CountCompiledCalls;
|
||
|
|
+ bool inc_counter = UseCompiler || CountCompiledCalls || LogTouchedMethods;
|
||
|
|
|
||
|
|
// rbx,: Method*
|
||
|
|
// rsi: sender sp
|
||
|
|
diff --git a/hotspot/src/cpu/x86/vm/templateInterpreter_x86_64.cpp b/hotspot/src/cpu/x86/vm/templateInterpreter_x86_64.cpp
|
||
|
|
index 209a3f676..0f3bfc979 100644
|
||
|
|
--- a/hotspot/src/cpu/x86/vm/templateInterpreter_x86_64.cpp
|
||
|
|
+++ b/hotspot/src/cpu/x86/vm/templateInterpreter_x86_64.cpp
|
||
|
|
@@ -962,7 +962,7 @@ address InterpreterGenerator::generate_CRC32_updateBytes_entry(AbstractInterpret
|
||
|
|
// native method than the typical interpreter frame setup.
|
||
|
|
address InterpreterGenerator::generate_native_entry(bool synchronized) {
|
||
|
|
// determine code generation flags
|
||
|
|
- bool inc_counter = UseCompiler || CountCompiledCalls;
|
||
|
|
+ bool inc_counter = UseCompiler || CountCompiledCalls || LogTouchedMethods;
|
||
|
|
|
||
|
|
// rbx: Method*
|
||
|
|
// r13: sender sp
|
||
|
|
@@ -1409,7 +1409,7 @@ address InterpreterGenerator::generate_native_entry(bool synchronized) {
|
||
|
|
//
|
||
|
|
address InterpreterGenerator::generate_normal_entry(bool synchronized) {
|
||
|
|
// determine code generation flags
|
||
|
|
- bool inc_counter = UseCompiler || CountCompiledCalls;
|
||
|
|
+ bool inc_counter = UseCompiler || CountCompiledCalls || LogTouchedMethods;
|
||
|
|
|
||
|
|
// ebx: Method*
|
||
|
|
// r13: sender sp
|
||
|
|
diff --git a/hotspot/src/share/vm/ci/ciMethod.cpp b/hotspot/src/share/vm/ci/ciMethod.cpp
|
||
|
|
index 22fe6c8f1..50fafc4f8 100644
|
||
|
|
--- a/hotspot/src/share/vm/ci/ciMethod.cpp
|
||
|
|
+++ b/hotspot/src/share/vm/ci/ciMethod.cpp
|
||
|
|
@@ -74,6 +74,9 @@ ciMethod::ciMethod(methodHandle h_m, ciInstanceKlass* holder) :
|
||
|
|
{
|
||
|
|
assert(h_m() != NULL, "no null method");
|
||
|
|
|
||
|
|
+ if (LogTouchedMethods) {
|
||
|
|
+ h_m()->log_touched(Thread::current());
|
||
|
|
+ }
|
||
|
|
// These fields are always filled in in loaded methods.
|
||
|
|
_flags = ciFlags(h_m()->access_flags());
|
||
|
|
|
||
|
|
diff --git a/hotspot/src/share/vm/oops/method.cpp b/hotspot/src/share/vm/oops/method.cpp
|
||
|
|
index 305348bd0..406cd485e 100644
|
||
|
|
--- a/hotspot/src/share/vm/oops/method.cpp
|
||
|
|
+++ b/hotspot/src/share/vm/oops/method.cpp
|
||
|
|
@@ -408,6 +408,11 @@ MethodCounters* Method::build_method_counters(Method* m, TRAPS) {
|
||
|
|
if (!mh->init_method_counters(counters)) {
|
||
|
|
MetadataFactory::free_metadata(loader_data, counters);
|
||
|
|
}
|
||
|
|
+
|
||
|
|
+ if (LogTouchedMethods) {
|
||
|
|
+ mh->log_touched(CHECK_NULL);
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
return mh->method_counters();
|
||
|
|
}
|
||
|
|
|
||
|
|
@@ -2088,6 +2093,85 @@ void Method::collect_statistics(KlassSizeStats *sz) const {
|
||
|
|
}
|
||
|
|
#endif // INCLUDE_SERVICES
|
||
|
|
|
||
|
|
+// LogTouchedMethods and PrintTouchedMethods
|
||
|
|
+
|
||
|
|
+// TouchedMethodRecord -- we can't use a HashtableEntry<Method*> because
|
||
|
|
+// the Method may be garbage collected. Let's roll our own hash table.
|
||
|
|
+class TouchedMethodRecord : CHeapObj<mtTracing> {
|
||
|
|
+public:
|
||
|
|
+ // It's OK to store Symbols here because they will NOT be GC'ed if
|
||
|
|
+ // LogTouchedMethods is enabled.
|
||
|
|
+ TouchedMethodRecord* _next;
|
||
|
|
+ Symbol* _class_name;
|
||
|
|
+ Symbol* _method_name;
|
||
|
|
+ Symbol* _method_signature;
|
||
|
|
+};
|
||
|
|
+
|
||
|
|
+static const int TOUCHED_METHOD_TABLE_SIZE = 20011;
|
||
|
|
+static TouchedMethodRecord** _touched_method_table = NULL;
|
||
|
|
+
|
||
|
|
+void Method::log_touched(TRAPS) {
|
||
|
|
+
|
||
|
|
+ const int table_size = TOUCHED_METHOD_TABLE_SIZE;
|
||
|
|
+ Symbol* my_class = klass_name();
|
||
|
|
+ Symbol* my_name = name();
|
||
|
|
+ Symbol* my_sig = signature();
|
||
|
|
+
|
||
|
|
+ unsigned int hash = my_class->identity_hash() +
|
||
|
|
+ my_name->identity_hash() +
|
||
|
|
+ my_sig->identity_hash();
|
||
|
|
+ juint index = juint(hash) % table_size;
|
||
|
|
+
|
||
|
|
+ MutexLocker ml(TouchedMethodLog_lock, THREAD);
|
||
|
|
+ if (_touched_method_table == NULL) {
|
||
|
|
+ _touched_method_table = NEW_C_HEAP_ARRAY2(TouchedMethodRecord*, table_size,
|
||
|
|
+ mtTracing, CURRENT_PC);
|
||
|
|
+ memset(_touched_method_table, 0, sizeof(TouchedMethodRecord*) * table_size);
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ TouchedMethodRecord* ptr = _touched_method_table[index];
|
||
|
|
+ while (ptr) {
|
||
|
|
+ if (ptr->_class_name == my_class &&
|
||
|
|
+ ptr->_method_name == my_name &&
|
||
|
|
+ ptr->_method_signature == my_sig) {
|
||
|
|
+ return;
|
||
|
|
+ }
|
||
|
|
+ if (ptr->_next == NULL) break;
|
||
|
|
+ ptr = ptr->_next;
|
||
|
|
+ }
|
||
|
|
+ TouchedMethodRecord* nptr = NEW_C_HEAP_OBJ(TouchedMethodRecord, mtTracing);
|
||
|
|
+ my_class->increment_refcount();
|
||
|
|
+ my_name->increment_refcount();
|
||
|
|
+ my_sig->increment_refcount();
|
||
|
|
+ nptr->_class_name = my_class;
|
||
|
|
+ nptr->_method_name = my_name;
|
||
|
|
+ nptr->_method_signature = my_sig;
|
||
|
|
+ nptr->_next = NULL;
|
||
|
|
+
|
||
|
|
+ if (ptr == NULL) {
|
||
|
|
+ // first
|
||
|
|
+ _touched_method_table[index] = nptr;
|
||
|
|
+ } else {
|
||
|
|
+ ptr->_next = nptr;
|
||
|
|
+ }
|
||
|
|
+}
|
||
|
|
+
|
||
|
|
+void Method::print_touched_methods(outputStream* out) {
|
||
|
|
+ MutexLockerEx ml(Thread::current()->is_VM_thread() ? NULL : TouchedMethodLog_lock);
|
||
|
|
+ out->print_cr("# Method::print_touched_methods version 1");
|
||
|
|
+ if (_touched_method_table) {
|
||
|
|
+ for (int i = 0; i < TOUCHED_METHOD_TABLE_SIZE; i++) {
|
||
|
|
+ TouchedMethodRecord* ptr = _touched_method_table[i];
|
||
|
|
+ while(ptr) {
|
||
|
|
+ ptr->_class_name->print_symbol_on(out); out->print(".");
|
||
|
|
+ ptr->_method_name->print_symbol_on(out); out->print(":");
|
||
|
|
+ ptr->_method_signature->print_symbol_on(out); out->cr();
|
||
|
|
+ ptr = ptr->_next;
|
||
|
|
+ }
|
||
|
|
+ }
|
||
|
|
+ }
|
||
|
|
+}
|
||
|
|
+
|
||
|
|
// Verification
|
||
|
|
|
||
|
|
void Method::verify_on(outputStream* st) {
|
||
|
|
diff --git a/hotspot/src/share/vm/oops/method.hpp b/hotspot/src/share/vm/oops/method.hpp
|
||
|
|
index ec93f2fb4..ee74d959d 100644
|
||
|
|
--- a/hotspot/src/share/vm/oops/method.hpp
|
||
|
|
+++ b/hotspot/src/share/vm/oops/method.hpp
|
||
|
|
@@ -650,6 +650,8 @@ class Method : public Metadata {
|
||
|
|
#if INCLUDE_SERVICES
|
||
|
|
void collect_statistics(KlassSizeStats *sz) const;
|
||
|
|
#endif
|
||
|
|
+ void log_touched(TRAPS);
|
||
|
|
+ static void print_touched_methods(outputStream* out);
|
||
|
|
|
||
|
|
// interpreter support
|
||
|
|
static ByteSize const_offset() { return byte_offset_of(Method, _constMethod ); }
|
||
|
|
diff --git a/hotspot/src/share/vm/runtime/globals.hpp b/hotspot/src/share/vm/runtime/globals.hpp
|
||
|
|
index 210e6bd67..11f713013 100644
|
||
|
|
--- a/hotspot/src/share/vm/runtime/globals.hpp
|
||
|
|
+++ b/hotspot/src/share/vm/runtime/globals.hpp
|
||
|
|
@@ -2694,6 +2694,12 @@ class CommandLineFlags {
|
||
|
|
develop(bool, EagerInitialization, false, \
|
||
|
|
"Eagerly initialize classes if possible") \
|
||
|
|
\
|
||
|
|
+ diagnostic(bool, LogTouchedMethods, false, \
|
||
|
|
+ "Log methods which have been ever touched in runtime") \
|
||
|
|
+ \
|
||
|
|
+ diagnostic(bool, PrintTouchedMethodsAtExit, false, \
|
||
|
|
+ "Print all methods that have been ever touched in runtime") \
|
||
|
|
+ \
|
||
|
|
develop(bool, TraceMethodReplacement, false, \
|
||
|
|
"Print when methods are replaced do to recompilation") \
|
||
|
|
\
|
||
|
|
diff --git a/hotspot/src/share/vm/runtime/java.cpp b/hotspot/src/share/vm/runtime/java.cpp
|
||
|
|
index c72a5a766..5a628b73e 100644
|
||
|
|
--- a/hotspot/src/share/vm/runtime/java.cpp
|
||
|
|
+++ b/hotspot/src/share/vm/runtime/java.cpp
|
||
|
|
@@ -360,6 +360,10 @@ void print_statistics() {
|
||
|
|
SystemDictionary::print();
|
||
|
|
}
|
||
|
|
|
||
|
|
+ if (LogTouchedMethods && PrintTouchedMethodsAtExit) {
|
||
|
|
+ Method::print_touched_methods(tty);
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
if (PrintBiasedLockingStatistics) {
|
||
|
|
BiasedLocking::print_counters();
|
||
|
|
}
|
||
|
|
@@ -408,6 +412,10 @@ void print_statistics() {
|
||
|
|
if (PrintNMTStatistics) {
|
||
|
|
MemTracker::final_report(tty);
|
||
|
|
}
|
||
|
|
+
|
||
|
|
+ if (LogTouchedMethods && PrintTouchedMethodsAtExit) {
|
||
|
|
+ Method::print_touched_methods(tty);
|
||
|
|
+ }
|
||
|
|
}
|
||
|
|
|
||
|
|
#endif
|
||
|
|
diff --git a/hotspot/src/share/vm/runtime/mutexLocker.cpp b/hotspot/src/share/vm/runtime/mutexLocker.cpp
|
||
|
|
index a1c61f864..0b4a98cb7 100644
|
||
|
|
--- a/hotspot/src/share/vm/runtime/mutexLocker.cpp
|
||
|
|
+++ b/hotspot/src/share/vm/runtime/mutexLocker.cpp
|
||
|
|
@@ -63,6 +63,7 @@ Monitor* StringDedupQueue_lock = NULL;
|
||
|
|
Mutex* StringDedupTable_lock = NULL;
|
||
|
|
Mutex* CodeCache_lock = NULL;
|
||
|
|
Mutex* MethodData_lock = NULL;
|
||
|
|
+Mutex* TouchedMethodLog_lock = NULL;
|
||
|
|
Mutex* RetData_lock = NULL;
|
||
|
|
Monitor* VMOperationQueue_lock = NULL;
|
||
|
|
Monitor* VMOperationRequest_lock = NULL;
|
||
|
|
@@ -282,6 +283,7 @@ void mutex_init() {
|
||
|
|
|
||
|
|
def(Compile_lock , Mutex , nonleaf+3, true );
|
||
|
|
def(MethodData_lock , Mutex , nonleaf+3, false);
|
||
|
|
+ def(TouchedMethodLog_lock , Mutex , nonleaf+3, false);
|
||
|
|
|
||
|
|
def(MethodCompileQueue_lock , Monitor, nonleaf+4, true );
|
||
|
|
def(Debug2_lock , Mutex , nonleaf+4, true );
|
||
|
|
diff --git a/hotspot/src/share/vm/runtime/mutexLocker.hpp b/hotspot/src/share/vm/runtime/mutexLocker.hpp
|
||
|
|
index f28058b0e..0d6de9ea0 100644
|
||
|
|
--- a/hotspot/src/share/vm/runtime/mutexLocker.hpp
|
||
|
|
+++ b/hotspot/src/share/vm/runtime/mutexLocker.hpp
|
||
|
|
@@ -71,6 +71,7 @@ extern Monitor* StringDedupQueue_lock; // a lock on the string dedupli
|
||
|
|
extern Mutex* StringDedupTable_lock; // a lock on the string deduplication table
|
||
|
|
extern Mutex* CodeCache_lock; // a lock on the CodeCache, rank is special, use MutexLockerEx
|
||
|
|
extern Mutex* MethodData_lock; // a lock on installation of method data
|
||
|
|
+extern Mutex* TouchedMethodLog_lock; // a lock on allocation of LogExecutedMethods info
|
||
|
|
extern Mutex* RetData_lock; // a lock on installation of RetData inside method data
|
||
|
|
extern Mutex* DerivedPointerTableGC_lock; // a lock to protect the derived pointer table
|
||
|
|
extern Monitor* VMOperationQueue_lock; // a lock on queue of vm_operations waiting to execute
|
||
|
|
diff --git a/hotspot/src/share/vm/runtime/vm_operations.hpp b/hotspot/src/share/vm/runtime/vm_operations.hpp
|
||
|
|
index 19463b137..f2071a9d6 100644
|
||
|
|
--- a/hotspot/src/share/vm/runtime/vm_operations.hpp
|
||
|
|
+++ b/hotspot/src/share/vm/runtime/vm_operations.hpp
|
||
|
|
@@ -99,6 +99,7 @@
|
||
|
|
template(WhiteBoxOperation) \
|
||
|
|
template(ClassLoaderStatsOperation) \
|
||
|
|
template(ClassLoaderHierarchyOperation) \
|
||
|
|
+ template(DumpTouchedMethods) \
|
||
|
|
template(JFROldObject) \
|
||
|
|
template(PrintClasses) \
|
||
|
|
template(PrintMetadata) \
|
||
|
|
diff --git a/hotspot/src/share/vm/services/diagnosticCommand.cpp b/hotspot/src/share/vm/services/diagnosticCommand.cpp
|
||
|
|
index e37ba3cc8..50050a169 100644
|
||
|
|
--- a/hotspot/src/share/vm/services/diagnosticCommand.cpp
|
||
|
|
+++ b/hotspot/src/share/vm/services/diagnosticCommand.cpp
|
||
|
|
@@ -76,6 +76,7 @@ void DCmdRegistrant::register_dcmds(){
|
||
|
|
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<CompileQueueDCmd>(full_export, true, false));
|
||
|
|
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<CodeListDCmd>(full_export, true, false));
|
||
|
|
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<CodeCacheDCmd>(full_export, true, false));
|
||
|
|
+ DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<TouchedMethodsDCmd>(full_export, true, false));
|
||
|
|
#ifdef LINUX
|
||
|
|
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<TrimCLibcHeapDCmd>(full_export, true, false));
|
||
|
|
#endif // LINUX
|
||
|
|
@@ -569,6 +570,38 @@ int ThreadDumpDCmd::num_arguments() {
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
+class VM_DumpTouchedMethods : public VM_Operation {
|
||
|
|
+private:
|
||
|
|
+ outputStream* _out;
|
||
|
|
+public:
|
||
|
|
+ VM_DumpTouchedMethods(outputStream* out) {
|
||
|
|
+ _out = out;
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ virtual VMOp_Type type() const { return VMOp_DumpTouchedMethods; }
|
||
|
|
+
|
||
|
|
+ virtual void doit() {
|
||
|
|
+ Method::print_touched_methods(_out);
|
||
|
|
+ }
|
||
|
|
+};
|
||
|
|
+
|
||
|
|
+TouchedMethodsDCmd::TouchedMethodsDCmd(outputStream* output, bool heap) :
|
||
|
|
+ DCmdWithParser(output, heap)
|
||
|
|
+{}
|
||
|
|
+
|
||
|
|
+void TouchedMethodsDCmd::execute(DCmdSource source, TRAPS) {
|
||
|
|
+ if (!UnlockDiagnosticVMOptions) {
|
||
|
|
+ output()->print_cr("VM.touched_methods command requires -XX:+UnlockDiagnosticVMOptions");
|
||
|
|
+ return;
|
||
|
|
+ }
|
||
|
|
+ VM_DumpTouchedMethods dumper(output());
|
||
|
|
+ VMThread::execute(&dumper);
|
||
|
|
+}
|
||
|
|
+
|
||
|
|
+int TouchedMethodsDCmd::num_arguments() {
|
||
|
|
+ return 0;
|
||
|
|
+}
|
||
|
|
+
|
||
|
|
// Enhanced JMX Agent support
|
||
|
|
|
||
|
|
JMXStartRemoteDCmd::JMXStartRemoteDCmd(outputStream *output, bool heap_allocated) :
|
||
|
|
diff --git a/hotspot/src/share/vm/services/diagnosticCommand.hpp b/hotspot/src/share/vm/services/diagnosticCommand.hpp
|
||
|
|
index 65c04571b..3733fa7f7 100644
|
||
|
|
--- a/hotspot/src/share/vm/services/diagnosticCommand.hpp
|
||
|
|
+++ b/hotspot/src/share/vm/services/diagnosticCommand.hpp
|
||
|
|
@@ -36,6 +36,7 @@
|
||
|
|
#include "services/diagnosticFramework.hpp"
|
||
|
|
#include "services/diagnosticCommand_ext.hpp"
|
||
|
|
#include "utilities/macros.hpp"
|
||
|
|
+#include "oops/method.hpp"
|
||
|
|
|
||
|
|
class HelpDCmd : public DCmdWithParser {
|
||
|
|
protected:
|
||
|
|
@@ -358,6 +359,22 @@ public:
|
||
|
|
virtual void execute(DCmdSource source, TRAPS);
|
||
|
|
};
|
||
|
|
|
||
|
|
+class TouchedMethodsDCmd : public DCmdWithParser {
|
||
|
|
+public:
|
||
|
|
+ TouchedMethodsDCmd(outputStream* output, bool heap);
|
||
|
|
+ static const char* name() {
|
||
|
|
+ return "VM.print_touched_methods";
|
||
|
|
+ }
|
||
|
|
+ static const char* description() {
|
||
|
|
+ return "Print all methods that have ever been touched during the lifetime of this JVM.";
|
||
|
|
+ }
|
||
|
|
+ static const char* impact() {
|
||
|
|
+ return "Medium: Depends on Java content.";
|
||
|
|
+ }
|
||
|
|
+ static int num_arguments();
|
||
|
|
+ virtual void execute(DCmdSource source, TRAPS);
|
||
|
|
+};
|
||
|
|
+
|
||
|
|
// See also: thread_dump in attachListener.cpp
|
||
|
|
class ThreadDumpDCmd : public DCmdWithParser {
|
||
|
|
protected:
|
||
|
|
diff --git a/hotspot/test/runtime/CommandLine/PrintTouchedMethods.java b/hotspot/test/runtime/CommandLine/PrintTouchedMethods.java
|
||
|
|
new file mode 100644
|
||
|
|
index 000000000..c85b2a5d8
|
||
|
|
--- /dev/null
|
||
|
|
+++ b/hotspot/test/runtime/CommandLine/PrintTouchedMethods.java
|
||
|
|
@@ -0,0 +1,134 @@
|
||
|
|
+/*
|
||
|
|
+ * Copyright (c) 2023, 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.
|
||
|
|
+ */
|
||
|
|
+
|
||
|
|
+/*
|
||
|
|
+ * @test
|
||
|
|
+ * @bug 8025692
|
||
|
|
+ * @modules java.base/jdk.internal.misc
|
||
|
|
+ * java.management
|
||
|
|
+ * @library /testlibrary
|
||
|
|
+ * @compile TestLogTouchedMethods.java PrintTouchedMethods.java
|
||
|
|
+ * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+LogTouchedMethods PrintTouchedMethods
|
||
|
|
+ */
|
||
|
|
+
|
||
|
|
+import java.io.File;
|
||
|
|
+import java.util.List;
|
||
|
|
+import com.oracle.java.testlibrary.*;
|
||
|
|
+
|
||
|
|
+public class PrintTouchedMethods {
|
||
|
|
+
|
||
|
|
+ public static void main(String args[]) throws Exception {
|
||
|
|
+ ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
|
||
|
|
+ "-XX:-UnlockDiagnosticVMOptions",
|
||
|
|
+ "-XX:+LogTouchedMethods",
|
||
|
|
+ "-XX:+PrintTouchedMethodsAtExit",
|
||
|
|
+ "TestLogTouchedMethods");
|
||
|
|
+
|
||
|
|
+ // UnlockDiagnostic turned off, should fail
|
||
|
|
+ OutputAnalyzer output = new OutputAnalyzer(pb.start());
|
||
|
|
+ output.shouldContain("Error: VM option 'LogTouchedMethods' is diagnostic and must be enabled via -XX:+UnlockDiagnosticVMOptions.");
|
||
|
|
+ output.shouldContain("Error: Could not create the Java Virtual Machine.");
|
||
|
|
+
|
||
|
|
+ pb = ProcessTools.createJavaProcessBuilder(
|
||
|
|
+ "-XX:+UnlockDiagnosticVMOptions",
|
||
|
|
+ "-XX:+LogTouchedMethods",
|
||
|
|
+ "-XX:+PrintTouchedMethodsAtExit",
|
||
|
|
+ "TestLogTouchedMethods");
|
||
|
|
+ output = new OutputAnalyzer(pb.start());
|
||
|
|
+ // check order:
|
||
|
|
+ // 1 "# Method::print_touched_methods version 1" is the first in first line
|
||
|
|
+ // 2 should contain TestLogMethods.methodA:()V
|
||
|
|
+ // 3 should not contain TestLogMethods.methodB:()V
|
||
|
|
+ // Repeat above for another run with -Xint
|
||
|
|
+ List<String> lines = output.asLines();
|
||
|
|
+
|
||
|
|
+ if (lines.size() < 1) {
|
||
|
|
+ throw new Exception("Empty output");
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ String first = lines.get(0);
|
||
|
|
+ if (!first.equals("# Method::print_touched_methods version 1")) {
|
||
|
|
+ throw new Exception("First line mismatch");
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ output.shouldContain("TestLogTouchedMethods.methodA:()V");
|
||
|
|
+ output.shouldNotContain("TestLogTouchedMethods.methodB:()V");
|
||
|
|
+ output.shouldHaveExitValue(0);
|
||
|
|
+
|
||
|
|
+ pb = ProcessTools.createJavaProcessBuilder(
|
||
|
|
+ "-XX:+UnlockDiagnosticVMOptions",
|
||
|
|
+ "-Xint",
|
||
|
|
+ "-XX:+LogTouchedMethods",
|
||
|
|
+ "-XX:+PrintTouchedMethodsAtExit",
|
||
|
|
+ "TestLogTouchedMethods");
|
||
|
|
+ output = new OutputAnalyzer(pb.start());
|
||
|
|
+ lines = output.asLines();
|
||
|
|
+
|
||
|
|
+ if (lines.size() < 1) {
|
||
|
|
+ throw new Exception("Empty output");
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ first = lines.get(0);
|
||
|
|
+ if (!first.equals("# Method::print_touched_methods version 1")) {
|
||
|
|
+ throw new Exception("First line mismatch");
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ output.shouldContain("TestLogTouchedMethods.methodA:()V");
|
||
|
|
+ output.shouldNotContain("TestLogTouchedMethods.methodB:()V");
|
||
|
|
+ output.shouldHaveExitValue(0);
|
||
|
|
+
|
||
|
|
+ pb = ProcessTools.createJavaProcessBuilder(
|
||
|
|
+ "-XX:+UnlockDiagnosticVMOptions",
|
||
|
|
+ "-Xint",
|
||
|
|
+ "-XX:+LogTouchedMethods",
|
||
|
|
+ "-XX:+PrintTouchedMethodsAtExit",
|
||
|
|
+ "-XX:-TieredCompilation",
|
||
|
|
+ "TestLogTouchedMethods");
|
||
|
|
+ output = new OutputAnalyzer(pb.start());
|
||
|
|
+ lines = output.asLines();
|
||
|
|
+
|
||
|
|
+ if (lines.size() < 1) {
|
||
|
|
+ throw new Exception("Empty output");
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ first = lines.get(0);
|
||
|
|
+ if (!first.equals("# Method::print_touched_methods version 1")) {
|
||
|
|
+ throw new Exception("First line mismatch");
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ output.shouldContain("TestLogTouchedMethods.methodA:()V");
|
||
|
|
+ output.shouldNotContain("TestLogTouchedMethods.methodB:()V");
|
||
|
|
+ output.shouldHaveExitValue(0);
|
||
|
|
+
|
||
|
|
+ // Test jcmd PrintTouchedMethods VM.print_touched_methods
|
||
|
|
+ String pid = Long.toString(ProcessTools.getProcessId());
|
||
|
|
+ pb = new ProcessBuilder();
|
||
|
|
+ pb.command(new String[] {JDKToolFinder.getJDKTool("jcmd"), pid, "VM.print_touched_methods"});
|
||
|
|
+ output = new OutputAnalyzer(pb.start());
|
||
|
|
+ try {
|
||
|
|
+ output.shouldContain("PrintTouchedMethods.main:([Ljava/lang/String;)V");
|
||
|
|
+ } catch (RuntimeException e) {
|
||
|
|
+ output.shouldContain("Unknown diagnostic command");
|
||
|
|
+ }
|
||
|
|
+ }
|
||
|
|
+}
|
||
|
|
\ No newline at end of file
|
||
|
|
diff --git a/hotspot/test/runtime/CommandLine/TestLogTouchedMethods.java b/hotspot/test/runtime/CommandLine/TestLogTouchedMethods.java
|
||
|
|
new file mode 100644
|
||
|
|
index 000000000..655d4a916
|
||
|
|
--- /dev/null
|
||
|
|
+++ b/hotspot/test/runtime/CommandLine/TestLogTouchedMethods.java
|
||
|
|
@@ -0,0 +1,35 @@
|
||
|
|
+/*
|
||
|
|
+ * Copyright (c) 2023, 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.
|
||
|
|
+ */
|
||
|
|
+
|
||
|
|
+/* used by PrintTouchedMethods.java */
|
||
|
|
+public class TestLogTouchedMethods {
|
||
|
|
+ public static void main(String[] args) {
|
||
|
|
+ new TestLogTouchedMethods().methodA();
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ // methods without return values and local variables are optimized in interpreter mode
|
||
|
|
+ // it can not be recorded
|
||
|
|
+ // leave at least one local variable
|
||
|
|
+ public void methodA() { int a = 0; } // called
|
||
|
|
+ public void methodB() { int b = 0; } // this should not be called
|
||
|
|
+}
|
||
|
|
--
|
||
|
|
2.22.0
|
||
|
|
|