Line data Source code
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/frames-inl.h"
10 : #include "src/isolate.h"
11 : #include "src/objects-inl.h"
12 : #include "src/objects.h"
13 :
14 : namespace v8 {
15 : namespace internal {
16 :
17 : class SharedToCounterMap
18 : : public base::TemplateHashMapImpl<SharedFunctionInfo*, uint32_t,
19 : base::KeyEqualityMatcher<void*>,
20 : base::DefaultAllocationPolicy> {
21 : public:
22 : typedef base::TemplateHashMapEntry<SharedFunctionInfo*, uint32_t> Entry;
23 1028 : inline void Add(SharedFunctionInfo* key, uint32_t count) {
24 2056 : Entry* entry = LookupOrInsert(key, Hash(key), []() { return 0; });
25 1028 : uint32_t old_count = entry->value;
26 1028 : if (UINT32_MAX - count < old_count) {
27 0 : entry->value = UINT32_MAX;
28 : } else {
29 1028 : entry->value = old_count + count;
30 : }
31 1028 : }
32 :
33 2937 : inline uint32_t Get(SharedFunctionInfo* key) {
34 2937 : Entry* entry = Lookup(key, Hash(key));
35 2937 : if (entry == nullptr) return 0;
36 840 : return entry->value;
37 : }
38 :
39 : private:
40 : static uint32_t Hash(SharedFunctionInfo* key) {
41 3965 : return static_cast<uint32_t>(reinterpret_cast<intptr_t>(key));
42 : }
43 :
44 : DisallowHeapAllocation no_gc;
45 : };
46 :
47 : namespace {
48 : int StartPosition(SharedFunctionInfo* info) {
49 : int start = info->function_token_position();
50 26611 : if (start == kNoSourcePosition) start = info->start_position();
51 : return start;
52 : }
53 :
54 11837 : bool CompareSharedFunctionInfo(SharedFunctionInfo* a, SharedFunctionInfo* b) {
55 : int a_start = StartPosition(a);
56 : int b_start = StartPosition(b);
57 11947 : if (a_start == b_start) return a->end_position() > b->end_position();
58 11727 : return a_start < b_start;
59 : }
60 : } // anonymous namespace
61 :
62 80 : Coverage* Coverage::CollectPrecise(Isolate* isolate) {
63 : DCHECK(!isolate->is_best_effort_code_coverage());
64 80 : Coverage* result = Collect(isolate, isolate->code_coverage_mode());
65 80 : if (isolate->is_precise_binary_code_coverage()) {
66 : // We do not have to hold onto feedback vectors for invocations we already
67 : // reported. So we can reset the list.
68 36 : isolate->SetCodeCoverageList(*ArrayList::New(isolate, 0));
69 : }
70 80 : return result;
71 : }
72 :
73 78 : Coverage* Coverage::CollectBestEffort(Isolate* isolate) {
74 78 : return Collect(isolate, v8::debug::Coverage::kBestEffort);
75 : }
76 :
77 316 : Coverage* Coverage::Collect(Isolate* isolate,
78 : v8::debug::Coverage::Mode collectionMode) {
79 : SharedToCounterMap counter_map;
80 :
81 158 : switch (isolate->code_coverage_mode()) {
82 : case v8::debug::Coverage::kPreciseBinary:
83 : case v8::debug::Coverage::kPreciseCount: {
84 : bool reset_count = collectionMode != v8::debug::Coverage::kBestEffort;
85 : // Feedback vectors are already listed to prevent losing them to GC.
86 : DCHECK(isolate->factory()->code_coverage_list()->IsArrayList());
87 : Handle<ArrayList> list =
88 : Handle<ArrayList>::cast(isolate->factory()->code_coverage_list());
89 774 : for (int i = 0; i < list->Length(); i++) {
90 : FeedbackVector* vector = FeedbackVector::cast(list->Get(i));
91 : SharedFunctionInfo* shared = vector->shared_function_info();
92 : DCHECK(shared->IsSubjectToDebugging());
93 566 : uint32_t count = static_cast<uint32_t>(vector->invocation_count());
94 566 : if (reset_count) vector->clear_invocation_count();
95 566 : counter_map.Add(shared, count);
96 : }
97 : break;
98 : }
99 : case v8::debug::Coverage::kBestEffort: {
100 : DCHECK(!isolate->factory()->code_coverage_list()->IsArrayList());
101 : DCHECK_EQ(v8::debug::Coverage::kBestEffort, collectionMode);
102 54 : HeapIterator heap_iterator(isolate->heap());
103 580492 : while (HeapObject* current_obj = heap_iterator.next()) {
104 580438 : if (!current_obj->IsFeedbackVector()) continue;
105 : FeedbackVector* vector = FeedbackVector::cast(current_obj);
106 : SharedFunctionInfo* shared = vector->shared_function_info();
107 8118 : if (!shared->IsSubjectToDebugging()) continue;
108 462 : uint32_t count = static_cast<uint32_t>(vector->invocation_count());
109 462 : counter_map.Add(shared, count);
110 : }
111 54 : break;
112 : }
113 : }
114 :
115 : // Iterate shared function infos of every script and build a mapping
116 : // between source ranges and invocation counts.
117 158 : Coverage* result = new Coverage();
118 158 : Script::Iterator scripts(isolate);
119 3505 : while (Script* script = scripts.Next()) {
120 6335 : if (!script->IsUserJavaScript()) continue;
121 :
122 : // Create and add new script data.
123 : Handle<Script> script_handle(script, isolate);
124 359 : result->emplace_back(script_handle);
125 2864 : std::vector<CoverageFunction>* functions = &result->back().functions;
126 :
127 : std::vector<SharedFunctionInfo*> sorted;
128 :
129 : {
130 : // Sort functions by start position, from outer to inner functions.
131 359 : SharedFunctionInfo::ScriptIterator infos(script_handle);
132 3296 : while (SharedFunctionInfo* info = infos.Next()) {
133 2937 : sorted.push_back(info);
134 : }
135 3296 : std::sort(sorted.begin(), sorted.end(), CompareSharedFunctionInfo);
136 : }
137 :
138 : // Stack to track nested functions, referring function by index.
139 : std::vector<size_t> nesting;
140 :
141 : // Use sorted list to reconstruct function nesting.
142 3655 : for (SharedFunctionInfo* info : sorted) {
143 2937 : int start = StartPosition(info);
144 2937 : int end = info->end_position();
145 2937 : uint32_t count = counter_map.Get(info);
146 : // Find the correct outer function based on start position.
147 11897 : while (!nesting.empty() && functions->at(nesting.back()).end <= start) {
148 : nesting.pop_back();
149 : }
150 2937 : if (count != 0) {
151 752 : switch (collectionMode) {
152 : case v8::debug::Coverage::kPreciseCount:
153 : break;
154 : case v8::debug::Coverage::kPreciseBinary:
155 38 : count = info->has_reported_binary_coverage() ? 0 : 1;
156 38 : info->set_has_reported_binary_coverage(true);
157 38 : break;
158 : case v8::debug::Coverage::kBestEffort:
159 502 : count = 1;
160 502 : break;
161 : }
162 : }
163 : // Only include a function range if it has a non-0 count, or
164 : // if it is directly nested inside a function with non-0 count.
165 8061 : if (count != 0 ||
166 3522 : (!nesting.empty() && functions->at(nesting.back()).count != 0)) {
167 2505 : Handle<String> name(info->DebugName(), isolate);
168 5010 : nesting.push_back(functions->size());
169 2505 : functions->emplace_back(start, end, count, name);
170 : }
171 : }
172 :
173 : // Remove entries for scripts that have no coverage.
174 359 : if (functions->empty()) result->pop_back();
175 : }
176 158 : return result;
177 : }
178 :
179 196 : void Coverage::SelectMode(Isolate* isolate, debug::Coverage::Mode mode) {
180 196 : switch (mode) {
181 : case debug::Coverage::kBestEffort:
182 135 : isolate->SetCodeCoverageList(isolate->heap()->undefined_value());
183 135 : break;
184 : case debug::Coverage::kPreciseBinary:
185 : case debug::Coverage::kPreciseCount: {
186 61 : HandleScope scope(isolate);
187 : // Remove all optimized function. Optimized and inlined functions do not
188 : // increment invocation count.
189 61 : Deoptimizer::DeoptimizeAll(isolate);
190 : // Collect existing feedback vectors.
191 : std::vector<Handle<FeedbackVector>> vectors;
192 : {
193 61 : HeapIterator heap_iterator(isolate->heap());
194 696981 : while (HeapObject* current_obj = heap_iterator.next()) {
195 696920 : if (current_obj->IsSharedFunctionInfo()) {
196 : SharedFunctionInfo* shared = SharedFunctionInfo::cast(current_obj);
197 57713 : shared->set_has_reported_binary_coverage(false);
198 639207 : } else if (current_obj->IsFeedbackVector()) {
199 10258 : FeedbackVector* vector = FeedbackVector::cast(current_obj);
200 : SharedFunctionInfo* shared = vector->shared_function_info();
201 20444 : if (!shared->IsSubjectToDebugging()) continue;
202 72 : vectors.emplace_back(vector, isolate);
203 : }
204 61 : }
205 : }
206 : // Add collected feedback vectors to the root list lest we lose them to
207 : // GC.
208 : Handle<ArrayList> list =
209 122 : ArrayList::New(isolate, static_cast<int>(vectors.size()));
210 266 : for (const auto& vector : vectors) list = ArrayList::Add(list, vector);
211 61 : isolate->SetCodeCoverageList(*list);
212 : break;
213 : }
214 : }
215 196 : isolate->set_code_coverage_mode(mode);
216 196 : }
217 :
218 : } // namespace internal
219 : } // namespace v8
|