8221507: Implement JFR Events for Shenandoah
authorkdobson
Tue, 21 May 2019 10:36:23 +0200
changeset 54956 43340a79840d
parent 54955 46409371a691
child 54957 b6b02a503d02
8221507: Implement JFR Events for Shenandoah Reviewed-by: rkennke, shade, egahlin, mseledtsov Contributed-by: Ken Dobson <kdobson@redhat.com>
src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp
src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp
src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.hpp
src/hotspot/share/gc/shenandoah/shenandoahJfrSupport.cpp
src/hotspot/share/gc/shenandoah/shenandoahJfrSupport.hpp
src/hotspot/share/jfr/metadata/metadata.xml
src/hotspot/share/jfr/periodic/jfrPeriodic.cpp
src/jdk.jfr/share/conf/jfr/default.jfc
src/jdk.jfr/share/conf/jfr/profile.jfc
test/jdk/jdk/jfr/event/gc/detailed/TestShenandoahHeapRegionInformationEvent.java
test/jdk/jdk/jfr/event/gc/detailed/TestShenandoahHeapRegionStateChangeEvent.java
test/lib/jdk/test/lib/jfr/EventNames.java
test/lib/jdk/test/lib/jfr/GCHelper.java
--- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp	Tue May 21 10:34:57 2019 +0200
+++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp	Tue May 21 10:36:23 2019 +0200
@@ -67,6 +67,10 @@
 #include "gc/shenandoah/heuristics/shenandoahPassiveHeuristics.hpp"
 #include "gc/shenandoah/heuristics/shenandoahStaticHeuristics.hpp"
 #include "gc/shenandoah/heuristics/shenandoahTraversalHeuristics.hpp"
+#if INCLUDE_JFR
+#include "gc/shenandoah/shenandoahJfrSupport.hpp"
+#endif
+
 #include "memory/metaspace.hpp"
 #include "oops/compressedOops.inline.hpp"
 #include "runtime/globals.hpp"
@@ -596,6 +600,8 @@
   ref_processing_init();
 
   _heuristics->initialize();
