# HG changeset patch # User stuefe # Date 1558630731 -7200 # Thu May 23 18:58:51 2019 +0200 # Node ID 6f1fcc175dddcfddbc6a208cba75951c2dfe401d # Parent fb0cfce19262025138bde11df3dd4336676c6f00 [mq]: VM.events diff -r fb0cfce19262 -r 6f1fcc175ddd src/hotspot/share/compiler/compileBroker.cpp --- a/src/hotspot/share/compiler/compileBroker.cpp Thu May 23 11:37:24 2019 -0700 +++ b/src/hotspot/share/compiler/compileBroker.cpp Thu May 23 18:58:51 2019 +0200 @@ -187,7 +187,7 @@ class CompilationLog : public StringEventLog { public: - CompilationLog() : StringEventLog("Compilation events") { + CompilationLog() : StringEventLog("Compilation events", "jit") { } void log_compile(JavaThread* thread, CompileTask* task) { diff -r fb0cfce19262 -r 6f1fcc175ddd src/hotspot/share/gc/shared/collectedHeap.hpp --- a/src/hotspot/share/gc/shared/collectedHeap.hpp Thu May 23 11:37:24 2019 -0700 +++ b/src/hotspot/share/gc/shared/collectedHeap.hpp Thu May 23 18:58:51 2019 +0200 @@ -73,7 +73,7 @@ void log_heap(CollectedHeap* heap, bool before); public: - GCHeapLog() : EventLogBase("GC Heap History") {} + GCHeapLog() : EventLogBase("GC Heap History", "gc") {} void log_heap_before(CollectedHeap* heap) { log_heap(heap, true); diff -r fb0cfce19262 -r 6f1fcc175ddd src/hotspot/share/services/diagnosticCommand.cpp --- a/src/hotspot/share/services/diagnosticCommand.cpp Thu May 23 11:37:24 2019 -0700 +++ b/src/hotspot/share/services/diagnosticCommand.cpp Thu May 23 18:58:51 2019 +0200 @@ -48,6 +48,7 @@ #include "services/management.hpp" #include "services/writeableFlags.hpp" #include "utilities/debug.hpp" +#include "utilities/events.hpp" #include "utilities/formatBuffer.hpp" #include "utilities/macros.hpp" @@ -95,6 +96,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)); #if INCLUDE_JVMTI // Both JVMTI and SERVICES have to be enabled to have this dcmd DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); #endif // INCLUDE_JVMTI @@ -965,6 +967,45 @@ } //---< END >--- CodeHeap State Analytics. +EventLogDCmd::EventLogDCmd(outputStream* output, bool heap) : + DCmdWithParser(output, heap), + _log("log", "Name of log to be printed. If omitted, all logs are printed.", "STRING", false, NULL), + _max("max", "Maximum number of events to be printed (newest first). If omitted, all events are printed.", "STRING", false, NULL) +{ + _dcmdparser.add_dcmd_option(&_log); + _dcmdparser.add_dcmd_option(&_max); +} + +void EventLogDCmd::execute(DCmdSource source, TRAPS) { + const char* max_value = _max.value(); + long max = -1; + if (max_value != NULL) { + char* endptr = NULL; + max = ::strtol(max_value, &endptr, 10); + if (max == 0 && max_value == endptr) { + output()->print_cr("Invalid max option: \"%s\".", max_value); + return; + } + } + const char* log_name = _log.value(); + if (log_name != NULL) { + Events::print_one(output(), log_name, max); + } else { + Events::print_all(output(), max); + } +} + +int EventLogDCmd::num_arguments() { + ResourceMark rm; + EventLogDCmd* dcmd = new EventLogDCmd(NULL, false); + if (dcmd != NULL) { + DCmdMark mark(dcmd); + return dcmd->_dcmdparser.num_arguments(); + } else { + return 0; + } +} + void CompilerDirectivesPrintDCmd::execute(DCmdSource source, TRAPS) { DirectivesStack::print(output()); } diff -r fb0cfce19262 -r 6f1fcc175ddd src/hotspot/share/services/diagnosticCommand.hpp --- a/src/hotspot/share/services/diagnosticCommand.hpp Thu May 23 11:37:24 2019 -0700 +++ b/src/hotspot/share/services/diagnosticCommand.hpp Thu May 23 18:58:51 2019 +0200 @@ -888,4 +888,28 @@ }; #endif // INCLUDE_JVMTI +class EventLogDCmd : public DCmdWithParser { +protected: + DCmdArgument _log; + DCmdArgument _max; +public: + EventLogDCmd(outputStream* output, bool heap); + static const char* name() { + return "VM.events"; + } + static const char* description() { + return "Print VM event logs"; + } + static const char* impact() { + return "Low: Depends on event log size. "; + } + static const JavaPermission permission() { + JavaPermission p = {"java.lang.management.ManagementPermission", + "monitor", NULL}; + return p; + } + static int num_arguments(); + virtual void execute(DCmdSource source, TRAPS); +}; + #endif // SHARE_SERVICES_DIAGNOSTICCOMMAND_HPP diff -r fb0cfce19262 -r 6f1fcc175ddd src/hotspot/share/utilities/events.cpp --- a/src/hotspot/share/utilities/events.cpp Thu May 23 11:37:24 2019 -0700 +++ b/src/hotspot/share/utilities/events.cpp Thu May 23 18:58:51 2019 +0200 @@ -51,26 +51,51 @@ } // For each registered event logger, print out the current contents of -// the buffer. This is normally called when the JVM is crashing. -void Events::print_all(outputStream* out) { +// the buffer. +void Events::print_all(outputStream* out, int max) { EventLog* log = _logs; while (log != NULL) { - log->print_log_on(out); + log->print_log_on(out, max); log = log->next(); } } +// Print a single event log specified by name. +void Events::print_one(outputStream* out, const char* log_name, int max) { + EventLog* log = _logs; + int num_printed = 0; + while (log != NULL) { + if (log->matches_name(log_name)) { + log->print_log_on(out, max); + num_printed ++; + } + log = log->next(); + } + // Write a short error note if no name matched. + if (num_printed == 0) { + out->print_cr("The name \"%s\" did not match any known event log. " + "Valid event log names are:", log_name); + EventLog* log = _logs; + while (log != NULL) { + log->print_names(out); + out->cr(); + log = log->next(); + } + } +} + + void Events::print() { print_all(tty); } void Events::init() { if (LogEvents) { - _messages = new StringEventLog("Events"); - _exceptions = new ExceptionsEventLog("Internal exceptions"); - _redefinitions = new StringEventLog("Classes redefined"); - _class_unloading = new UnloadingEventLog("Classes unloaded"); - _deopt_messages = new StringEventLog("Deoptimization events"); + _messages = new StringEventLog("Events", "events"); + _exceptions = new ExceptionsEventLog("Internal exceptions", "exc"); + _redefinitions = new StringEventLog("Classes redefined", "redef"); + _class_unloading = new UnloadingEventLog("Classes unloaded", "unload"); + _deopt_messages = new StringEventLog("Deoptimization events", "deopt"); } } diff -r fb0cfce19262 -r 6f1fcc175ddd src/hotspot/share/utilities/events.hpp --- a/src/hotspot/share/utilities/events.hpp Thu May 23 11:37:24 2019 -0700 +++ b/src/hotspot/share/utilities/events.hpp Thu May 23 18:58:51 2019 +0200 @@ -29,6 +29,8 @@ #include "runtime/mutexLocker.hpp" #include "runtime/thread.hpp" #include "utilities/formatBuffer.hpp" +#include "utilities/globalDefinitions.hpp" +#include "utilities/ostream.hpp" #include "utilities/vmError.hpp" // Events and EventMark provide interfaces to log events taking place in the vm. @@ -59,7 +61,15 @@ // crashes. EventLog(); - virtual void print_log_on(outputStream* out) = 0; + // Print log to output stream. + virtual void print_log_on(outputStream* out, int max = -1) = 0; + + // returns true if log matches name. + virtual bool matches_name(const char* name) const = 0; + + // Print log names (for help output of VM.events + virtual void print_names(outputStream* out) const = 0; + }; @@ -78,16 +88,20 @@ protected: Mutex _mutex; + // Name is printed out as a header. Short name can be used as short identifier + // in VM.events command. const char* _name; + const char* _short_name; int _length; int _index; int _count; EventRecord* _records; public: - EventLogBase(const char* name, int length = LogEventsBufferEntries): + EventLogBase(const char* name, const char* short_name, int length = LogEventsBufferEntries): _mutex(Mutex::event, name, false, Monitor::_safepoint_check_never), _name(name), + _short_name(short_name), _length(length), _index(0), _count(0) { @@ -116,10 +130,16 @@ } // Print the contents of the log - void print_log_on(outputStream* out); + void print_log_on(outputStream* out, int max = -1); + + // returns true if log matches name. + bool matches_name(const char* name) const; + + // Print log names (for help output of VM.events + void print_names(outputStream* out) const; private: - void print_log_impl(outputStream* out); + void print_log_impl(outputStream* out, int max = -1); // Print a single element. A templated implementation might need to // be declared by subclasses. @@ -145,7 +165,8 @@ template class FormatStringEventLog : public EventLogBase< FormatStringLogMessage > { public: - FormatStringEventLog(const char* name, int count = LogEventsBufferEntries) : EventLogBase< FormatStringLogMessage >(name, count) {} + FormatStringEventLog(const char* name, const char* short_name, int count = LogEventsBufferEntries) + : EventLogBase< FormatStringLogMessage >(name, short_name, count) {} void logv(Thread* thread, const char* format, va_list ap) ATTRIBUTE_PRINTF(3, 0) { if (!this->should_log()) return; @@ -173,7 +194,8 @@ // Event log for class unloading events to materialize the class name in place in the log stream. class UnloadingEventLog : public EventLogBase { public: - UnloadingEventLog(const char* name, int count = LogEventsBufferEntries) : EventLogBase(name, count) {} + UnloadingEventLog(const char* name, const char* short_name, int count = LogEventsBufferEntries) + : EventLogBase(name, short_name, count) {} void log(Thread* thread, InstanceKlass* ik); }; @@ -181,7 +203,8 @@ // Event log for exceptions class ExceptionsEventLog : public ExtendedStringEventLog { public: - ExceptionsEventLog(const char* name, int count = LogEventsBufferEntries) : ExtendedStringEventLog(name, count) {} + ExceptionsEventLog(const char* name, const char* short_name, int count = LogEventsBufferEntries) + : ExtendedStringEventLog(name, short_name, count) {} void log(Thread* thread, Handle h_exception, const char* message, const char* file, int line); }; @@ -209,7 +232,13 @@ // Class unloading events static UnloadingEventLog* _class_unloading; public: - static void print_all(outputStream* out); + + // Print all event logs; limit number of events per event log to be printed with max + // (max == -1 prints all events). + static void print_all(outputStream* out, int max = -1); + + // Print a single event log specified by name. + static void print_one(outputStream* out, const char* log_name, int max = -1); // Dump all events to the tty static void print(); @@ -279,21 +308,33 @@ } } - template -inline void EventLogBase::print_log_on(outputStream* out) { +inline void EventLogBase::print_log_on(outputStream* out, int max) { if (Thread::current_or_null() == NULL) { // Not yet attached? Don't try to use locking - print_log_impl(out); + print_log_impl(out, max); } else { MutexLocker ml(&_mutex, Mutex::_no_safepoint_check_flag); - print_log_impl(out); + print_log_impl(out, max); } } +// returns true if log matches name. +template +inline bool EventLogBase::matches_name(const char* name) const { + return ::strcasecmp(name, _name) == 0 || + ::strcasecmp(name, _short_name) == 0; +} + +// Print log names (for help output of VM.events +template +inline void EventLogBase::print_names(outputStream* out) const { + out->print("\"%s\" : %s", _short_name, _name); +} + // Dump the ring buffer entries that current have entries. template -inline void EventLogBase::print_log_impl(outputStream* out) { +inline void EventLogBase::print_log_impl(outputStream* out, int max) { out->print_cr("%s (%d events):", _name, _count); if (_count == 0) { out->print_cr("No events"); @@ -301,18 +342,36 @@ return; } + int printed = 0; if (_count < _length) { for (int i = 0; i < _count; i++) { + if (max > 0 && printed == max) { + break; + } print(out, _records[i]); + printed ++; } } else { for (int i = _index; i < _length; i++) { + if (max > 0 && printed == max) { + break; + } print(out, _records[i]); + printed ++; } for (int i = 0; i < _index; i++) { + if (max > 0 && printed == max) { + break; + } print(out, _records[i]); + printed ++; } } + + if (printed == max) { + out->print_cr("...(skipped)"); + } + out->cr(); } diff -r fb0cfce19262 -r 6f1fcc175ddd test/hotspot/jtreg/serviceability/dcmd/vm/EventsTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/hotspot/jtreg/serviceability/dcmd/vm/EventsTest.java Thu May 23 18:58:51 2019 +0200 @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2015, 2017, 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. + */ + +import jdk.test.lib.dcmd.CommandExecutor; +import jdk.test.lib.dcmd.JMXExecutor; +import jdk.test.lib.process.OutputAnalyzer; +import org.testng.annotations.Test; + +/* + * @test + * @summary Test of diagnostic command VM.events + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.compiler + * java.management + * jdk.internal.jvmstat/sun.jvmstat.monitor + * @run testng EventsTest + */ +public class EventsTest { + public void run(CommandExecutor executor) { + OutputAnalyzer output = executor.execute("VM.events"); + + output.shouldContain("Compilation"); + output.shouldContain("GC"); + output.shouldContain("Deoptimization"); + output.shouldContain("Classes unloaded"); + } + + @Test + public void jmx() { + run(new JMXExecutor()); + } +}