Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 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/objects-inl.h" | |
| 9 #include "src/objects.h" | |
| 10 | |
| 11 namespace v8 { | |
| 12 namespace internal { | |
| 13 | |
| 14 class SharedToCounterMap | |
| 15 : public base::TemplateHashMapImpl<SharedFunctionInfo*, uint32_t, | |
| 16 base::KeyEqualityMatcher<void*>, | |
| 17 base::DefaultAllocationPolicy> { | |
| 18 public: | |
| 19 typedef base::TemplateHashMapEntry<SharedFunctionInfo*, uint32_t> Entry; | |
| 20 inline void Add(SharedFunctionInfo* key, uint32_t count) { | |
| 21 Entry* entry = LookupOrInsert(key, Hash(key), []() { return 0; }); | |
| 22 uint32_t old_count = entry->value; | |
| 23 if (UINT32_MAX - count < old_count) { | |
| 24 entry->value = UINT32_MAX; | |
| 25 } else { | |
| 26 entry->value = old_count + count; | |
| 27 } | |
| 28 } | |
| 29 | |
| 30 inline uint32_t Get(SharedFunctionInfo* key) { | |
| 31 Entry* entry = Lookup(key, Hash(key)); | |
| 32 if (entry == nullptr) return 0; | |
| 33 return entry->value; | |
| 34 } | |
| 35 | |
| 36 private: | |
| 37 static uint32_t Hash(SharedFunctionInfo* key) { | |
| 38 return static_cast<uint32_t>(reinterpret_cast<intptr_t>(key)); | |
| 39 } | |
| 40 }; | |
| 41 | |
| 42 class ScriptDataBuilder { | |
| 43 public: | |
| 44 void Add(int end_position, uint32_t count) { | |
| 45 DCHECK(entries_.empty() || entries_.back().end_position <= end_position); | |
| 46 if (entries_.empty()) { | |
| 47 if (end_position > 0) entries_.push_back({end_position, count}); | |
| 48 } else if (entries_.back().count == count) { | |
| 49 // Extend last range. | |
| 50 entries_.back().end_position = end_position; | |
| 51 } else if (entries_.back().end_position < end_position) { | |
| 52 // Add new range. | |
| 53 entries_.push_back({end_position, count}); | |
| 54 } | |
| 55 } | |
| 56 std::vector<Coverage::RangeEntry> Finish() { return std::move(entries_); } | |
|
jgruber
2017/02/09 15:43:22
To be safe we could add a check to make sure Add i
Yang
2017/02/09 17:11:01
Apparently creating a temporary vector, swapping t
| |
| 57 | |
| 58 private: | |
| 59 std::vector<Coverage::RangeEntry> entries_; | |
| 60 }; | |
| 61 | |
| 62 std::vector<Coverage::ScriptData> Coverage::Collect(Isolate* isolate) { | |
| 63 SharedToCounterMap counter_map; | |
| 64 // Iterate the heap to find all feedback vectors and accumulate the | |
| 65 // invocation counts into the map for each shared function info. | |
| 66 HeapIterator heap_iterator(isolate->heap()); | |
| 67 HeapObject* current_obj; | |
| 68 while ((current_obj = heap_iterator.next())) { | |
| 69 if (!current_obj->IsFeedbackVector()) continue; | |
| 70 FeedbackVector* vector = FeedbackVector::cast(current_obj); | |
| 71 SharedFunctionInfo* shared = vector->shared_function_info(); | |
| 72 if (!shared->IsSubjectToDebugging()) continue; | |
| 73 uint32_t count = static_cast<uint32_t>(vector->invocation_count()); | |
| 74 counter_map.Add(shared, count); | |
| 75 } | |
| 76 | |
| 77 // Make sure entries in the counter map is not invalidated by GC. | |
| 78 DisallowHeapAllocation no_gc; | |
| 79 | |
| 80 // Stack to track nested functions. | |
| 81 struct FunctionNode { | |
| 82 int start; | |
| 83 int end; | |
| 84 uint32_t count; | |
| 85 }; | |
| 86 std::vector<FunctionNode> stack; | |
| 87 | |
| 88 // Iterate shared function infos of every script and build a mapping | |
| 89 // between source ranges and invocation counts. | |
| 90 std::vector<Coverage::ScriptData> result; | |
| 91 Script::Iterator scripts(isolate); | |
| 92 while (Script* script = scripts.Next()) { | |
| 93 // Dismiss non-user scripts. | |
| 94 if (script->type() != Script::TYPE_NORMAL) continue; | |
| 95 DCHECK(stack.empty()); | |
| 96 int script_end = String::cast(script->source())->length(); | |
| 97 // If not rooted, the top-level function is likely no longer alive. Set the | |
| 98 // outer-most count to 1 to indicate that the script has run at least once. | |
| 99 stack.push_back({0, script_end, 1}); | |
| 100 ScriptDataBuilder builder; | |
| 101 // Iterate through the list of shared function infos, reconstruct the | |
| 102 // nesting, and compute the ranges covering different invocation counts. | |
| 103 HandleScope scope(isolate); | |
| 104 SharedFunctionInfo::ScriptIterator infos(Handle<Script>(script, isolate)); | |
| 105 while (SharedFunctionInfo* info = infos.Next()) { | |
| 106 int start = info->function_token_position(); | |
| 107 if (start == kNoSourcePosition) start = info->start_position(); | |
| 108 int end = info->end_position(); | |
| 109 uint32_t count = counter_map.Get(info); | |
| 110 // The shared function infos are sorted by start. | |
|
jgruber
2017/02/09 15:43:22
Is end also taken into account? I'm thinking of a
Yang
2017/02/09 17:11:01
actually b can only come before a, since we parse
| |
| 111 DCHECK(stack.back().start <= start); | |
|
jgruber
2017/02/09 15:43:22
Nit: DCHECK_LE here and below.
Yang
2017/02/09 17:11:01
Done.
| |
| 112 // Drop the stack to the outer function. | |
| 113 while (start > stack.back().end) { | |
| 114 // Write out rest of function being dropped. | |
| 115 builder.Add(stack.back().end, stack.back().count); | |
| 116 stack.pop_back(); | |
| 117 } | |
| 118 // Write out outer function up to the start of new function. | |
| 119 builder.Add(start, stack.back().count); | |
| 120 // New nested function. | |
| 121 DCHECK(end <= stack.back().end); | |
| 122 stack.push_back({start, end, count}); | |
| 123 } | |
| 124 | |
| 125 // Drop the stack to the script level. | |
| 126 while (!stack.empty()) { | |
| 127 // Write out rest of function being dropped. | |
| 128 builder.Add(stack.back().end, stack.back().count); | |
| 129 stack.pop_back(); | |
| 130 } | |
| 131 result.push_back({script->id(), builder.Finish()}); | |
|
jgruber
2017/02/09 15:43:22
I guess these vector copies are turned into moves
jgruber
2017/02/09 15:53:22
Actually, we probably need a move constructor for
Yang
2017/02/09 17:11:01
Done.
Yang
2017/02/09 17:11:01
We are fine with the default move constructor?
| |
| 132 } | |
| 133 return result; | |
| 134 } | |
| 135 | |
| 136 } // namespace internal | |
| 137 } // namespace v8 | |
| OLD | NEW |