OLD | NEW |
1 // Copyright 2009 the V8 project authors. All rights reserved. | 1 // Copyright 2009 the V8 project authors. All rights reserved. |
2 // Redistribution and use in source and binary forms, with or without | 2 // Redistribution and use in source and binary forms, with or without |
3 // modification, are permitted provided that the following conditions are | 3 // modification, are permitted provided that the following conditions are |
4 // met: | 4 // met: |
5 // | 5 // |
6 // * Redistributions of source code must retain the above copyright | 6 // * Redistributions of source code must retain the above copyright |
7 // notice, this list of conditions and the following disclaimer. | 7 // notice, this list of conditions and the following disclaimer. |
8 // * Redistributions in binary form must reproduce the above | 8 // * Redistributions in binary form must reproduce the above |
9 // copyright notice, this list of conditions and the following | 9 // copyright notice, this list of conditions and the following |
10 // disclaimer in the documentation and/or other materials provided | 10 // disclaimer in the documentation and/or other materials provided |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
49 * Should be overriden by subclasses. | 49 * Should be overriden by subclasses. |
50 * | 50 * |
51 * @param {string} name Function name. | 51 * @param {string} name Function name. |
52 */ | 52 */ |
53 devtools.profiler.Profile.prototype.skipThisFunction = function(name) { | 53 devtools.profiler.Profile.prototype.skipThisFunction = function(name) { |
54 return false; | 54 return false; |
55 }; | 55 }; |
56 | 56 |
57 | 57 |
58 /** | 58 /** |
| 59 * Enum for profiler operations that involve looking up existing |
| 60 * code entries. |
| 61 * |
| 62 * @enum {number} |
| 63 */ |
| 64 devtools.profiler.Profile.Operation = { |
| 65 MOVE: 0, |
| 66 DELETE: 1, |
| 67 TICK: 2 |
| 68 }; |
| 69 |
| 70 |
| 71 /** |
59 * Called whenever the specified operation has failed finding a function | 72 * Called whenever the specified operation has failed finding a function |
60 * containing the specified address. Should be overriden by subclasses. | 73 * containing the specified address. Should be overriden by subclasses. |
61 * Operation is one of the following: 'move', 'delete', 'tick'. | 74 * See the devtools.profiler.Profile.Operation enum for the list of |
| 75 * possible operations. |
62 * | 76 * |
63 * @param {string} operation Operation name. | 77 * @param {number} operation Operation. |
64 * @param {number} addr Address of the unknown code. | 78 * @param {number} addr Address of the unknown code. |
| 79 * @param {number} opt_stackPos If an unknown address is encountered |
| 80 * during stack strace processing, specifies a position of the frame |
| 81 * containing the address. |
65 */ | 82 */ |
66 devtools.profiler.Profile.prototype.handleUnknownCode = function( | 83 devtools.profiler.Profile.prototype.handleUnknownCode = function( |
67 operation, addr) { | 84 operation, addr, opt_stackPos) { |
68 }; | 85 }; |
69 | 86 |
70 | 87 |
71 /** | 88 /** |
72 * Registers static (library) code entry. | 89 * Registers static (library) code entry. |
73 * | 90 * |
74 * @param {string} name Code entry name. | 91 * @param {string} name Code entry name. |
75 * @param {number} startAddr Starting address. | 92 * @param {number} startAddr Starting address. |
76 * @param {number} endAddr Ending address. | 93 * @param {number} endAddr Ending address. |
77 */ | 94 */ |
78 devtools.profiler.Profile.prototype.addStaticCode = function( | 95 devtools.profiler.Profile.prototype.addStaticCode = function( |
79 name, startAddr, endAddr) { | 96 name, startAddr, endAddr) { |
80 this.codeMap_.addStaticCode(startAddr, | 97 var entry = new devtools.profiler.CodeMap.CodeEntry( |
81 new devtools.profiler.CodeMap.CodeEntry(endAddr - startAddr, name)); | 98 endAddr - startAddr, name); |
| 99 this.codeMap_.addStaticCode(startAddr, entry); |
| 100 return entry; |
82 }; | 101 }; |
83 | 102 |
84 | 103 |
85 /** | 104 /** |
86 * Registers dynamic (JIT-compiled) code entry. | 105 * Registers dynamic (JIT-compiled) code entry. |
87 * | 106 * |
88 * @param {string} type Code entry type. | 107 * @param {string} type Code entry type. |
89 * @param {string} name Code entry name. | 108 * @param {string} name Code entry name. |
90 * @param {number} start Starting address. | 109 * @param {number} start Starting address. |
91 * @param {number} size Code entry size. | 110 * @param {number} size Code entry size. |
92 */ | 111 */ |
93 devtools.profiler.Profile.prototype.addCode = function( | 112 devtools.profiler.Profile.prototype.addCode = function( |
94 type, name, start, size) { | 113 type, name, start, size) { |
95 this.codeMap_.addCode(start, | 114 var entry = new devtools.profiler.Profile.DynamicCodeEntry(size, type, name); |
96 new devtools.profiler.Profile.DynamicCodeEntry(size, type, name)); | 115 this.codeMap_.addCode(start, entry); |
| 116 return entry; |
97 }; | 117 }; |
98 | 118 |
99 | 119 |
100 /** | 120 /** |
101 * Reports about moving of a dynamic code entry. | 121 * Reports about moving of a dynamic code entry. |
102 * | 122 * |
103 * @param {number} from Current code entry address. | 123 * @param {number} from Current code entry address. |
104 * @param {number} to New code entry address. | 124 * @param {number} to New code entry address. |
105 */ | 125 */ |
106 devtools.profiler.Profile.prototype.moveCode = function(from, to) { | 126 devtools.profiler.Profile.prototype.moveCode = function(from, to) { |
107 try { | 127 try { |
108 this.codeMap_.moveCode(from, to); | 128 this.codeMap_.moveCode(from, to); |
109 } catch (e) { | 129 } catch (e) { |
110 this.handleUnknownCode('move', from); | 130 this.handleUnknownCode(devtools.profiler.Profile.Operation.MOVE, from); |
111 } | 131 } |
112 }; | 132 }; |
113 | 133 |
114 | 134 |
115 /** | 135 /** |
116 * Reports about deletion of a dynamic code entry. | 136 * Reports about deletion of a dynamic code entry. |
117 * | 137 * |
118 * @param {number} start Starting address. | 138 * @param {number} start Starting address. |
119 */ | 139 */ |
120 devtools.profiler.Profile.prototype.deleteCode = function(start) { | 140 devtools.profiler.Profile.prototype.deleteCode = function(start) { |
121 try { | 141 try { |
122 this.codeMap_.deleteCode(start); | 142 this.codeMap_.deleteCode(start); |
123 } catch (e) { | 143 } catch (e) { |
124 this.handleUnknownCode('delete', start); | 144 this.handleUnknownCode(devtools.profiler.Profile.Operation.DELETE, start); |
125 } | 145 } |
126 }; | 146 }; |
127 | 147 |
128 | 148 |
129 /** | 149 /** |
130 * Records a tick event. Stack must contain a sequence of | 150 * Records a tick event. Stack must contain a sequence of |
131 * addresses starting with the program counter value. | 151 * addresses starting with the program counter value. |
132 * | 152 * |
133 * @param {Array<number>} stack Stack sample. | 153 * @param {Array<number>} stack Stack sample. |
134 */ | 154 */ |
(...skipping 14 matching lines...) Expand all Loading... |
149 devtools.profiler.Profile.prototype.resolveAndFilterFuncs_ = function(stack) { | 169 devtools.profiler.Profile.prototype.resolveAndFilterFuncs_ = function(stack) { |
150 var result = []; | 170 var result = []; |
151 for (var i = 0; i < stack.length; ++i) { | 171 for (var i = 0; i < stack.length; ++i) { |
152 var entry = this.codeMap_.findEntry(stack[i]); | 172 var entry = this.codeMap_.findEntry(stack[i]); |
153 if (entry) { | 173 if (entry) { |
154 var name = entry.getName(); | 174 var name = entry.getName(); |
155 if (!this.skipThisFunction(name)) { | 175 if (!this.skipThisFunction(name)) { |
156 result.push(name); | 176 result.push(name); |
157 } | 177 } |
158 } else { | 178 } else { |
159 this.handleUnknownCode('tick', stack[i]); | 179 this.handleUnknownCode( |
| 180 devtools.profiler.Profile.Operation.TICK, stack[i], i); |
160 } | 181 } |
161 } | 182 } |
162 return result; | 183 return result; |
163 }; | 184 }; |
164 | 185 |
165 | 186 |
166 /** | 187 /** |
167 * Returns the root of the top down call graph. | 188 * Returns the root of the top down call graph. |
168 */ | 189 */ |
169 devtools.profiler.Profile.prototype.getTopDownTreeRoot = function() { | 190 devtools.profiler.Profile.prototype.getTopDownTreeRoot = function() { |
170 this.topDownTree_.computeTotalWeights(); | 191 this.topDownTree_.computeTotalWeights(); |
171 return this.topDownTree_.root_; | 192 return this.topDownTree_.getRoot(); |
172 }; | 193 }; |
173 | 194 |
174 | 195 |
175 /** | 196 /** |
176 * Returns the root of the bottom up call graph. | 197 * Returns the root of the bottom up call graph. |
177 */ | 198 */ |
178 devtools.profiler.Profile.prototype.getBottomUpTreeRoot = function() { | 199 devtools.profiler.Profile.prototype.getBottomUpTreeRoot = function() { |
179 this.bottomUpTree_.computeTotalWeights(); | 200 this.bottomUpTree_.computeTotalWeights(); |
180 return this.bottomUpTree_.root_; | 201 return this.bottomUpTree_.getRoot(); |
181 }; | 202 }; |
182 | 203 |
183 | 204 |
184 /** | 205 /** |
185 * Traverses the top down call graph in preorder. | 206 * Traverses the top down call graph in preorder. |
186 * | 207 * |
187 * @param {function(devtools.profiler.CallTree.Node)} f Visitor function. | 208 * @param {function(devtools.profiler.CallTree.Node)} f Visitor function. |
188 */ | 209 */ |
189 devtools.profiler.Profile.prototype.traverseTopDownTree = function(f) { | 210 devtools.profiler.Profile.prototype.traverseTopDownTree = function(f) { |
190 this.topDownTree_.traverse(f); | 211 this.topDownTree_.traverse(f); |
191 }; | 212 }; |
192 | 213 |
193 | 214 |
194 /** | 215 /** |
195 * Traverses the bottom up call graph in preorder. | 216 * Traverses the bottom up call graph in preorder. |
196 * | 217 * |
197 * @param {function(devtools.profiler.CallTree.Node)} f Visitor function. | 218 * @param {function(devtools.profiler.CallTree.Node)} f Visitor function. |
198 */ | 219 */ |
199 devtools.profiler.Profile.prototype.traverseBottomUpTree = function(f) { | 220 devtools.profiler.Profile.prototype.traverseBottomUpTree = function(f) { |
200 this.bottomUpTree_.traverse(f); | 221 this.bottomUpTree_.traverse(f); |
201 }; | 222 }; |
202 | 223 |
203 | 224 |
204 /** | 225 /** |
| 226 * Calculates a top down profile starting from the specified node. |
| 227 * |
| 228 * @param {devtools.profiler.CallTree.Node} opt_root Starting node. |
| 229 */ |
| 230 devtools.profiler.Profile.prototype.getTopDownProfile = function(opt_root) { |
| 231 if (!opt_root) { |
| 232 this.topDownTree_.computeTotalWeights(); |
| 233 return this.topDownTree_; |
| 234 } else { |
| 235 throw Error('not implemented'); |
| 236 } |
| 237 }; |
| 238 |
| 239 |
| 240 /** |
| 241 * Calculates a bottom up profile starting from the specified node. |
| 242 * |
| 243 * @param {devtools.profiler.CallTree.Node} opt_root Starting node. |
| 244 */ |
| 245 devtools.profiler.Profile.prototype.getBottomUpProfile = function(opt_root) { |
| 246 if (!opt_root) { |
| 247 this.bottomUpTree_.computeTotalWeights(); |
| 248 return this.bottomUpTree_; |
| 249 } else { |
| 250 throw Error('not implemented'); |
| 251 } |
| 252 }; |
| 253 |
| 254 |
| 255 /** |
205 * Calculates a flat profile of callees starting from the specified node. | 256 * Calculates a flat profile of callees starting from the specified node. |
206 * | 257 * |
207 * @param {devtools.profiler.CallTree.Node} opt_root Starting node. | 258 * @param {devtools.profiler.CallTree.Node} opt_root Starting node. |
208 */ | 259 */ |
209 devtools.profiler.Profile.prototype.getFlatProfile = function(opt_root) { | 260 devtools.profiler.Profile.prototype.getFlatProfile = function(opt_root) { |
210 var counters = new devtools.profiler.CallTree.Node(''); | 261 var counters = new devtools.profiler.CallTree(); |
211 var precs = {}; | 262 var precs = {}; |
212 this.topDownTree_.computeTotalWeights(); | 263 this.topDownTree_.computeTotalWeights(); |
213 this.topDownTree_.traverseInDepth( | 264 this.topDownTree_.traverseInDepth( |
214 function onEnter(node) { | 265 function onEnter(node) { |
215 if (!(node.label in precs)) { | 266 if (!(node.label in precs)) { |
216 precs[node.label] = 0; | 267 precs[node.label] = 0; |
217 } | 268 } |
218 var rec = counters.findOrAddChild(node.label); | 269 var rec = counters.findOrAddChild(node.label); |
219 rec.selfWeight += node.selfWeight; | 270 rec.selfWeight += node.selfWeight; |
220 if (precs[node.label] == 0) { | 271 if (precs[node.label] == 0) { |
221 rec.totalWeight += node.totalWeight; | 272 rec.totalWeight += node.totalWeight; |
222 } | 273 } |
223 precs[node.label]++; | 274 precs[node.label]++; |
224 }, | 275 }, |
225 function onExit(node) { | 276 function onExit(node) { |
226 precs[node.label]--; | 277 precs[node.label]--; |
227 }, | 278 }, |
228 opt_root); | 279 opt_root); |
229 return counters.exportChildren(); | 280 return counters; |
230 }; | 281 }; |
231 | 282 |
232 | 283 |
233 /** | 284 /** |
234 * Creates a dynamic code entry. | 285 * Creates a dynamic code entry. |
235 * | 286 * |
236 * @param {number} size Code size. | 287 * @param {number} size Code size. |
237 * @param {string} type Code type. | 288 * @param {string} type Code type. |
238 * @param {string} name Function name. | 289 * @param {string} name Function name. |
239 * @constructor | 290 * @constructor |
(...skipping 29 matching lines...) Expand all Loading... |
269 }; | 320 }; |
270 | 321 |
271 | 322 |
272 /** | 323 /** |
273 * @private | 324 * @private |
274 */ | 325 */ |
275 devtools.profiler.CallTree.prototype.totalsComputed_ = false; | 326 devtools.profiler.CallTree.prototype.totalsComputed_ = false; |
276 | 327 |
277 | 328 |
278 /** | 329 /** |
| 330 * Returns the tree root. |
| 331 */ |
| 332 devtools.profiler.CallTree.prototype.getRoot = function() { |
| 333 return this.root_; |
| 334 }; |
| 335 |
| 336 |
| 337 /** |
279 * Adds the specified call path, constructing nodes as necessary. | 338 * Adds the specified call path, constructing nodes as necessary. |
280 * | 339 * |
281 * @param {Array<string>} path Call path. | 340 * @param {Array<string>} path Call path. |
282 */ | 341 */ |
283 devtools.profiler.CallTree.prototype.addPath = function(path) { | 342 devtools.profiler.CallTree.prototype.addPath = function(path) { |
284 if (path.length == 0) { | 343 if (path.length == 0) { |
285 return; | 344 return; |
286 } | 345 } |
287 var curr = this.root_; | 346 var curr = this.root_; |
288 for (var i = 0; i < path.length; ++i) { | 347 for (var i = 0; i < path.length; ++i) { |
289 curr = curr.findOrAddChild(path[i]); | 348 curr = curr.findOrAddChild(path[i]); |
290 } | 349 } |
291 curr.selfWeight++; | 350 curr.selfWeight++; |
292 this.totalsComputed_ = false; | 351 this.totalsComputed_ = false; |
293 }; | 352 }; |
294 | 353 |
295 | 354 |
296 /** | 355 /** |
| 356 * Finds an immediate child of the specified parent with the specified |
| 357 * label, creates a child node if necessary. If a parent node isn't |
| 358 * specified, uses tree root. |
| 359 * |
| 360 * @param {string} label Child node label. |
| 361 */ |
| 362 devtools.profiler.CallTree.prototype.findOrAddChild = function( |
| 363 label, opt_parent) { |
| 364 var parent = opt_parent || this.root_; |
| 365 return parent.findOrAddChild(label); |
| 366 }; |
| 367 |
| 368 |
| 369 /** |
297 * Computes total weights in the call graph. | 370 * Computes total weights in the call graph. |
298 */ | 371 */ |
299 devtools.profiler.CallTree.prototype.computeTotalWeights = function() { | 372 devtools.profiler.CallTree.prototype.computeTotalWeights = function() { |
300 if (this.totalsComputed_) { | 373 if (this.totalsComputed_) { |
301 return; | 374 return; |
302 } | 375 } |
303 this.root_.computeTotalWeight(); | 376 this.root_.computeTotalWeight(); |
304 this.totalsComputed_ = true; | 377 this.totalsComputed_ = true; |
305 }; | 378 }; |
306 | 379 |
307 | 380 |
308 /** | 381 /** |
309 * Traverses the call graph in preorder. | 382 * Traverses the call graph in preorder. This function can be used for |
| 383 * building optionally modified tree clones. This is the boilerplate code |
| 384 * for this scenario: |
310 * | 385 * |
311 * @param {function(devtools.profiler.CallTree.Node)} f Visitor function. | 386 * callTree.traverse(function(node, parentClone) { |
| 387 * var nodeClone = cloneNode(node); |
| 388 * if (parentClone) |
| 389 * parentClone.addChild(nodeClone); |
| 390 * return nodeClone; |
| 391 * }); |
| 392 * |
| 393 * @param {function(devtools.profiler.CallTree.Node, *)} f Visitor function. |
| 394 * The second parameter is the result of calling 'f' on the parent node. |
312 * @param {devtools.profiler.CallTree.Node} opt_start Starting node. | 395 * @param {devtools.profiler.CallTree.Node} opt_start Starting node. |
313 */ | 396 */ |
314 devtools.profiler.CallTree.prototype.traverse = function(f, opt_start) { | 397 devtools.profiler.CallTree.prototype.traverse = function(f, opt_start) { |
315 var nodesToVisit = [opt_start || this.root_]; | 398 var pairsToProcess = [{node: opt_start || this.root_, param: null}]; |
316 while (nodesToVisit.length > 0) { | 399 while (pairsToProcess.length > 0) { |
317 var node = nodesToVisit.shift(); | 400 var pair = pairsToProcess.shift(); |
318 f(node); | 401 var node = pair.node; |
319 nodesToVisit = nodesToVisit.concat(node.exportChildren()); | 402 var newParam = f(node, pair.param); |
| 403 node.forEachChild( |
| 404 function (child) { pairsToProcess.push({node: child, param: newParam}); } |
| 405 ); |
320 } | 406 } |
321 }; | 407 }; |
322 | 408 |
323 | 409 |
324 /** | 410 /** |
325 * Performs an indepth call graph traversal. | 411 * Performs an indepth call graph traversal. |
326 * | 412 * |
327 * @param {function(devtools.profiler.CallTree.Node)} enter A function called | 413 * @param {function(devtools.profiler.CallTree.Node)} enter A function called |
328 * prior to visiting node's children. | 414 * prior to visiting node's children. |
329 * @param {function(devtools.profiler.CallTree.Node)} exit A function called | 415 * @param {function(devtools.profiler.CallTree.Node)} exit A function called |
(...skipping 129 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
459 labels, opt_f) { | 545 labels, opt_f) { |
460 for (var pos = 0, curr = this; pos < labels.length && curr != null; pos++) { | 546 for (var pos = 0, curr = this; pos < labels.length && curr != null; pos++) { |
461 var child = curr.findChild(labels[pos]); | 547 var child = curr.findChild(labels[pos]); |
462 if (opt_f) { | 548 if (opt_f) { |
463 opt_f(child, pos); | 549 opt_f(child, pos); |
464 } | 550 } |
465 curr = child; | 551 curr = child; |
466 } | 552 } |
467 return curr; | 553 return curr; |
468 }; | 554 }; |
OLD | NEW |