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