changeset 9146:a248d0be1309

8229401: Fix JFR code cache test failures 8223689: Add JFR Thread Sampling Support 8223690: Add JFR BiasedLock Event Support 8223691: Add JFR G1 Region Type Change Event Support 8223692: Add JFR G1 Heap Summary Event Support Summary: Backport JFR from JDK11, additional fixes Reviewed-by: neugens, apetushkov Contributed-by: denghui.ddh@alibaba-inc.com
author neugens
date Mon, 19 Aug 2019 10:11:31 +0200
parents 6c8e5745df03
children f162232da105
files src/share/vm/code/codeBlob.cpp src/share/vm/code/codeBlob.hpp src/share/vm/code/codeCache.cpp src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp src/share/vm/gc_implementation/g1/g1HeapRegionTraceType.hpp src/share/vm/gc_implementation/g1/heapRegion.cpp src/share/vm/gc_implementation/g1/heapRegion.hpp src/share/vm/gc_implementation/g1/heapRegionTracer.cpp src/share/vm/gc_implementation/g1/heapRegionTracer.hpp src/share/vm/gc_implementation/g1/heapRegionType.cpp src/share/vm/gc_implementation/g1/heapRegionType.hpp src/share/vm/gc_implementation/shared/gcHeapSummary.hpp src/share/vm/gc_implementation/shared/gcTraceSend.cpp src/share/vm/jfr/periodic/sampling/jfrThreadSampler.cpp src/share/vm/jfr/recorder/checkpoint/types/jfrType.cpp src/share/vm/jfr/support/jfrThreadExtension.hpp src/share/vm/prims/whitebox.cpp src/share/vm/prims/whitebox.hpp src/share/vm/runtime/biasedLocking.cpp src/share/vm/runtime/globals.cpp src/share/vm/runtime/globals.hpp src/share/vm/runtime/thread.hpp
diffstat 23 files changed, 546 insertions(+), 126 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/vm/code/codeBlob.cpp	Wed May 16 15:25:51 2018 +0200
+++ b/src/share/vm/code/codeBlob.cpp	Mon Aug 19 10:11:31 2019 +0200
@@ -58,7 +58,7 @@
 #include "c1/c1_Runtime1.hpp"
 #endif
 
