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