mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-26 14:24:46 +02:00
8025692: Log what methods are touched at run-time
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
This commit is contained in:
parent
99e5ddaa45
commit
6384ca7b1f
19 changed files with 319 additions and 18 deletions
|
@ -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 @@ 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
|
||||
|
@ -1307,7 +1307,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();
|
||||
|
|
|
@ -668,7 +668,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).
|
||||
|
@ -1118,7 +1118,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.
|
||||
|
|
|
@ -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 @@ 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);
|
||||
|
@ -1225,7 +1225,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 ;
|
||||
|
|
|
@ -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_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
|
||||
|
@ -1265,7 +1265,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
|
||||
|
|
|
@ -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 @@ 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
|
||||
|
@ -1256,7 +1256,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
|
||||
|
|
|
@ -75,6 +75,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());
|
||||
|
||||
|
|
|
@ -58,14 +58,14 @@ Symbol* SymbolTable::allocate_symbol(const u1* name, int len, bool c_heap, TRAPS
|
|||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -422,6 +422,11 @@ MethodCounters* Method::build_method_counters(Method* m, TRAPS) {
|
|||
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 @@ 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->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) {
|
||||
|
|
|
@ -625,6 +625,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 ); }
|
||||
|
|
|
@ -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 @@ class Symbol : private SymbolBase {
|
|||
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");
|
||||
|
|
|
@ -2717,6 +2717,12 @@ public:
|
|||
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") \
|
||||
\
|
||||
|
|
|
@ -330,6 +330,10 @@ void print_statistics() {
|
|||
SystemDictionary::print();
|
||||
}
|
||||
|
||||
if (LogTouchedMethods && PrintTouchedMethodsAtExit) {
|
||||
Method::print_touched_methods(tty);
|
||||
}
|
||||
|
||||
if (PrintBiasedLockingStatistics) {
|
||||
BiasedLocking::print_counters();
|
||||
}
|
||||
|
@ -382,6 +386,10 @@ void print_statistics() {
|
|||
if (PrintNMTStatistics) {
|
||||
MemTracker::final_report(tty);
|
||||
}
|
||||
|
||||
if (LogTouchedMethods && PrintTouchedMethodsAtExit) {
|
||||
Method::print_touched_methods(tty);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -63,6 +63,7 @@ Monitor* StringDedupQueue_lock = NULL;
|
|||
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 @@ void mutex_init() {
|
|||
|
||||
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);
|
||||
|
|
|
@ -55,6 +55,7 @@ extern Monitor* StringDedupQueue_lock; // a lock on the string dedupli
|
|||
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
|
||||
|
|
|
@ -101,6 +101,7 @@
|
|||
template(WhiteBoxOperation) \
|
||||
template(ClassLoaderStatsOperation) \
|
||||
template(DumpHashtable) \
|
||||
template(DumpTouchedMethods) \
|
||||
template(MarkActiveNMethods) \
|
||||
template(PrintCompileQueue) \
|
||||
template(PrintCodeList) \
|
||||
|
|
|
@ -74,6 +74,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));
|
||||
|
||||
// Enhanced JMX Agent Support
|
||||
// These commands won't be exported via the DiagnosticCommandMBean until an
|
||||
|
@ -808,3 +809,35 @@ int ClassHierarchyDCmd::num_arguments() {
|
|||
}
|
||||
|
||||
#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;
|
||||
}
|
||||
|
|
|
@ -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 @@ 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:
|
||||
|
|
101
hotspot/test/runtime/CommandLine/PrintTouchedMethods.java
Normal file
101
hotspot/test/runtime/CommandLine/PrintTouchedMethods.java
Normal file
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
32
hotspot/test/runtime/CommandLine/TestLogTouchedMethods.java
Normal file
32
hotspot/test/runtime/CommandLine/TestLogTouchedMethods.java
Normal file
|
@ -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
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue