OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2010 the V8 project authors. All rights reserved. | |
2 // Redistribution and use in source and binary forms, with or without | |
3 // modification, are permitted provided that the following conditions are | |
4 // met: | |
5 // | |
6 // * Redistributions of source code must retain the above copyright | |
7 // notice, this list of conditions and the following disclaimer. | |
8 // * Redistributions in binary form must reproduce the above | |
9 // copyright notice, this list of conditions and the following | |
10 // disclaimer in the documentation and/or other materials provided | |
11 // with the distribution. | |
12 // * Neither the name of Google Inc. nor the names of its | |
13 // contributors may be used to endorse or promote products derived | |
14 // from this software without specific prior written permission. | |
15 // | |
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
27 | |
28 #include "v8.h" | |
29 | |
30 #include "profile-generator-inl.h" | |
31 | |
32 | |
33 namespace v8 { | |
34 namespace internal { | |
35 | |
36 | |
37 ProfileNode* ProfileNode::FindChild(CodeEntry* entry) { | |
38 HashMap::Entry* map_entry = | |
39 children_.Lookup(entry, CodeEntryHash(entry), false); | |
40 return map_entry != NULL ? | |
41 reinterpret_cast<ProfileNode*>(map_entry->value) : NULL; | |
42 } | |
43 | |
44 | |
45 ProfileNode* ProfileNode::FindOrAddChild(CodeEntry* entry) { | |
46 HashMap::Entry* map_entry = | |
47 children_.Lookup(entry, CodeEntryHash(entry), true); | |
48 if (map_entry->value == NULL) { | |
49 // New node added. | |
50 map_entry->value = new ProfileNode(entry); | |
51 } | |
52 return reinterpret_cast<ProfileNode*>(map_entry->value); | |
53 } | |
54 | |
55 | |
56 void ProfileNode::Print(int indent) { | |
57 OS::Print("%4u %4u %*c %s\n", | |
58 total_ticks_, self_ticks_, | |
59 indent, ' ', | |
60 entry_ != NULL ? entry_->name() : ""); | |
61 for (HashMap::Entry* p = children_.Start(); | |
62 p != NULL; | |
63 p = children_.Next(p)) { | |
64 reinterpret_cast<ProfileNode*>(p->value)->Print(indent + 2); | |
65 } | |
66 } | |
67 | |
68 | |
69 namespace { | |
70 | |
71 class DeleteNodesCallback { | |
72 public: | |
73 void AfterAllChildrenTraversed(ProfileNode* node) { | |
74 delete node; | |
75 } | |
76 | |
77 void AfterChildTraversed(ProfileNode*, ProfileNode*) { } | |
78 }; | |
79 | |
80 } // namespace | |
81 | |
82 | |
83 ProfileTree::~ProfileTree() { | |
84 DeleteNodesCallback cb; | |
85 TraverseBreadthFirstPostOrder(&cb); | |
86 } | |
87 | |
88 | |
89 void ProfileTree::AddPathFromEnd(const Vector<CodeEntry*>& path) { | |
90 ProfileNode* node = root_; | |
91 for (CodeEntry** entry = path.start() + path.length() - 1; | |
92 entry != path.start() - 1; | |
93 --entry) { | |
94 if (*entry != NULL) { | |
95 node = node->FindOrAddChild(*entry); | |
96 } | |
97 } | |
98 node->IncrementSelfTicks(); | |
99 } | |
100 | |
101 | |
102 void ProfileTree::AddPathFromStart(const Vector<CodeEntry*>& path) { | |
103 ProfileNode* node = root_; | |
104 for (CodeEntry** entry = path.start(); | |
105 entry != path.start() + path.length(); | |
106 ++entry) { | |
107 if (*entry != NULL) { | |
108 node = node->FindOrAddChild(*entry); | |
109 } | |
110 } | |
111 node->IncrementSelfTicks(); | |
112 } | |
113 | |
114 | |
115 namespace { | |
116 | |
117 struct Position { | |
118 Position(ProfileNode* a_node, HashMap::Entry* a_p) | |
119 : node(a_node), p(a_p) { } | |
120 INLINE(ProfileNode* current_child()) { | |
121 return reinterpret_cast<ProfileNode*>(p->value); | |
122 } | |
123 ProfileNode* node; | |
124 HashMap::Entry* p; | |
125 }; | |
126 | |
127 } // namespace | |
128 | |
129 | |
130 template <typename Callback> | |
131 void ProfileTree::TraverseBreadthFirstPostOrder(Callback* callback) { | |
132 List<Position> stack(10); | |
133 stack.Add(Position(root_, root_->children_.Start())); | |
134 do { | |
135 Position& current = stack.last(); | |
136 if (current.p != NULL) { | |
137 stack.Add(Position(current.current_child(), | |
138 current.current_child()->children_.Start())); | |
139 } else { | |
140 callback->AfterAllChildrenTraversed(current.node); | |
141 if (stack.length() > 1) { | |
142 Position& parent = stack[stack.length() - 2]; | |
143 callback->AfterChildTraversed(parent.node, current.node); | |
Finnur
2011/03/25 15:18:48
If the callback is a DeleteNodesCallback, then we'
mnaganov (inactive)
2011/03/28 08:16:37
You are right. But in DeleteNodesCallback, AfterCh
| |
144 parent.p = parent.node->children_.Next(parent.p); | |
145 // Remove child from the stack. | |
146 stack.RemoveLast(); | |
147 } | |
148 } | |
149 } while (stack.length() > 1 || stack.last().p != NULL); | |
150 } | |
151 | |
152 | |
153 namespace { | |
154 | |
155 class CalculateTotalTicksCallback { | |
156 public: | |
157 void AfterAllChildrenTraversed(ProfileNode* node) { | |
158 node->IncreaseTotalTicks(node->self_ticks()); | |
159 } | |
160 | |
161 void AfterChildTraversed(ProfileNode* parent, ProfileNode* child) { | |
162 parent->IncreaseTotalTicks(child->total_ticks()); | |
163 } | |
164 }; | |
165 | |
166 } // namespace | |
167 | |
168 | |
169 // Non-recursive implementation of breadth-first tree traversal. | |
170 void ProfileTree::CalculateTotalTicks() { | |
171 CalculateTotalTicksCallback cb; | |
172 TraverseBreadthFirstPostOrder(&cb); | |
173 } | |
174 | |
175 | |
176 void ProfileTree::ShortPrint() { | |
177 OS::Print("root: %u %u\n", root_->total_ticks(), root_->self_ticks()); | |
178 } | |
179 | |
180 | |
181 void CpuProfile::AddPath(const Vector<CodeEntry*>& path) { | |
182 top_down_.AddPathFromEnd(path); | |
183 bottom_up_.AddPathFromStart(path); | |
184 } | |
185 | |
186 | |
187 void CpuProfile::CalculateTotalTicks() { | |
188 top_down_.CalculateTotalTicks(); | |
189 bottom_up_.CalculateTotalTicks(); | |
190 } | |
191 | |
192 | |
193 void CpuProfile::ShortPrint() { | |
194 OS::Print("top down "); | |
195 top_down_.ShortPrint(); | |
196 OS::Print("bottom up "); | |
197 bottom_up_.ShortPrint(); | |
198 } | |
199 | |
200 | |
201 void CpuProfile::Print() { | |
202 OS::Print("[Top down]:\n"); | |
203 top_down_.Print(); | |
204 OS::Print("[Bottom up]:\n"); | |
205 bottom_up_.Print(); | |
206 } | |
207 | |
208 | |
209 const CodeMap::CodeTreeConfig::Key CodeMap::CodeTreeConfig::kNoKey = NULL; | |
210 const CodeMap::CodeTreeConfig::Value CodeMap::CodeTreeConfig::kNoValue = | |
211 CodeMap::CodeEntryInfo(NULL, 0); | |
212 | |
213 | |
214 void CodeMap::AddAlias(Address alias, Address addr) { | |
215 CodeTree::Locator locator; | |
216 if (tree_.Find(addr, &locator)) { | |
217 const CodeEntryInfo& entry_info = locator.value(); | |
218 tree_.Insert(alias, &locator); | |
219 locator.set_value(entry_info); | |
220 } | |
221 } | |
222 | |
223 | |
224 CodeEntry* CodeMap::FindEntry(Address addr) { | |
225 CodeTree::Locator locator; | |
226 if (tree_.FindGreatestLessThan(addr, &locator)) { | |
227 // locator.key() <= addr. Need to check that addr is within entry. | |
228 const CodeEntryInfo& entry = locator.value(); | |
229 if (addr < (locator.key() + entry.size)) | |
230 return entry.entry; | |
231 } | |
232 return NULL; | |
233 } | |
234 | |
235 | |
236 ProfileGenerator::ProfileGenerator() | |
237 : resource_names_(StringsMatch) { | |
238 } | |
239 | |
240 | |
241 static void CodeEntriesDeleter(CodeEntry** entry_ptr) { | |
242 delete *entry_ptr; | |
243 } | |
244 | |
245 | |
246 ProfileGenerator::~ProfileGenerator() { | |
247 for (HashMap::Entry* p = resource_names_.Start(); | |
248 p != NULL; | |
249 p = resource_names_.Next(p)) { | |
250 DeleteArray(reinterpret_cast<const char*>(p->value)); | |
251 } | |
252 | |
253 code_entries_.Iterate(CodeEntriesDeleter); | |
254 } | |
255 | |
256 | |
257 CodeEntry* ProfileGenerator::NewCodeEntry( | |
258 Logger::LogEventsAndTags tag, | |
259 String* name, | |
260 String* resource_name, int line_number) { | |
261 const char* cached_resource_name = NULL; | |
262 if (resource_name->IsString()) { | |
263 // As we copy contents of resource names, and usually they are repeated, | |
264 // we cache names by string hashcode. | |
265 HashMap::Entry* cache_entry = | |
266 resource_names_.Lookup(resource_name, | |
267 StringEntryHash(resource_name), | |
268 true); | |
269 if (cache_entry->value == NULL) { | |
270 // New entry added. | |
271 cache_entry->value = | |
272 resource_name->ToCString(DISALLOW_NULLS, | |
273 ROBUST_STRING_TRAVERSAL).Detach(); | |
274 } | |
275 cached_resource_name = reinterpret_cast<const char*>(cache_entry->value); | |
276 } | |
277 | |
278 CodeEntry* entry = new ManagedNameCodeEntry(tag, | |
279 name, | |
280 cached_resource_name, | |
281 line_number); | |
282 code_entries_.Add(entry); | |
283 return entry; | |
284 } | |
285 | |
286 | |
287 CodeEntry* ProfileGenerator::NewCodeEntry( | |
288 Logger::LogEventsAndTags tag, | |
289 const char* name) { | |
290 CodeEntry* entry = new StaticNameCodeEntry(tag, name); | |
291 code_entries_.Add(entry); | |
292 return entry; | |
293 } | |
294 | |
295 } } // namespace v8::internal | |
OLD | NEW |