-unsigned int align_code_offset(int offset) {
+unsigned int CodeBlob::align_code_offset(int offset) {
   // align the size to CodeEntryAlignment
   return
     ((offset + (int)CodeHeap::header_size() + (CodeEntryAlignment-1)) & ~(CodeEntryAlignment-1))
--- a/src/share/vm/code/codeBlob.hpp	Wed May 16 15:25:51 2018 +0200
+++ b/src/share/vm/code/codeBlob.hpp	Mon Aug 19 10:11:31 2019 +0200
@@ -30,6 +30,15 @@
 #include "runtime/frame.hpp"
 #include "runtime/handles.hpp"
 
+// CodeBlob Types
+// Used in the CodeCache to assign CodeBlobs to different CodeHeaps
+struct CodeBlobType {
+  enum {
+    All                 = 0,    // All types (No code cache segmentation)
+    NumTypes            = 1     // Number of CodeBlobTypes
+  };
+};
+
 // CodeBlob - superclass for all entries in the CodeCache.
 //
 // Suptypes are:
@@ -71,6 +80,7 @@
  public:
   // Returns the space needed for CodeBlob
   static unsigned int allocation_size(CodeBuffer* cb, int header_size);
+  static unsigned int align_code_offset(int offset);
 
   // Creation
   // a) simple CodeBlob
@@ -205,6 +215,7 @@
   friend class AdapterBlob;
   friend class VtableBlob;
   friend class MethodHandlesAdapterBlob;
+  friend class WhiteBox;
 
  private:
   // Creation support
--- a/src/share/vm/code/codeCache.cpp	Wed May 16 15:25:51 2018 +0200
+++ b/src/share/vm/code/codeCache.cpp	Mon Aug 19 10:11:31 2019 +0200
@@ -189,6 +189,12 @@
     if (cb != NULL) break;
     if (!_heap->expand_by(CodeCacheExpansionSize)) {
       // Expansion failed
+      if (CodeCache_lock->owned_by_self()) {
+        MutexUnlockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
+        report_codemem_full();
+      } else {
+        report_codemem_full();
+      }
       return NULL;
     }
     if (PrintCodeCacheExtension) {
@@ -780,6 +786,7 @@
   _codemem_full_count++;
   EventCodeCacheFull event;
   if (event.should_commit()) {
+    event.set_codeBlobType((u1)CodeBlobType::All);
     event.set_startAddress((u8)low_bound());
     event.set_commitedTopAddress((u8)high());
     event.set_reservedTopAddress((u8)high_bound());
--- a/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp	Wed May 16 15:25:51 2018 +0200
+++ b/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp	Mon Aug 19 10:11:31 2019 +0200
@@ -3572,6 +3572,28 @@
 }
 #endif // PRODUCT
 
+G1HeapSummary G1CollectedHeap::create_g1_heap_summary() {
+
+  size_t eden_used_bytes = _young_list->eden_used_bytes();
+  size_t survivor_used_bytes = _young_list->survivor_used_bytes();
+  size_t heap_used = Heap_lock->owned_by_self() ? used() : used_unlocked();
+
+  size_t eden_capacity_bytes =
+    (g1_policy()->young_list_target_length() * HeapRegion::GrainBytes) - survivor_used_bytes;
+
+  VirtualSpaceSummary heap_summary = create_heap_space_summary();
+  return G1HeapSummary(heap_summary, heap_used, eden_used_bytes,
+                       eden_capacity_bytes, survivor_used_bytes, num_regions());
+}
+
+void G1CollectedHeap::trace_heap(GCWhen::Type when, GCTracer* gc_tracer) {
+  const G1HeapSummary& heap_summary = create_g1_heap_summary();
+  gc_tracer->report_gc_heap_summary(when, heap_summary);
+
+  const MetaspaceSummary& metaspace_summary = create_metaspace_summary();
+  gc_tracer->report_metaspace_summary(when, metaspace_summary);
+}
+
 G1CollectedHeap* G1CollectedHeap::heap() {
   assert(_sh->kind() == CollectedHeap::G1CollectedHeap,
          "not a garbage-first heap");
--- a/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp	Wed May 16 15:25:51 2018 +0200
+++ b/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp	Mon Aug 19 10:11:31 2019 +0200
@@ -375,6 +375,8 @@
                                                          size_t size,
                                                          size_t translation_factor);
 
+  void trace_heap(GCWhen::Type when, GCTracer* tracer);
+
   double verify(bool guard, const char* msg);
   void verify_before_gc();
   void verify_after_gc();
@@ -1621,6 +1623,8 @@
   bool is_obj_dead_cond(const oop obj,
                         const VerifyOption vo) const;
 
+  G1HeapSummary create_g1_heap_summary();
+
   // Printing
 
   virtual void print_on(outputStream* st) const;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/vm/gc_implementation/g1/g1HeapRegionTraceType.hpp	Mon Aug 19 10:11:31 2019 +0200
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#ifndef SHARE_GC_G1_G1HEAPREGIONTRACETYPE_HPP
+#define SHARE_GC_G1_G1HEAPREGIONTRACETYPE_HPP
+
+#include "memory/allocation.hpp"
+#include "utilities/debug.hpp"
+
+class G1HeapRegionTraceType : AllStatic {
+ public:
+  enum Type {
+    Free,
+    Eden,
+    Survivor,
+    StartsHumongous,
+    ContinuesHumongous,
+    Old,
+    G1HeapRegionTypeEndSentinel
+  };
+
+  static const char* to_string(G1HeapRegionTraceType::Type type) {
+    switch (type) {
+      case Free:               return "Free";
+      case Eden:               return "Eden";
+      case Survivor:           return "Survivor";
+      case StartsHumongous:    return "Starts Humongous";
+      case ContinuesHumongous: return "Continues Humongous";
+      case Old:                return "Old";
+      default: ShouldNotReachHere(); return NULL;
+    }
+  }
+};
+
+#endif // SHARE_GC_G1_G1HEAPREGIONTRACETYPE_HPP
--- a/src/share/vm/gc_implementation/g1/heapRegion.cpp	Wed May 16 15:25:51 2018 +0200
+++ b/src/share/vm/gc_implementation/g1/heapRegion.cpp	Mon Aug 19 10:11:31 2019 +0200
@@ -37,6 +37,7 @@
 #include "memory/space.inline.hpp"
 #include "oops/oop.inline.hpp"
 #include "runtime/orderAccess.inline.hpp"
+#include "gc_implementation/g1/heapRegionTracer.hpp"
 
 PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC
 
@@ -211,6 +212,31 @@
   _gc_efficiency = (double) reclaimable_bytes() / region_elapsed_time_ms;
 }
 
+void HeapRegion::set_free() {
+  report_region_type_change(G1HeapRegionTraceType::Free);
+  _type.set_free();
+}
+
+void HeapRegion::set_eden() {
+  report_region_type_change(G1HeapRegionTraceType::Eden);
+  _type.set_eden();
+}
+
+void HeapRegion::set_eden_pre_gc() {
+  report_region_type_change(G1HeapRegionTraceType::Eden);
+  _type.set_eden_pre_gc();
+}
+
+void HeapRegion::set_survivor() {
+  report_region_type_change(G1HeapRegionTraceType::Survivor);
+  _type.set_survivor();
+}
+
+void HeapRegion::set_old() {
+  report_region_type_change(G1HeapRegionTraceType::Old);
+  _type.set_old();
+}
+
 void HeapRegion::set_startsHumongous(HeapWord* new_top, HeapWord* new_end) {
   assert(!isHumongous(), "sanity / pre-condition");
   assert(end() == _orig_end,
@@ -218,6 +244,7 @@
   assert(top() == bottom(), "should be empty");
   assert(bottom() <= new_top && new_top <= new_end, "pre-condition");
 
+  report_region_type_change(G1HeapRegionTraceType::StartsHumongous);
   _type.set_starts_humongous();
   _humongous_start_region = this;
 
@@ -232,6 +259,7 @@
   assert(top() == bottom(), "should be empty");
   assert(first_hr->startsHumongous(), "pre-condition");
 
+  report_region_type_change(G1HeapRegionTraceType::ContinuesHumongous);
   _type.set_continues_humongous();
   _humongous_start_region = first_hr;
 }
@@ -303,6 +331,14 @@
   record_timestamp();
 }
 
+void HeapRegion::report_region_type_change(G1HeapRegionTraceType::Type to) {
+  HeapRegionTracer::send_region_type_change(_hrm_index,
+                                            get_trace_type(),
+                                            to,
+                                            (uintptr_t)bottom(),
+                                            used());
+}
+
 CompactibleSpace* HeapRegion::next_compaction_space() const {
   return G1CollectedHeap::heap()->next_compaction_region(this);
 }
--- a/src/share/vm/gc_implementation/g1/heapRegion.hpp	Wed May 16 15:25:51 2018 +0200
+++ b/src/share/vm/gc_implementation/g1/heapRegion.hpp	Mon Aug 19 10:11:31 2019 +0200
@@ -35,6 +35,7 @@
 #include "memory/space.inline.hpp"
 #include "memory/watermark.hpp"
 #include "utilities/macros.hpp"
+#include "gc_implementation/g1/g1HeapRegionTraceType.hpp"
 
 // A HeapRegion is the smallest piece of a G1CollectedHeap that
 // can be collected independently.
@@ -211,6 +212,8 @@
 
   G1BlockOffsetArrayContigSpace* offsets() { return &_offsets; }
 
+  void report_region_type_change(G1HeapRegionTraceType::Type to);
+
  protected:
   // The index of this region in the heap region sequence.
   uint  _hrm_index;
@@ -405,6 +408,7 @@
 
   const char* get_type_str() const { return _type.get_str(); }
   const char* get_short_type_str() const { return _type.get_short_str(); }
+  G1HeapRegionTraceType::Type get_trace_type() { return _type.get_trace_type(); }
 
   bool is_free() const { return _type.is_free(); }
 
@@ -667,13 +671,13 @@
     }
   }
 
-  void set_free() { _type.set_free(); }
+  void set_free();
 
-  void set_eden()        { _type.set_eden();        }
-  void set_eden_pre_gc() { _type.set_eden_pre_gc(); }
-  void set_survivor()    { _type.set_survivor();    }
+  void set_eden();
+  void set_eden_pre_gc();
+  void set_survivor();
 
