Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 /* | 1 /* |
| 2 * Copyright (C) 2013 Google Inc. All rights reserved. | 2 * Copyright (C) 2013 Google Inc. All rights reserved. |
| 3 * | 3 * |
| 4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
| 5 * modification, are permitted provided that the following conditions are | 5 * modification, are permitted provided that the following conditions are |
| 6 * met: | 6 * met: |
| 7 * | 7 * |
| 8 * * Redistributions of source code must retain the above copyright | 8 * * Redistributions of source code must retain the above copyright |
| 9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
| 10 * * Redistributions in binary form must reproduce the above | 10 * * Redistributions in binary form must reproduce the above |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 29 */ | 29 */ |
| 30 | 30 |
| 31 /** | 31 /** |
| 32 * @constructor | 32 * @constructor |
| 33 * @extends {WebInspector.Object} | |
| 33 */ | 34 */ |
| 34 WebInspector.TracingAgent = function() | 35 WebInspector.TracingAgent = function() |
| 35 { | 36 { |
| 37 WebInspector.Object.call(this); | |
| 36 this._active = false; | 38 this._active = false; |
| 37 InspectorBackend.registerTracingDispatcher(new WebInspector.TracingDispatche r(this)); | 39 InspectorBackend.registerTracingDispatcher(new WebInspector.TracingDispatche r(this)); |
| 38 } | 40 } |
| 39 | 41 |
| 42 WebInspector.TracingAgent.Events = { | |
| 43 EventsCollected: "EventsCollected" | |
| 44 }; | |
| 45 | |
| 46 /** @typedef {!{ | |
| 47 cat: string, | |
| 48 pid: number, | |
| 49 tid: number, | |
| 50 ts: number, | |
| 51 ph: string, | |
| 52 name: string, | |
| 53 args: !Object, | |
| 54 dur: number, | |
| 55 id: number, | |
| 56 s: string | |
| 57 }} | |
| 58 */ | |
| 59 WebInspector.TracingAgent.Event; | |
| 60 | |
| 61 /** | |
| 62 * @enum {string} | |
| 63 */ | |
| 64 WebInspector.TracingAgent.Phase = { | |
| 65 Begin: "B", | |
| 66 End: "E", | |
| 67 Complete: "X", | |
| 68 Instant: "i", | |
| 69 AsyncBegin: "S", | |
| 70 AsyncStepInto: "T", | |
| 71 AsyncStepPast: "p", | |
| 72 AsyncEnd: "F", | |
| 73 FlowBegin: "s", | |
| 74 FlowStep: "t", | |
| 75 FlowEnd: "f", | |
| 76 Metadata: "M", | |
| 77 Counter: "C", | |
| 78 Sample: "P", | |
| 79 CreateObject: "N", | |
| 80 SnapshotObject: "O", | |
| 81 DeleteObject: "D" | |
| 82 }; | |
| 83 | |
| 84 WebInspector.TracingAgent.MetadataEvent = { | |
| 85 ProcessSortIndex: "process_sort_index", | |
| 86 ProcessName: "process_name", | |
| 87 ThreadSortIndex: "thread_sort_index", | |
| 88 ThreadName: "thread_name" | |
| 89 } | |
| 90 | |
| 40 WebInspector.TracingAgent.prototype = { | 91 WebInspector.TracingAgent.prototype = { |
| 41 /** | 92 /** |
| 42 * @param {string} categoryPatterns | 93 * @param {string} categoryPatterns |
| 43 * @param {string} options | 94 * @param {string} options |
| 44 * @param {function(?string)=} callback | 95 * @param {function(?string)=} callback |
| 45 */ | 96 */ |
| 46 start: function(categoryPatterns, options, callback) | 97 start: function(categoryPatterns, options, callback) |
| 47 { | 98 { |
| 48 TracingAgent.start(categoryPatterns, options, callback); | 99 TracingAgent.start(categoryPatterns, options, callback); |
| 49 this._active = true; | 100 this._active = true; |
| 50 this._events = []; | |
| 51 }, | 101 }, |
| 52 | 102 |
| 53 /** | 103 /** |
| 54 * @param {function()} callback | 104 * @param {function()} callback |
| 55 */ | 105 */ |
| 56 stop: function(callback) | 106 stop: function(callback) |
| 57 { | 107 { |
| 58 if (!this._active) { | 108 if (!this._active) { |
| 59 callback(); | 109 callback(); |
| 60 return; | 110 return; |
| 61 } | 111 } |
| 62 this._pendingStopCallback = callback; | 112 this._pendingStopCallback = callback; |
| 63 TracingAgent.end(); | 113 TracingAgent.end(); |
| 64 }, | 114 }, |
| 65 | 115 |
| 66 /** | |
| 67 * @return {!Array.<!{cat: string, args: !Object, ph: string, ts: number}>} | |
| 68 */ | |
| 69 events: function() | |
| 70 { | |
| 71 return this._events; | |
| 72 }, | |
| 73 | |
| 74 _eventsCollected: function(events) | 116 _eventsCollected: function(events) |
| 75 { | 117 { |
| 76 Array.prototype.push.apply(this._events, events); | 118 this.dispatchEventToListeners(WebInspector.TracingAgent.Events.EventsCol lected, events); |
| 77 }, | 119 }, |
| 78 | 120 |
| 79 _tracingComplete: function() | 121 _tracingComplete: function() |
| 80 { | 122 { |
| 81 this._active = false; | 123 this._active = false; |
| 82 if (this._pendingStopCallback) { | 124 if (!this._pendingStopCallback) |
| 83 this._pendingStopCallback(); | 125 return; |
| 84 this._pendingStopCallback = null; | 126 this._pendingStopCallback(); |
| 85 } | 127 this._pendingStopCallback = null; |
| 86 } | 128 }, |
| 129 | |
| 130 __proto__: WebInspector.Object.prototype | |
| 87 } | 131 } |
| 88 | 132 |
| 89 /** | 133 /** |
| 90 * @constructor | 134 * @constructor |
| 91 * @implements {TracingAgent.Dispatcher} | 135 * @implements {TracingAgent.Dispatcher} |
| 92 * @param {!WebInspector.TracingAgent} tracingAgent | 136 * @param {!WebInspector.TracingAgent} tracingAgent |
| 93 */ | 137 */ |
| 94 WebInspector.TracingDispatcher = function(tracingAgent) | 138 WebInspector.TracingDispatcher = function(tracingAgent) |
| 95 { | 139 { |
| 96 this._tracingAgent = tracingAgent; | 140 this._tracingAgent = tracingAgent; |
| 97 } | 141 } |
| 98 | 142 |
| 99 WebInspector.TracingDispatcher.prototype = { | 143 WebInspector.TracingDispatcher.prototype = { |
| 100 dataCollected: function(data) | 144 dataCollected: function(data) |
| 101 { | 145 { |
| 102 this._tracingAgent._eventsCollected(data); | 146 this._tracingAgent._eventsCollected(data); |
| 103 }, | 147 }, |
| 104 | 148 |
| 105 tracingComplete: function() | 149 tracingComplete: function() |
| 106 { | 150 { |
| 107 this._tracingAgent._tracingComplete(); | 151 this._tracingAgent._tracingComplete(); |
| 108 } | 152 } |
| 109 } | 153 } |
| 110 | 154 |
| 111 /** | 155 /** |
| 112 * @type {!WebInspector.TracingAgent} | 156 * @type {!WebInspector.TracingAgent} |
| 113 */ | 157 */ |
| 114 WebInspector.tracingAgent; | 158 WebInspector.tracingAgent; |
| 159 | |
| 160 /** | |
| 161 * @constructor | |
| 162 */ | |
| 163 WebInspector.TracingModel = function() | |
| 164 { | |
| 165 this.reset(); | |
| 166 } | |
| 167 | |
| 168 WebInspector.TracingModel.prototype = { | |
| 169 reset: function() | |
| 170 { | |
| 171 this._processById = {}; | |
| 172 this._minimumRecordTime = null; | |
| 173 this._maximumRecordTime = null; | |
| 174 }, | |
| 175 | |
| 176 /** | |
| 177 * @param {!Array.<!WebInspector.TracingAgent.Event>} payload | |
| 178 */ | |
| 179 addEvents: function(payload) | |
| 180 { | |
| 181 for (var i = 0; i < payload.length; ++i) | |
| 182 this.addEvent(payload[i]); | |
| 183 }, | |
| 184 | |
| 185 /** | |
| 186 * @param {!WebInspector.TracingAgent.Event} payload | |
| 187 */ | |
| 188 addEvent: function(payload) | |
| 189 { | |
| 190 var process = this._processById[payload.pid]; | |
| 191 if (!process) { | |
| 192 process = new WebInspector.TracingModel.Process(payload.pid); | |
| 193 this._processById[payload.pid] = process; | |
| 194 } | |
| 195 var thread = process.threadById(payload.tid); | |
| 196 if (payload.ph !== WebInspector.TracingAgent.Phase.Metadata) { | |
| 197 var timestamp = payload.ts; | |
| 198 // We do allow records for unrelated threads to arrive out-of-order, | |
| 199 // so there's a chance we're getting records from the past. | |
| 200 if (timestamp && (!this._minimumRecordTime || timestamp < this._mini mumRecordTime)) | |
| 201 this._minimumRecordTime = timestamp; | |
| 202 if (!this._maximumRecordTime || timestamp > this._maximumRecordTime) | |
| 203 this._maximumRecordTime = timestamp; | |
| 204 thread.addEvent(payload); | |
| 205 return; | |
| 206 } | |
| 207 switch (payload.name) { | |
| 208 case WebInspector.TracingAgent.MetadataEvent.ProcessSortIndex: | |
| 209 process._setSortIndex(payload.args["sort_index"]); | |
| 210 break; | |
| 211 case WebInspector.TracingAgent.MetadataEvent.ProcessName: | |
| 212 process._setName(payload.args["name"]); | |
| 213 break; | |
| 214 case WebInspector.TracingAgent.MetadataEvent.ThreadSortIndex: | |
| 215 thread._setSortIndex(payload.args["sort_index"]); | |
| 216 break; | |
| 217 case WebInspector.TracingAgent.MetadataEvent.ThreadName: | |
| 218 thread._setName(payload.args["name"]); | |
| 219 break; | |
| 220 } | |
| 221 }, | |
| 222 | |
| 223 /** | |
| 224 * @return {?number} | |
| 225 */ | |
| 226 minimumRecordTime: function() | |
| 227 { | |
| 228 return this._minimumRecordTime; | |
| 229 }, | |
| 230 | |
| 231 /** | |
| 232 * @return {?number} | |
| 233 */ | |
| 234 maximumRecordTime: function() | |
| 235 { | |
| 236 return this._maximumRecordTime; | |
| 237 }, | |
| 238 | |
| 239 /** | |
| 240 * @return {!Array.<!WebInspector.TracingModel.Process>} | |
| 241 */ | |
| 242 sortedProcesses: function() | |
| 243 { | |
| 244 return WebInspector.TracingModel.NamedObject._sort(Object.values(this._p rocessById)); | |
| 245 } | |
| 246 } | |
| 247 | |
| 248 /** | |
| 249 * @constructor | |
| 250 * @param {!WebInspector.TracingAgent.Event} payload | |
| 251 * @param {number} level | |
| 252 */ | |
| 253 WebInspector.TracingModel.Event = function(payload, level) | |
|
pfeldman
2014/03/26 20:10:06
Can we actually afford keeping both - payload and
caseq
2014/03/28 17:13:41
Dropped payload, we just shallow-copy properties o
| |
| 254 { | |
| 255 this._payload = payload; | |
| 256 this.level = level; | |
| 257 } | |
| 258 | |
| 259 WebInspector.TracingModel.Event.prototype = { | |
| 260 /** | |
| 261 * @return {string} | |
| 262 */ | |
| 263 get name() | |
|
pfeldman
2014/03/26 18:32:09
no getters please.
caseq
2014/03/28 17:13:41
Gone with the above change.
| |
| 264 { | |
| 265 return this._payload.name; | |
| 266 }, | |
| 267 | |
| 268 /** | |
| 269 * @return {number} | |
| 270 */ | |
| 271 get startTime() | |
| 272 { | |
| 273 return this._payload.ts; | |
| 274 }, | |
| 275 | |
| 276 /** | |
| 277 * @return {!Object} | |
| 278 */ | |
| 279 get args() | |
| 280 { | |
| 281 return this._payload.args; | |
| 282 }, | |
| 283 | |
| 284 /** | |
| 285 * @return {string} | |
| 286 */ | |
| 287 get phase() | |
| 288 { | |
| 289 return this._payload.ph; | |
| 290 }, | |
| 291 | |
| 292 /** | |
| 293 * @param {number} duration | |
| 294 */ | |
| 295 _setDuration: function(duration) | |
| 296 { | |
| 297 this.endTime = this.startTime + duration; | |
| 298 this.duration = duration; | |
| 299 }, | |
| 300 | |
| 301 /** | |
| 302 * @param {!WebInspector.TracingAgent.Event} payload | |
| 303 */ | |
| 304 _complete: function(payload) | |
| 305 { | |
| 306 if (this.name !== payload.name) { | |
| 307 console.assert(false, "Open/close event mismatch: " + JSON.stringify (this._payload) + " vs. " + JSON.stringify(payload)); | |
|
pfeldman
2014/03/27 13:09:53
It is fine to complete the event you did not start
caseq
2014/03/28 17:13:41
This check shouldn't normally hit in the case you
| |
| 308 return; | |
| 309 } | |
| 310 var duration = payload.ts - this.startTime; | |
| 311 if (duration < 0) { | |
| 312 console.assert(false, "Event out of order: " + this.name); | |
| 313 return; | |
| 314 } | |
| 315 this._setDuration(duration); | |
| 316 } | |
| 317 }; | |
| 318 | |
| 319 /** | |
| 320 * @constructor | |
| 321 */ | |
| 322 WebInspector.TracingModel.NamedObject = function() | |
| 323 { | |
| 324 } | |
| 325 | |
| 326 WebInspector.TracingModel.NamedObject.prototype = | |
| 327 { | |
| 328 /** | |
| 329 * @param {string} name | |
| 330 */ | |
| 331 _setName: function(name) | |
| 332 { | |
| 333 this._name = name; | |
| 334 }, | |
| 335 | |
| 336 /** | |
| 337 * @return {string} | |
| 338 */ | |
| 339 name: function() | |
| 340 { | |
| 341 return this._name; | |
| 342 }, | |
| 343 | |
| 344 /** | |
| 345 * @param {number} sortIndex | |
| 346 */ | |
| 347 _setSortIndex: function(sortIndex) | |
| 348 { | |
| 349 this._sortIndex = sortIndex; | |
| 350 }, | |
| 351 } | |
| 352 | |
| 353 /** | |
| 354 * @param {!Array.<!WebInspector.TracingModel.NamedObject>} array | |
| 355 */ | |
| 356 WebInspector.TracingModel.NamedObject._sort = function(array) | |
| 357 { | |
| 358 /** | |
| 359 * @param {!WebInspector.TracingModel.NamedObject} a | |
| 360 * @param {!WebInspector.TracingModel.NamedObject} b | |
| 361 */ | |
| 362 function comparator(a, b) | |
| 363 { | |
| 364 return a._sortIndex !== b._sortIndex ? a._sortIndex - b._sortIndex : a.n ame().localeCompare(b.name()); | |
| 365 } | |
| 366 return array.sort(comparator); | |
| 367 } | |
| 368 | |
| 369 /** | |
| 370 * @constructor | |
| 371 * @extends {WebInspector.TracingModel.NamedObject} | |
| 372 * @param {number} id | |
| 373 */ | |
| 374 WebInspector.TracingModel.Process = function(id) | |
| 375 { | |
| 376 WebInspector.TracingModel.NamedObject.call(this); | |
| 377 this._setName("Process " + id); | |
| 378 this._threads = {}; | |
| 379 } | |
| 380 | |
| 381 WebInspector.TracingModel.Process.prototype = { | |
| 382 /** | |
| 383 * @param {number} id | |
| 384 * @return {!WebInspector.TracingModel.Thread} | |
| 385 */ | |
| 386 threadById: function(id) | |
| 387 { | |
| 388 var thread = this._threads[id]; | |
| 389 if (!thread) { | |
| 390 thread = new WebInspector.TracingModel.Thread(id); | |
| 391 this._threads[id] = thread; | |
| 392 } | |
| 393 return thread; | |
| 394 }, | |
| 395 | |
| 396 /** | |
| 397 * @return {!Array.<!WebInspector.TracingModel.Thread>} | |
| 398 */ | |
| 399 sortedThreads: function() | |
| 400 { | |
| 401 return WebInspector.TracingModel.NamedObject._sort(Object.values(this._t hreads)); | |
| 402 }, | |
| 403 | |
| 404 __proto__: WebInspector.TracingModel.NamedObject.prototype | |
| 405 } | |
| 406 | |
| 407 /** | |
| 408 * @constructor | |
| 409 * @extends {WebInspector.TracingModel.NamedObject} | |
| 410 * @param {number} id | |
| 411 */ | |
| 412 WebInspector.TracingModel.Thread = function(id) | |
| 413 { | |
| 414 WebInspector.TracingModel.NamedObject.call(this); | |
| 415 this._setName("Thread " + id); | |
| 416 this._events = []; | |
| 417 this._stack = []; | |
| 418 this._maxStackDepth = 0; | |
| 419 } | |
| 420 | |
| 421 WebInspector.TracingModel.Thread.prototype = { | |
| 422 /** | |
| 423 * @param {!WebInspector.TracingAgent.Event} payload | |
| 424 */ | |
| 425 addEvent: function(payload) | |
| 426 { | |
| 427 for (var top = this._stack.peekLast(); top && top.endTime && top.endTime <= payload.ts;) { | |
| 428 this._stack.pop(); | |
| 429 top = this._stack.peekLast(); | |
| 430 } | |
| 431 if (payload.ph === WebInspector.TracingAgent.Phase.End) { | |
| 432 var openEvent = this._stack.pop(); | |
| 433 // Quietly ignore unbalanced close events, they're legit (we could h ave missed start one). | |
| 434 if (openEvent) | |
| 435 openEvent._complete(payload); | |
| 436 return; | |
| 437 } | |
| 438 | |
| 439 var event = new WebInspector.TracingModel.Event(payload, this._stack.len gth); | |
| 440 if (payload.ph === WebInspector.TracingAgent.Phase.Begin || payload.ph = == WebInspector.TracingAgent.Phase.Complete) { | |
| 441 if (payload.ph === WebInspector.TracingAgent.Phase.Complete) | |
| 442 event._setDuration(payload.dur); | |
| 443 this._stack.push(event); | |
|
pfeldman
2014/03/27 13:09:53
You should not put the complete event into the sta
caseq
2014/03/28 17:13:41
I should -- we wouldn't compute level properly oth
| |
| 444 if (this._maxStackDepth < this._stack.length) | |
| 445 this._maxStackDepth = this._stack.length; | |
| 446 } | |
| 447 if (this._events.length && this._events.peekLast().startTime > event.sta rtTime) | |
| 448 console.assert(false, "Event is our of order: " + event.name); | |
| 449 this._events.push(event); | |
| 450 }, | |
| 451 | |
| 452 /** | |
| 453 * @return {!Array.<!WebInspector.TracingModel.Event>} | |
| 454 */ | |
| 455 events: function() | |
| 456 { | |
| 457 return this._events; | |
| 458 }, | |
| 459 | |
| 460 /** | |
| 461 * @return {number} | |
| 462 */ | |
| 463 maxStackDepth: function() | |
| 464 { | |
| 465 // Reserve one for non-container events. | |
| 466 return this._maxStackDepth + 1; | |
| 467 }, | |
| 468 | |
| 469 __proto__: WebInspector.TracingModel.NamedObject.prototype | |
| 470 } | |
| OLD | NEW |