diff a/src/hotspot/share/runtime/deoptimization.cpp b/src/hotspot/share/runtime/deoptimization.cpp
--- a/src/hotspot/share/runtime/deoptimization.cpp
+++ b/src/hotspot/share/runtime/deoptimization.cpp
@@ -75,10 +75,11 @@
 #include "runtime/javaThread.hpp"
 #include "runtime/jniHandles.inline.hpp"
 #include "runtime/keepStackGCProcessed.hpp"
 #include "runtime/objectMonitor.inline.hpp"
 #include "runtime/osThread.hpp"
+#include "runtime/perfData.inline.hpp"
 #include "runtime/safepointVerifiers.hpp"
 #include "runtime/sharedRuntime.hpp"
 #include "runtime/signature.hpp"
 #include "runtime/stackFrameStream.inline.hpp"
 #include "runtime/stackValue.hpp"
@@ -89,10 +90,11 @@
 #include "runtime/threadWXSetters.inline.hpp"
 #include "runtime/vframe.hpp"
 #include "runtime/vframeArray.hpp"
 #include "runtime/vframe_hp.hpp"
 #include "runtime/vmOperations.hpp"
+#include "services/management.hpp"
 #include "utilities/checkedCast.hpp"
 #include "utilities/events.hpp"
 #include "utilities/growableArray.hpp"
 #include "utilities/macros.hpp"
 #include "utilities/preserveException.hpp"
@@ -272,11 +274,11 @@
 
 // In order to make fetch_unroll_info work properly with escape
 // analysis, the method was changed from JRT_LEAF to JRT_BLOCK_ENTRY.
 // The actual reallocation of previously eliminated objects occurs in realloc_objects,
 // which is called from the method fetch_unroll_info_helper below.
-JRT_BLOCK_ENTRY(Deoptimization::UnrollBlock*, Deoptimization::fetch_unroll_info(JavaThread* current, int exec_mode))
+JRT_BLOCK_ENTRY_PROF(Deoptimization::UnrollBlock*, Deoptimization, fetch_unroll_info, Deoptimization::fetch_unroll_info(JavaThread* current, int exec_mode))
   // fetch_unroll_info() is called at the beginning of the deoptimization
   // handler. Note this fact before we start generating temporary frames
   // that can confuse an asynchronous stack walker. This counter is
   // decremented at the end of unpack_frames().
   current->inc_in_deopt_handler();
@@ -844,11 +846,11 @@
   }
 }
 #endif
 
 // Return BasicType of value being returned
-JRT_LEAF(BasicType, Deoptimization::unpack_frames(JavaThread* thread, int exec_mode))
+JRT_LEAF_PROF_NO_THREAD(BasicType, Deoptimization, unpack_frames, Deoptimization::unpack_frames(JavaThread* thread, int exec_mode))
   assert(thread == JavaThread::current(), "pre-condition");
 
   // We are already active in the special DeoptResourceMark any ResourceObj's we
   // allocate will be freed at the end of the routine.
 