-  void set_old() { _type.set_old(); }
+  void set_old();
 
   // Determine if an object has been allocated since the last
   // mark performed by the collector. This returns true iff the object
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/vm/gc_implementation/g1/heapRegionTracer.cpp	Mon Aug 19 10:11:31 2019 +0200
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#include "precompiled.hpp"
+#include "gc_implementation/g1/heapRegionTracer.hpp"
+#include "jfr/jfrEvents.hpp"
+
+void HeapRegionTracer::send_region_type_change(uint index,
+                                               G1HeapRegionTraceType::Type from,
+                                               G1HeapRegionTraceType::Type to,
+                                               uintptr_t start,
+                                               size_t used) {
+  EventG1HeapRegionTypeChange e;
+  if (e.should_commit()) {
+    e.set_index(index);
+    e.set_from(from);
+    e.set_to(to);
+    e.set_start(start);
+    e.set_used(used);
+    e.commit();
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/vm/gc_implementation/g1/heapRegionTracer.hpp	Mon Aug 19 10:11:31 2019 +0200
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#ifndef SHARE_GC_G1_HEAPREGIONTRACER_HPP
+#define SHARE_GC_G1_HEAPREGIONTRACER_HPP
+
+#include "gc_implementation/g1/g1HeapRegionTraceType.hpp"
+#include "memory/allocation.hpp"
+
+class HeapRegionTracer : AllStatic {
+  public:
+    static void send_region_type_change(uint index,
+                                        G1HeapRegionTraceType::Type from,
+                                        G1HeapRegionTraceType::Type to,
+                                        uintptr_t start,
+                                        size_t used);
+};
+
+#endif // SHARE_GC_G1_HEAPREGIONTRACER_HPP
--- a/src/share/vm/gc_implementation/g1/heapRegionType.cpp	Wed May 16 15:25:51 2018 +0200
+++ b/src/share/vm/gc_implementation/g1/heapRegionType.cpp	Mon Aug 19 10:11:31 2019 +0200
@@ -67,3 +67,18 @@
   // keep some compilers happy
   return NULL;
 }
+
+G1HeapRegionTraceType::Type HeapRegionType::get_trace_type() {
+  hrt_assert_is_valid(_tag);
+  switch (_tag) {
+    case FreeTag:               return G1HeapRegionTraceType::Free;
+    case EdenTag:               return G1HeapRegionTraceType::Eden;
+    case SurvTag:               return G1HeapRegionTraceType::Survivor;
+    case HumStartsTag:          return G1HeapRegionTraceType::StartsHumongous;
+    case HumContTag:            return G1HeapRegionTraceType::ContinuesHumongous;
+    case OldTag:                return G1HeapRegionTraceType::Old;
+    default:
+      ShouldNotReachHere();
+      return G1HeapRegionTraceType::Free; // keep some compilers happy
+  }
+}
--- a/src/share/vm/gc_implementation/g1/heapRegionType.hpp	Wed May 16 15:25:51 2018 +0200
+++ b/src/share/vm/gc_implementation/g1/heapRegionType.hpp	Mon Aug 19 10:11:31 2019 +0200
@@ -26,6 +26,7 @@
 #define SHARE_VM_GC_IMPLEMENTATION_G1_HEAPREGIONTYPE_HPP
 
 #include "memory/allocation.hpp"
+#include "gc_implementation/g1/g1HeapRegionTraceType.hpp"
 
 #define hrt_assert_is_valid(tag) \
   assert(is_valid((tag)), err_msg("invalid HR type: %u", (uint) (tag)))
@@ -127,6 +128,7 @@
 
   const char* get_str() const;
   const char* get_short_str() const;
+  G1HeapRegionTraceType::Type get_trace_type();
 
   HeapRegionType() : _tag(FreeTag) { hrt_assert_is_valid(_tag); }
 };
--- a/src/share/vm/gc_implementation/shared/gcHeapSummary.hpp	Wed May 16 15:25:51 2018 +0200
+++ b/src/share/vm/gc_implementation/shared/gcHeapSummary.hpp	Mon Aug 19 10:11:31 2019 +0200
@@ -78,11 +78,13 @@
 
 class GCHeapSummary;
 class PSHeapSummary;
+class G1HeapSummary;
 
 class GCHeapSummaryVisitor {
  public:
   virtual void visit(const GCHeapSummary* heap_summary) const = 0;
   virtual void visit(const PSHeapSummary* heap_summary) const {}
+  virtual void visit(const G1HeapSummary* heap_summary) const {}
 };
 
 class GCHeapSummary : public StackObj {
@@ -125,6 +127,24 @@
    }
 };
 
+class G1HeapSummary : public GCHeapSummary {
+  size_t  _edenUsed;
+  size_t  _edenCapacity;
+  size_t  _survivorUsed;
+  uint    _numberOfRegions;
+ public:
+   G1HeapSummary(VirtualSpaceSummary& heap_space, size_t heap_used, size_t edenUsed, size_t edenCapacity, size_t survivorUsed, uint numberOfRegions) :
+      GCHeapSummary(heap_space, heap_used), _edenUsed(edenUsed), _edenCapacity(edenCapacity), _survivorUsed(survivorUsed), _numberOfRegions(numberOfRegions) { }
+   const size_t edenUsed() const { return _edenUsed; }
+   const size_t edenCapacity() const { return _edenCapacity; }
+   const size_t survivorUsed() const { return _survivorUsed; }
+   const uint   numberOfRegions() const { return _numberOfRegions; }
+
+   virtual void accept(GCHeapSummaryVisitor* visitor) const {
+     visitor->visit(this);
+   }
+};
+
 class MetaspaceSummary : public StackObj {
   size_t _capacity_until_GC;
   MetaspaceSizes _meta_space;
--- a/src/share/vm/gc_implementation/shared/gcTraceSend.cpp	Wed May 16 15:25:51 2018 +0200
+++ b/src/share/vm/gc_implementation/shared/gcTraceSend.cpp	Mon Aug 19 10:11:31 2019 +0200
@@ -349,20 +349,20 @@
     }
   }
 
