[vm] Make profiler bailout counters available over the VM service.

Change-Id: I4c0d30b21371e33f66ab142636aa669a46f8a799
Reviewed-on: https://dart-review.googlesource.com/c/85547
Reviewed-by: Zach Anderson <zra@google.com>
Commit-Queue: Ryan Macnak <rmacnak@google.com>
diff --git a/runtime/observatory/lib/sample_profile.dart b/runtime/observatory/lib/sample_profile.dart
index f81f3bd..e755ad3 100644
--- a/runtime/observatory/lib/sample_profile.dart
+++ b/runtime/observatory/lib/sample_profile.dart
@@ -6,6 +6,7 @@
 
 import 'dart:async';
 import 'dart:typed_data';
+import 'package:logging/logging.dart';
 import 'package:observatory/models.dart' as M;
 import 'package:observatory/service.dart';
 import 'package:observatory/utils.dart';
diff --git a/runtime/observatory/lib/src/elements/sample_buffer_control.dart b/runtime/observatory/lib/src/elements/sample_buffer_control.dart
index c9b8452..0d5b38c 100644
--- a/runtime/observatory/lib/src/elements/sample_buffer_control.dart
+++ b/runtime/observatory/lib/src/elements/sample_buffer_control.dart
@@ -220,7 +220,7 @@
   List<Element> _createTagSelect() {
     var values = M.SampleProfileTag.values;
     if (!_profileVM) {
-      values = const [M.SampleProfileTag.userOnly, M.SampleProfileTag.none];
+      values = const [M.SampleProfileTag.userOnly, M.SampleProfileTag.vmOnly, M.SampleProfileTag.none];
     }
     var s;
     return [
diff --git a/runtime/observatory/lib/src/sample_profile/sample_profile.dart b/runtime/observatory/lib/src/sample_profile/sample_profile.dart
index ebc625a..8ff90eb 100644
--- a/runtime/observatory/lib/src/sample_profile/sample_profile.dart
+++ b/runtime/observatory/lib/src/sample_profile/sample_profile.dart
@@ -830,6 +830,8 @@
   }
 
   Stream<double> loadProgress(ServiceObjectOwner owner, ServiceMap profile) {
+    Logger.root.info('sampling counters ${profile['counters']}');
+
     var progress = new StreamController<double>.broadcast();
 
     (() async {
diff --git a/runtime/vm/profiler.cc b/runtime/vm/profiler.cc
index 4cb44d8..c0f8ecb 100644
--- a/runtime/vm/profiler.cc
+++ b/runtime/vm/profiler.cc
@@ -725,7 +725,8 @@
 //
 class ProfilerNativeStackWalker : public ProfilerStackWalker {
  public:
-  ProfilerNativeStackWalker(Dart_Port port_id,
+  ProfilerNativeStackWalker(ProfilerCounters* counters,
+                            Dart_Port port_id,
                             Sample* sample,
                             SampleBuffer* sample_buffer,
                             uword stack_lower,
@@ -740,6 +741,7 @@
                             sample_buffer,
                             skip_count,
                             try_symbolize_dart_frames),
+        counters_(counters),
         stack_upper_(stack_upper),
         original_pc_(pc),
         original_fp_(fp),
@@ -759,10 +761,14 @@
     if (gap >= kMaxStep) {
       // Gap between frame pointer and stack pointer is
       // too large.
+      AtomicOperations::IncrementInt64By(&counters_->incomplete_sample_fp_step,
+                                         1);
       return;
     }
 
     if (!ValidFramePointer(fp)) {
+      AtomicOperations::IncrementInt64By(
+          &counters_->incomplete_sample_fp_bounds, 1);
       return;
     }
 
@@ -781,17 +787,23 @@
 
       if (fp <= previous_fp) {
         // Frame pointer did not move to a higher address.
+        AtomicOperations::IncrementInt64By(
+            &counters_->incomplete_sample_fp_step, 1);
         return;
       }
 
       gap = fp - previous_fp;
       if (gap >= kMaxStep) {
         // Frame pointer step is too large.
+        AtomicOperations::IncrementInt64By(
+            &counters_->incomplete_sample_fp_step, 1);
         return;
       }
 
       if (!ValidFramePointer(fp)) {
         // Frame pointer is outside of isolate stack boundary.
+        AtomicOperations::IncrementInt64By(
+            &counters_->incomplete_sample_fp_bounds, 1);
         return;
       }
 
@@ -801,6 +813,8 @@
         // the pc is so large that adding one to it will cause an
         // overflow it is invalid and it will cause headaches later
         // while we are building the profile.  Discard it.
+        AtomicOperations::IncrementInt64By(&counters_->incomplete_sample_bad_pc,
+                                           1);
         return;
       }
 
@@ -838,6 +852,7 @@
     return r;
   }
 
+  ProfilerCounters* const counters_;
   const uword stack_upper_;
   const uword original_pc_;
   const uword original_fp_;
@@ -1146,7 +1161,8 @@
   }
 
   ProfilerNativeStackWalker native_stack_walker(
-      ILLEGAL_PORT, NULL, NULL, stack_lower, stack_upper, pc, fp, sp,
+      &counters_, ILLEGAL_PORT, NULL, NULL, stack_lower, stack_upper, pc, fp,
+      sp,
       /*skip_count=*/0,
       /*try_symbolize_dart_frames=*/!for_crash);
   native_stack_walker.walk();
@@ -1194,8 +1210,8 @@
 
   if (FLAG_profile_vm_allocation) {
     ProfilerNativeStackWalker native_stack_walker(
-        (isolate != NULL) ? isolate->main_port() : ILLEGAL_PORT, sample,
-        sample_buffer, stack_lower, stack_upper, pc, fp, sp);
+        &counters_, (isolate != NULL) ? isolate->main_port() : ILLEGAL_PORT,
+        sample, sample_buffer, stack_lower, stack_upper, pc, fp, sp);
     native_stack_walker.walk();
   } else if (exited_dart_code) {
     ProfilerDartStackWalker dart_exit_stack_walker(
@@ -1257,8 +1273,8 @@
   sample->set_native_allocation_size_bytes(allocation_size);
 
   ProfilerNativeStackWalker native_stack_walker(
-      ILLEGAL_PORT, sample, sample_buffer, stack_lower, stack_upper, pc, fp, sp,
-      skip_count);
+      &counters_, ILLEGAL_PORT, sample, sample_buffer, stack_lower, stack_upper,
+      pc, fp, sp, skip_count);
 
   native_stack_walker.walk();
 
@@ -1396,8 +1412,8 @@
   }
 
   ProfilerNativeStackWalker native_stack_walker(
-      (isolate != NULL) ? isolate->main_port() : ILLEGAL_PORT, sample,
-      sample_buffer, stack_lower, stack_upper, pc, fp, sp);
+      &counters_, (isolate != NULL) ? isolate->main_port() : ILLEGAL_PORT,
+      sample, sample_buffer, stack_lower, stack_upper, pc, fp, sp);
   const bool exited_dart_code = thread->HasExitedDartCode();
   ProfilerDartStackWalker dart_stack_walker(thread, sample, sample_buffer,
                                             stack_lower, stack_upper, pc, fp,
diff --git a/runtime/vm/profiler.h b/runtime/vm/profiler.h
index c6f868d..7ac6741 100644
--- a/runtime/vm/profiler.h
+++ b/runtime/vm/profiler.h
@@ -30,22 +30,26 @@
 class SampleBuffer;
 class ProfileTrieNode;
 
+#define PROFILER_COUNTERS(V)                                                   \
+  V(bail_out_unknown_task)                                                     \
+  V(bail_out_jump_to_exception_handler)                                        \
+  V(bail_out_check_isolate)                                                    \
+  V(single_frame_sample_deoptimizing)                                          \
+  V(single_frame_sample_register_check)                                        \
+  V(single_frame_sample_get_and_validate_stack_bounds)                         \
+  V(stack_walker_native)                                                       \
+  V(stack_walker_dart_exit)                                                    \
+  V(stack_walker_dart)                                                         \
+  V(stack_walker_none)                                                         \
+  V(incomplete_sample_fp_bounds)                                               \
+  V(incomplete_sample_fp_step)                                                 \
+  V(incomplete_sample_bad_pc)                                                  \
+  V(failure_native_allocation_sample)
+
 struct ProfilerCounters {
-  // Count of bail out reasons:
-  ALIGN8 int64_t bail_out_unknown_task;
-  ALIGN8 int64_t bail_out_jump_to_exception_handler;
-  ALIGN8 int64_t bail_out_check_isolate;
-  // Count of single frame sampling reasons:
-  ALIGN8 int64_t single_frame_sample_deoptimizing;
-  ALIGN8 int64_t single_frame_sample_register_check;
-  ALIGN8 int64_t single_frame_sample_get_and_validate_stack_bounds;
-  // Count of stack walkers used:
-  ALIGN8 int64_t stack_walker_native;
-  ALIGN8 int64_t stack_walker_dart_exit;
-  ALIGN8 int64_t stack_walker_dart;
-  ALIGN8 int64_t stack_walker_none;
-  // Count of failed checks:
-  ALIGN8 int64_t failure_native_allocation_sample;
+#define DECLARE_PROFILER_COUNTER(name) ALIGN8 int64_t name;
+  PROFILER_COUNTERS(DECLARE_PROFILER_COUNTER)
+#undef DECLARE_PROFILER_COUNTER
 };
 
 class Profiler : public AllStatic {
diff --git a/runtime/vm/profiler_service.cc b/runtime/vm/profiler_service.cc
index b2405f8..27eb4fe 100644
--- a/runtime/vm/profiler_service.cc
+++ b/runtime/vm/profiler_service.cc
@@ -2542,6 +2542,14 @@
     ASSERT(root != NULL);
     root->PrintToJSONArray(&function_trie);
   }
+  {
+    ProfilerCounters counters = Profiler::counters();
+    JSONObject js_counters(&obj, "counters");
+#define ADD_PROFILER_COUNTER(name)                                             \
+    js_counters.AddProperty64("" #name, counters.name);
+PROFILER_COUNTERS(ADD_PROFILER_COUNTER)
+#undef ADD_PROFILER_COUNTER
+  }
 }
 
 void ProfileTrieWalker::Reset(Profile::TrieKind trie_kind) {