| OLD | NEW |
| 1 // Copyright 2017 the V8 project authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "src/debug/debug-coverage.h" | 5 #include "src/debug/debug-coverage.h" |
| 6 | 6 |
| 7 #include "src/base/hashmap.h" | 7 #include "src/base/hashmap.h" |
| 8 #include "src/deoptimizer.h" | 8 #include "src/deoptimizer.h" |
| 9 #include "src/isolate.h" | 9 #include "src/isolate.h" |
| 10 #include "src/objects-inl.h" | 10 #include "src/objects-inl.h" |
| (...skipping 21 matching lines...) Expand all Loading... |
| 32 inline uint32_t Get(SharedFunctionInfo* key) { | 32 inline uint32_t Get(SharedFunctionInfo* key) { |
| 33 Entry* entry = Lookup(key, Hash(key)); | 33 Entry* entry = Lookup(key, Hash(key)); |
| 34 if (entry == nullptr) return 0; | 34 if (entry == nullptr) return 0; |
| 35 return entry->value; | 35 return entry->value; |
| 36 } | 36 } |
| 37 | 37 |
| 38 private: | 38 private: |
| 39 static uint32_t Hash(SharedFunctionInfo* key) { | 39 static uint32_t Hash(SharedFunctionInfo* key) { |
| 40 return static_cast<uint32_t>(reinterpret_cast<intptr_t>(key)); | 40 return static_cast<uint32_t>(reinterpret_cast<intptr_t>(key)); |
| 41 } | 41 } |
| 42 }; | |
| 43 | 42 |
| 44 class ScriptDataBuilder { | 43 DisallowHeapAllocation no_gc; |
| 45 public: | |
| 46 void Add(int end_position, uint32_t count) { | |
| 47 DCHECK(entries_.empty() || entries_.back().end_position <= end_position); | |
| 48 if (entries_.empty()) { | |
| 49 if (end_position > 0) entries_.push_back({end_position, count}); | |
| 50 } else if (entries_.back().count == count) { | |
| 51 // Extend last range. | |
| 52 entries_.back().end_position = end_position; | |
| 53 } else if (entries_.back().end_position < end_position) { | |
| 54 // Add new range. | |
| 55 entries_.push_back({end_position, count}); | |
| 56 } | |
| 57 } | |
| 58 std::vector<Coverage::RangeEntry> Finish() { | |
| 59 std::vector<Coverage::RangeEntry> result; | |
| 60 std::swap(result, entries_); | |
| 61 return result; | |
| 62 } | |
| 63 | |
| 64 private: | |
| 65 std::vector<Coverage::RangeEntry> entries_; | |
| 66 }; | 44 }; |
| 67 | 45 |
| 68 std::vector<Coverage::ScriptData> Coverage::Collect(Isolate* isolate) { | 46 std::vector<Coverage::ScriptData> Coverage::Collect(Isolate* isolate) { |
| 69 SharedToCounterMap counter_map; | 47 SharedToCounterMap counter_map; |
| 70 | 48 |
| 71 if (isolate->IsCodeCoverageEnabled()) { | 49 if (isolate->IsCodeCoverageEnabled()) { |
| 72 // Feedback vectors are already listed to prevent losing them to GC. | 50 // Feedback vectors are already listed to prevent losing them to GC. |
| 73 Handle<ArrayList> list = | 51 Handle<ArrayList> list = |
| 74 Handle<ArrayList>::cast(isolate->factory()->code_coverage_list()); | 52 Handle<ArrayList>::cast(isolate->factory()->code_coverage_list()); |
| 75 for (int i = 0; i < list->Length(); i++) { | 53 for (int i = 0; i < list->Length(); i++) { |
| (...skipping 16 matching lines...) Expand all Loading... |
| 92 SharedFunctionInfo* shared = vector->shared_function_info(); | 70 SharedFunctionInfo* shared = vector->shared_function_info(); |
| 93 if (!shared->IsSubjectToDebugging()) continue; | 71 if (!shared->IsSubjectToDebugging()) continue; |
| 94 uint32_t count = static_cast<uint32_t>(vector->invocation_count()); | 72 uint32_t count = static_cast<uint32_t>(vector->invocation_count()); |
| 95 counter_map.Add(shared, count); | 73 counter_map.Add(shared, count); |
| 96 } | 74 } |
| 97 } | 75 } |
| 98 | 76 |
| 99 // Make sure entries in the counter map is not invalidated by GC. | 77 // Make sure entries in the counter map is not invalidated by GC. |
| 100 DisallowHeapAllocation no_gc; | 78 DisallowHeapAllocation no_gc; |
| 101 | 79 |
| 102 // Stack to track nested functions. | 80 std::vector<Range*> stack; |
| 103 struct FunctionNode { | |
| 104 FunctionNode(int s, int e, uint32_t c) : start(s), end(e), count(c) {} | |
| 105 int start; | |
| 106 int end; | |
| 107 uint32_t count; | |
| 108 }; | |
| 109 std::vector<FunctionNode> stack; | |
| 110 | 81 |
| 111 // Iterate shared function infos of every script and build a mapping | 82 // Iterate shared function infos of every script and build a mapping |
| 112 // between source ranges and invocation counts. | 83 // between source ranges and invocation counts. |
| 113 std::vector<Coverage::ScriptData> result; | 84 std::vector<Coverage::ScriptData> result; |
| 114 Script::Iterator scripts(isolate); | 85 Script::Iterator scripts(isolate); |
| 115 while (Script* script = scripts.Next()) { | 86 while (Script* script = scripts.Next()) { |
| 116 // Dismiss non-user scripts. | 87 // Dismiss non-user scripts. |
| 117 if (script->type() != Script::TYPE_NORMAL) continue; | 88 if (script->type() != Script::TYPE_NORMAL) continue; |
| 118 DCHECK(stack.empty()); | 89 DCHECK(stack.empty()); |
| 119 int script_end = String::cast(script->source())->length(); | 90 int source_length = String::cast(script->source())->length(); |
| 120 // If not rooted, the top-level function is likely no longer alive. Set the | 91 result.emplace_back(Handle<Script>(script, isolate), source_length); |
| 121 // outer-most count to 1 to indicate that the script has run at least once. | 92 stack.push_back(&result.back().toplevel); |
| 122 stack.push_back({0, script_end, 1}); | |
| 123 ScriptDataBuilder builder; | |
| 124 // Iterate through the list of shared function infos, reconstruct the | 93 // Iterate through the list of shared function infos, reconstruct the |
| 125 // nesting, and compute the ranges covering different invocation counts. | 94 // nesting, and compute the ranges covering different invocation counts. |
| 126 HandleScope scope(isolate); | 95 HandleScope scope(isolate); |
| 127 SharedFunctionInfo::ScriptIterator infos(Handle<Script>(script, isolate)); | 96 SharedFunctionInfo::ScriptIterator infos(Handle<Script>(script, isolate)); |
| 128 while (SharedFunctionInfo* info = infos.Next()) { | 97 while (SharedFunctionInfo* info = infos.Next()) { |
| 129 int start = info->function_token_position(); | 98 int start = info->function_token_position(); |
| 130 if (start == kNoSourcePosition) start = info->start_position(); | 99 if (start == kNoSourcePosition) start = info->start_position(); |
| 131 int end = info->end_position(); | 100 int end = info->end_position(); |
| 132 uint32_t count = counter_map.Get(info); | 101 uint32_t count = counter_map.Get(info); |
| 133 // The shared function infos are sorted by start. | 102 if (info->is_toplevel()) { |
| 134 DCHECK_LE(stack.back().start, start); | 103 // Top-level function is available. |
| 135 // If the start are the same, the outer function comes before the inner. | 104 DCHECK_EQ(1, stack.size()); |
| 136 DCHECK(stack.back().start < start || stack.back().end >= end); | 105 result.back().toplevel.start = start; |
| 137 // Drop the stack to the outer function. | 106 result.back().toplevel.end = end; |
| 138 while (start > stack.back().end) { | 107 result.back().toplevel.count = count; |
| 139 // Write out rest of function being dropped. | 108 } else { |
| 140 builder.Add(stack.back().end, stack.back().count); | 109 // The shared function infos are sorted by start. |
| 141 stack.pop_back(); | 110 DCHECK_LE(stack.back()->start, start); |
| 111 // Drop the stack to the outer function. |
| 112 while (start > stack.back()->end) stack.pop_back(); |
| 113 Range* outer = stack.back(); |
| 114 // New nested function. |
| 115 DCHECK_LE(end, outer->end); |
| 116 outer->inner.emplace_back(start, end, count); |
| 117 Range& nested = outer->inner.back(); |
| 118 String* name = info->DebugName(); |
| 119 nested.name.resize(name->length()); |
| 120 String::WriteToFlat(name, nested.name.data(), 0, name->length()); |
| 121 stack.push_back(&nested); |
| 142 } | 122 } |
| 143 // Write out outer function up to the start of new function. | |
| 144 builder.Add(start, stack.back().count); | |
| 145 // New nested function. | |
| 146 DCHECK_LE(end, stack.back().end); | |
| 147 stack.emplace_back(start, end, count); | |
| 148 } | 123 } |
| 149 | 124 stack.clear(); |
| 150 // Drop the stack to the script level. | |
| 151 while (!stack.empty()) { | |
| 152 // Write out rest of function being dropped. | |
| 153 builder.Add(stack.back().end, stack.back().count); | |
| 154 stack.pop_back(); | |
| 155 } | |
| 156 result.emplace_back(script->id(), builder.Finish()); | |
| 157 } | 125 } |
| 158 return result; | 126 return result; |
| 159 } | 127 } |
| 160 | 128 |
| 161 void Coverage::EnablePrecise(Isolate* isolate) { | 129 void Coverage::EnablePrecise(Isolate* isolate) { |
| 162 HandleScope scope(isolate); | 130 HandleScope scope(isolate); |
| 163 // Remove all optimized function. Optimized and inlined functions do not | 131 // Remove all optimized function. Optimized and inlined functions do not |
| 164 // increment invocation count. | 132 // increment invocation count. |
| 165 Deoptimizer::DeoptimizeAll(isolate); | 133 Deoptimizer::DeoptimizeAll(isolate); |
| 166 // Collect existing feedback vectors. | 134 // Collect existing feedback vectors. |
| (...skipping 15 matching lines...) Expand all Loading... |
| 182 for (const auto& vector : vectors) list = ArrayList::Add(list, vector); | 150 for (const auto& vector : vectors) list = ArrayList::Add(list, vector); |
| 183 isolate->SetCodeCoverageList(*list); | 151 isolate->SetCodeCoverageList(*list); |
| 184 } | 152 } |
| 185 | 153 |
| 186 void Coverage::DisablePrecise(Isolate* isolate) { | 154 void Coverage::DisablePrecise(Isolate* isolate) { |
| 187 isolate->SetCodeCoverageList(isolate->heap()->undefined_value()); | 155 isolate->SetCodeCoverageList(isolate->heap()->undefined_value()); |
| 188 } | 156 } |
| 189 | 157 |
| 190 } // namespace internal | 158 } // namespace internal |
| 191 } // namespace v8 | 159 } // namespace v8 |
| OLD | NEW |