[debug] Update code-coverage / type-profile to work with lazy feedback

Precise code-coverage, collecting type profile and logging function events
need feedback vectors. This cl allocates feedback vector eagerly when any of
these features are required. When the code-coverage mode changes to anything
other than best case, this scans over the entire heap and allocates feedback
vectors for the required functions.

For best case code coverage we use interrupt budget field on the feedback
cell to infer if a function has executed. We still use the invocation count
on the feedback vector if feedback vector is available.

Bug: v8:8394
Change-Id: Ia0e656aaaa024d6d893a5badafc9a42ce36e9ea3
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1601143
Reviewed-by: Yang Guo <yangguo@chromium.org>
Commit-Queue: Mythri Alle <mythria@chromium.org>
Cr-Commit-Position: refs/heads/master@{#61410}
diff --git a/src/debug/debug-coverage.cc b/src/debug/debug-coverage.cc
index f4a7e5a..b69a654 100644
--- a/src/debug/debug-coverage.cc
+++ b/src/debug/debug-coverage.cc
@@ -495,13 +495,35 @@
       HeapIterator heap_iterator(isolate->heap());
       for (HeapObject current_obj = heap_iterator.next();
            !current_obj.is_null(); current_obj = heap_iterator.next()) {
-        if (!current_obj->IsFeedbackVector()) continue;
-        FeedbackVector vector = FeedbackVector::cast(current_obj);
-        SharedFunctionInfo shared = vector->shared_function_info();
+        if (!current_obj->IsJSFunction()) continue;
+        JSFunction func = JSFunction::cast(current_obj);
+        SharedFunctionInfo shared = func->shared();
         if (!shared->IsSubjectToDebugging()) continue;
-        uint32_t count = static_cast<uint32_t>(vector->invocation_count());
+        if (!(func->has_feedback_vector() ||
+              func->has_closure_feedback_cell_array()))
+          continue;
+        uint32_t count = 0;
+        if (func->has_feedback_vector()) {
+          count = static_cast<uint32_t>(
+              func->feedback_vector()->invocation_count());
+        } else if (func->raw_feedback_cell()->interrupt_budget() <
+                   FLAG_budget_for_feedback_vector_allocation) {
+          // We haven't allocated feedback vector, but executed the function
+          // atleast once. We don't have precise invocation count here.
+          count = 1;
+        }
         counter_map.Add(shared, count);
       }
+
+      // Also check functions on the stack to collect the count map. With lazy
+      // feedback allocation we may miss counting functions if the feedback
+      // vector wasn't allocated yet and the function's interrupt budget wasn't
+      // updated (i.e. it didn't execute return / jump).
+      for (JavaScriptFrameIterator it(isolate); !it.done(); it.Advance()) {
+        SharedFunctionInfo shared = it.frame()->function()->shared();
+        if (counter_map.Get(shared) != 0) continue;
+        counter_map.Add(shared, 1);
+      }
       break;
     }
   }
@@ -607,24 +629,37 @@
       // increment invocation count.
       Deoptimizer::DeoptimizeAll(isolate);
 
-      // Root all feedback vectors to avoid early collection.
-      isolate->MaybeInitializeVectorListFromHeap();
-
-      HeapIterator heap_iterator(isolate->heap());
-      for (HeapObject o = heap_iterator.next(); !o.is_null();
-           o = heap_iterator.next()) {
-        if (IsBinaryMode(mode) && o->IsSharedFunctionInfo()) {
-          // If collecting binary coverage, reset
-          // SFI::has_reported_binary_coverage to avoid optimizing / inlining
-          // functions before they have reported coverage.
-          SharedFunctionInfo shared = SharedFunctionInfo::cast(o);
-          shared->set_has_reported_binary_coverage(false);
-        } else if (o->IsFeedbackVector()) {
-          // In any case, clear any collected invocation counts.
-          FeedbackVector::cast(o)->clear_invocation_count();
+      std::vector<Handle<JSFunction>> funcs_needing_feedback_vector;
+      {
+        HeapIterator heap_iterator(isolate->heap());
+        for (HeapObject o = heap_iterator.next(); !o.is_null();
+             o = heap_iterator.next()) {
+          if (o->IsJSFunction()) {
+            JSFunction func = JSFunction::cast(o);
+            if (func->has_closure_feedback_cell_array()) {
+              funcs_needing_feedback_vector.push_back(
+                  Handle<JSFunction>(func, isolate));
+            }
+          } else if (IsBinaryMode(mode) && o->IsSharedFunctionInfo()) {
+            // If collecting binary coverage, reset
+            // SFI::has_reported_binary_coverage to avoid optimizing / inlining
+            // functions before they have reported coverage.
+            SharedFunctionInfo shared = SharedFunctionInfo::cast(o);
+            shared->set_has_reported_binary_coverage(false);
+          } else if (o->IsFeedbackVector()) {
+            // In any case, clear any collected invocation counts.
+            FeedbackVector::cast(o)->clear_invocation_count();
+          }
         }
       }
 
+      for (Handle<JSFunction> func : funcs_needing_feedback_vector) {
+        JSFunction::EnsureFeedbackVector(func);
+      }
+
+      // Root all feedback vectors to avoid early collection.
+      isolate->MaybeInitializeVectorListFromHeap();
+
       break;
     }
   }
diff --git a/src/objects/js-objects.cc b/src/objects/js-objects.cc
index eecf42e..cae2d82 100644
--- a/src/objects/js-objects.cc
+++ b/src/objects/js-objects.cc
@@ -4969,10 +4969,19 @@
 
 // static
 void JSFunction::InitializeFeedbackCell(Handle<JSFunction> function) {
-  if (FLAG_lazy_feedback_allocation) {
-    EnsureClosureFeedbackCellArray(function);
-  } else {
+  Isolate* const isolate = function->GetIsolate();
+  bool needs_feedback_vector = !FLAG_lazy_feedback_allocation;
+  // We need feedback vector for certain log events, collecting type profile
+  // and more precise code coverage.
+  if (FLAG_log_function_events) needs_feedback_vector = true;
+  if (!isolate->is_best_effort_code_coverage()) needs_feedback_vector = true;
+  if (isolate->is_collecting_type_profile()) needs_feedback_vector = true;
+  if (FLAG_always_opt) needs_feedback_vector = true;
+
+  if (needs_feedback_vector) {
     EnsureFeedbackVector(function);
+  } else {
+    EnsureClosureFeedbackCellArray(function);
   }
 }