@@ -1740,16 +1742,19 @@
 #endif
 
 void Deoptimization::deoptimize_single_frame(JavaThread* thread, frame fr, Deoptimization::DeoptReason reason) {
   assert(fr.can_be_deoptimized(), "checking frame type");
 
-  gather_statistics(reason, Action_none, Bytecodes::_illegal);
+  nmethod* nm = fr.cb()->as_nmethod_or_null();
+  assert(nm != nullptr, "only compiled methods can deopt");
+  DeoptAction action = (nm->is_not_entrant() ? Action_make_not_entrant : Action_none);
+  ScopeDesc* cur_sd = nm->scope_desc_at(fr.pc());
+  Bytecodes::Code bc = (cur_sd->bci() == -1 ? Bytecodes::_nop // deopt on method entry
+                                            : cur_sd->method()->java_code_at(cur_sd->bci()));
+  gather_statistics(nm, reason, action, bc);
 
   if (LogCompilation && xtty != nullptr) {
-    nmethod* nm = fr.cb()->as_nmethod_or_null();
-    assert(nm != nullptr, "only compiled methods can deopt");
-
     ttyLocker ttyl;
     xtty->begin_head("deoptimized thread='" UINTX_FORMAT "' reason='%s' pc='" INTPTR_FORMAT "'",(uintx)thread->osthread()->thread_id(), trap_reason_name(reason), p2i(fr.pc()));
     nm->log_identity(xtty);
     xtty->end_head();
     for (ScopeDesc* sd = nm->scope_desc_at(fr.pc()); ; sd = sd->sender()) {
@@ -1995,11 +2000,11 @@
     ls.print_cr("pc=" INTPTR_FORMAT " relative_pc=" INTPTR_FORMAT,
              pc, fr.pc() - nm->code_begin());
   }
 }
 
-JRT_ENTRY(void, Deoptimization::uncommon_trap_inner(JavaThread* current, jint trap_request)) {
+JRT_ENTRY_PROF(void, Deoptimization, uncommon_trap_inner, Deoptimization::uncommon_trap_inner(JavaThread* current, jint trap_request)) {
   HandleMark hm(current);
 
   // uncommon_trap() is called at the beginning of the uncommon trap
   // handler. Note this fact before we start generating temporary frames
   // that can confuse an asynchronous stack walker. This counter is
@@ -2071,11 +2076,11 @@
     }
 #endif
 
     Bytecodes::Code trap_bc     = trap_method->java_code_at(trap_bci);
     // Record this event in the histogram.
-    gather_statistics(reason, action, trap_bc);
+    gather_statistics(nm, reason, action, trap_bc);
 
     // Ensure that we can record deopt. history:
     bool create_if_missing = ProfileTraps;
 
     methodHandle profiled_method;
@@ -2583,22 +2588,22 @@
                            ignore_this_trap_count,
                            ignore_maybe_prior_trap,
                            ignore_maybe_prior_recompile);
 }
 
-Deoptimization::UnrollBlock* Deoptimization::uncommon_trap(JavaThread* current, jint trap_request, jint exec_mode) {
+PROF_ENTRY(Deoptimization::UnrollBlock*, Deoptimization, uncommon_trap, Deoptimization::uncommon_trap(JavaThread* current, jint trap_request, jint exec_mode))
   // Enable WXWrite: current function is called from methods compiled by C2 directly
   MACOS_AARCH64_ONLY(ThreadWXEnable wx(WXWrite, current));
 
   // Still in Java no safepoints
   {
     // This enters VM and may safepoint
     uncommon_trap_inner(current, trap_request);
   }
   HandleMark hm(current);
   return fetch_unroll_info_helper(current, exec_mode);
-}
+PROF_END
 
 // Local derived constants.
 // Further breakdown of DataLayout::trap_state, as promised by DataLayout.
 const int DS_REASON_MASK   = ((uint)DataLayout::trap_mask) >> 1;
 const int DS_RECOMPILE_BIT = DataLayout::trap_mask - DS_REASON_MASK;
@@ -2783,32 +2788,27 @@
   }
   return buf;
 }
 
 juint Deoptimization::_deoptimization_hist
+        [1 + 4 + 5] // total + online + archived
         [Deoptimization::Reason_LIMIT]
     [1 + Deoptimization::Action_LIMIT]
         [Deoptimization::BC_CASE_LIMIT]
   = {0};
 
 enum {
   LSB_BITS = 8,
   LSB_MASK = right_n_bits(LSB_BITS)
 };
 
