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
--- 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();
--- 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.
--- 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 ;
--- 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
--- 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
--- 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());
--- 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;
}
--- 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<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->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) {
--- 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 ); }
--- 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");
--- 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") \
\
--- 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
--- 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);
--- 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
--- 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) \
--- 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<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));
// 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;
+}
--- 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:
--- /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<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);
+
+ 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");
+ }
+ }
+}
--- /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
+}