+
+  JFR_ONLY(ShenandoahJFRSupport::register_jfr_type_serializers());
 }
 
 size_t ShenandoahHeap::used() const {
--- a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp	Tue May 21 10:34:57 2019 +0200
+++ b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp	Tue May 21 10:36:23 2019 +0200
@@ -30,6 +30,7 @@
 #include "gc/shenandoah/shenandoahMarkingContext.inline.hpp"
 #include "gc/shenandoah/shenandoahTraversalGC.hpp"
 #include "gc/shared/space.inline.hpp"
+#include "jfr/jfrEvents.hpp"
 #include "memory/iterator.inline.hpp"
 #include "memory/resourceArea.hpp"
 #include "memory/universe.hpp"
@@ -93,7 +94,7 @@
     case _empty_uncommitted:
       do_commit();
     case _empty_committed:
-      _state = _regular;
+      set_state(_regular);
     case _regular:
     case _pinned:
       return;
@@ -114,10 +115,10 @@
     case _cset:
     case _humongous_start:
     case _humongous_cont:
-      _state = _regular;
+      set_state(_regular);
       return;
     case _pinned_cset:
-      _state = _pinned;
+      set_state(_pinned);
       return;
     case _regular:
     case _pinned:
@@ -133,7 +134,7 @@
     case _empty_uncommitted:
       do_commit();
     case _empty_committed:
-      _state = _humongous_start;
+      set_state(_humongous_start);
       return;
     default:
       report_illegal_transition("humongous start allocation");
@@ -149,7 +150,7 @@
     case _regular:
     case _humongous_start:
     case _humongous_cont:
-      _state = _humongous_start;
+      set_state(_humongous_start);
       return;
     default:
       report_illegal_transition("humongous start bypass");
@@ -162,7 +163,7 @@
     case _empty_uncommitted:
       do_commit();
     case _empty_committed:
-      _state = _humongous_cont;
+     set_state(_humongous_cont);
       return;
     default:
       report_illegal_transition("humongous continuation allocation");
@@ -178,7 +179,7 @@
     case _regular:
     case _humongous_start:
     case _humongous_cont:
-      _state = _humongous_cont;
+      set_state(_humongous_cont);
       return;
     default:
       report_illegal_transition("humongous continuation bypass");
@@ -190,14 +191,14 @@
   switch (_state) {
     case _regular:
       assert (_critical_pins == 0, "sanity");
-      _state = _pinned;
+      set_state(_pinned);
     case _pinned_cset:
     case _pinned:
       _critical_pins++;
       return;
     case _humongous_start:
       assert (_critical_pins == 0, "sanity");
-      _state = _pinned_humongous_start;
+      set_state(_pinned_humongous_start);
     case _pinned_humongous_start:
       _critical_pins++;
       return;
@@ -219,7 +220,7 @@
       assert (_critical_pins > 0, "sanity");
       _critical_pins--;
       if (_critical_pins == 0) {
-        _state = _regular;
+        set_state(_regular);
       }
       return;
     case _regular:
@@ -231,14 +232,14 @@
       assert (_critical_pins > 0, "sanity");
       _critical_pins--;
       if (_critical_pins == 0) {
-        _state = _cset;
+        set_state(_cset);
       }
       return;
     case _pinned_humongous_start:
       assert (_critical_pins > 0, "sanity");
       _critical_pins--;
       if (_critical_pins == 0) {
-        _state = _humongous_start;
+        set_state(_humongous_start);
       }
       return;
     default:
@@ -250,7 +251,7 @@
   _heap->assert_heaplock_owned_by_current_thread();
   switch (_state) {
     case _regular:
-      _state = _cset;
+      set_state(_cset);
     case _cset:
       return;
     default:
@@ -268,7 +269,7 @@
       // Reclaiming humongous regions
     case _regular:
       // Immediate region reclaim
-      _state = _trash;
+      set_state(_trash);
       return;
     default:
       report_illegal_transition("trashing");
@@ -287,7 +288,7 @@
   _heap->assert_heaplock_owned_by_current_thread();
   switch (_state) {
     case _trash:
-      _state = _empty_committed;
+      set_state(_empty_committed);
       _empty_time = os::elapsedTime();
       return;
     default:
@@ -300,7 +301,7 @@
   switch (_state) {
     case _empty_committed:
       do_uncommit();
-      _state = _empty_uncommitted;
+      set_state(_empty_uncommitted);
       return;
     default:
       report_illegal_transition("uncommiting");
@@ -314,7 +315,7 @@
   switch (_state) {
     case _empty_uncommitted:
       do_commit();
-      _state = _empty_committed;
+      set_state(_empty_committed);
       return;
     default:
       report_illegal_transition("commit bypass");
@@ -679,3 +680,16 @@
   }
   _heap->decrease_committed(ShenandoahHeapRegion::region_size_bytes());
 }
+
+void ShenandoahHeapRegion::set_state(RegionState to) {
+  EventShenandoahHeapRegionStateChange evt;
+  if (evt.should_commit()){
+    evt.set_index(region_number());
+    evt.set_start((uintptr_t)bottom());
+    evt.set_used(used());
+    evt.set_from(_state);
+    evt.set_to(to);
+    evt.commit();
+  }
+  _state = to;
+}
--- a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.hpp	Tue May 21 10:34:57 2019 +0200
+++ b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.hpp	Tue May 21 10:36:23 2019 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2018, Red Hat, Inc. All rights reserved.
+ * Copyright (c) 2013, 2019, Red Hat, Inc. All rights reserved.
  *
  * 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
@@ -32,9 +32,11 @@
 #include "utilities/sizes.hpp"
 
 class VMStructs;
+class ShenandoahHeapRegionStateConstant;
 
 class ShenandoahHeapRegion : public ContiguousSpace {
   friend class VMStructs;
+  friend class ShenandoahHeapRegionStateConstant;
 private:
   /*
     Region state is described by a state machine. Transitions are guarded by
@@ -114,10 +116,11 @@
     _cset,                    // region is in collection set
     _pinned,                  // region is pinned
     _pinned_cset,             // region is pinned and in cset (evac failure path)
-    _trash                    // region contains only trash
+    _trash,                   // region contains only trash
+    _REGION_STATES_NUM        // last
   };
 
-  const char* region_state_to_string(RegionState s) const {
+  static const char* region_state_to_string(RegionState s) {
     switch (s) {
       case _empty_uncommitted:       return "Empty Uncommitted";
       case _empty_committed:         return "Empty Committed";
@@ -157,6 +160,10 @@
   void report_illegal_transition(const char* method);
 
 public:
+  static const int region_states_num() {
+    return _REGION_STATES_NUM;
+  }
+
   // Allowed transitions from the outside code:
   void make_regular_allocation();
   void make_regular_bypass();
@@ -424,6 +431,8 @@
   void oop_iterate_humongous(OopIterateClosure* cl);
 
   inline void internal_increase_live_data(size_t s);
+
+  void set_state(RegionState to);
 };
 
 #endif // SHARE_GC_SHENANDOAH_SHENANDOAHHEAPREGION_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/gc/shenandoah/shenandoahJfrSupport.cpp	Tue May 21 10:36:23 2019 +0200
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2019, Red Hat, Inc. All rights reserved.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#include "precompiled.hpp"
+#include "gc/shenandoah/shenandoahHeap.hpp"
+#include "gc/shenandoah/shenandoahHeapRegion.hpp"
+#include "gc/shenandoah/shenandoahJfrSupport.hpp"
+#include "jfr/jfrEvents.hpp"
+#if INCLUDE_JFR
+#include "jfr/metadata/jfrSerializer.hpp"
+#endif
+
+#if INCLUDE_JFR
+
+class ShenandoahHeapRegionStateConstant : public JfrSerializer {
+  friend class ShenandoahHeapRegion;
+public:
+  virtual void serialize(JfrCheckpointWriter& writer) {
+    static const u4 nof_entries = ShenandoahHeapRegion::region_states_num();
+    writer.write_count(nof_entries);
+    for (u4 i = 0; i < nof_entries; ++i) {
+      writer.write_key(i);
+      writer.write(ShenandoahHeapRegion::region_state_to_string((ShenandoahHeapRegion::RegionState)i));
+    }
+  }
+};
+
+void ShenandoahJFRSupport::register_jfr_type_serializers() {
+  JfrSerializer::register_serializer(TYPE_SHENANDOAHHEAPREGIONSTATE,
+                                     false,
+                                     true,
+                                     new ShenandoahHeapRegionStateConstant());
+}
+#endif
+
+class ShenandoahDumpHeapRegionInfoClosure : public ShenandoahHeapRegionClosure {
+public:
+  virtual void heap_region_do(ShenandoahHeapRegion* r) {
+    EventShenandoahHeapRegionInformation evt;
+    evt.set_index(r->region_number());
+    evt.set_state((u8)r->state());
+    evt.set_start((uintptr_t)r->bottom());
+    evt.set_used(r->used());
+    evt.commit();
+  }
+};
+
+void VM_ShenandoahSendHeapRegionInfoEvents::doit() {
+  ShenandoahDumpHeapRegionInfoClosure c;
+  ShenandoahHeap::heap()->heap_region_iterate(&c);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/gc/shenandoah/shenandoahJfrSupport.hpp	Tue May 21 10:36:23 2019 +0200
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2019, Red Hat, Inc. All rights reserved.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#ifndef SHARE_GC_SHENANDOAH_SHENANDOAHJFRSUPPORT_HPP
+#define SHARE_GC_SHENANDOAH_SHENANDOAHJFRSUPPORT_HPP
+
+#include "runtime/vmOperations.hpp"
+
+class VM_ShenandoahSendHeapRegionInfoEvents : public VM_Operation {
+public:
+  virtual void doit();
+  virtual VMOp_Type type() const { return VMOp_HeapIterateOperation; }
+};
+
+class ShenandoahJFRSupport {
+public:
+  static void register_jfr_type_serializers();
+};
+
+#endif // SHARE_VM_GC_SHENANDOAH_SHENANDOAHJFRSUPPORT_HPP
--- a/src/hotspot/share/jfr/metadata/metadata.xml	Tue May 21 10:34:57 2019 +0200
+++ b/src/hotspot/share/jfr/metadata/metadata.xml	Tue May 21 10:36:23 2019 +0200
@@ -983,6 +983,27 @@
     <Field type="ulong" name="value" label="Value" />
   </Event>
 
+  <Event name="ShenandoahHeapRegionStateChange" category="Java Virtual Machine, GC, Detailed" label="Shenandoah Heap Region State Change" description="Information about a Shenandoah heap region state change"
+    startTime="false">
+    <Field type="uint" name="index" label="Index" />
+    <Field type="ShenandoahHeapRegionState" name="from" label="From" />
+    <Field type="ShenandoahHeapRegionState" name="to" label="To" />
+    <Field type="ulong" contentType="address" name="start" label="Start" />
+    <Field type="ulong" contentType="bytes" name="used" label="Used" />
+  </Event>
+
+  <Event name="ShenandoahHeapRegionInformation" category="Java Virtual Machine, GC, Detailed" label="Shenandoah Heap Region Information" description="Information about a specific heap region in the Shenandoah GC"
+    period="everyChunk">
+    <Field type="uint" name="index" label="Index" />
+    <Field type="ShenandoahHeapRegionState" name="state" label="State" />
+    <Field type="ulong" contentType="address" name="start" label="Start" />
+    <Field type="ulong" contentType="bytes" name="used" label="Used" />
+  </Event>
+
+  <Type name="ShenandoahHeapRegionState" label="Shenandoah Heap Region State">
+    <Field type="string" name="state" label="State" />
+  </Type>
+
   <Type name="ZStatisticsCounterType" label="Z Statistics Counter">
     <Field type="string" name="counter" label="Counter" />
   </Type>
--- a/src/hotspot/share/jfr/periodic/jfrPeriodic.cpp	Tue May 21 10:34:57 2019 +0200
+++ b/src/hotspot/share/jfr/periodic/jfrPeriodic.cpp	Tue May 21 10:36:23 2019 +0200
@@ -65,7 +65,9 @@
 #include "services/threadService.hpp"
 #include "utilities/exceptions.hpp"
 #include "utilities/globalDefinitions.hpp"
-
+#if INCLUDE_SHENANDOAHGC
+#include "gc/shenandoah/shenandoahJfrSupport.hpp"
+#endif
 /**
  *  JfrPeriodic class
  *  Implementation of declarations in
@@ -626,3 +628,14 @@
   event.set_flushingEnabled(UseCodeCacheFlushing);
   event.commit();
 }
+
+
+TRACE_REQUEST_FUNC(ShenandoahHeapRegionInformation) {
+#if INCLUDE_SHENANDOAHGC
+  if (UseShenandoahGC) {
+    VM_ShenandoahSendHeapRegionInfoEvents op;
+    VMThread::execute(&op);
+  }
+#endif
+}
+
--- a/src/jdk.jfr/share/conf/jfr/default.jfc	Tue May 21 10:34:57 2019 +0200
+++ b/src/jdk.jfr/share/conf/jfr/default.jfc	Tue May 21 10:36:23 2019 +0200
@@ -445,6 +445,15 @@
       <setting name="enabled" control="gc-enabled-all">false</setting>
     </event>
 
+    <event name="jdk.ShenandoahHeapRegionInformation">
+      <setting name="enabled" control="gc-enabled-all">false</setting>
+      <setting name="period">everyChunk</setting>
+    </event>
+
+    <event name="jdk.ShenandoahHeapRegionStateChange">
+      <setting name="enabled" control="gc-enabled-all">false</setting>
+    </event>
+
     <event name="jdk.OldObjectSample">
       <setting name="enabled" control="memory-leak-detection-enabled">true</setting>
       <setting name="stackTrace" control="memory-leak-detection-stack-trace">false</setting>
--- a/src/jdk.jfr/share/conf/jfr/profile.jfc	Tue May 21 10:34:57 2019 +0200
+++ b/src/jdk.jfr/share/conf/jfr/profile.jfc	Tue May 21 10:36:23 2019 +0200
@@ -445,6 +445,15 @@
       <setting name="enabled" control="gc-enabled-all">false</setting>
     </event>
 
+    <event name="jdk.ShenandoahHeapRegionInformation">
+      <setting name="enabled" control="gc-enabled-all">false</setting>
+      <setting name="period">everyChunk</setting>
+    </event>
+
+    <event name="jdk.ShenandoahHeapRegionStateChange">
+      <setting name="enabled" control="gc-enabled-all">false</setting>
+    </event>
+
     <event name="jdk.OldObjectSample">
       <setting name="enabled" control="memory-leak-detection-enabled">true</setting>
       <setting name="stackTrace" control="memory-leak-detection-stack-trace">true</setting>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/event/gc/detailed/TestShenandoahHeapRegionInformationEvent.java	Tue May 21 10:36:23 2019 +0200
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2019, Red Hat, Inc. All rights reserved.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+package jdk.jfr.event.gc.detailed;
+
+import java.nio.file.Paths;
+import java.util.List;
+
+import jdk.jfr.EventType;
+import jdk.jfr.FlightRecorder;
+import jdk.jfr.Recording;
+import jdk.jfr.consumer.RecordedEvent;
+import jdk.test.lib.Asserts;
+import jdk.test.lib.jfr.EventNames;
+import jdk.test.lib.jfr.Events;
+import jdk.test.lib.jfr.GCHelper;
+
+/**
+ * @test
+ * @bug 8221507
+ * @requires vm.hasJFR
+ * @requires vm.gc == "Shenandoah" | vm.gc == null
+ * @key jfr
+ * @library /test/lib /test/jdk
+ * @run main/othervm  -Xmx32m -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGarbageThreshold=1 jdk.jfr.event.gc.detailed.TestShenandoahHeapRegionInformationEvent
+ */
+
+
+public class TestShenandoahHeapRegionInformationEvent {
+    private final static String EVENT_NAME = EventNames.ShenandoahHeapRegionInformation;
+    public static void main(String[] args) throws Exception {
+        try (Recording recording = new Recording()) {
+            // activate the event we are interested in and start recording
+            for (EventType t : FlightRecorder.getFlightRecorder().getEventTypes()) {
+                System.out.println(t.getName());
+            }
+            recording.enable(EVENT_NAME);
+            recording.start();
+            recording.stop();
+
+            // Verify recording
+            List<RecordedEvent> events = Events.fromRecording(recording);
+            Events.hasEvents(events);
+            for (RecordedEvent event : events) {
+                Events.assertField(event, "index").notEqual(-1);
+                GCHelper.assertIsValidShenandoahHeapRegionState(Events.assertField(event, "state").getValue());
+                Events.assertField(event, "used").atMost(1L*1024*1024);
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/event/gc/detailed/TestShenandoahHeapRegionStateChangeEvent.java	Tue May 21 10:36:23 2019 +0200
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2019, Red Hat, Inc. All rights reserved.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+package jdk.jfr.event.gc.detailed;
+
+import java.nio.file.Paths;
+import java.time.Duration;
+import java.util.List;
+
+import jdk.jfr.Recording;
+import jdk.jfr.consumer.RecordedEvent;
+import jdk.test.lib.Asserts;
+import jdk.test.lib.jfr.EventNames;
+import jdk.test.lib.jfr.Events;
+import jdk.test.lib.jfr.GCHelper;
+
+/**
+ * @test
+ * @bug 8221507
+ * @requires vm.hasJFR
+ * @requires vm.gc == "Shenandoah" | vm.gc == null
+ * @key jfr
+ * @library /test/lib /test/jdk
+ * @run main/othervm  -Xmx32m -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGarbageThreshold=1 jdk.jfr.event.gc.detailed.TestShenandoahHeapRegionStateChangeEvent
+ */
+
+public class TestShenandoahHeapRegionStateChangeEvent {
+    private final static String EVENT_NAME = EventNames.ShenandoahHeapRegionStateChange;
+
+    public static void main(String[] args) throws Exception {
+        try (Recording recording = new Recording()) {
+            // activate the event we are interested in and start recording
+            recording.enable(EVENT_NAME).withThreshold(Duration.ofMillis(0));
+            recording.start();
+
+            byte[][] array = new byte[1024][];
+            for (int i = 0; i < array.length; i++) {
+                array[i] = new byte[20 * 1024];
+            }
+            recording.stop();
+
+            // Verify recording
+            List<RecordedEvent> events = Events.fromRecording(recording);
+            Asserts.assertFalse(events.isEmpty(), "No events found");
+
+            for (RecordedEvent event : events) {
+                Events.assertField(event, "index").notEqual(-1);
+                GCHelper.assertIsValidShenandoahHeapRegionState(Events.assertField(event, "from").getValue());
+                GCHelper.assertIsValidShenandoahHeapRegionState(Events.assertField(event, "to").getValue());
+                Events.assertField(event, "used").atMost(1L*1024*1024);
+            }
+        }
+    }
+}
--- a/test/lib/jdk/test/lib/jfr/EventNames.java	Tue May 21 10:34:57 2019 +0200
+++ b/test/lib/jdk/test/lib/jfr/EventNames.java	Tue May 21 10:36:23 2019 +0200
@@ -101,6 +101,8 @@
     public final static String G1HeapSummary = PREFIX + "G1HeapSummary";
     public final static String G1HeapRegionInformation = PREFIX + "G1HeapRegionInformation";
     public final static String G1HeapRegionTypeChange = PREFIX + "G1HeapRegionTypeChange";
+    public final static String ShenandoahHeapRegionInformation = PREFIX + "ShenandoahHeapRegionInformation";
+    public final static String ShenandoahHeapRegionStateChange = PREFIX + "ShenandoahHeapRegionStateChange";
     public final static String TenuringDistribution = PREFIX + "TenuringDistribution";
     public final static String GarbageCollection = PREFIX + "GarbageCollection";
     public final static String ParallelOldGarbageCollection = PREFIX + "ParallelOldGarbageCollection";
--- a/test/lib/jdk/test/lib/jfr/GCHelper.java	Tue May 21 10:34:57 2019 +0200
+++ b/test/lib/jdk/test/lib/jfr/GCHelper.java	Tue May 21 10:36:23 2019 +0200
@@ -80,6 +80,7 @@
     public static final String pauseLevelEvent = "GCPhasePauseLevel";
 
     private static final List<String> g1HeapRegionTypes;
+    private static final List<String> shenandoahHeapRegionStates;
     private static PrintStream defaultErrorLog = null;
 
     public static int getGcId(RecordedEvent event) {
@@ -207,6 +208,21 @@
                                                          };
 
         g1HeapRegionTypes = Collections.unmodifiableList(Arrays.asList(g1HeapRegionTypeLiterals));
+
+        String[] shenandoahHeapRegionStateLiterals = new String[] {
+                                                                    "Empty Uncommitted",
+                                                                    "Empty Committed",
+                                                                    "Regular",
+                                                                    "Humongous Start",
+                                                                    "Humongous Continuation",
+                                                                    "Humongous Start, Pinned",
+                                                                    "Collection Set",
+                                                                    "Pinned",
+                                                                    "Collection Set, Pinned",
+                                                                    "Trash"
+        };
+
+        shenandoahHeapRegionStates = Collections.unmodifiableList(Arrays.asList(shenandoahHeapRegionStateLiterals));
     }
 
     /**
@@ -443,6 +459,13 @@
         return g1HeapRegionTypes.contains(type);
     }
 
+    public static boolean assertIsValidShenandoahHeapRegionState(final String state) {
+        if (!shenandoahHeapRegionStates.contains(state)) {
+            throw new AssertionError("Unknown state '" + state + "', valid heap region states are " + shenandoahHeapRegionStates);
+        }
+        return true;
+    }
+
     /**
      * Helper function to align heap size up.
      *