• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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;
Add(SharedFunctionInfo * key,uint32_t count)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 
Get(SharedFunctionInfo * key)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:
Hash(SharedFunctionInfo * key)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 {
StartPosition(SharedFunctionInfo * info)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 
CompareSharedFunctionInfo(SharedFunctionInfo * a,SharedFunctionInfo * b)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 
Collect(Isolate * isolate,bool reset_count)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 
TogglePrecise(Isolate * isolate,bool enable)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