-void Deoptimization::gather_statistics(DeoptReason reason, DeoptAction action,
-                                       Bytecodes::Code bc) {
-  assert(reason >= 0 && reason < Reason_LIMIT, "oob");
-  assert(action >= 0 && action < Action_LIMIT, "oob");
-  _deoptimization_hist[Reason_none][0][0] += 1;  // total
-  _deoptimization_hist[reason][0][0]      += 1;  // per-reason total
-  juint* cases = _deoptimization_hist[reason][1+action];
+static void update(juint* cases, Bytecodes::Code bc) {
   juint* bc_counter_addr = nullptr;
   juint  bc_counter      = 0;
   // Look for an unused counter, or an exact match to this BC.
   if (bc != Bytecodes::_illegal) {
-    for (int bc_case = 0; bc_case < BC_CASE_LIMIT; bc_case++) {
+    for (int bc_case = 0; bc_case < Deoptimization::BC_CASE_LIMIT; bc_case++) {
       juint* counter_addr = &cases[bc_case];
       juint  counter = *counter_addr;
       if ((counter == 0 && bc_counter_addr == nullptr)
           || (Bytecodes::Code)(counter & LSB_MASK) == bc) {
         // this counter is either free or is already devoted to this BC
@@ -2817,18 +2817,34 @@
       }
     }
   }
   if (bc_counter_addr == nullptr) {
     // Overflow, or no given bytecode.
-    bc_counter_addr = &cases[BC_CASE_LIMIT-1];
+    bc_counter_addr = &cases[Deoptimization::BC_CASE_LIMIT-1];
     bc_counter = (*bc_counter_addr & ~LSB_MASK);  // clear LSB
   }
   *bc_counter_addr = bc_counter + (1 << LSB_BITS);
 }
 
+
+void Deoptimization::gather_statistics(nmethod* nm, DeoptReason reason, DeoptAction action,
+                                       Bytecodes::Code bc) {
+  assert(reason >= 0 && reason < Reason_LIMIT, "oob");
+  assert(action >= 0 && action < Action_LIMIT, "oob");
+  _deoptimization_hist[0][Reason_none][0][0] += 1;  // total
+  _deoptimization_hist[0][reason][0][0]      += 1;  // per-reason total
+
+  update(_deoptimization_hist[0][reason][1+action], bc);
+
+  uint lvl = nm->comp_level() + (nm->is_scc() ? 4 : 0) + (nm->preloaded() ? 1 : 0);
+  _deoptimization_hist[lvl][Reason_none][0][0] += 1;  // total
+  _deoptimization_hist[lvl][reason][0][0]      += 1;  // per-reason total
+  update(_deoptimization_hist[lvl][reason][1+action], bc);
+}
+
 jint Deoptimization::total_deoptimization_count() {
-  return _deoptimization_hist[Reason_none][0][0];
+  return _deoptimization_hist[0][Reason_none][0][0];
 }
 
 // Get the deopt count for a specific reason and a specific action. If either
 // one of 'reason' or 'action' is null, the method returns the sum of all
 // deoptimizations with the specific 'action' or 'reason' respectively.
@@ -2840,11 +2856,11 @@
   juint counter = 0;
   for (int reason = 0; reason < Reason_LIMIT; reason++) {
     if (reason_str == nullptr || !strcmp(reason_str, trap_reason_name(reason))) {
       for (int action = 0; action < Action_LIMIT; action++) {
         if (action_str == nullptr || !strcmp(action_str, trap_action_name(action))) {
-          juint* cases = _deoptimization_hist[reason][1+action];
+          juint* cases = _deoptimization_hist[0][reason][1+action];
           for (int bc_case = 0; bc_case < BC_CASE_LIMIT; bc_case++) {
             counter += cases[bc_case] >> LSB_BITS;
           }
         }
       }
@@ -2852,50 +2868,69 @@
   }
   return counter;
 }
 
 void Deoptimization::print_statistics() {
-  juint total = total_deoptimization_count();
+  ttyLocker ttyl;
+  if (xtty != nullptr)  xtty->head("statistics type='deoptimization'");
+  tty->print_cr("Deoptimization traps recorded:");
+  print_statistics_on(tty);
+  if (xtty != nullptr)  xtty->tail("statistics");
+}
+
+void Deoptimization::print_statistics_on(const char* title, int lvl, outputStream* st) {
+  juint total = _deoptimization_hist[lvl][Reason_none][0][0];
   juint account = total;
-  if (total != 0) {
-    ttyLocker ttyl;
-    if (xtty != nullptr)  xtty->head("statistics type='deoptimization'");
-    tty->print_cr("Deoptimization traps recorded:");
-    #define PRINT_STAT_LINE(name, r) \
-      tty->print_cr("  %4d (%4.1f%%) %s", (int)(r), ((r) * 100.0) / total, name);
+#define PRINT_STAT_LINE(name, r) \
+      st->print_cr("    %d (%4.1f%%) %s", (int)(r), ((r) == total ? 100.0 : (((r) * 100.0) / total)), name);
+  if (total > 0) {
+    st->print("  %s: ", title);
     PRINT_STAT_LINE("total", total);
     // For each non-zero entry in the histogram, print the reason,
     // the action, and (if specifically known) the type of bytecode.
     for (int reason = 0; reason < Reason_LIMIT; reason++) {
       for (int action = 0; action < Action_LIMIT; action++) {
-        juint* cases = _deoptimization_hist[reason][1+action];
+        juint* cases = Deoptimization::_deoptimization_hist[lvl][reason][1+action];
         for (int bc_case = 0; bc_case < BC_CASE_LIMIT; bc_case++) {
           juint counter = cases[bc_case];
           if (counter != 0) {
-            char name[1*K];
             Bytecodes::Code bc = (Bytecodes::Code)(counter & LSB_MASK);
-            if (bc_case == BC_CASE_LIMIT && (int)bc == 0)
-              bc = Bytecodes::_illegal;
-            os::snprintf_checked(name, sizeof(name), "%s/%s/%s",
-                    trap_reason_name(reason),
-                    trap_action_name(action),
-                    Bytecodes::is_defined(bc)? Bytecodes::name(bc): "other");
+            const char* bc_name = "other";
+            if (bc_case == (BC_CASE_LIMIT-1) && bc == Bytecodes::_nop) {
+              // overwritten
+            } else if (Bytecodes::is_defined(bc)) {
+              bc_name = Bytecodes::name(bc);
+            }
             juint r = counter >> LSB_BITS;
-            tty->print_cr("  %40s: " UINT32_FORMAT " (%.1f%%)", name, r, (r * 100.0) / total);
+            st->print_cr("    %-34s %16s %16s: " UINT32_FORMAT_W(5) " (%4.1f%%)",
+                         trap_reason_name(reason), trap_action_name(action), bc_name,
+                         r, (r * 100.0) / total);
             account -= r;
           }
         }
       }
     }
     if (account != 0) {
       PRINT_STAT_LINE("unaccounted", account);
     }
-    #undef PRINT_STAT_LINE
-    if (xtty != nullptr)  xtty->tail("statistics");
+#undef PRINT_STAT_LINE
   }
 }
 
+void Deoptimization::print_statistics_on(outputStream* st) {
+//  print_statistics_on("Total", 0, st);
+  print_statistics_on("Tier1", 1, st);
+  print_statistics_on("Tier2", 2, st);
+  print_statistics_on("Tier3", 3, st);
+  print_statistics_on("Tier4", 4, st);
+
+  print_statistics_on("SC Tier1", 5, st);
+  print_statistics_on("SC Tier2", 6, st);
+  print_statistics_on("SC Tier4", 8, st);
+  print_statistics_on("SC Tier5 (preloaded)", 9, st);
+}
+
 #else // COMPILER2_OR_JVMCI
 
 
 // Stubs for C1 only system.
 bool Deoptimization::trap_state_is_recompiled(int trap_state) {
@@ -2937,5 +2972,48 @@
   jio_snprintf(buf, buflen, "#%d", trap_state);
   return buf;
 }
 
 #endif // COMPILER2_OR_JVMCI
+
+#define DO_COUNTERS(macro) \
+  macro(Deoptimization, fetch_unroll_info)   \
+  macro(Deoptimization, unpack_frames) \
+  macro(Deoptimization, uncommon_trap_inner) \
+  macro(Deoptimization, uncommon_trap)
+
+#define INIT_COUNTER(sub, name) \
+  NEWPERFTICKCOUNTERS(_perf_##sub##_##name##_timer, SUN_RT, #sub "::" #name) \
+  NEWPERFEVENTCOUNTER(_perf_##sub##_##name##_count, SUN_RT, #sub "::" #name "_count");
+
+void Deoptimization::init_counters() {
+  if (ProfileRuntimeCalls && UsePerfData) {
+    EXCEPTION_MARK;
+
+    DO_COUNTERS(INIT_COUNTER)
+
+    if (HAS_PENDING_EXCEPTION) {
+      vm_exit_during_initialization("jvm_perf_init failed unexpectedly");
+    }
+  }
+}
+#undef INIT_COUNTER
+
+#define PRINT_COUNTER(sub, name) { \
+  jlong count = _perf_##sub##_##name##_count->get_value(); \
+  if (count > 0) { \
+    st->print_cr("  %-50s = %4ldms (elapsed) %4ldms (thread) (%5ld events)", #sub "::" #name, \
+                 _perf_##sub##_##name##_timer->elapsed_counter_value_ms(), \
+                 _perf_##sub##_##name##_timer->thread_counter_value_ms(), \
+                 count); \
+  }}
+
+void Deoptimization::print_counters_on(outputStream* st) {
+  if (ProfileRuntimeCalls && UsePerfData) {
+    DO_COUNTERS(PRINT_COUNTER)
+  } else {
+    st->print_cr("  Deoptimization: no info (%s is disabled)", (UsePerfData ? "ProfileRuntimeCalls" : "UsePerfData"));
+  }
+}
+
+#undef PRINT_COUNTER
+#undef DO_COUNTERS
