# HG changeset patch # User egahlin # Date 1540948221 -3600 # Node ID a181612f0715c2ac008885e51f44ed1081a4ae9c # Parent c401c536cea121a89316441d98a14cb8ca1bb960 8203629: Produce events in the JDK without a dependency on jdk.jfr Reviewed-by: mgronlun diff -r c401c536cea1 -r a181612f0715 src/hotspot/share/jfr/instrumentation/jfrEventClassTransformer.cpp --- a/src/hotspot/share/jfr/instrumentation/jfrEventClassTransformer.cpp Tue Oct 30 15:17:58 2018 -0700 +++ b/src/hotspot/share/jfr/instrumentation/jfrEventClassTransformer.cpp Wed Oct 31 02:10:21 2018 +0100 @@ -27,6 +27,8 @@ #include "classfile/classFileParser.hpp" #include "classfile/classFileStream.hpp" #include "classfile/javaClasses.inline.hpp" +#include "classfile/moduleEntry.hpp" +#include "classfile/modules.hpp" #include "classfile/stackMapTable.hpp" #include "classfile/verificationType.hpp" #include "interpreter/bytecodes.hpp" @@ -61,25 +63,26 @@ "J", // 1 "commit", // 2 "eventHandler", // 3 - "Ljdk/jfr/internal/handlers/EventHandler;", // 4 - "duration", // 5 - "begin", // 6 - "()V", // 7 - "isEnabled", // 8 - "()Z", // 9 - "end", // 10 - "shouldCommit", // 11 - "startTime", // 12 - "", // 13 - "jdk/jfr/FlightRecorder", // 14 - "register", // 15 - "(Ljava/lang/Class;)V", // 16 // LAST_REQUIRED_UTF8 - "StackMapTable", // 17 - "Exceptions", // 18 + "duration", // 4 + "begin", // 5 + "()V", // 6 + "isEnabled", // 7 + "()Z", // 8 + "end", // 9 + "shouldCommit", // 10 + "startTime", // 11 // LAST_REQUIRED_UTF8 + "Ljdk/jfr/internal/handlers/EventHandler;", // 12 + "Ljava/lang/Object;", // 13 + "", // 14 + "jdk/jfr/FlightRecorder", // 15 + "register", // 16 + "(Ljava/lang/Class;)V", // 17 + "StackMapTable", // 18 + "Exceptions", // 19 "LineNumberTable", // 20 "LocalVariableTable", // 21 "LocalVariableTypeTable", // 22 - "RuntimeVisibleAnnotation" // 23 + "RuntimeVisibleAnnotation", // 23 }; enum utf8_req_symbols { @@ -87,7 +90,6 @@ UTF8_REQ_J_FIELD_DESC, UTF8_REQ_commit, UTF8_REQ_eventHandler, - UTF8_REQ_eventHandler_FIELD_DESC, UTF8_REQ_duration, UTF8_REQ_begin, UTF8_REQ_EMPTY_VOID_METHOD_DESC, @@ -96,15 +98,17 @@ UTF8_REQ_end, UTF8_REQ_shouldCommit, UTF8_REQ_startTime, - UTF8_REQ_clinit, - UTF8_REQ_FlightRecorder, - UTF8_REQ_register, - UTF8_REQ_CLASS_VOID_METHOD_DESC, NOF_UTF8_REQ_SYMBOLS }; enum utf8_opt_symbols { - UTF8_OPT_StackMapTable = NOF_UTF8_REQ_SYMBOLS, + UTF8_OPT_eventHandler_FIELD_DESC = NOF_UTF8_REQ_SYMBOLS, + UTF8_OPT_LjavaLangObject, + UTF8_OPT_clinit, + UTF8_OPT_FlightRecorder, + UTF8_OPT_register, + UTF8_OPT_CLASS_VOID_METHOD_DESC, + UTF8_OPT_StackMapTable, UTF8_OPT_Exceptions, UTF8_OPT_LineNumberTable, UTF8_OPT_LocalVariableTable, @@ -353,7 +357,7 @@ static unsigned int unused_hash = 0; static const char value_name[] = "value"; -static bool has_registered_annotation(const InstanceKlass* ik, const Symbol* annotation_type, bool& value) { +static bool has_annotation(const InstanceKlass* ik, const Symbol* annotation_type, bool& value) { assert(annotation_type != NULL, "invariant"); AnnotationArray* class_annotations = ik->class_annotations(); if (class_annotations == NULL) { @@ -383,16 +387,51 @@ return false; } -static bool registered_annotation_value(const InstanceKlass* ik, const Symbol* const registered_symbol) { - assert(registered_symbol != NULL, "invariant"); +// Evaluate to the value of the first found Symbol* annotation type. +// Searching moves upwards in the klass hierarchy in order to support +// inherited annotations in addition to the ability to override. +static bool annotation_value(const InstanceKlass* ik, const Symbol* annotation_type, bool& value) { assert(ik != NULL, "invariant"); + assert(annotation_type != NULL, "invariant"); assert(JdkJfrEvent::is_a(ik), "invariant"); - bool registered_value = false; - if (has_registered_annotation(ik, registered_symbol, registered_value)) { - return registered_value; + if (has_annotation(ik, annotation_type, value)) { + return true; + } + InstanceKlass* const super = InstanceKlass::cast(ik->super()); + return super != NULL && JdkJfrEvent::is_a(super) ? annotation_value(super, annotation_type, value) : false; +} + +static const char jdk_jfr_module_name[] = "jdk.jfr"; + +static bool java_base_can_read_jdk_jfr() { + static bool can_read = false; + if (can_read) { + return true; } - InstanceKlass* super = InstanceKlass::cast(ik->super()); - return registered_annotation_value(super, registered_symbol); + static Symbol* jdk_jfr_module_symbol = NULL; + if (jdk_jfr_module_symbol == NULL) { + jdk_jfr_module_symbol = SymbolTable::lookup_only(jdk_jfr_module_name, sizeof jdk_jfr_module_name - 1, unused_hash); + if (jdk_jfr_module_symbol == NULL) { + return false; + } + } + assert(jdk_jfr_module_symbol != NULL, "invariant"); + ModuleEntryTable* const table = Modules::get_module_entry_table(Handle()); + assert(table != NULL, "invariant"); + const ModuleEntry* const java_base_module = table->javabase_moduleEntry(); + if (java_base_module == NULL) { + return false; + } + assert(java_base_module != NULL, "invariant"); + ModuleEntry* const jdk_jfr_module = table->lookup_only(jdk_jfr_module_symbol); + if (jdk_jfr_module == NULL) { + return false; + } + assert(jdk_jfr_module != NULL, "invariant"); + if (java_base_module->can_read(jdk_jfr_module)) { + can_read = true; + } + return can_read; } static const char registered_constant[] = "Ljdk/jfr/Registered;"; @@ -400,13 +439,23 @@ // Evaluate to the value of the first found "Ljdk/jfr/Registered;" annotation. // Searching moves upwards in the klass hierarchy in order to support // inherited annotations in addition to the ability to override. -static bool should_register_klass(const InstanceKlass* ik) { - static const Symbol* const registered_symbol = SymbolTable::lookup_only(registered_constant, - sizeof registered_constant - 1, - unused_hash); +static bool should_register_klass(const InstanceKlass* ik, bool& untypedEventHandler) { + assert(ik != NULL, "invariant"); + assert(JdkJfrEvent::is_a(ik), "invariant"); + assert(!untypedEventHandler, "invariant"); + static const Symbol* registered_symbol = NULL; + if (registered_symbol == NULL) { + registered_symbol = SymbolTable::lookup_only(registered_constant, sizeof registered_constant - 1, unused_hash); + if (registered_symbol == NULL) { + return false; + } + } assert(registered_symbol != NULL, "invariant"); - return registered_annotation_value(ik, registered_symbol); + bool value = false; // to be set by annotation_value + untypedEventHandler = !(annotation_value(ik, registered_symbol, value) || java_base_can_read_jdk_jfr()); + return value; } + /* * Map an utf8 constant back to its CONSTANT_UTF8_INFO */ @@ -450,6 +499,9 @@ u2 orig_cp_len, u2& number_of_new_constants, TRAPS) { + assert(cls_name_index != invalid_cp_index, "invariant"); + assert(method_index != invalid_cp_index, "invariant"); + assert(desc_index != invalid_cp_index, "invariant"); assert(is_index_within_range(cls_name_index, orig_cp_len, number_of_new_constants), "invariant"); assert(is_index_within_range(method_index, orig_cp_len, number_of_new_constants), "invariant"); assert(is_index_within_range(desc_index, orig_cp_len, number_of_new_constants), "invariant"); @@ -477,9 +529,9 @@ TRAPS) { assert(utf8_indexes != NULL, "invariant"); return add_method_ref_info(writer, - utf8_indexes[UTF8_REQ_FlightRecorder], - utf8_indexes[UTF8_REQ_register], - utf8_indexes[UTF8_REQ_CLASS_VOID_METHOD_DESC], + utf8_indexes[UTF8_OPT_FlightRecorder], + utf8_indexes[UTF8_OPT_register], + utf8_indexes[UTF8_OPT_CLASS_VOID_METHOD_DESC], orig_cp_len, number_of_new_constants, THREAD); @@ -495,8 +547,8 @@ * } */ static jlong add_field_info(JfrBigEndianWriter& writer, u2 name_index, u2 desc_index, bool is_static = false) { - assert(name_index > 0, "invariant"); - assert(desc_index > 0, "invariant"); + assert(name_index != invalid_cp_index, "invariant"); + assert(desc_index != invalid_cp_index, "invariant"); DEBUG_ONLY(const jlong start_offset = writer.current_offset();) writer.write(JVM_ACC_SYNTHETIC | JVM_ACC_PRIVATE | (is_static ? JVM_ACC_STATIC : JVM_ACC_TRANSIENT)); // flags writer.write(name_index); @@ -507,11 +559,11 @@ return writer.current_offset(); } -static u2 add_field_infos(JfrBigEndianWriter& writer, const u2* utf8_indexes) { +static u2 add_field_infos(JfrBigEndianWriter& writer, const u2* utf8_indexes, bool untypedEventHandler) { assert(utf8_indexes != NULL, "invariant"); add_field_info(writer, utf8_indexes[UTF8_REQ_eventHandler], - utf8_indexes[UTF8_REQ_eventHandler_FIELD_DESC], + untypedEventHandler ? utf8_indexes[UTF8_OPT_LjavaLangObject] : utf8_indexes[UTF8_OPT_eventHandler_FIELD_DESC], true); // static add_field_info(writer, @@ -989,7 +1041,8 @@ // This is to ensure that padding can be done // where needed and to simplify size calculations. static const u2 injected_code_length = 8; - const u2 name_index = utf8_indexes[UTF8_REQ_clinit]; + const u2 name_index = utf8_indexes[UTF8_OPT_clinit]; + assert(name_index != invalid_cp_index, "invariant"); const u2 desc_index = utf8_indexes[UTF8_REQ_EMPTY_VOID_METHOD_DESC]; const u2 max_stack = MAX2(clinit_method != NULL ? clinit_method->verifier_max_stack() : 1, 1); const u2 max_locals = MAX2(clinit_method != NULL ? clinit_method->max_locals() : 0, 0); @@ -1138,59 +1191,58 @@ u2* const utf8_indexes, u2 orig_cp_len, const Method* clinit_method, + bool register_klass, + bool untypedEventHandler, TRAPS) { assert(utf8_indexes != NULL, "invariant"); u2 added_cp_entries = 0; // resolve all required symbols for (u2 index = 0; index < NOF_UTF8_REQ_SYMBOLS; ++index) { - utf8_indexes[index] = find_or_add_utf8_info(writer, - ik, - utf8_constants[index], - orig_cp_len, - added_cp_entries, - THREAD); + utf8_indexes[index] = find_or_add_utf8_info(writer, ik, utf8_constants[index], orig_cp_len, added_cp_entries, THREAD); } - // Now determine optional constants (mainly "Code" attributes) + + // resolve optional constants + utf8_indexes[UTF8_OPT_eventHandler_FIELD_DESC] = untypedEventHandler ? invalid_cp_index : + find_or_add_utf8_info(writer, ik, utf8_constants[UTF8_OPT_eventHandler_FIELD_DESC], orig_cp_len, added_cp_entries, THREAD); + + utf8_indexes[UTF8_OPT_LjavaLangObject] = untypedEventHandler ? + find_or_add_utf8_info(writer, ik, utf8_constants[UTF8_OPT_LjavaLangObject], orig_cp_len, added_cp_entries, THREAD) : invalid_cp_index; + + if (register_klass) { + utf8_indexes[UTF8_OPT_clinit] = + find_or_add_utf8_info(writer, ik, utf8_constants[UTF8_OPT_clinit], orig_cp_len, added_cp_entries, THREAD); + utf8_indexes[UTF8_OPT_FlightRecorder] = + find_or_add_utf8_info(writer, ik, utf8_constants[UTF8_OPT_FlightRecorder], orig_cp_len, added_cp_entries, THREAD); + utf8_indexes[UTF8_OPT_register] = + find_or_add_utf8_info(writer, ik, utf8_constants[UTF8_OPT_register], orig_cp_len, added_cp_entries, THREAD); + utf8_indexes[UTF8_OPT_CLASS_VOID_METHOD_DESC] = + find_or_add_utf8_info(writer, ik, utf8_constants[UTF8_OPT_CLASS_VOID_METHOD_DESC], orig_cp_len, added_cp_entries, THREAD); + } else { + utf8_indexes[UTF8_OPT_clinit] = invalid_cp_index; + utf8_indexes[UTF8_OPT_FlightRecorder] = invalid_cp_index; + utf8_indexes[UTF8_OPT_register] = invalid_cp_index; + utf8_indexes[UTF8_OPT_CLASS_VOID_METHOD_DESC] = invalid_cp_index; + } + if (clinit_method != NULL && clinit_method->has_stackmap_table()) { utf8_indexes[UTF8_OPT_StackMapTable] = - find_or_add_utf8_info(writer, - ik, - utf8_constants[UTF8_OPT_StackMapTable], - orig_cp_len, - added_cp_entries, - THREAD); + find_or_add_utf8_info(writer, ik, utf8_constants[UTF8_OPT_StackMapTable], orig_cp_len, added_cp_entries, THREAD); } else { utf8_indexes[UTF8_OPT_StackMapTable] = invalid_cp_index; } if (clinit_method != NULL && clinit_method->has_linenumber_table()) { utf8_indexes[UTF8_OPT_LineNumberTable] = - find_or_add_utf8_info(writer, - ik, - utf8_constants[UTF8_OPT_LineNumberTable], - orig_cp_len, - added_cp_entries, - THREAD); + find_or_add_utf8_info(writer, ik, utf8_constants[UTF8_OPT_LineNumberTable], orig_cp_len, added_cp_entries, THREAD); } else { utf8_indexes[UTF8_OPT_LineNumberTable] = invalid_cp_index; } if (clinit_method != NULL && clinit_method->has_localvariable_table()) { utf8_indexes[UTF8_OPT_LocalVariableTable] = - find_or_add_utf8_info(writer, - ik, - utf8_constants[UTF8_OPT_LocalVariableTable], - orig_cp_len, - added_cp_entries, - THREAD); - + find_or_add_utf8_info(writer, ik, utf8_constants[UTF8_OPT_LocalVariableTable], orig_cp_len, added_cp_entries, THREAD); utf8_indexes[UTF8_OPT_LocalVariableTypeTable] = - find_or_add_utf8_info(writer, - ik, - utf8_constants[UTF8_OPT_LocalVariableTypeTable], - orig_cp_len, - added_cp_entries, - THREAD); + find_or_add_utf8_info(writer, ik, utf8_constants[UTF8_OPT_LocalVariableTypeTable], orig_cp_len, added_cp_entries, THREAD); } else { utf8_indexes[UTF8_OPT_LocalVariableTable] = invalid_cp_index; utf8_indexes[UTF8_OPT_LocalVariableTypeTable] = invalid_cp_index; @@ -1207,7 +1259,8 @@ // If the class already has a clinit method // we need to take that into account const Method* clinit_method = ik->class_initializer(); - const bool register_klass = should_register_klass(ik); + bool untypedEventHandler = false; + const bool register_klass = should_register_klass(ik, untypedEventHandler); const ClassFileStream* const orig_stream = parser.clone_stream(); const int orig_stream_size = orig_stream->length(); assert(orig_stream->current_offset() == 0, "invariant"); @@ -1241,7 +1294,8 @@ // Resolve_utf8_indexes will be conservative in attempting to // locate an existing UTF8_INFO; it will only append constants // that is absolutely required - u2 number_of_new_constants = resolve_utf8_indexes(writer, ik, utf8_indexes, orig_cp_len, clinit_method, THREAD); + u2 number_of_new_constants = + resolve_utf8_indexes(writer, ik, utf8_indexes, orig_cp_len, clinit_method, register_klass, untypedEventHandler, THREAD); // UTF8_INFO entries now added to the constant pool // In order to invoke a method we would need additional // constants, JVM_CONSTANT_Class, JVM_CONSTANT_NameAndType @@ -1274,7 +1328,7 @@ assert(writer.is_valid(), "invariant"); // We are sitting just after the original number of field_infos // so this is a position where we can add (append) new field_infos - const u2 number_of_new_fields = add_field_infos(writer, utf8_indexes); + const u2 number_of_new_fields = add_field_infos(writer, utf8_indexes, untypedEventHandler); assert(writer.is_valid(), "invariant"); const jlong new_method_len_offset = writer.current_offset(); // Additional field_infos added, update classfile fields_count diff -r c401c536cea1 -r a181612f0715 src/hotspot/share/jfr/jni/jfrGetAllEventClasses.cpp --- a/src/hotspot/share/jfr/jni/jfrGetAllEventClasses.cpp Tue Oct 30 15:17:58 2018 -0700 +++ b/src/hotspot/share/jfr/jni/jfrGetAllEventClasses.cpp Wed Oct 31 02:10:21 2018 +0100 @@ -132,7 +132,7 @@ DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD)); initialize(THREAD); assert(empty_java_util_arraylist != NULL, "should have been setup already!"); - static const char jdk_jfr_event_name[] = "jdk/jfr/Event"; + static const char jdk_jfr_event_name[] = "jdk/internal/event/Event"; unsigned int unused_hash = 0; Symbol* const event_klass_name = SymbolTable::lookup_only(jdk_jfr_event_name, sizeof jdk_jfr_event_name - 1, unused_hash); diff -r c401c536cea1 -r a181612f0715 src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceId.cpp --- a/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceId.cpp Tue Oct 30 15:17:58 2018 -0700 +++ b/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceId.cpp Wed Oct 31 02:10:21 2018 +0100 @@ -76,22 +76,43 @@ return atomic_inc(&cld_id_counter) << TRACE_ID_SHIFT; } +static bool found_jdk_internal_event_klass = false; static bool found_jdk_jfr_event_klass = false; static void check_klass(const Klass* klass) { assert(klass != NULL, "invariant"); - if (found_jdk_jfr_event_klass) { + if (found_jdk_internal_event_klass && found_jdk_jfr_event_klass) { return; } + static const Symbol* jdk_internal_event_sym = NULL; + if (jdk_internal_event_sym == NULL) { + // setup when loading the first TypeArrayKlass (Universe::genesis) hence single threaded invariant + jdk_internal_event_sym = SymbolTable::new_permanent_symbol("jdk/internal/event/Event", Thread::current()); + } + assert(jdk_internal_event_sym != NULL, "invariant"); + static const Symbol* jdk_jfr_event_sym = NULL; if (jdk_jfr_event_sym == NULL) { // setup when loading the first TypeArrayKlass (Universe::genesis) hence single threaded invariant jdk_jfr_event_sym = SymbolTable::new_permanent_symbol("jdk/jfr/Event", Thread::current()); } assert(jdk_jfr_event_sym != NULL, "invariant"); - if (jdk_jfr_event_sym == klass->name() && klass->class_loader() == NULL) { - found_jdk_jfr_event_klass = true; - JfrTraceId::tag_as_jdk_jfr_event(klass); + const Symbol* const klass_name = klass->name(); + + if (!found_jdk_internal_event_klass) { + if (jdk_internal_event_sym == klass_name && klass->class_loader() == NULL) { + found_jdk_internal_event_klass = true; + JfrTraceId::tag_as_jdk_jfr_event(klass); + return; + } + } + + if (!found_jdk_jfr_event_klass) { + if (jdk_jfr_event_sym == klass_name && klass->class_loader() == NULL) { + found_jdk_jfr_event_klass = true; + JfrTraceId::tag_as_jdk_jfr_event(klass); + return; + } } } diff -r c401c536cea1 -r a181612f0715 src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp --- a/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp Tue Oct 30 15:17:58 2018 -0700 +++ b/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp Wed Oct 31 02:10:21 2018 +0100 @@ -112,10 +112,8 @@ inline void JfrTraceId::tag_as_jdk_jfr_event(const Klass* klass) { assert(klass != NULL, "invariant"); - assert(IS_NOT_AN_EVENT_KLASS(klass), "invariant"); SET_TAG(klass, JDK_JFR_EVENT_KLASS); assert(IS_JDK_JFR_EVENT_KLASS(klass), "invariant"); - assert(IS_NOT_AN_EVENT_SUB_KLASS(klass), "invariant"); } inline bool JfrTraceId::is_jdk_jfr_event_sub(const Klass* k) { @@ -125,7 +123,7 @@ inline void JfrTraceId::tag_as_jdk_jfr_event_sub(const Klass* k) { assert(k != NULL, "invariant"); - if (IS_NOT_AN_EVENT_KLASS(k)) { + if (IS_NOT_AN_EVENT_SUB_KLASS(k)) { SET_TAG(k, JDK_JFR_EVENT_SUBKLASS); } assert(IS_JDK_JFR_EVENT_SUBKLASS(k), "invariant"); diff -r c401c536cea1 -r a181612f0715 src/java.base/share/classes/jdk/internal/event/Event.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/jdk/internal/event/Event.java Wed Oct 31 02:10:21 2018 +0100 @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2018, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +package jdk.internal.event; + +/** + * Base class for events, to be subclassed in order to define events and their + * fields. + */ +public abstract class Event { + /** + * Sole constructor, for invocation by subclass constructors, typically + * implicit. + */ + protected Event() { + } + + /** + * Starts the timing of this event. + */ + public void begin() { + } + + /** + * Ends the timing of this event. + * + * The {@code end} method must be invoked after the {@code begin} method. + */ + public void end() { + } + + /** + * Writes the field values, time stamp, and event duration. + *