-//  void visit(const G1HeapSummary* g1_heap_summary) const {
-//    visit((GCHeapSummary*)g1_heap_summary);
-//
-//    EventG1HeapSummary e;
-//    if (e.should_commit()) {
-//      e.set_gcId(_shared_gc_info.gc_id().id());
-//      e.set_when((u1)_when);
-//      e.set_edenUsedSize(g1_heap_summary->edenUsed());
-//      e.set_edenTotalSize(g1_heap_summary->edenCapacity());
-//      e.set_survivorUsedSize(g1_heap_summary->survivorUsed());
-//      e.set_numberOfRegions(g1_heap_summary->numberOfRegions());
-//      e.commit();
-//    }
-//  }
+  void visit(const G1HeapSummary* g1_heap_summary) const {
+    visit((GCHeapSummary*)g1_heap_summary);
+
+    EventG1HeapSummary e;
+    if (e.should_commit()) {
+      e.set_gcId(_gc_id.id());
+      e.set_when((u1)_when);
+      e.set_edenUsedSize(g1_heap_summary->edenUsed());
+      e.set_edenTotalSize(g1_heap_summary->edenCapacity());
+      e.set_survivorUsedSize(g1_heap_summary->survivorUsed());
+      e.set_numberOfRegions(g1_heap_summary->numberOfRegions());
+      e.commit();
+    }
+  }
 
   void visit(const PSHeapSummary* ps_heap_summary) const {
     visit((GCHeapSummary*)ps_heap_summary);
--- a/src/share/vm/jfr/periodic/sampling/jfrThreadSampler.cpp	Wed May 16 15:25:51 2018 +0200
+++ b/src/share/vm/jfr/periodic/sampling/jfrThreadSampler.cpp	Mon Aug 19 10:11:31 2019 +0200
@@ -321,7 +321,8 @@
   volatile bool _disenrolled;
   static Monitor* _transition_block_lock;
 
-//  JavaThread* next_thread(ThreadsList* t_list, JavaThread* first_sampled, JavaThread* current);
+  int find_index_of_JavaThread(JavaThread** t_list, uint length, JavaThread *target);
+  JavaThread* next_thread(JavaThread** t_list, uint length, JavaThread* first_sampled, JavaThread* current);
   void task_stacktrace(JfrSampleType type, JavaThread** last_thread);
   JfrThreadSampler(size_t interval_java, size_t interval_native, u4 max_frames);
   ~JfrThreadSampler();
@@ -344,7 +345,7 @@
 Monitor* JfrThreadSampler::_transition_block_lock = new Monitor(Mutex::leaf, "Trace block", true);
 
 static void clear_transition_block(JavaThread* jt) {
-//  jt->clear_trace_flag();
+  jt->clear_trace_flag();
   JfrThreadLocal* const tl = jt->jfr_thread_local();
   if (tl->is_trace_block()) {
     MutexLockerEx ml(JfrThreadSampler::transition_block(), Mutex::_no_safepoint_check_flag);
@@ -359,7 +360,7 @@
   }
 
   bool ret = false;
-//  thread->set_trace_flag();
+  thread->set_trace_flag();
   if (!UseMembar) {
     os::serialize_thread_states();
   }
@@ -398,37 +399,61 @@
   JfrThreadLocal* const tl = thread->jfr_thread_local();
   tl->set_trace_block();
   {
-//    MutexLockerEx ml(transition_block(), Mutex::_no_safepoint_check_flag);
-//    while (thread->is_trace_suspend()) {
-//      transition_block()->wait(true);
-//    }
-//    tl->clear_trace_block();
+    MutexLockerEx ml(transition_block(), Mutex::_no_safepoint_check_flag);
+    while (thread->is_trace_suspend()) {
+      transition_block()->wait(true);
+    }
+    tl->clear_trace_block();
   }
 }
 
-//JavaThread* JfrThreadSampler::next_thread(ThreadsList* t_list, JavaThread* first_sampled, JavaThread* current) {
-//  assert(t_list != NULL, "invariant");
-//  assert(Threads_lock->owned_by_self(), "Holding the thread table lock.");
-//  assert(_cur_index >= -1 && (uint)_cur_index + 1 <= t_list->length(), "invariant");
-//  assert((current == NULL && -1 == _cur_index) || (t_list->find_index_of_JavaThread(current) == _cur_index), "invariant");
-//  if ((uint)_cur_index + 1 == t_list->length()) {
-//    // wrap
-//    _cur_index = 0;
-//  } else {
-//    _cur_index++;
-//  }
-//  assert(_cur_index >= 0 && (uint)_cur_index < t_list->length(), "invariant");
-//  JavaThread* const next = t_list->thread_at(_cur_index);
-//  return next != first_sampled ? next : NULL;
-//}
+int JfrThreadSampler::find_index_of_JavaThread(JavaThread** t_list, uint length, JavaThread *target) {
+  assert(Threads_lock->owned_by_self(), "Holding the thread table lock.");
+  if (target == NULL) {
+    return -1;
+  }
+  for (uint i = 0; i < length; i++) {
+    if (target == t_list[i]) {
+      return (int)i;
+    }
+  }
+  return -1;
+}
+
+JavaThread* JfrThreadSampler::next_thread(JavaThread** t_list, uint length, JavaThread* first_sampled, JavaThread* current) {
+  assert(Threads_lock->owned_by_self(), "Holding the thread table lock.");
+  if (current == NULL) {
+    _cur_index = 0;
+    return t_list[_cur_index];
+  }
+
+  if (_cur_index == -1 || t_list[_cur_index] != current) {
+    // 'current' is not at '_cur_index' so find it:
+    _cur_index = find_index_of_JavaThread(t_list, length, current);
+    assert(_cur_index != -1, "current JavaThread should be findable.");
+  }
+  _cur_index++;
+
+  JavaThread* next = NULL;
+  // wrap
+  if ((uint)_cur_index >= length) {
+    _cur_index = 0;
+  }
+  next = t_list[_cur_index];
+
+  // sample wrap
+  if (next == first_sampled) {
+    return NULL;
+  }
+  return next;
+}
 
 void JfrThreadSampler::start_thread() {
-  // XXX TODO implement sampling
-//  if (os::create_thread(this, os::os_thread)) {
-//    os::start_thread(this);
-//  } else {
-//    if (true) tty->print_cr("Failed to create thread for thread sampling");
-//  }
+  if (os::create_thread(this, os::os_thread)) {
+    os::start_thread(this);
+  } else {
+    tty->print_cr("Failed to create thread for thread sampling");
+  }
 }
 
 void JfrThreadSampler::enroll() {
@@ -510,28 +535,33 @@
     elapsedTimer sample_time;
     sample_time.start();
     {
-//      MonitorLockerEx tlock(Threads_lock, Mutex::_allow_vm_block_flag);
-//      ThreadsListHandle tlh;
-//      // Resolve a sample session relative start position index into the thread list array.
-//      // In cases where the last sampled thread is NULL or not-NULL but stale, find_index() returns -1.
-//      _cur_index = tlh.list()->find_index_of_JavaThread(*last_thread);
-//      JavaThread* current = _cur_index != -1 ? *last_thread : NULL;
-//
-//      while (num_sample_attempts < sample_limit) {
-//        current = next_thread(tlh.list(), start, current);
-//        if (current == NULL) {
-//          break;
-//        }
-//        if (start == NULL) {
-//          start = current;  // remember the thread where we started to attempt sampling
-//        }
-//        if (current->is_Compiler_thread()) {
-//          continue;
-//        }
-//        sample_task.do_sample_thread(current, _frames, _max_frames, type);
-//        num_sample_attempts++;
-//      }
-//      *last_thread = current;  // remember the thread we last attempted to sample
+      MonitorLockerEx tlock(Threads_lock, Mutex::_allow_vm_block_flag);
+      int max_threads = Threads::number_of_threads();
+      assert(max_threads >= 0, "Threads list is empty");
+      uint index = 0;
+      JavaThread** threads_list = NEW_C_HEAP_ARRAY(JavaThread *, max_threads, mtInternal);
+      for (JavaThread* tp = Threads::first(); tp != NULL; tp = tp->next()) {
+        threads_list[index++] = tp;
+      }
+      JavaThread* current = Threads::includes(*last_thread) ? *last_thread : NULL;
+      JavaThread* start = NULL;
+
+      while (num_sample_attempts < sample_limit) {
+        current = next_thread(threads_list, index, start, current);
+        if (current == NULL) {
+          break;
+        }
+        if (start == NULL) {
+          start = current;  // remember the thread where we started to attempt sampling
+        }
+        if (current->is_Compiler_thread()) {
+          continue;
+        }
+        sample_task.do_sample_thread(current, _frames, _max_frames, type);
+        num_sample_attempts++;
+      }
+      *last_thread = current;  // remember the thread we last attempted to sample
+      FREE_C_HEAP_ARRAY(JavaThread *, threads_list, mtInternal);
     }
     sample_time.stop();
     if (LogJFR && Verbose) tty->print_cr("JFR thread sampling done in %3.7f secs with %d java %d native samples",
--- a/src/share/vm/jfr/recorder/checkpoint/types/jfrType.cpp	Wed May 16 15:25:51 2018 +0200
+++ b/src/share/vm/jfr/recorder/checkpoint/types/jfrType.cpp	Mon Aug 19 10:11:31 2019 +0200
@@ -188,13 +188,12 @@
 }
 
 void G1HeapRegionTypeConstant::serialize(JfrCheckpointWriter& writer) {
-  // XXX TODO?
-//  static const u4 nof_entries = G1HeapRegionTraceType::G1HeapRegionTypeEndSentinel;
-//  writer.write_count(nof_entries);
-//  for (u4 i = 0; i < nof_entries; ++i) {
-//    writer.write_key(i);
-//    writer.write(G1HeapRegionTraceType::to_string((G1HeapRegionTraceType::Type)i));
-//  }
+  static const u4 nof_entries = G1HeapRegionTraceType::G1HeapRegionTypeEndSentinel;
+  writer.write_count(nof_entries);
+  for (u4 i = 0; i < nof_entries; ++i) {
+    writer.write_key(i);
+    writer.write(G1HeapRegionTraceType::to_string((G1HeapRegionTraceType::Type)i));
+  }
 }
 
 void GCThresholdUpdaterConstant::serialize(JfrCheckpointWriter& writer) {
@@ -279,13 +278,10 @@
 }
 
 void CodeBlobTypeConstant::serialize(JfrCheckpointWriter& writer) {
-  // XXX no code blob types. need to send any stub value?
-//  static const u4 nof_entries = CodeBlobType::NumTypes;
-//  writer.write_count(nof_entries);
-//  for (u4 i = 0; i < nof_entries; ++i) {
-//    writer.write_key(i);
-//    writer.write(CodeCache::get_code_heap_name(i));
-//  }
+  static const u4 nof_entries = CodeBlobType::NumTypes;
+  writer.write_count(nof_entries);
+  writer.write_key((u4)CodeBlobType::All);
+  writer.write("CodeCache");
 };
 
 void VMOperationTypeConstant::serialize(JfrCheckpointWriter& writer) {
--- a/src/share/vm/jfr/support/jfrThreadExtension.hpp	Wed May 16 15:25:51 2018 +0200
+++ b/src/share/vm/jfr/support/jfrThreadExtension.hpp	Mon Aug 19 10:11:31 2019 +0200
@@ -46,7 +46,11 @@
 #define THREAD_LOCAL_WRITER_OFFSET_JFR \
   JfrThreadLocal::java_event_writer_offset() + THREAD_LOCAL_OFFSET_JFR
 
-// XXX consider implementing thread suspend tracing
-#define SUSPEND_THREAD_CONDITIONAL(thread) if (false/*(thread)->is_trace_suspend()*/) JfrThreadSampling::on_javathread_suspend(thread)
+#define DEFINE_TRACE_SUSPEND_FLAG_METHODS \
+  void set_trace_flag() { set_suspend_flag(_trace_flag); } \
+  void clear_trace_flag() { clear_suspend_flag(_trace_flag); } \
+  bool is_trace_suspend() { return (_suspend_flags & _trace_flag) != 0; }
+
+#define SUSPEND_THREAD_CONDITIONAL(thread) if ((thread)->is_trace_suspend()) JfrThreadSampling::on_javathread_suspend(thread)
 
 #endif // SHARE_VM_JFR_SUPPORT_JFRTHREADEXTENSION_HPP
--- a/src/share/vm/prims/whitebox.cpp	Wed May 16 15:25:51 2018 +0200
+++ b/src/share/vm/prims/whitebox.cpp	Mon Aug 19 10:11:31 2019 +0200
@@ -40,6 +40,7 @@
 #include "runtime/interfaceSupport.hpp"
 #include "runtime/os.hpp"
 #include "utilities/array.hpp"
+#include "utilities/align.hpp"
 #include "utilities/debug.hpp"
 #include "utilities/macros.hpp"
 #include "utilities/exceptions.hpp"
@@ -653,13 +654,13 @@
 WB_END
 
 template <typename T>
-static bool GetVMFlag(JavaThread* thread, JNIEnv* env, jstring name, T* value, bool (*TAt)(const char*, T*)) {
+static bool GetVMFlag(JavaThread* thread, JNIEnv* env, jstring name, T* value, bool (*TAt)(const char*, T*, bool, bool)) {
   if (name == NULL) {
     return false;
   }
   ThreadToNativeFromVM ttnfv(thread);   // can't be in VM when we call JNI
   const char* flag_name = env->GetStringUTFChars(name, NULL);
-  bool result = (*TAt)(flag_name, value);
+  bool result = (*TAt)(flag_name, value, true, true);
   env->ReleaseStringUTFChars(name, flag_name);
   return result;
 }
@@ -851,6 +852,47 @@
   return features_string;
 WB_END
 
+int WhiteBox::get_blob_type(const CodeBlob* code) {
+  guarantee(WhiteBoxAPI, "internal testing API :: WhiteBox has to be enabled");
+  return CodeBlobType::All;;
+}
+
+struct CodeBlobStub {
+  CodeBlobStub(const CodeBlob* blob) :
+      name(os::strdup(blob->name())),
+      size(blob->size()),
+      blob_type(WhiteBox::get_blob_type(blob)),
+      address((jlong) blob) { }
+  ~CodeBlobStub() { os::free((void*) name); }
+  const char* const name;
+  const jint        size;
+  const jint        blob_type;
+  const jlong       address;
+};
+
+static jobjectArray codeBlob2objectArray(JavaThread* thread, JNIEnv* env, CodeBlobStub* cb) {
+  jclass clazz = env->FindClass(vmSymbols::java_lang_Object()->as_C_string());
+  CHECK_JNI_EXCEPTION_(env, NULL);
+  jobjectArray result = env->NewObjectArray(4, clazz, NULL);
+
+  jstring name = env->NewStringUTF(cb->name);
+  CHECK_JNI_EXCEPTION_(env, NULL);
+  env->SetObjectArrayElement(result, 0, name);
+
+  jobject obj = integerBox(thread, env, cb->size);
+  CHECK_JNI_EXCEPTION_(env, NULL);
+  env->SetObjectArrayElement(result, 1, obj);
+
+  obj = integerBox(thread, env, cb->blob_type);
+  CHECK_JNI_EXCEPTION_(env, NULL);
+  env->SetObjectArrayElement(result, 2, obj);
+
+  obj = longBox(thread, env, cb->address);
+  CHECK_JNI_EXCEPTION_(env, NULL);
+  env->SetObjectArrayElement(result, 3, obj);
+
+  return result;
+}
 
 WB_ENTRY(jobjectArray, WB_GetNMethod(JNIEnv* env, jobject o, jobject method, jboolean is_osr))
   ResourceMark rm(THREAD);
@@ -888,6 +930,47 @@
   return result;
 WB_END
 
+CodeBlob* WhiteBox::allocate_code_blob(int size, int blob_type) {
+  guarantee(WhiteBoxAPI, "internal testing API :: WhiteBox has to be enabled");
+  BufferBlob* blob;
+  int full_size = CodeBlob::align_code_offset(sizeof(BufferBlob));
+  if (full_size < size) {
+    full_size += align_up(size - full_size, oopSize);
+  }
+  {
+    MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
+    blob = (BufferBlob*) CodeCache::allocate(full_size);
+    ::new (blob) BufferBlob("WB::DummyBlob", full_size);
+  }
+  // Track memory usage statistic after releasing CodeCache_lock
+  MemoryService::track_code_cache_memory_usage();
+  return blob;
+}
+
+WB_ENTRY(jlong, WB_AllocateCodeBlob(JNIEnv* env, jobject o, jint size, jint blob_type))
+  if (size < 0) {
+    THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(),
+      err_msg("WB_AllocateCodeBlob: size is negative: " INT32_FORMAT, size));
+  }
+  return (jlong) WhiteBox::allocate_code_blob(size, blob_type);
+WB_END
+
+WB_ENTRY(void, WB_FreeCodeBlob(JNIEnv* env, jobject o, jlong addr))
+  if (addr == 0) {
+    return;
+  }
+  BufferBlob::free((BufferBlob*) addr);
+WB_END
+
+WB_ENTRY(jobjectArray, WB_GetCodeBlob(JNIEnv* env, jobject o, jlong addr))
+  if (addr == 0) {
+    THROW_MSG_NULL(vmSymbols::java_lang_NullPointerException(),
+      "WB_GetCodeBlob: addr is null");
+  }
+  ThreadToNativeFromVM ttn(thread);
+  CodeBlobStub stub((CodeBlob*) addr);
+  return codeBlob2objectArray(thread, env, &stub);
+WB_END
 
 int WhiteBox::array_bytes_to_length(size_t bytes) {
   return Array<u1>::bytes_to_length(bytes);
@@ -1167,6 +1250,9 @@
   {CC"fullGC",   CC"()V",                             (void*)&WB_FullGC },
   {CC"youngGC",  CC"()V",                             (void*)&WB_YoungGC },
   {CC"readReservedMemory", CC"()V",                   (void*)&WB_ReadReservedMemory },
+  {CC"allocateCodeBlob",   CC"(II)J",                 (void*)&WB_AllocateCodeBlob   },
+  {CC"freeCodeBlob",       CC"(J)V",                  (void*)&WB_FreeCodeBlob       },
+  {CC"getCodeBlob",        CC"(J)[Ljava/lang/Object;",(void*)&WB_GetCodeBlob        },
   {CC"allocateMetaspace",
      CC"(Ljava/lang/ClassLoader;J)J",                 (void*)&WB_AllocateMetaspace },
   {CC"freeMetaspace",
--- a/src/share/vm/prims/whitebox.hpp	Wed May 16 15:25:51 2018 +0200
+++ b/src/share/vm/prims/whitebox.hpp	Mon Aug 19 10:11:31 2019 +0200
@@ -64,7 +64,8 @@
     Symbol* signature_symbol);
   static const char* lookup_jstring(const char* field_name, oop object);
   static bool lookup_bool(const char* field_name, oop object);
-
+  static int get_blob_type(const CodeBlob* code);
+  static CodeBlob* allocate_code_blob(int size, int blob_type);
   static int array_bytes_to_length(size_t bytes);
   static void register_methods(JNIEnv* env, jclass wbclass, JavaThread* thread,
     JNINativeMethod* method_array, int method_count);
--- a/src/share/vm/runtime/biasedLocking.cpp	Wed May 16 15:25:51 2018 +0200
+++ b/src/share/vm/runtime/biasedLocking.cpp	Mon Aug 19 10:11:31 2019 +0200
@@ -31,6 +31,8 @@
 #include "runtime/vframe.hpp"
 #include "runtime/vmThread.hpp"
 #include "runtime/vm_operations.hpp"
+#include "jfr/support/jfrThreadId.hpp"
+#include "jfr/jfrEvents.hpp"
 
 static bool _biased_locking_enabled = false;
 BiasedLockingCounters BiasedLocking::_counters;
@@ -142,8 +144,9 @@
   return info;
 }
 
-
-static BiasedLocking::Condition revoke_bias(oop obj, bool allow_rebias, bool is_bulk, JavaThread* requesting_thread) {
+// After the call, *biased_locker will be set to obj->mark()->biased_locker() if biased_locker != NULL,
+// AND it is a living thread. Otherwise it will not be updated, (i.e. the caller is responsible for initialization).
+static BiasedLocking::Condition revoke_bias(oop obj, bool allow_rebias, bool is_bulk, JavaThread* requesting_thread, JavaThread** biased_locker) {
   markOop mark = obj->mark();
   if (!mark->has_bias_pattern()) {
     if (TraceBiasedLocking) {
@@ -253,6 +256,11 @@
     }
   }
 
+  // If requested, return information on which thread held the bias
+  if (biased_locker != NULL) {
+    *biased_locker = biased_thread;
+  }
+
   return BiasedLocking::BIAS_REVOKED;
 }
 
@@ -373,7 +381,7 @@
 
     // At this point we're done. All we have to do is potentially
     // adjust the header of the given object to revoke its bias.
-    revoke_bias(o, attempt_rebias_of_object && klass->prototype_header()->has_bias_pattern(), true, requesting_thread);
+    revoke_bias(o, attempt_rebias_of_object && klass->prototype_header()->has_bias_pattern(), true, requesting_thread, NULL);
   } else {
     if (TraceBiasedLocking) {
       ResourceMark rm;
@@ -395,14 +403,14 @@
         oop owner = mon_info->owner();
         markOop mark = owner->mark();
         if ((owner->klass() == k_o) && mark->has_bias_pattern()) {
-          revoke_bias(owner, false, true, requesting_thread);
+          revoke_bias(owner, false, true, requesting_thread, NULL);
         }
       }
     }
 
     // Must force the bias of the passed object to be forcibly revoked
     // as well to ensure guarantees to callers
-    revoke_bias(o, false, true, requesting_thread);
+    revoke_bias(o, false, true, requesting_thread, NULL);
   }
 
   if (TraceBiasedLocking) {
@@ -445,19 +453,22 @@
   GrowableArray<Handle>* _objs;
   JavaThread* _requesting_thread;
   BiasedLocking::Condition _status_code;
+  traceid _biased_locker_id;
 
 public:
   VM_RevokeBias(Handle* obj, JavaThread* requesting_thread)
     : _obj(obj)
     , _objs(NULL)
     , _requesting_thread(requesting_thread)
-    , _status_code(BiasedLocking::NOT_BIASED) {}
+    , _status_code(BiasedLocking::NOT_BIASED)
+    , _biased_locker_id(0) {}
 
   VM_RevokeBias(GrowableArray<Handle>* objs, JavaThread* requesting_thread)
     : _obj(NULL)
     , _objs(objs)
     , _requesting_thread(requesting_thread)
-    , _status_code(BiasedLocking::NOT_BIASED) {}
+    , _status_code(BiasedLocking::NOT_BIASED)
+    , _biased_locker_id(0) {}
 
   virtual VMOp_Type type() const { return VMOp_RevokeBias; }
 
@@ -486,7 +497,11 @@
       if (TraceBiasedLocking) {
         tty->print_cr("Revoking bias with potentially per-thread safepoint:");
       }
-      _status_code = revoke_bias((*_obj)(), false, false, _requesting_thread);
+      JavaThread* biased_locker = NULL;
+      _status_code = revoke_bias((*_obj)(), false, false, _requesting_thread, &biased_locker);
+      if (biased_locker != NULL) {
+        _biased_locker_id = JFR_THREAD_ID(biased_locker);
+      }
       clean_up_cached_monitor_info();
       return;
     } else {
@@ -500,6 +515,10 @@
   BiasedLocking::Condition status_code() const {
     return _status_code;
   }
+
+  traceid biased_locker() const {
+    return _biased_locker_id;
+  }
 };
 
 
@@ -609,23 +628,44 @@
       if (TraceBiasedLocking) {
         tty->print_cr("Revoking bias by walking my own stack:");
       }
-      BiasedLocking::Condition cond = revoke_bias(obj(), false, false, (JavaThread*) THREAD);
+      EventBiasedLockSelfRevocation event;
+      BiasedLocking::Condition cond = revoke_bias(obj(), false, false, (JavaThread*) THREAD, NULL);
       ((JavaThread*) THREAD)->set_cached_monitor_info(NULL);
       assert(cond == BIAS_REVOKED, "why not?");
+      if (event.should_commit()) {
+        event.set_lockClass(k);
+        event.commit();
+      }
       return cond;
     } else {
+      EventBiasedLockRevocation event;
       VM_RevokeBias revoke(&obj, (JavaThread*) THREAD);
       VMThread::execute(&revoke);
+      if (event.should_commit() && (revoke.status_code() != NOT_BIASED)) {
+        event.set_lockClass(k);
+        // Subtract 1 to match the id of events committed inside the safepoint
+        event.set_safepointId(SafepointSynchronize::safepoint_counter() - 1);
+        event.set_previousOwner(revoke.biased_locker());
+        event.commit();
+      }
       return revoke.status_code();
     }
   }
 
   assert((heuristics == HR_BULK_REVOKE) ||
          (heuristics == HR_BULK_REBIAS), "?");
