Home | History | Annotate | Download | only in debug
      1 // Copyright 2017 the V8 project authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "src/debug/debug-coverage.h"
      6 
      7 #include "src/base/hashmap.h"
      8 #include "src/deoptimizer.h"
      9 #include "src/isolate.h"
     10 #include "src/objects-inl.h"
     11 #include "src/objects.h"
     12 
     13 namespace v8 {
     14 namespace internal {
     15 
     16 class SharedToCounterMap
     17     : public base::TemplateHashMapImpl<SharedFunctionInfo*, uint32_t,
     18                                        base::KeyEqualityMatcher<void*>,
     19                                        base::DefaultAllocationPolicy> {
     20  public:
     21   typedef base::TemplateHashMapEntry<SharedFunctionInfo*, uint32_t> Entry;
     22   inline void Add(SharedFunctionInfo* key, uint32_t count) {
     23     Entry* entry = LookupOrInsert(key, Hash(key), []() { return 0; });
     24     uint32_t old_count = entry->value;
     25     if (UINT32_MAX - count < old_count) {
     26       entry->value = UINT32_MAX;
     27     } else {
     28       entry->value = old_count + count;
     29     }
     30   }
     31 
     32   inline uint32_t Get(SharedFunctionInfo* key) {
     33     Entry* entry = Lookup(key, Hash(key));
     34     if (entry == nullptr) return 0;
     35     return entry->value;
     36   }
     37 
     38  private:
     39   static uint32_t Hash(SharedFunctionInfo* key) {
     40     return static_cast<uint32_t>(reinterpret_cast<intptr_t>(key));
     41   }
     42 
     43   DisallowHeapAllocation no_gc;
     44 };
     45 
     46 namespace {
     47 int StartPosition(SharedFunctionInfo* info) {
     48   int start = info->function_token_position();
     49   if (start == kNoSourcePosition) start = info->start_position();
     50   return start;
     51 }
     52 
     53 bool CompareSharedFunctionInfo(SharedFunctionInfo* a, SharedFunctionInfo* b) {
     54   int a_start = StartPosition(a);
     55   int b_start = StartPosition(b);
     56   if (a_start == b_start) return a->end_position() > b->end_position();
     57   return a_start < b_start;
     58 }
     59 }  // anonymous namespace
     60 
     61 Coverage* Coverage::Collect(Isolate* isolate, bool reset_count) {
     62   SharedToCounterMap counter_map;
     63 
     64   // Feed invocation count into the counter map.
     65   if (isolate->IsCodeCoverageEnabled()) {
     66     // Feedback vectors are already listed to prevent losing them to GC.
     67     Handle<ArrayList> list =
     68         Handle<ArrayList>::cast(isolate->factory()->code_coverage_list());
     69     for (int i = 0; i < list->Length(); i++) {
     70       FeedbackVector* vector = FeedbackVector::cast(list->Get(i));
     71       SharedFunctionInfo* shared = vector->shared_function_info();
     72       DCHECK(shared->IsSubjectToDebugging());
     73       uint32_t count = static_cast<uint32_t>(vector->invocation_count());
     74       if (reset_count) vector->clear_invocation_count();
     75       counter_map.Add(shared, count);
     76     }
     77   } else {
     78     // Iterate the heap to find all feedback vectors and accumulate the
     79     // invocation counts into the map for each shared function info.
     80     HeapIterator heap_iterator(isolate->heap());
     81     while (HeapObject* current_obj = heap_iterator.next()) {
     82       if (!current_obj->IsFeedbackVector()) continue;
     83       FeedbackVector* vector = FeedbackVector::cast(current_obj);
     84       SharedFunctionInfo* shared = vector->shared_function_info();
     85       if (!shared->IsSubjectToDebugging()) continue;
     86       uint32_t count = static_cast<uint32_t>(vector->invocation_count());
     87       if (reset_count) vector->clear_invocation_count();
     88       counter_map.Add(shared, count);
     89     }
     90   }
     91 
     92   // Iterate shared function infos of every script and build a mapping
     93   // between source ranges and invocation counts.
     94   Coverage* result = new Coverage();
     95   Script::Iterator scripts(isolate);
     96   while (Script* script = scripts.Next()) {
     97     // Dismiss non-user scripts.
     98     if (script->type() != Script::TYPE_NORMAL) continue;
     99 
    100     // Create and add new script data.
    101     Handle<Script> script_handle(script, isolate);
    102     result->emplace_back(isolate, script_handle);
    103     std::vector<CoverageFunction>* functions = &result->back().functions;
    104 
    105     std::vector<SharedFunctionInfo*> sorted;
    106     bool has_toplevel = false;
    107 
    108     {
    109       // Sort functions by start position, from outer to inner functions.
    110       SharedFunctionInfo::ScriptIterator infos(script_handle);
    111       while (SharedFunctionInfo* info = infos.Next()) {
    112         has_toplevel |= info->is_toplevel();
    113         sorted.push_back(info);
    114       }
    115       std::sort(sorted.begin(), sorted.end(), CompareSharedFunctionInfo);
    116     }
    117 
    118     functions->reserve(sorted.size() + (has_toplevel ? 0 : 1));
    119 
    120     if (!has_toplevel) {
    121       // Add a replacement toplevel function if it does not exist.
    122       int source_end = String::cast(script->source())->length();
    123       functions->emplace_back(0, source_end, 1u,
    124                               isolate->factory()->empty_string());
    125     }
    126 
    127     // Use sorted list to reconstruct function nesting.
    128     for (SharedFunctionInfo* info : sorted) {
    129       int start = StartPosition(info);
    130       int end = info->end_position();
    131       uint32_t count = counter_map.Get(info);
    132       Handle<String> name(info->DebugName(), isolate);
    133       functions->emplace_back(start, end, count, name);
    134     }
    135   }
    136   return result;
    137 }
    138 
    139 void Coverage::TogglePrecise(Isolate* isolate, bool enable) {
    140   if (enable) {
    141     HandleScope scope(isolate);
    142     // Remove all optimized function. Optimized and inlined functions do not
    143     // increment invocation count.
    144     Deoptimizer::DeoptimizeAll(isolate);
    145     // Collect existing feedback vectors.
    146     std::vector<Handle<FeedbackVector>> vectors;
    147     {
    148       HeapIterator heap_iterator(isolate->heap());
    149       while (HeapObject* current_obj = heap_iterator.next()) {
    150         if (!current_obj->IsFeedbackVector()) continue;
    151         FeedbackVector* vector = FeedbackVector::cast(current_obj);
    152         SharedFunctionInfo* shared = vector->shared_function_info();
    153         if (!shared->IsSubjectToDebugging()) continue;
    154         vector->clear_invocation_count();
    155         vectors.emplace_back(vector, isolate);
    156       }
    157     }
    158     // Add collected feedback vectors to the root list lest we lose them to GC.
    159     Handle<ArrayList> list =
    160         ArrayList::New(isolate, static_cast<int>(vectors.size()));
    161     for (const auto& vector : vectors) list = ArrayList::Add(list, vector);
    162     isolate->SetCodeCoverageList(*list);
    163   } else {
    164     isolate->SetCodeCoverageList(isolate->heap()->undefined_value());
    165   }
    166 }
    167 
    168 }  // namespace internal
    169 }  // namespace v8
    170