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 |