+  EventBiasedLockClassRevocation event;
   VM_BulkRevokeBias bulk_revoke(&obj, (JavaThread*) THREAD,
                                 (heuristics == HR_BULK_REBIAS),
                                 attempt_rebias);
   VMThread::execute(&bulk_revoke);
+  if (event.should_commit()) {
+    event.set_revokedClass(obj->klass());
+    event.set_disableBiasing((heuristics != HR_BULK_REBIAS));
+    // Subtract 1 to match the id of events committed inside the safepoint
+    event.set_safepointId(SafepointSynchronize::safepoint_counter() - 1);
+    event.commit();
+  }
   return bulk_revoke.status_code();
 }
 
@@ -645,7 +685,7 @@
   oop obj = h_obj();
   HeuristicsResult heuristics = update_heuristics(obj, false);
   if (heuristics == HR_SINGLE_REVOKE) {
-    revoke_bias(obj, false, false, NULL);
+    revoke_bias(obj, false, false, NULL, NULL);
   } else if ((heuristics == HR_BULK_REBIAS) ||
              (heuristics == HR_BULK_REVOKE)) {
     bulk_revoke_or_rebias_at_safepoint(obj, (heuristics == HR_BULK_REBIAS), false, NULL);
@@ -661,7 +701,7 @@
     oop obj = (objs->at(i))();
     HeuristicsResult heuristics = update_heuristics(obj, false);
     if (heuristics == HR_SINGLE_REVOKE) {
-      revoke_bias(obj, false, false, NULL);
+      revoke_bias(obj, false, false, NULL, NULL);
     } else if ((heuristics == HR_BULK_REBIAS) ||
                (heuristics == HR_BULK_REVOKE)) {
       bulk_revoke_or_rebias_at_safepoint(obj, (heuristics == HR_BULK_REBIAS), false, NULL);
--- a/src/share/vm/runtime/globals.cpp	Wed May 16 15:25:51 2018 +0200
+++ b/src/share/vm/runtime/globals.cpp	Mon Aug 19 10:11:31 2019 +0200
@@ -616,8 +616,8 @@
   e.commit();
 }
 
-bool CommandLineFlags::boolAt(const char* name, size_t len, bool* value) {
-  Flag* result = Flag::find_flag(name, len);
+bool CommandLineFlags::boolAt(const char* name, size_t len, bool* value, bool allow_locked, bool return_flag) {
+  Flag* result = Flag::find_flag(name, len, allow_locked, return_flag);
   if (result == NULL) return false;
   if (!result->is_bool()) return false;
   *value = result->get_bool();
@@ -644,8 +644,8 @@
   faddr->set_origin(origin);
 }
 
-bool CommandLineFlags::intxAt(const char* name, size_t len, intx* value) {
-  Flag* result = Flag::find_flag(name, len);
+bool CommandLineFlags::intxAt(const char* name, size_t len, intx* value, bool allow_locked, bool return_flag) {
+  Flag* result = Flag::find_flag(name, len, allow_locked, return_flag);
   if (result == NULL) return false;
   if (!result->is_intx()) return false;
   *value = result->get_intx();
@@ -672,8 +672,8 @@
   faddr->set_origin(origin);
 }
 
-bool CommandLineFlags::uintxAt(const char* name, size_t len, uintx* value) {
-  Flag* result = Flag::find_flag(name, len);
+bool CommandLineFlags::uintxAt(const char* name, size_t len, uintx* value, bool allow_locked, bool return_flag) {
+  Flag* result = Flag::find_flag(name, len, allow_locked, return_flag);
   if (result == NULL) return false;
   if (!result->is_uintx()) return false;
   *value = result->get_uintx();
@@ -700,8 +700,8 @@
   faddr->set_origin(origin);
 }
 
-bool CommandLineFlags::uint64_tAt(const char* name, size_t len, uint64_t* value) {
-  Flag* result = Flag::find_flag(name, len);
+bool CommandLineFlags::uint64_tAt(const char* name, size_t len, uint64_t* value, bool allow_locked, bool return_flag) {
+  Flag* result = Flag::find_flag(name, len, allow_locked, return_flag);
   if (result == NULL) return false;
   if (!result->is_uint64_t()) return false;
   *value = result->get_uint64_t();
@@ -728,8 +728,8 @@
   faddr->set_origin(origin);
 }
 
-bool CommandLineFlags::doubleAt(const char* name, size_t len, double* value) {
-  Flag* result = Flag::find_flag(name, len);
+bool CommandLineFlags::doubleAt(const char* name, size_t len, double* value, bool allow_locked, bool return_flag) {
+  Flag* result = Flag::find_flag(name, len, allow_locked, return_flag);
   if (result == NULL) return false;
   if (!result->is_double()) return false;
   *value = result->get_double();
@@ -756,8 +756,8 @@
   faddr->set_origin(origin);
 }
 
-bool CommandLineFlags::ccstrAt(const char* name, size_t len, ccstr* value) {
-  Flag* result = Flag::find_flag(name, len);
+bool CommandLineFlags::ccstrAt(const char* name, size_t len, ccstr* value, bool allow_locked, bool return_flag) {
+  Flag* result = Flag::find_flag(name, len, allow_locked, return_flag);
   if (result == NULL) return false;
   if (!result->is_ccstr()) return false;
   *value = result->get_ccstr();
--- a/src/share/vm/runtime/globals.hpp	Wed May 16 15:25:51 2018 +0200
+++ b/src/share/vm/runtime/globals.hpp	Mon Aug 19 10:11:31 2019 +0200
@@ -369,33 +369,33 @@
 
 class CommandLineFlags {
  public:
-  static bool boolAt(const char* name, size_t len, bool* value);
-  static bool boolAt(const char* name, bool* value)      { return boolAt(name, strlen(name), value); }
+  static bool boolAt(const char* name, size_t len, bool* value, bool allow_locked = false, bool return_flag = false);
+  static bool boolAt(const char* name, bool* value, bool allow_locked = false, bool return_flag = false)   { return boolAt(name, strlen(name), value, allow_locked, return_flag); }
   static bool boolAtPut(const char* name, size_t len, bool* value, Flag::Flags origin);
   static bool boolAtPut(const char* name, bool* value, Flag::Flags origin)   { return boolAtPut(name, strlen(name), value, origin); }
 
-  static bool intxAt(const char* name, size_t len, intx* value);
-  static bool intxAt(const char* name, intx* value)      { return intxAt(name, strlen(name), value); }
+  static bool intxAt(const char* name, size_t len, intx* value, bool allow_locked = false, bool return_flag = false);
+  static bool intxAt(const char* name, intx* value, bool allow_locked = false, bool return_flag = false)      { return intxAt(name, strlen(name), value, allow_locked, return_flag); }
   static bool intxAtPut(const char* name, size_t len, intx* value, Flag::Flags origin);
   static bool intxAtPut(const char* name, intx* value, Flag::Flags origin)   { return intxAtPut(name, strlen(name), value, origin); }
 
-  static bool uintxAt(const char* name, size_t len, uintx* value);
-  static bool uintxAt(const char* name, uintx* value)    { return uintxAt(name, strlen(name), value); }
+  static bool uintxAt(const char* name, size_t len, uintx* value, bool allow_locked = false, bool return_flag = false);
+  static bool uintxAt(const char* name, uintx* value, bool allow_locked = false, bool return_flag = false)    { return uintxAt(name, strlen(name), value, allow_locked, return_flag); }
   static bool uintxAtPut(const char* name, size_t len, uintx* value, Flag::Flags origin);
   static bool uintxAtPut(const char* name, uintx* value, Flag::Flags origin) { return uintxAtPut(name, strlen(name), value, origin); }
 
-  static bool uint64_tAt(const char* name, size_t len, uint64_t* value);
-  static bool uint64_tAt(const char* name, uint64_t* value) { return uint64_tAt(name, strlen(name), value); }
+  static bool uint64_tAt(const char* name, size_t len, uint64_t* value, bool allow_locked = false, bool return_flag = false);
+  static bool uint64_tAt(const char* name, uint64_t* value, bool allow_locked = false, bool return_flag = false) { return uint64_tAt(name, strlen(name), value, allow_locked, return_flag); }
   static bool uint64_tAtPut(const char* name, size_t len, uint64_t* value, Flag::Flags origin);
   static bool uint64_tAtPut(const char* name, uint64_t* value, Flag::Flags origin) { return uint64_tAtPut(name, strlen(name), value, origin); }
 
-  static bool doubleAt(const char* name, size_t len, double* value);
-  static bool doubleAt(const char* name, double* value)    { return doubleAt(name, strlen(name), value); }
+  static bool doubleAt(const char* name, size_t len, double* value, bool allow_locked = false, bool return_flag = false);
+  static bool doubleAt(const char* name, double* value, bool allow_locked = false, bool return_flag = false)    { return doubleAt(name, strlen(name), value, allow_locked, return_flag); }
   static bool doubleAtPut(const char* name, size_t len, double* value, Flag::Flags origin);
   static bool doubleAtPut(const char* name, double* value, Flag::Flags origin) { return doubleAtPut(name, strlen(name), value, origin); }
 
-  static bool ccstrAt(const char* name, size_t len, ccstr* value);
-  static bool ccstrAt(const char* name, ccstr* value)    { return ccstrAt(name, strlen(name), value); }
+  static bool ccstrAt(const char* name, size_t len, ccstr* value, bool allow_locked = false, bool return_flag = false);
+  static bool ccstrAt(const char* name, ccstr* value, bool allow_locked = false, bool return_flag = false)    { return ccstrAt(name, strlen(name), value, allow_locked, return_flag); }
   // Contract:  Flag will make private copy of the incoming value.
   // Outgoing value is always malloc-ed, and caller MUST call free.
   static bool ccstrAtPut(const char* name, size_t len, ccstr* value, Flag::Flags origin);
--- a/src/share/vm/runtime/thread.hpp	Wed May 16 15:25:51 2018 +0200
+++ b/src/share/vm/runtime/thread.hpp	Mon Aug 19 10:11:31 2019 +0200
@@ -196,7 +196,9 @@
     _deopt_suspend          = 0x10000000U, // thread needs to self suspend for deopt
 
     _has_async_exception    = 0x00000001U, // there is a pending async exception
-    _critical_native_unlock = 0x00000002U  // Must call back to unlock JNI critical lock
+    _critical_native_unlock = 0x00000002U, // Must call back to unlock JNI critical lock
+
+    JFR_ONLY(_trace_flag    = 0x00000004U)  // call jfr tracing
   };
 
   // various suspension related flags - atomically updated
@@ -443,6 +445,7 @@
   inline jlong cooked_allocated_bytes();
 
   JFR_ONLY(DEFINE_THREAD_LOCAL_ACCESSOR_JFR;)
+  JFR_ONLY(DEFINE_TRACE_SUSPEND_FLAG_METHODS)
 
   const ThreadExt& ext() const          { return _ext; }
   ThreadExt& ext()                      { return _ext; }