8203629: Produce events in the JDK without a dependency on jdk.jfr
authoregahlin
Wed, 31 Oct 2018 02:10:21 +0100
changeset 52334 a181612f0715
parent 52333 c401c536cea1
child 52335 6507eeb6f047
8203629: Produce events in the JDK without a dependency on jdk.jfr Reviewed-by: mgronlun
src/hotspot/share/jfr/instrumentation/jfrEventClassTransformer.cpp
src/hotspot/share/jfr/jni/jfrGetAllEventClasses.cpp
src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceId.cpp
src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp
src/java.base/share/classes/jdk/internal/event/Event.java
src/java.base/share/classes/module-info.java
src/jdk.jfr/share/classes/jdk/jfr/Event.java
src/jdk.jfr/share/classes/jdk/jfr/internal/EventControl.java
src/jdk.jfr/share/classes/jdk/jfr/internal/EventHandlerCreator.java
src/jdk.jfr/share/classes/jdk/jfr/internal/EventInstrumentation.java
src/jdk.jfr/share/classes/jdk/jfr/internal/JVM.java
src/jdk.jfr/share/classes/jdk/jfr/internal/JVMUpcalls.java
src/jdk.jfr/share/classes/jdk/jfr/internal/MetadataRepository.java
src/jdk.jfr/share/classes/jdk/jfr/internal/MirrorEvent.java
src/jdk.jfr/share/classes/jdk/jfr/internal/PlatformRecorder.java
src/jdk.jfr/share/classes/jdk/jfr/internal/SecuritySupport.java
src/jdk.jfr/share/classes/jdk/jfr/internal/SettingsManager.java
src/jdk.jfr/share/classes/jdk/jfr/internal/Type.java
src/jdk.jfr/share/classes/jdk/jfr/internal/TypeLibrary.java
src/jdk.jfr/share/classes/jdk/jfr/internal/Utils.java
src/jdk.jfr/share/classes/jdk/jfr/internal/instrument/JDKEvents.java
test/jdk/jdk/jfr/jvm/TestGetAllEventClasses.java
--- 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
-  "<clinit>",     // 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
+  "<clinit>",     // 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<u2>(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
--- 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);
 
--- 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;
+    }
   }
 }
 