+ * If the event starts with an invocation of the {@code begin} method, but does + * not end with an explicit invocation of the {@code end} method, then the event + * ends when the {@code commit} method is invoked. + */ + public void commit() { + } + + /** + * Returns {@code true} if the event is enabled, {@code false} otherwise + * + * @return {@code true} if event is enabled, {@code false} otherwise + */ + public boolean isEnabled() { + return false; + } + + /** + * Returns {@code true} if the event is enabled and if the duration is within + * the threshold for the event, {@code false} otherwise. + * + * @return {@code true} if the event can be written, {@code false} otherwise + */ + public boolean shouldCommit() { + return false; + } + + /** + * Sets a field value. + * + * @param index the index of the field to set + * @param value value to set, can be {@code null} + * @throws UnsupportedOperationException if functionality is not supported + * @throws IndexOutOfBoundsException if {@code index} is less than {@code 0} or + * greater than or equal to the number of fields specified for the event + */ + public void set(int index, Object value) { + } +} diff -r c401c536cea1 -r a181612f0715 src/java.base/share/classes/module-info.java --- a/src/java.base/share/classes/module-info.java Tue Oct 30 15:17:58 2018 -0700 +++ b/src/java.base/share/classes/module-info.java Wed Oct 31 02:10:21 2018 +0100 @@ -136,6 +136,8 @@ java.security.sasl; exports jdk.internal to jdk.jfr; + exports jdk.internal.event to + jdk.jfr; exports jdk.internal.jimage to jdk.jlink; exports jdk.internal.jimage.decompressor to diff -r c401c536cea1 -r a181612f0715 src/jdk.jfr/share/classes/jdk/jfr/Event.java --- a/src/jdk.jfr/share/classes/jdk/jfr/Event.java Tue Oct 30 15:17:58 2018 -0700 +++ b/src/jdk.jfr/share/classes/jdk/jfr/Event.java Wed Oct 31 02:10:21 2018 +0100 @@ -88,7 +88,7 @@ @Enabled(true) @StackTrace(true) @Registered(true) -abstract public class Event { +abstract public class Event extends jdk.internal.event.Event { /** * Sole constructor, for invocation by subclass constructors, typically * implicit. diff -r c401c536cea1 -r a181612f0715 src/jdk.jfr/share/classes/jdk/jfr/internal/EventControl.java --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/EventControl.java Tue Oct 30 15:17:58 2018 -0700 +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/EventControl.java Wed Oct 31 02:10:21 2018 +0100 @@ -41,7 +41,6 @@ import jdk.internal.module.Modules; import jdk.jfr.AnnotationElement; import jdk.jfr.Enabled; -import jdk.jfr.Event; import jdk.jfr.Name; import jdk.jfr.Period; import jdk.jfr.SettingControl; @@ -109,7 +108,7 @@ } } - EventControl(PlatformEventType es, Class eventClass) { + EventControl(PlatformEventType es, Class eventClass) { this(es); defineSettings(eventClass); } diff -r c401c536cea1 -r a181612f0715 src/jdk.jfr/share/classes/jdk/jfr/internal/EventHandlerCreator.java --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/EventHandlerCreator.java Tue Oct 30 15:17:58 2018 -0700 +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/EventHandlerCreator.java Wed Oct 31 02:10:21 2018 +0100 @@ -93,11 +93,11 @@ return EventHandler.class.getName() + id + SUFFIX; } - public EventHandlerCreator(long id, List settingInfos, EventType type, Class eventClass) { + public EventHandlerCreator(long id, List settingInfos, EventType type, Class eventClass) { this(id, settingInfos, createFieldInfos(eventClass, type)); } - private static List createFieldInfos(Class eventClass, EventType type) throws Error { + private static List createFieldInfos(Class eventClass, EventType type) throws Error { List fieldInfos = new ArrayList<>(); for (ValueDescriptor v : type.getFields()) { // Only value descriptors that are not fields on the event class. diff -r c401c536cea1 -r a181612f0715 src/jdk.jfr/share/classes/jdk/jfr/internal/EventInstrumentation.java --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/EventInstrumentation.java Tue Oct 30 15:17:58 2018 -0700 +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/EventInstrumentation.java Wed Oct 31 02:10:21 2018 +0100 @@ -102,6 +102,7 @@ private static final Type ANNOTATION_TYPE_ENABLED = Type.getType(Enabled.class); private static final Type TYPE_EVENT_HANDLER = Type.getType(EventHandler.class); private static final Type TYPE_SETTING_CONTROL = Type.getType(SettingControl.class); + private static final Type TYPE_OBJECT = Type.getType(Object.class); private static final Method METHOD_COMMIT = new Method("commit", Type.VOID_TYPE, new Type[0]); private static final Method METHOD_BEGIN = new Method("begin", Type.VOID_TYPE, new Type[0]); private static final Method METHOD_END = new Method("end", Type.VOID_TYPE, new Type[0]); @@ -117,6 +118,7 @@ private final Method writeMethod; private final String eventHandlerXInternalName; private final String eventName; + private final boolean untypedEventHandler; private boolean guardHandlerReference; private Class superClass; @@ -125,11 +127,20 @@ this.classNode = createClassNode(bytes); this.settingInfos = buildSettingInfos(superClass, classNode); this.fieldInfos = buildFieldInfos(superClass, classNode); + this.untypedEventHandler = hasUntypedHandler(); this.writeMethod = makeWriteMethod(fieldInfos); this.eventHandlerXInternalName = ASMToolkit.getInternalName(EventHandlerCreator.makeEventHandlerName(id)); String n = annotationValue(classNode, ANNOTATION_TYPE_NAME.getDescriptor(), String.class); this.eventName = n == null ? classNode.name.replace("/", ".") : n; + } + private boolean hasUntypedHandler() { + for (FieldNode field : classNode.fields) { + if (FIELD_EVENT_HANDLER.equals(field.name)) { + return field.desc.equals(TYPE_OBJECT.getDescriptor()); + } + } + throw new InternalError("Class missing handler field"); } public String getClassName() { @@ -225,7 +236,7 @@ } } } - for (Class c = superClass; c != Event.class; c = c.getSuperclass()) { + for (Class c = superClass; c != jdk.internal.event.Event.class; c = c.getSuperclass()) { for (java.lang.reflect.Method method : c.getDeclaredMethods()) { if (!methodSet.contains(method.getName())) { // skip private method in base classes @@ -249,7 +260,6 @@ } } return settingInfos; - } private static List buildFieldInfos(Class superClass, ClassNode classNode) { @@ -264,14 +274,13 @@ fieldInfos.add(new FieldInfo("startTime", Type.LONG_TYPE.getDescriptor(), classNode.name)); fieldInfos.add(new FieldInfo("duration", Type.LONG_TYPE.getDescriptor(), classNode.name)); for (FieldNode field : classNode.fields) { - String className = Type.getType(field.desc).getClassName(); - if (!fieldSet.contains(field.name) && isValidField(field.access, className)) { + if (!fieldSet.contains(field.name) && isValidField(field.access, Type.getType(field.desc).getClassName())) { FieldInfo fi = new FieldInfo(field.name, field.desc, classNode.name); fieldInfos.add(fi); fieldSet.add(field.name); } } - for (Class c = superClass; c != Event.class; c = c.getSuperclass()) { + for (Class c = superClass; c != jdk.internal.event.Event.class; c = c.getSuperclass()) { for (Field field : c.getDeclaredFields()) { // skip private field in base classes if (!Modifier.isPrivate(field.getModifiers())) { @@ -321,10 +330,10 @@ updateMethod(METHOD_IS_ENABLED, methodVisitor -> { Label nullLabel = new Label(); if (guardHandlerReference) { - methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, getInternalClassName(), FIELD_EVENT_HANDLER, TYPE_EVENT_HANDLER.getDescriptor()); + getEventHandler(methodVisitor); methodVisitor.visitJumpInsn(Opcodes.IFNULL, nullLabel); } - methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, getInternalClassName(), FIELD_EVENT_HANDLER, TYPE_EVENT_HANDLER.getDescriptor()); + getEventHandler(methodVisitor); ASMToolkit.invokeVirtual(methodVisitor, TYPE_EVENT_HANDLER.getInternalName(), METHOD_IS_ENABLED); methodVisitor.visitInsn(Opcodes.IRETURN); if (guardHandlerReference) { @@ -408,7 +417,7 @@ // eventHandler.write(...); // } methodVisitor.visitJumpInsn(Opcodes.IFEQ, end); - methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, getInternalClassName(), FIELD_EVENT_HANDLER, Type.getDescriptor(EventHandler.class)); + getEventHandler(methodVisitor); methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, eventHandlerXInternalName); for (FieldInfo fi : fieldInfos) { @@ -426,8 +435,8 @@ // MyEvent#shouldCommit() updateMethod(METHOD_EVENT_SHOULD_COMMIT, methodVisitor -> { Label fail = new Label(); - // if (!eventHandler.shoouldCommit(duration) goto fail; - methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, getInternalClassName(), FIELD_EVENT_HANDLER, Type.getDescriptor(EventHandler.class)); + // if (!eventHandler.shouldCommit(duration) goto fail; + getEventHandler(methodVisitor); methodVisitor.visitVarInsn(Opcodes.ALOAD, 0); methodVisitor.visitFieldInsn(Opcodes.GETFIELD, getInternalClassName(), FIELD_DURATION, "J"); ASMToolkit.invokeVirtual(methodVisitor, TYPE_EVENT_HANDLER.getInternalName(), METHOD_EVENT_HANDLER_SHOULD_COMMIT); @@ -435,7 +444,11 @@ for (SettingInfo si : settingInfos) { // if (!settingsMethod(eventHandler.settingX)) goto fail; methodVisitor.visitIntInsn(Opcodes.ALOAD, 0); - methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, getInternalClassName(), FIELD_EVENT_HANDLER, Type.getDescriptor(EventHandler.class)); + if (untypedEventHandler) { + methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, getInternalClassName(), FIELD_EVENT_HANDLER, TYPE_OBJECT.getDescriptor()); + } else { + methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, getInternalClassName(), FIELD_EVENT_HANDLER, Type.getDescriptor(EventHandler.class)); + } methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, eventHandlerXInternalName); methodVisitor.visitFieldInsn(Opcodes.GETFIELD, eventHandlerXInternalName, si.fieldName, TYPE_SETTING_CONTROL.getDescriptor()); methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, si.internalSettingName); @@ -452,6 +465,15 @@ }); } + private void getEventHandler(MethodVisitor methodVisitor) { + if (untypedEventHandler) { + methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, getInternalClassName(), FIELD_EVENT_HANDLER, TYPE_OBJECT.getDescriptor()); + methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, TYPE_EVENT_HANDLER.getInternalName()); + } else { + methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, getInternalClassName(), FIELD_EVENT_HANDLER, Type.getDescriptor(EventHandler.class)); + } + } + private void makeUninstrumented() { updateExistingWithReturnFalse(METHOD_EVENT_SHOULD_COMMIT); updateExistingWithReturnFalse(METHOD_IS_ENABLED); diff -r c401c536cea1 -r a181612f0715 src/jdk.jfr/share/classes/jdk/jfr/internal/JVM.java --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/JVM.java Tue Oct 30 15:17:58 2018 -0700 +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/JVM.java Wed Oct 31 02:10:21 2018 +0100 @@ -106,11 +106,11 @@ public native void endRecording(); /** - * Return a list of all classes deriving from {@link Event} + * Return a list of all classes deriving from {@link jdk.internal.event.Event} * * @return list of event classes. */ - public native List> getAllEventClasses(); + public native List> getAllEventClasses(); /** * Return a count of the number of unloaded classes deriving from {@link Event} diff -r c401c536cea1 -r a181612f0715 src/jdk.jfr/share/classes/jdk/jfr/internal/JVMUpcalls.java --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/JVMUpcalls.java Tue Oct 30 15:17:58 2018 -0700 +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/JVMUpcalls.java Wed Oct 31 02:10:21 2018 +0100 @@ -26,7 +26,6 @@ import java.lang.reflect.Modifier; -import jdk.jfr.Event; import jdk.jfr.internal.handlers.EventHandler; import jdk.jfr.internal.instrument.JDKEvents; @@ -53,8 +52,8 @@ */ static byte[] onRetransform(long traceId, boolean dummy, Class clazz, byte[] oldBytes) throws Throwable { try { - if (Event.class.isAssignableFrom(clazz) && !Modifier.isAbstract(clazz.getModifiers())) { - EventHandler handler = Utils.getHandler(clazz.asSubclass(Event.class)); + if (jdk.internal.event.Event.class.isAssignableFrom(clazz) && !Modifier.isAbstract(clazz.getModifiers())) { + EventHandler handler = Utils.getHandler(clazz.asSubclass(jdk.internal.event.Event.class)); if (handler == null) { Logger.log(LogTag.JFR_SYSTEM, LogLevel.INFO, "No event handler found for " + clazz.getName() + ". Ignoring instrumentation request."); // Probably triggered by some other agent diff -r c401c536cea1 -r a181612f0715 src/jdk.jfr/share/classes/jdk/jfr/internal/MetadataRepository.java --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/MetadataRepository.java Tue Oct 30 15:17:58 2018 -0700 +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/MetadataRepository.java Wed Oct 31 02:10:21 2018 +0100 @@ -33,6 +33,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -56,6 +57,7 @@ private final List nativeControls = new ArrayList(100); private final TypeLibrary typeLibrary = TypeLibrary.getInstance(); private final SettingsManager settingsManager = new SettingsManager(); + private final Map> mirrors = new HashMap<>(); private boolean staleMetadata = true; private boolean unregistered; private long lastUnloaded = -1; @@ -105,7 +107,7 @@ return eventTypes; } - public synchronized EventType getEventType(Class eventClass) { + public synchronized EventType getEventType(Class eventClass) { EventHandler h = getHandler(eventClass); if (h != null && h.isRegistered()) { return h.getEventType(); @@ -121,15 +123,20 @@ } // never registered, ignore call } - public synchronized EventType register(Class eventClass) { + public synchronized EventType register(Class eventClass) { return register(eventClass, Collections.emptyList(), Collections.emptyList()); } - public synchronized EventType register(Class eventClass, List dynamicAnnotations, List dynamicFields) { + public synchronized EventType register(Class eventClass, List dynamicAnnotations, List dynamicFields) { Utils.checkRegisterPermission(); EventHandler handler = getHandler(eventClass); if (handler == null) { - handler = makeHandler(eventClass, dynamicAnnotations, dynamicFields); + if (eventClass.getAnnotation(MirrorEvent.class) != null) { + // don't register mirrors + return null; + } + PlatformEventType pe = findMirrorType(eventClass); + handler = makeHandler(eventClass, pe, dynamicAnnotations, dynamicFields); } handler.setRegistered(true); typeLibrary.addType(handler.getPlatformEventType()); @@ -143,16 +150,32 @@ return handler.getEventType(); } - private EventHandler getHandler(Class eventClass) { + private PlatformEventType findMirrorType(Class eventClass) throws InternalError { + String fullName = eventClass.getModule().getName() + ":" + eventClass.getName(); + Class mirrorClass = mirrors.get(fullName); + if (mirrorClass == null) { + return null; // not a mirror + } + Utils.verifyMirror(mirrorClass, eventClass); + PlatformEventType et = (PlatformEventType) TypeLibrary.createType(mirrorClass); + typeLibrary.removeType(et.getId()); + long id = Type.getTypeId(eventClass); + et.setId(id); + return et; + } + + private EventHandler getHandler(Class eventClass) { Utils.ensureValidEventSubclass(eventClass); SecuritySupport.makeVisibleToJFR(eventClass); Utils.ensureInitialized(eventClass); return Utils.getHandler(eventClass); } - private EventHandler makeHandler(Class eventClass, List dynamicAnnotations, List dynamicFields) throws InternalError { + private EventHandler makeHandler(Class eventClass, PlatformEventType pEventType, List dynamicAnnotations, List dynamicFields) throws InternalError { SecuritySupport.addHandlerExport(eventClass); - PlatformEventType pEventType = (PlatformEventType) TypeLibrary.createType(eventClass, dynamicAnnotations, dynamicFields); + if (pEventType == null) { + pEventType = (PlatformEventType) TypeLibrary.createType(eventClass, dynamicAnnotations, dynamicFields); + } EventType eventType = PrivateAccess.getInstance().newEventType(pEventType); EventControl ec = new EventControl(pEventType, eventClass); Class handlerClass = null; @@ -198,9 +221,9 @@ } private static List getEventHandlers() { - List> allEventClasses = jvm.getAllEventClasses(); + List> allEventClasses = jvm.getAllEventClasses(); List eventHandlers = new ArrayList<>(allEventClasses.size()); - for (Class clazz : allEventClasses) { + for (Class clazz : allEventClasses) { EventHandler eh = Utils.getHandler(clazz); if (eh != null) { eventHandlers.add(eh); @@ -252,9 +275,9 @@ long unloaded = jvm.getUnloadedEventClassCount(); if (this.lastUnloaded != unloaded) { this.lastUnloaded = unloaded; - List> eventClasses = jvm.getAllEventClasses(); + List> eventClasses = jvm.getAllEventClasses(); HashSet knownIds = new HashSet<>(eventClasses.size()); - for (Class ec: eventClasses) { + for (Class ec: eventClasses) { knownIds.add(Type.getTypeId(ec)); } for (Type type : typeLibrary.getTypes()) { @@ -270,8 +293,18 @@ } } - synchronized public void setUnregistered() { + synchronized void setUnregistered() { unregistered = true; } + public synchronized void registerMirror(Class eventClass) { + MirrorEvent me = eventClass.getAnnotation(MirrorEvent.class); + if (me != null) { + String fullName = me.module() + ":" + me.className(); + mirrors.put(fullName, eventClass); + return; + } + throw new InternalError("Mirror class must have annotation " + MirrorEvent.class.getName()); + } + } diff -r c401c536cea1 -r a181612f0715 src/jdk.jfr/share/classes/jdk/jfr/internal/MirrorEvent.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/MirrorEvent.java Wed Oct 31 02:10:21 2018 +0100 @@ -0,0 +1,25 @@ +package jdk.jfr.internal; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.ElementType; + +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.TYPE }) +public @interface MirrorEvent { + /** + * Fully qualified name of the class to mirror metadata for (for example, + * {@code "jdk.internal.event.Example"}) + * + * @return the fully qualified class name of the event + */ + String className(); + + /** + * The module where the event is located, by default {@code "java.base"}. + * + * @return the module name + */ + String module() default "java.base"; +} diff -r c401c536cea1 -r a181612f0715 src/jdk.jfr/share/classes/jdk/jfr/internal/PlatformRecorder.java --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/PlatformRecorder.java Tue Oct 30 15:17:58 2018 -0700 +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/PlatformRecorder.java Wed Oct 31 02:10:21 2018 +0100 @@ -81,9 +81,7 @@ Logger.log(JFR_SYSTEM, INFO, "Registered JDK events"); JDKEvents.addInstrumentation(); startDiskMonitor(); - SecuritySupport.registerEvent(ActiveRecordingEvent.class); activeRecordingEvent = EventType.getEventType(ActiveRecordingEvent.class); - SecuritySupport.registerEvent(ActiveSettingEvent.class); activeSettingEvent = EventType.getEventType(ActiveSettingEvent.class); shutdownHook = SecuritySupport.createThreadWitNoPermissions("JFR: Shutdown Hook", new ShutdownHook(this)); SecuritySupport.setUncaughtExceptionHandler(shutdownHook, new ShutdownHook.ExceptionHandler()); @@ -91,6 +89,7 @@ timer = createTimer(); } + private static Timer createTimer() { try { List result = new CopyOnWriteArrayList<>(); diff -r c401c536cea1 -r a181612f0715 src/jdk.jfr/share/classes/jdk/jfr/internal/SecuritySupport.java --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/SecuritySupport.java Tue Oct 30 15:17:58 2018 -0700 +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/SecuritySupport.java Wed Oct 31 02:10:21 2018 +0100 @@ -262,8 +262,12 @@ Modules.addExports(JFR_MODULE, Utils.HANDLERS_PACKAGE_NAME, clazz.getModule()); } - public static void registerEvent(Class eventClass) { - doPrivileged(() -> FlightRecorder.register(eventClass), new FlightRecorderPermission(Utils.REGISTER_EVENT)); + public static void registerEvent(Class eventClass) { + doPrivileged(() -> MetadataRepository.getInstance().register(eventClass), new FlightRecorderPermission(Utils.REGISTER_EVENT)); + } + + public static void registerMirror(Class eventClass) { + doPrivileged(() -> MetadataRepository.getInstance().registerMirror(eventClass), new FlightRecorderPermission(Utils.REGISTER_EVENT)); } static boolean getBooleanProperty(String propertyName) { diff -r c401c536cea1 -r a181612f0715 src/jdk.jfr/share/classes/jdk/jfr/internal/SettingsManager.java --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/SettingsManager.java Tue Oct 30 15:17:58 2018 -0700 +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/SettingsManager.java Wed Oct 31 02:10:21 2018 +0100 @@ -37,7 +37,6 @@ import java.util.Set; import java.util.StringJoiner; -import jdk.jfr.Event; import jdk.jfr.internal.handlers.EventHandler; final class SettingsManager { @@ -152,9 +151,9 @@ } } - public void updateRetransform(List> eventClasses) { + public void updateRetransform(List> eventClasses) { List> classes = new ArrayList<>(); - for(Class eventClass: eventClasses) { + for(Class eventClass: eventClasses) { EventHandler eh = Utils.getHandler(eventClass); if (eh != null ) { PlatformEventType eventType = eh.getPlatformEventType(); diff -r c401c536cea1 -r a181612f0715 src/jdk.jfr/share/classes/jdk/jfr/internal/Type.java --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/Type.java Tue Oct 30 15:17:58 2018 -0700 +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/Type.java Wed Oct 31 02:10:21 2018 +0100 @@ -71,10 +71,11 @@ private final String name; private final String superType; private final boolean constantPool; - private final long id; private final ArrayList fields = new ArrayList<>(); private Boolean simpleType; // calculated lazy private boolean remove = true; + private long id; + /** * Creates a type * @@ -318,4 +319,8 @@ public boolean getRemove() { return remove; } + + public void setId(long id) { + this.id = id; + } } diff -r c401c536cea1 -r a181612f0715 src/jdk.jfr/share/classes/jdk/jfr/internal/TypeLibrary.java --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/TypeLibrary.java Tue Oct 30 15:17:58 2018 -0700 +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/TypeLibrary.java Wed Oct 31 02:10:21 2018 +0100 @@ -49,7 +49,6 @@ import jdk.jfr.AnnotationElement; import jdk.jfr.Description; -import jdk.jfr.Event; import jdk.jfr.Label; import jdk.jfr.MetadataDefinition; import jdk.jfr.Name; @@ -240,7 +239,7 @@ // STRUCT String superType = null; boolean eventType = false; - if (Event.class.isAssignableFrom(clazz)) { + if (jdk.internal.event.Event.class.isAssignableFrom(clazz)) { superType = Type.SUPER_TYPE_EVENT; eventType= true; } @@ -489,4 +488,8 @@ aQ.addAll(ae.getAnnotationElements()); } } + + public void removeType(long id) { + types.remove(id); + } } diff -r c401c536cea1 -r a181612f0715 src/jdk.jfr/share/classes/jdk/jfr/internal/Utils.java --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/Utils.java Tue Oct 30 15:17:58 2018 -0700 +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/Utils.java Wed Oct 31 02:10:21 2018 +0100 @@ -267,7 +267,7 @@ return (long) (nanos * JVM.getJVM().getTimeConversionFactor()); } - static synchronized EventHandler getHandler(Class eventClass) { + static synchronized EventHandler getHandler(Class eventClass) { Utils.ensureValidEventSubclass(eventClass); try { Field f = eventClass.getDeclaredField(EventInstrumentation.FIELD_EVENT_HANDLER); @@ -278,7 +278,7 @@ } } - static synchronized void setHandler(Class eventClass, EventHandler handler) { + static synchronized void setHandler(Class eventClass, EventHandler handler) { Utils.ensureValidEventSubclass(eventClass); try { Field field = eventClass.getDeclaredField(EventInstrumentation.FIELD_EVENT_HANDLER); @@ -322,7 +322,7 @@ static List getVisibleEventFields(Class clazz) { Utils.ensureValidEventSubclass(clazz); List fields = new ArrayList<>(); - for (Class c = clazz; c != Event.class; c = c.getSuperclass()) { + for (Class c = clazz; c != jdk.internal.event.Event.class; c = c.getSuperclass()) { for (Field field : c.getDeclaredFields()) { // skip private field in base classes if (c == clazz || !Modifier.isPrivate(field.getModifiers())) { @@ -334,10 +334,10 @@ } public static void ensureValidEventSubclass(Class eventClass) { - if (Event.class.isAssignableFrom(eventClass) && Modifier.isAbstract(eventClass.getModifiers())) { + if (jdk.internal.event.Event.class.isAssignableFrom(eventClass) && Modifier.isAbstract(eventClass.getModifiers())) { throw new IllegalArgumentException("Abstract event classes are not allowed"); } - if (eventClass == Event.class || !Event.class.isAssignableFrom(eventClass)) { + if (eventClass == Event.class || eventClass == jdk.internal.event.Event.class || !jdk.internal.event.Event.class.isAssignableFrom(eventClass)) { throw new IllegalArgumentException("Must be a subclass to " + Event.class.getName()); } } @@ -366,7 +366,7 @@ } } - public static void ensureInitialized(Class eventClass) { + public static void ensureInitialized(Class eventClass) { SecuritySupport.ensureClassIsInitialized(eventClass); } @@ -499,6 +499,50 @@ return eventName; } + public static void verifyMirror(Class mirror, Class real) { + Class cMirror = Objects.requireNonNull(mirror); + Class cReal = Objects.requireNonNull(real); + + while (cReal != null) { + Map mirrorFields = new HashMap<>(); + if (cMirror != null) { + for (Field f : cMirror.getDeclaredFields()) { + if (isSupportedType(f.getType())) { + mirrorFields.put(f.getName(), f); + } + } + } + for (Field realField : cReal.getDeclaredFields()) { + if (isSupportedType(realField.getType())) { + String fieldName = realField.getName(); + Field mirrorField = mirrorFields.get(fieldName); + if (mirrorField == null) { + throw new InternalError("Missing mirror field for " + cReal.getName() + "#" + fieldName); + } + if (realField.getModifiers() != mirrorField.getModifiers()) { + throw new InternalError("Incorrect modifier for mirror field "+ cMirror.getName() + "#" + fieldName); + } + mirrorFields.remove(fieldName); + } + } + if (!mirrorFields.isEmpty()) { + throw new InternalError( + "Found additional fields in mirror class " + cMirror.getName() + " " + mirrorFields.keySet()); + } + if (cMirror != null) { + cMirror = cMirror.getSuperclass(); + } + cReal = cReal.getSuperclass(); + } + } + + private static boolean isSupportedType(Class type) { + if (Modifier.isTransient(type.getModifiers()) || Modifier.isStatic(type.getModifiers())) { + return false; + } + return Type.isValidJavaFieldType(type.getName()); + } + public static String makeFilename(Recording recording) { String pid = JVM.getJVM().getPid(); String date = Repository.REPO_DATE_FORMAT.format(LocalDateTime.now()); diff -r c401c536cea1 -r a181612f0715 src/jdk.jfr/share/classes/jdk/jfr/internal/instrument/JDKEvents.java --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/instrument/JDKEvents.java Tue Oct 30 15:17:58 2018 -0700 +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/instrument/JDKEvents.java Wed Oct 31 02:10:21 2018 +0100 @@ -51,6 +51,9 @@ public final class JDKEvents { + private static final Class[] mirrorEventClasses = { + }; + private static final Class[] eventClasses = { FileForceEvent.class, FileReadEvent.class, @@ -90,6 +93,9 @@ Modules.addExports(jdkJfrModule, Utils.EVENTS_PACKAGE_NAME, javaBaseModule); Modules.addExports(jdkJfrModule, Utils.INSTRUMENT_PACKAGE_NAME, javaBaseModule); Modules.addExports(jdkJfrModule, Utils.HANDLERS_PACKAGE_NAME, javaBaseModule); + for (Class mirrorEventClass : mirrorEventClasses) { + SecuritySupport.registerMirror(((Class)mirrorEventClass)); + } for (Class eventClass : eventClasses) { SecuritySupport.registerEvent((Class) eventClass); } diff -r c401c536cea1 -r a181612f0715 test/jdk/jdk/jfr/jvm/TestGetAllEventClasses.java --- a/test/jdk/jdk/jfr/jvm/TestGetAllEventClasses.java Tue Oct 30 15:17:58 2018 -0700 +++ b/test/jdk/jdk/jfr/jvm/TestGetAllEventClasses.java Wed Oct 31 02:10:21 2018 +0100 @@ -104,8 +104,9 @@ } @SafeVarargs + @SuppressWarnings("rawtypes") private static void assertEvents(JVM jvm, boolean inclusion, Class... targetEvents) { - final List> list = jvm.getAllEventClasses(); + final List list = jvm.getAllEventClasses(); for (Class ev : targetEvents) { if (list.contains(ev)) { if (inclusion) {