--- old/src/share/vm/ci/ciMethod.cpp 2015-03-26 15:13:34.372808801 -0700 +++ new/src/share/vm/ci/ciMethod.cpp 2015-03-26 15:13:34.235801968 -0700 @@ -76,6 +76,10 @@ { assert(h_m() != NULL, "no null method"); + if (TraceMethodUsage) { + h_m()->trace_usage(Thread::current()); + } + // These fields are always filled in in loaded methods. _flags = ciFlags(h_m()->access_flags()); --- old/src/share/vm/classfile/symbolTable.cpp 2015-03-26 15:13:34.886834435 -0700 +++ new/src/share/vm/classfile/symbolTable.cpp 2015-03-26 15:13:34.749827603 -0700 @@ -59,12 +59,12 @@ if (DumpSharedSpaces) { // Allocate all symbols to CLD shared metaspace sym = new (len, ClassLoaderData::the_null_class_loader_data(), THREAD) Symbol(name, len, -1); - } else if (c_heap) { + } else if (c_heap && !TraceMethodUsage) { // refcount starts as 1 sym = new (len, THREAD) Symbol(name, len, 1); assert(sym != NULL, "new should call vm_exit_out_of_memory if C_HEAP is exhausted"); } else { - // Allocate to global arena + // Allocate to global arena -- with TraceMethodUsage we cannot free any symbols. sym = new (len, arena(), THREAD) Symbol(name, len, -1); } return sym; --- old/src/share/vm/oops/method.cpp 2015-03-26 15:13:35.421861119 -0700 +++ new/src/share/vm/oops/method.cpp 2015-03-26 15:13:35.281854136 -0700 @@ -421,6 +421,9 @@ if (!mh->init_method_counters(counters)) { MetadataFactory::free_metadata(mh->method_holder()->class_loader_data(), counters); } + if (TraceMethodUsage) { + mh->trace_usage(CHECK_NULL); + } return mh->method_counters(); } @@ -2129,6 +2132,77 @@ } #endif // INCLUDE_SERVICES +// TraceMethodUsage and PrintMethodUsageAtExit + +// TraceMethodUsageRecord -- we can't use a HashtableEntry<> because +// the Method may be garbage collected. Let's roll our own hash table. +class TraceMethodUsageRecord : CHeapObj { +public: + // It's OK to store Symbols here because they will NOT be GC'ed if + // TraceMethodUsage is enabled. + TraceMethodUsageRecord* _next; + Symbol* _class_name; + Symbol* _method_name; + Symbol* _method_signature; +}; + +static const int TRACE_METHOD_USAGE_TABLE_SIZE = 20011; +static TraceMethodUsageRecord** _method_usage_table = NULL; + +void Method::trace_usage(TRAPS) const { + MutexLocker ml(MethodUsage_lock, THREAD); + + const int table_size = TRACE_METHOD_USAGE_TABLE_SIZE; + if (_method_usage_table == NULL) { + _method_usage_table = NEW_C_HEAP_ARRAY2(TraceMethodUsageRecord*, table_size, + mtTracing, CURRENT_PC); + memset(_method_usage_table, 0, sizeof(TraceMethodUsageRecord*)*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; + + for (TraceMethodUsageRecord* ptr = _method_usage_table[index]; + ptr; + ptr = ptr->_next) { + if (ptr->_class_name == my_class && + ptr->_method_name == my_name && + ptr->_method_signature == my_sig) { + return; + } + } + TraceMethodUsageRecord* nptr = NEW_C_HEAP_OBJ(TraceMethodUsageRecord, mtTracing); + nptr->_class_name = my_class; + nptr->_method_name = my_name; + nptr->_method_signature = my_sig; + + nptr->_next = _method_usage_table[index]; + _method_usage_table[index] = nptr; +} + +void Method::print_usage(outputStream* out) { + MutexLockerEx ml(Thread::current()->is_VM_thread() ? NULL : MethodUsage_lock); + out->print_cr("# Method::print_usage version 1"); + if (_method_usage_table) { + for (int i = 0; i < TRACE_METHOD_USAGE_TABLE_SIZE; i++) { + // Print one item per line to make it easy to read the table during CDS dumping. + for (TraceMethodUsageRecord* ptr = _method_usage_table[i]; + ptr; + ptr = ptr->_next) { + ptr->_class_name->print_symbol_on(out); out->cr(); + ptr->_method_name->print_symbol_on(out); out->cr(); + ptr->_method_signature->print_symbol_on(out); out->cr(); + } + } + } +} + // Verification void Method::verify_on(outputStream* st) { --- old/src/share/vm/oops/method.hpp 2015-03-26 15:13:35.976888797 -0700 +++ new/src/share/vm/oops/method.hpp 2015-03-26 15:13:35.840882015 -0700 @@ -623,6 +623,8 @@ #if INCLUDE_SERVICES void collect_statistics(KlassSizeStats *sz) const; #endif + void trace_usage(TRAPS) const; + static void print_usage(outputStream* out); // interpreter support static ByteSize const_offset() { return byte_offset_of(Method, _constMethod ); } --- old/src/share/vm/runtime/globals.hpp 2015-03-26 15:13:36.602920018 -0700 +++ new/src/share/vm/runtime/globals.hpp 2015-03-26 15:13:36.413910592 -0700 @@ -2543,6 +2543,12 @@ develop(bool, EagerInitialization, false, \ "Eagerly initialize classes if possible") \ \ + product(bool, TraceMethodUsage, false, \ + "Find out what methods have been used in runtime") \ + \ + product(bool, PrintMethodUsageAtExit, false, \ + "Print all methods that have been used in runtime") \ + \ develop(bool, TraceMethodReplacement, false, \ "Print when methods are replaced do to recompilation") \ \ --- old/src/share/vm/runtime/java.cpp 2015-03-26 15:13:37.436961614 -0700 +++ new/src/share/vm/runtime/java.cpp 2015-03-26 15:13:37.102944957 -0700 @@ -330,6 +330,10 @@ SystemDictionary::print(); } + if (PrintMethodUsageAtExit) { + Method::print_usage(tty); + } + if (PrintBiasedLockingStatistics) { BiasedLocking::print_counters(); } @@ -382,6 +386,10 @@ if (PrintNMTStatistics) { MemTracker::final_report(tty); } + + if (PrintMethodUsageAtExit) { + Method::print_usage(tty); + } } #endif --- old/src/share/vm/runtime/mutexLocker.cpp 2015-03-26 15:13:38.040991737 -0700 +++ new/src/share/vm/runtime/mutexLocker.cpp 2015-03-26 15:13:37.892984355 -0700 @@ -63,6 +63,7 @@ Mutex* StringDedupTable_lock = NULL; Monitor* CodeCache_lock = NULL; Mutex* MethodData_lock = NULL; +Mutex* MethodUsage_lock = NULL; Mutex* RetData_lock = NULL; Monitor* VMOperationQueue_lock = NULL; Monitor* VMOperationRequest_lock = NULL; @@ -271,6 +272,7 @@ def(Compile_lock , Mutex , nonleaf+3, true, Monitor::_safepoint_check_sometimes); def(MethodData_lock , Mutex , nonleaf+3, false, Monitor::_safepoint_check_always); + def(MethodUsage_lock , Mutex , nonleaf+3, false, Monitor::_safepoint_check_always); def(MethodCompileQueue_lock , Monitor, nonleaf+4, true, Monitor::_safepoint_check_always); def(Debug2_lock , Mutex , nonleaf+4, true, Monitor::_safepoint_check_never); --- old/src/share/vm/runtime/mutexLocker.hpp 2015-03-26 15:13:39.035041311 -0700 +++ new/src/share/vm/runtime/mutexLocker.hpp 2015-03-26 15:13:38.894034278 -0700 @@ -55,6 +55,7 @@ extern Mutex* StringDedupTable_lock; // a lock on the string deduplication table extern Monitor* 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* MethodUsage_lock; // a lock on allocation of TraceMethodUsage 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 --- old/src/share/vm/runtime/vm_operations.hpp 2015-03-26 15:13:39.619070438 -0700 +++ new/src/share/vm/runtime/vm_operations.hpp 2015-03-26 15:13:39.480063504 -0700 @@ -101,6 +101,7 @@ template(WhiteBoxOperation) \ template(ClassLoaderStatsOperation) \ template(DumpHashtable) \ + template(DumpMethodUsage) \ template(MarkActiveNMethods) \ template(PrintCompileQueue) \ template(PrintCodeList) \ --- old/src/share/vm/services/diagnosticCommand.cpp 2015-03-26 15:13:40.154097119 -0700 +++ new/src/share/vm/services/diagnosticCommand.cpp 2015-03-26 15:13:40.009089887 -0700 @@ -26,6 +26,7 @@ #include "classfile/classLoaderStats.hpp" #include "classfile/compactHashtable.hpp" #include "gc_implementation/shared/vmGCOperations.hpp" +#include "oops/method.hpp" #include "oops/oop.inline.hpp" #include "runtime/javaCalls.hpp" #include "runtime/os.hpp" @@ -68,6 +69,7 @@ DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); + DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); // Enhanced JMX Agent Support // These commands won't be exported via the DiagnosticCommandMBean until an @@ -729,3 +731,35 @@ } #endif + +class VM_DumpMethodUsage : public VM_Operation { +private: + outputStream* _out; +public: + VM_DumpMethodUsage(outputStream* out) { + _out = out; + } + + virtual VMOp_Type type() const { return VMOp_DumpMethodUsage; } + + virtual void doit() { + Method::print_usage(_out); + } +}; + +MethodUsageDCmd::MethodUsageDCmd(outputStream* output, bool heap) : + DCmdWithParser(output, heap) +{} + +void MethodUsageDCmd::execute(DCmdSource source, TRAPS) { + if (!UnlockDiagnosticVMOptions) { + output()->print_cr("VM.method_usage command requires -XX:+UnlockDiagnosticVMOptions"); + return; + } + VM_DumpMethodUsage dumper(output()); + VMThread::execute(&dumper); +} + +int MethodUsageDCmd::num_arguments() { + return 0; +} --- old/src/share/vm/services/diagnosticCommand.hpp 2015-03-26 15:13:40.700124351 -0700 +++ new/src/share/vm/services/diagnosticCommand.hpp 2015-03-26 15:13:40.553117019 -0700 @@ -299,6 +299,22 @@ virtual void execute(DCmdSource source, TRAPS); }; +class MethodUsageDCmd : public DCmdWithParser { +public: + MethodUsageDCmd(outputStream* output, bool heap); + static const char* name() { + return "VM.method_usage"; + } + static const char* description() { + return "Dump all methods that have been used 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: --- /dev/null 2015-03-16 21:16:05.445045700 -0700 +++ new/test/runtime/CommandLine/TraceMethodUsage.java 2015-03-26 15:13:41.333155920 -0700 @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2015, 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 + * @summary test -XX:+TraceMethodUsage -XX:+PrintMethodUsageAtExit + * @author Yumin Qi + * @library /testlibrary + */ + +import java.io.File; +import com.oracle.java.testlibrary.*; + +public class TraceMethodUsage { + public static void main(String args[]) throws Exception { + String[] javaArgs = {"-XX:+TraceMethodUsage", "-XX:+PrintMethodUsageAtExit", "-version"}; + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(javaArgs); + + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + output.shouldContain("version"); + output.shouldContain("java/lang/Object"); + output.shouldContain("hashCode"); + output.shouldContain("()I"); + output.shouldHaveExitValue(0); + } +}