# HG changeset patch # User minqi # Date 1436988281 25200 # Node ID 4a08476437e87256858b1da74b43998d672eb503 # Parent 04af91b7faddb389fb08438d1e1826a64de6e1bd 8025692: Log what methods are touched at run-time Summary: Added two diagnostic flags, LogTouchedMethods and PrintTouchedMethodsAtExit to list all methods that have been touched at run time. Added new jcmd, VM.print_touched_methods. Reviewed-by: acorn, iklam diff -r 04af91b7fadd -r 4a08476437e8 hotspot/src/cpu/aarch64/vm/templateInterpreter_aarch64.cpp --- a/hotspot/src/cpu/aarch64/vm/templateInterpreter_aarch64.cpp Tue Jul 14 16:28:53 2015 +0200 +++ b/hotspot/src/cpu/aarch64/vm/templateInterpreter_aarch64.cpp Wed Jul 15 12:24:41 2015 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, Red Hat Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -869,7 +869,7 @@ // 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 @@ -1307,7 +1307,7 @@ // 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 -r 04af91b7fadd -r 4a08476437e8 hotspot/src/cpu/ppc/vm/templateInterpreter_ppc.cpp --- a/hotspot/src/cpu/ppc/vm/templateInterpreter_ppc.cpp Tue Jul 14 16:28:53 2015 +0200 +++ b/hotspot/src/cpu/ppc/vm/templateInterpreter_ppc.cpp Wed Jul 15 12:24:41 2015 -0700 @@ -668,7 +668,7 @@ 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). @@ -1118,7 +1118,7 @@ // 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 -r 04af91b7fadd -r 4a08476437e8 hotspot/src/cpu/sparc/vm/templateInterpreter_sparc.cpp --- a/hotspot/src/cpu/sparc/vm/templateInterpreter_sparc.cpp Tue Jul 14 16:28:53 2015 +0200 +++ b/hotspot/src/cpu/sparc/vm/templateInterpreter_sparc.cpp Wed Jul 15 12:24:41 2015 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -801,7 +801,7 @@ // 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); @@ -1225,7 +1225,7 @@ 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 -r 04af91b7fadd -r 4a08476437e8 hotspot/src/cpu/x86/vm/templateInterpreter_x86_32.cpp --- a/hotspot/src/cpu/x86/vm/templateInterpreter_x86_32.cpp Tue Jul 14 16:28:53 2015 +0200 +++ b/hotspot/src/cpu/x86/vm/templateInterpreter_x86_32.cpp Wed Jul 15 12:24:41 2015 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -849,7 +849,7 @@ 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 @@ -1265,7 +1265,7 @@ // 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 -r 04af91b7fadd -r 4a08476437e8 hotspot/src/cpu/x86/vm/templateInterpreter_x86_64.cpp --- a/hotspot/src/cpu/x86/vm/templateInterpreter_x86_64.cpp Tue Jul 14 16:28:53 2015 +0200 +++ b/hotspot/src/cpu/x86/vm/templateInterpreter_x86_64.cpp Wed Jul 15 12:24:41 2015 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -809,7 +809,7 @@ // 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 @@ -1256,7 +1256,7 @@ // 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 -r 04af91b7fadd -r 4a08476437e8 hotspot/src/share/vm/ci/ciMethod.cpp --- a/hotspot/src/share/vm/ci/ciMethod.cpp Tue Jul 14 16:28:53 2015 +0200 +++ b/hotspot/src/share/vm/ci/ciMethod.cpp Wed Jul 15 12:24:41 2015 -0700 @@ -75,6 +75,9 @@ { 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 -r 04af91b7fadd -r 4a08476437e8 hotspot/src/share/vm/classfile/symbolTable.cpp --- a/hotspot/src/share/vm/classfile/symbolTable.cpp Tue Jul 14 16:28:53 2015 +0200 +++ b/hotspot/src/share/vm/classfile/symbolTable.cpp Wed Jul 15 12:24:41 2015 -0700 @@ -58,14 +58,14 @@ if (DumpSharedSpaces) { // Allocate all symbols to CLD shared metaspace - sym = new (len, ClassLoaderData::the_null_class_loader_data(), THREAD) Symbol(name, len, -1); + sym = new (len, ClassLoaderData::the_null_class_loader_data(), THREAD) Symbol(name, len, PERM_REFCOUNT); } else if (c_heap) { // 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 - sym = new (len, arena(), THREAD) Symbol(name, len, -1); + sym = new (len, arena(), THREAD) Symbol(name, len, PERM_REFCOUNT); } return sym; } diff -r 04af91b7fadd -r 4a08476437e8 hotspot/src/share/vm/oops/method.cpp --- a/hotspot/src/share/vm/oops/method.cpp Tue Jul 14 16:28:53 2015 +0200 +++ b/hotspot/src/share/vm/oops/method.cpp Wed Jul 15 12:24:41 2015 -0700 @@ -422,6 +422,11 @@ if (!mh->init_method_counters(counters)) { MetadataFactory::free_metadata(mh->method_holder()->class_loader_data(), counters); } + + if (LogTouchedMethods) { + mh->log_touched(CHECK_NULL); + } + return mh->method_counters(); } @@ -2130,6 +2135,85 @@ } #endif // INCLUDE_SERVICES +// LogTouchedMethods and PrintTouchedMethods + +// TouchedMethodRecord -- we can't use a HashtableEntry because +// the Method may be garbage collected. Let's roll our own hash table. +class TouchedMethodRecord : CHeapObj { +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->set_permanent(); // prevent reclaimed by GC + my_name->set_permanent(); + my_sig->set_permanent(); + 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 -r 04af91b7fadd -r 4a08476437e8 hotspot/src/share/vm/oops/method.hpp --- a/hotspot/src/share/vm/oops/method.hpp Tue Jul 14 16:28:53 2015 +0200 +++ b/hotspot/src/share/vm/oops/method.hpp Wed Jul 15 12:24:41 2015 -0700 @@ -625,6 +625,8 @@ #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 -r 04af91b7fadd -r 4a08476437e8 hotspot/src/share/vm/oops/symbol.hpp --- a/hotspot/src/share/vm/oops/symbol.hpp Tue Jul 14 16:28:53 2015 +0200 +++ b/hotspot/src/share/vm/oops/symbol.hpp Wed Jul 15 12:24:41 2015 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -96,12 +96,16 @@ // TempNewSymbol (passed in as a parameter) so the reference count on its symbol // will be decremented when it goes out of scope. - // This cannot be inherited from ResourceObj because it cannot have a vtable. // Since sometimes this is allocated from Metadata, pick a base allocation // type without virtual functions. class ClassLoaderData; +// Set _refcount to PERM_REFCOUNT to prevent the Symbol from being GC'ed. +#ifndef PERM_REFCOUNT +#define PERM_REFCOUNT -1 +#endif + // We separate the fields in SymbolBase from Symbol::_body so that // Symbol::size(int) can correctly calculate the space needed. class SymbolBase : public MetaspaceObj { @@ -160,6 +164,13 @@ int refcount() const { return _refcount; } void increment_refcount(); void decrement_refcount(); + // Set _refcount non zero to avoid being reclaimed by GC. + void set_permanent() { + assert(LogTouchedMethods, "Should not be called with LogTouchedMethods off"); + if (_refcount != PERM_REFCOUNT) { + _refcount = PERM_REFCOUNT; + } + } int byte_at(int index) const { assert(index >=0 && index < _length, "symbol index overflow"); diff -r 04af91b7fadd -r 4a08476437e8 hotspot/src/share/vm/runtime/globals.hpp --- a/hotspot/src/share/vm/runtime/globals.hpp Tue Jul 14 16:28:53 2015 +0200 +++ b/hotspot/src/share/vm/runtime/globals.hpp Wed Jul 15 12:24:41 2015 -0700 @@ -2717,6 +2717,12 @@ 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 -r 04af91b7fadd -r 4a08476437e8 hotspot/src/share/vm/runtime/java.cpp --- a/hotspot/src/share/vm/runtime/java.cpp Tue Jul 14 16:28:53 2015 +0200 +++ b/hotspot/src/share/vm/runtime/java.cpp Wed Jul 15 12:24:41 2015 -0700 @@ -330,6 +330,10 @@ SystemDictionary::print(); } + if (LogTouchedMethods && PrintTouchedMethodsAtExit) { + Method::print_touched_methods(tty); + } + if (PrintBiasedLockingStatistics) { BiasedLocking::print_counters(); } @@ -382,6 +386,10 @@ if (PrintNMTStatistics) { MemTracker::final_report(tty); } + + if (LogTouchedMethods && PrintTouchedMethodsAtExit) { + Method::print_touched_methods(tty); + } } #endif diff -r 04af91b7fadd -r 4a08476437e8 hotspot/src/share/vm/runtime/mutexLocker.cpp --- a/hotspot/src/share/vm/runtime/mutexLocker.cpp Tue Jul 14 16:28:53 2015 +0200 +++ b/hotspot/src/share/vm/runtime/mutexLocker.cpp Wed Jul 15 12:24:41 2015 -0700 @@ -63,6 +63,7 @@ Mutex* StringDedupTable_lock = NULL; Monitor* CodeCache_lock = NULL; Mutex* MethodData_lock = NULL; +Mutex* TouchedMethodLog_lock = NULL; Mutex* RetData_lock = NULL; Monitor* VMOperationQueue_lock = NULL; Monitor* VMOperationRequest_lock = NULL; @@ -274,6 +275,7 @@ def(Compile_lock , Mutex , nonleaf+3, true, Monitor::_safepoint_check_sometimes); def(MethodData_lock , Mutex , nonleaf+3, false, Monitor::_safepoint_check_always); + def(TouchedMethodLog_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); diff -r 04af91b7fadd -r 4a08476437e8 hotspot/src/share/vm/runtime/mutexLocker.hpp --- a/hotspot/src/share/vm/runtime/mutexLocker.hpp Tue Jul 14 16:28:53 2015 +0200 +++ b/hotspot/src/share/vm/runtime/mutexLocker.hpp Wed Jul 15 12:24:41 2015 -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* 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 -r 04af91b7fadd -r 4a08476437e8 hotspot/src/share/vm/runtime/vm_operations.hpp --- a/hotspot/src/share/vm/runtime/vm_operations.hpp Tue Jul 14 16:28:53 2015 +0200 +++ b/hotspot/src/share/vm/runtime/vm_operations.hpp Wed Jul 15 12:24:41 2015 -0700 @@ -101,6 +101,7 @@ template(WhiteBoxOperation) \ template(ClassLoaderStatsOperation) \ template(DumpHashtable) \ + template(DumpTouchedMethods) \ template(MarkActiveNMethods) \ template(PrintCompileQueue) \ template(PrintCodeList) \ diff -r 04af91b7fadd -r 4a08476437e8 hotspot/src/share/vm/services/diagnosticCommand.cpp --- a/hotspot/src/share/vm/services/diagnosticCommand.cpp Tue Jul 14 16:28:53 2015 +0200 +++ b/hotspot/src/share/vm/services/diagnosticCommand.cpp Wed Jul 15 12:24:41 2015 -0700 @@ -74,6 +74,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 @@ -808,3 +809,35 @@ } #endif + +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; +} diff -r 04af91b7fadd -r 4a08476437e8 hotspot/src/share/vm/services/diagnosticCommand.hpp --- a/hotspot/src/share/vm/services/diagnosticCommand.hpp Tue Jul 14 16:28:53 2015 +0200 +++ b/hotspot/src/share/vm/services/diagnosticCommand.hpp Wed Jul 15 12:24:41 2015 -0700 @@ -35,6 +35,7 @@ #include "services/diagnosticFramework.hpp" #include "utilities/macros.hpp" #include "utilities/ostream.hpp" +#include "oops/method.hpp" class HelpDCmd : public DCmdWithParser { protected: @@ -341,6 +342,22 @@ 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 -r 04af91b7fadd -r 4a08476437e8 hotspot/test/runtime/CommandLine/PrintTouchedMethods.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/runtime/CommandLine/PrintTouchedMethods.java Wed Jul 15 12:24:41 2015 -0700 @@ -0,0 +1,101 @@ +/* + * 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 + * @modules java.base/sun.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 jdk.test.lib.*; + +public class PrintTouchedMethods { + + public static void main(String args[]) throws Exception { + String[] javaArgs1 = {"-XX:-UnlockDiagnosticVMOptions", "-XX:+LogTouchedMethods", "-XX:+PrintTouchedMethodsAtExit", "TestLogTouchedMethods"}; + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(javaArgs1); + + // 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."); + + String[] javaArgs2 = {"-XX:+UnlockDiagnosticVMOptions", "-XX:+LogTouchedMethods", "-XX:+PrintTouchedMethodsAtExit", "TestLogTouchedMethods"}; + pb = ProcessTools.createJavaProcessBuilder(javaArgs2); + 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 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); + + String[] javaArgs3 = {"-XX:+UnlockDiagnosticVMOptions", "-Xint", "-XX:+LogTouchedMethods", "-XX:+PrintTouchedMethodsAtExit", "TestLogTouchedMethods"}; + pb = ProcessTools.createJavaProcessBuilder(javaArgs3); + 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 = Integer.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"); + } + } +} diff -r 04af91b7fadd -r 4a08476437e8 hotspot/test/runtime/CommandLine/TestLogTouchedMethods.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/runtime/CommandLine/TestLogTouchedMethods.java Wed Jul 15 12:24:41 2015 -0700 @@ -0,0 +1,32 @@ +/* + * 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. + */ + +/* used by PrintTouchedMethods.java */ +public class TestLogTouchedMethods { + public static void main(String[] args) { + new TestLogTouchedMethods().methodA(); + } + + public void methodA() {} // called + public void methodB() {} // this should not be called +}