--- 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");
--- /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.
+     * <p>
+     * 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) {
+    }
+}
--- 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
--- 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.
--- 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<? extends Event> eventClass) {
+    EventControl(PlatformEventType es, Class<? extends jdk.internal.event.Event> eventClass) {
         this(es);
         defineSettings(eventClass);
     }
--- 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<SettingInfo> settingInfos, EventType type, Class<? extends Event> eventClass) {
+    public EventHandlerCreator(long id, List<SettingInfo> settingInfos, EventType type, Class<? extends jdk.internal.event.Event> eventClass) {
         this(id, settingInfos, createFieldInfos(eventClass, type));
     }
 
-    private static List<FieldInfo> createFieldInfos(Class<? extends Event> eventClass, EventType type) throws Error {
+    private static List<FieldInfo> createFieldInfos(Class<? extends jdk.internal.event.Event> eventClass, EventType type) throws Error {
         List<FieldInfo> fieldInfos = new ArrayList<>();
         for (ValueDescriptor v : type.getFields()) {
             // Only value descriptors that are not fields on the event class.
--- 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<FieldInfo> 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);
--- 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<Class<? extends Event>> getAllEventClasses();
+    public native List<Class<? extends jdk.internal.event.Event>> getAllEventClasses();
 
     /**
      * Return a count of the number of unloaded classes deriving from {@link Event}
--- 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
--- 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<EventControl> nativeControls = new ArrayList<EventControl>(100);
     private final TypeLibrary typeLibrary = TypeLibrary.getInstance();
     private final SettingsManager settingsManager = new SettingsManager();
+    private final Map<String, Class<? extends Event>> 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<? extends Event> eventClass) {
+    public synchronized EventType getEventType(Class<? extends jdk.internal.event.Event> 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<? extends Event> eventClass) {
+    public synchronized EventType register(Class<? extends jdk.internal.event.Event> eventClass) {
         return register(eventClass, Collections.emptyList(), Collections.emptyList());
     }
 
-    public synchronized EventType register(Class<? extends Event> eventClass, List<AnnotationElement> dynamicAnnotations, List<ValueDescriptor> dynamicFields) {
+    public synchronized EventType register(Class<? extends jdk.internal.event.Event> eventClass, List<AnnotationElement> dynamicAnnotations, List<ValueDescriptor> 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<? extends Event> eventClass) {
+    private PlatformEventType findMirrorType(Class<? extends jdk.internal.event.Event> eventClass) throws InternalError {
+        String fullName = eventClass.getModule().getName() + ":" + eventClass.getName();
+        Class<? extends Event> 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<? extends jdk.internal.event.Event> eventClass) {
         Utils.ensureValidEventSubclass(eventClass);
         SecuritySupport.makeVisibleToJFR(eventClass);
         Utils.ensureInitialized(eventClass);
         return Utils.getHandler(eventClass);
     }
 
-    private EventHandler makeHandler(Class<? extends Event> eventClass, List<AnnotationElement> dynamicAnnotations, List<ValueDescriptor> dynamicFields) throws InternalError {
+    private EventHandler makeHandler(Class<? extends jdk.internal.event.Event> eventClass, PlatformEventType pEventType, List<AnnotationElement> dynamicAnnotations, List<ValueDescriptor> 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<? extends EventHandler> handlerClass = null;
@@ -198,9 +221,9 @@
     }
 
     private static List<EventHandler> getEventHandlers() {
-        List<Class<? extends Event>> allEventClasses = jvm.getAllEventClasses();
+        List<Class<? extends jdk.internal.event.Event>> allEventClasses = jvm.getAllEventClasses();
         List<EventHandler> eventHandlers = new ArrayList<>(allEventClasses.size());
-        for (Class<? extends Event> clazz : allEventClasses) {
+        for (Class<? extends jdk.internal.event.Event> 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<Class<? extends Event>> eventClasses = jvm.getAllEventClasses();
+            List<Class<? extends jdk.internal.event.Event>> eventClasses = jvm.getAllEventClasses();
             HashSet<Long> knownIds = new HashSet<>(eventClasses.size());
-            for (Class<? extends Event>  ec: eventClasses) {
+            for (Class<? extends jdk.internal.event.Event>  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<? extends Event> 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());
+    }
+
 }
--- /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";
+}
--- 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<Timer> result = new CopyOnWriteArrayList<>();
--- 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<? extends Event> eventClass) {
-        doPrivileged(() -> FlightRecorder.register(eventClass), new FlightRecorderPermission(Utils.REGISTER_EVENT));
+    public static void registerEvent(Class<? extends jdk.internal.event.Event> eventClass) {
+        doPrivileged(() ->  MetadataRepository.getInstance().register(eventClass), new FlightRecorderPermission(Utils.REGISTER_EVENT));
+    }
+
+    public static void registerMirror(Class<? extends Event> eventClass) {
+        doPrivileged(() ->  MetadataRepository.getInstance().registerMirror(eventClass), new FlightRecorderPermission(Utils.REGISTER_EVENT));
     }
 
     static boolean getBooleanProperty(String propertyName) {
--- 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<Class<? extends Event>> eventClasses) {
+    public void updateRetransform(List<Class<? extends jdk.internal.event.Event>> eventClasses) {
         List<Class<?>> classes = new ArrayList<>();
-        for(Class<? extends Event> eventClass: eventClasses) {
+        for(Class<? extends jdk.internal.event.Event> eventClass: eventClasses) {
             EventHandler eh = Utils.getHandler(eventClass);
             if (eh != null ) {
                 PlatformEventType eventType = eh.getPlatformEventType();
--- 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<ValueDescriptor> 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;
+    }
 }
--- 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);
+    }
 }
--- 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<? extends Event> eventClass) {
+    static synchronized EventHandler getHandler(Class<? extends jdk.internal.event.Event> eventClass) {
         Utils.ensureValidEventSubclass(eventClass);
         try {
             Field f = eventClass.getDeclaredField(EventInstrumentation.FIELD_EVENT_HANDLER);
@@ -278,7 +278,7 @@
         }
     }
 
-    static synchronized void setHandler(Class<? extends Event> eventClass, EventHandler handler) {
+    static synchronized void setHandler(Class<? extends jdk.internal.event.Event> eventClass, EventHandler handler) {
         Utils.ensureValidEventSubclass(eventClass);
         try {
             Field field = eventClass.getDeclaredField(EventInstrumentation.FIELD_EVENT_HANDLER);
@@ -322,7 +322,7 @@
     static List<Field> getVisibleEventFields(Class<?> clazz) {
         Utils.ensureValidEventSubclass(clazz);
         List<Field> 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<? extends Event> eventClass) {
+    public static void ensureInitialized(Class<? extends jdk.internal.event.Event> 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<String, Field> 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());
--- 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<? extends Event>)mirrorEventClass));
+                }
                 for (Class<?> eventClass : eventClasses) {
                     SecuritySupport.registerEvent((Class<? extends Event>) eventClass);
                 }
--- 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<? extends Event>... targetEvents) {
-        final List<Class<? extends Event>> list = jvm.getAllEventClasses();
+        final List list = jvm.getAllEventClasses();
         for (Class<? extends Event> ev : targetEvents) {
            if (list.contains(ev)) {
                if (inclusion) {