OLD | NEW |
(Empty) | |
| 1 /* |
| 2 * Copyright 2014 The Chromium Authors. All rights reserved. |
| 3 * Use of this source code is governed by a BSD-style license that can be |
| 4 * found in the LICENSE file. |
| 5 */ |
| 6 |
| 7 /** |
| 8 * @constructor |
| 9 * @implements {WebInspector.TimelineModeView} |
| 10 * @implements {WebInspector.FlameChartDelegate} |
| 11 * @extends {WebInspector.VBox} |
| 12 * @param {!WebInspector.TimelineModeViewDelegate} delegate |
| 13 */ |
| 14 WebInspector.TimelineTracingView = function(delegate) |
| 15 { |
| 16 WebInspector.VBox.call(this); |
| 17 this._delegate = delegate; |
| 18 this._tracingModel = new WebInspector.TracingModel(); |
| 19 this.element.classList.add("timeline-flamechart"); |
| 20 this.registerRequiredCSS("flameChart.css"); |
| 21 this._delegate = delegate; |
| 22 this._dataProvider = new WebInspector.TraceViewFlameChartDataProvider(this._
tracingModel); |
| 23 this._mainView = new WebInspector.FlameChart(this._dataProvider, this, true,
true); |
| 24 this._mainView.show(this.element); |
| 25 this._mainView.addEventListener(WebInspector.FlameChart.Events.EntrySelected
, this._onEntrySelected, this); |
| 26 } |
| 27 |
| 28 WebInspector.TimelineTracingView.prototype = { |
| 29 timelineStarted: function() |
| 30 { |
| 31 if (this._recordingTrace) |
| 32 return; |
| 33 if (!this._boundTraceEventListener) |
| 34 this._boundTraceEventListener = this._onTraceEventsCollected.bind(th
is); |
| 35 this._recordingTrace = true; |
| 36 WebInspector.tracingAgent.addEventListener(WebInspector.TracingAgent.Eve
nts.EventsCollected, this._boundTraceEventListener); |
| 37 this._tracingModel.reset(); |
| 38 WebInspector.tracingAgent.start("", ""); |
| 39 }, |
| 40 |
| 41 timelineStopped: function() |
| 42 { |
| 43 if (!this._recordingTrace) |
| 44 return; |
| 45 |
| 46 /** |
| 47 * @this {WebInspector.TimelineTracingView} |
| 48 */ |
| 49 function onTraceDataComplete() |
| 50 { |
| 51 WebInspector.tracingAgent.removeEventListener(WebInspector.TracingAg
ent.Events.EventsCollected, this._boundTraceEventListener); |
| 52 this.refreshRecords(null); |
| 53 } |
| 54 WebInspector.tracingAgent.stop(onTraceDataComplete.bind(this)); |
| 55 this._recordingTrace = false; |
| 56 }, |
| 57 |
| 58 /** |
| 59 * @param {!WebInspector.Event} event |
| 60 */ |
| 61 _onTraceEventsCollected: function(event) |
| 62 { |
| 63 var events = /** @type {!Array.<!WebInspector.TracingAgent.Event>} */ (e
vent.data); |
| 64 this._tracingModel.addEvents(events); |
| 65 }, |
| 66 |
| 67 /** |
| 68 * @param {number} windowStartTime |
| 69 * @param {number} windowEndTime |
| 70 */ |
| 71 requestWindowTimes: function(windowStartTime, windowEndTime) |
| 72 { |
| 73 this._delegate.requestWindowTimes(windowStartTime, windowEndTime); |
| 74 }, |
| 75 |
| 76 wasShown: function() |
| 77 { |
| 78 this._mainView._scheduleUpdate(); |
| 79 }, |
| 80 |
| 81 reset: function() |
| 82 { |
| 83 this._dataProvider.reset(); |
| 84 this._mainView.setWindowTimes(0, Infinity); |
| 85 }, |
| 86 |
| 87 /** |
| 88 * @param {?RegExp} textFilter |
| 89 */ |
| 90 refreshRecords: function(textFilter) |
| 91 { |
| 92 this._dataProvider.reset(); |
| 93 this._mainView._scheduleUpdate(); |
| 94 }, |
| 95 |
| 96 /** |
| 97 * @param {!WebInspector.TimelineModel.Record} record |
| 98 */ |
| 99 addRecord: function(record) {}, |
| 100 |
| 101 /** |
| 102 * @param {?WebInspector.TimelineModel.Record} record |
| 103 * @param {string=} regex |
| 104 * @param {boolean=} selectRecord |
| 105 */ |
| 106 highlightSearchResult: function(record, regex, selectRecord) {}, |
| 107 |
| 108 /** |
| 109 * @param {number} startTime |
| 110 * @param {number} endTime |
| 111 */ |
| 112 setWindowTimes: function(startTime, endTime) |
| 113 { |
| 114 this._mainView.setWindowTimes(startTime, endTime); |
| 115 }, |
| 116 |
| 117 /** |
| 118 * @param {number} width |
| 119 */ |
| 120 setSidebarSize: function(width) {}, |
| 121 |
| 122 /** |
| 123 * @param {?WebInspector.TimelineModel.Record} record |
| 124 */ |
| 125 setSelectedRecord: function(record) {}, |
| 126 |
| 127 /** |
| 128 * @param {!WebInspector.Event} event |
| 129 */ |
| 130 _onEntrySelected: function(event) |
| 131 { |
| 132 }, |
| 133 |
| 134 __proto__: WebInspector.VBox.prototype |
| 135 }; |
| 136 |
| 137 /** |
| 138 * @constructor |
| 139 * @implements {WebInspector.FlameChartDataProvider} |
| 140 * @param {!WebInspector.TracingModel} model |
| 141 */ |
| 142 WebInspector.TraceViewFlameChartDataProvider = function(model) |
| 143 { |
| 144 WebInspector.FlameChartDataProvider.call(this); |
| 145 this._model = model; |
| 146 this._font = "bold 12px " + WebInspector.fontFamily(); |
| 147 this._palette = new WebInspector.TraceViewPalette(); |
| 148 var dummyEventPayload = { |
| 149 cat: "dummy", |
| 150 pid: 0, |
| 151 tid: 0, |
| 152 ts: 0, |
| 153 ph: "dummy", |
| 154 name: "dummy", |
| 155 args: {}, |
| 156 dur: 0, |
| 157 id: 0, |
| 158 s: "" |
| 159 } |
| 160 this._processHeaderRecord = new WebInspector.TracingModel.Event(dummyEventPa
yload, 0); |
| 161 this._threadHeaderRecord = new WebInspector.TracingModel.Event(dummyEventPay
load, 0); |
| 162 } |
| 163 |
| 164 WebInspector.TraceViewFlameChartDataProvider.prototype = { |
| 165 /** |
| 166 * @return {number} |
| 167 */ |
| 168 barHeight: function() |
| 169 { |
| 170 return 20; |
| 171 }, |
| 172 |
| 173 /** |
| 174 * @return {number} |
| 175 */ |
| 176 textBaseline: function() |
| 177 { |
| 178 return 6; |
| 179 }, |
| 180 |
| 181 /** |
| 182 * @return {number} |
| 183 */ |
| 184 textPadding: function() |
| 185 { |
| 186 return 5; |
| 187 }, |
| 188 |
| 189 /** |
| 190 * @param {number} entryIndex |
| 191 * @return {string} |
| 192 */ |
| 193 entryFont: function(entryIndex) |
| 194 { |
| 195 return this._font; |
| 196 }, |
| 197 |
| 198 /** |
| 199 * @param {number} entryIndex |
| 200 * @return {?string} |
| 201 */ |
| 202 entryTitle: function(entryIndex) |
| 203 { |
| 204 var record = this._records[entryIndex]; |
| 205 if (record === this._threadHeaderRecord || record === this._processHeade
rRecord) |
| 206 return this._headerTitles[entryIndex] |
| 207 return record.name; |
| 208 }, |
| 209 |
| 210 /** |
| 211 * @param {number} startTime |
| 212 * @param {number} endTime |
| 213 * @return {?Array.<number>} |
| 214 */ |
| 215 dividerOffsets: function(startTime, endTime) |
| 216 { |
| 217 return null; |
| 218 }, |
| 219 |
| 220 reset: function() |
| 221 { |
| 222 this._timelineData = null; |
| 223 /** @type {!Array.<!WebInspector.TracingModel.Event>} */ |
| 224 this._records = []; |
| 225 }, |
| 226 |
| 227 /** |
| 228 * @return {!WebInspector.FlameChart.TimelineData} |
| 229 */ |
| 230 timelineData: function() |
| 231 { |
| 232 if (this._timelineData) |
| 233 return this._timelineData; |
| 234 |
| 235 /** |
| 236 * @type {?WebInspector.FlameChart.TimelineData} |
| 237 */ |
| 238 this._timelineData = { |
| 239 entryLevels: [], |
| 240 entryTotalTimes: [], |
| 241 entryOffsets: [] |
| 242 }; |
| 243 |
| 244 this._currentLevel = 0; |
| 245 this._headerTitles = {}; |
| 246 this._zeroTime = this._model.minimumRecordTime() || 0; |
| 247 this._timeSpan = Math.max((this._model.maximumRecordTime() || 0) - this.
_zeroTime, 1000000); |
| 248 var processes = this._model.sortedProcesses(); |
| 249 for (var i = 0; i < processes.length; ++i) { |
| 250 this._appendHeaderRecord(processes[i].name(), this._processHeaderRec
ord); |
| 251 var threads = processes[i].sortedThreads(); |
| 252 for (var j = 0; j < threads.length; ++j) { |
| 253 this._appendHeaderRecord(threads[j].name(), this._threadHeaderRe
cord); |
| 254 var events = threads[j].events(); |
| 255 for (var k = 0; k < events.length; ++k) { |
| 256 if (events[k].duration) |
| 257 this._appendRecord(events[k]); |
| 258 } |
| 259 this._currentLevel += threads[j].maxStackDepth(); |
| 260 } |
| 261 ++this._currentLevel; |
| 262 } |
| 263 return this._timelineData; |
| 264 }, |
| 265 |
| 266 /** |
| 267 * @return {number} |
| 268 */ |
| 269 zeroTime: function() |
| 270 { |
| 271 return this._toTimelineTime(this._zeroTime); |
| 272 }, |
| 273 |
| 274 /** |
| 275 * @return {number} |
| 276 */ |
| 277 totalTime: function() |
| 278 { |
| 279 return this._toTimelineTime(this._timeSpan); |
| 280 }, |
| 281 |
| 282 /** |
| 283 * @return {number} |
| 284 */ |
| 285 maxStackDepth: function() |
| 286 { |
| 287 return this._currentLevel; |
| 288 }, |
| 289 |
| 290 /** |
| 291 * @param {number} entryIndex |
| 292 * @return {?Array.<!{title: string, text: string}>} |
| 293 */ |
| 294 prepareHighlightedEntryInfo: function(entryIndex) |
| 295 { |
| 296 return null; |
| 297 }, |
| 298 |
| 299 /** |
| 300 * @param {number} entryIndex |
| 301 * @return {boolean} |
| 302 */ |
| 303 canJumpToEntry: function(entryIndex) |
| 304 { |
| 305 return false; |
| 306 }, |
| 307 |
| 308 /** |
| 309 * @param {number} entryIndex |
| 310 * @return {!string} |
| 311 */ |
| 312 entryColor: function(entryIndex) |
| 313 { |
| 314 var record = this._records[entryIndex]; |
| 315 if (record === this._processHeaderRecord) |
| 316 return "#555"; |
| 317 if (record === this._threadHeaderRecord) |
| 318 return "#777"; |
| 319 return this._palette.colorForString(record.name); |
| 320 }, |
| 321 |
| 322 |
| 323 /** |
| 324 * @param {number} entryIndex |
| 325 * @param {!CanvasRenderingContext2D} context |
| 326 * @param {?string} text |
| 327 * @param {number} barX |
| 328 * @param {number} barY |
| 329 * @param {number} barWidth |
| 330 * @param {number} barHeight |
| 331 * @param {function(number):number} offsetToPosition |
| 332 * @return {boolean} |
| 333 */ |
| 334 decorateEntry: function(entryIndex, context, text, barX, barY, barWidth, bar
Height, offsetToPosition) |
| 335 { |
| 336 return false; |
| 337 }, |
| 338 |
| 339 /** |
| 340 * @param {number} entryIndex |
| 341 * @return {boolean} |
| 342 */ |
| 343 forceDecoration: function(entryIndex) |
| 344 { |
| 345 return false; |
| 346 }, |
| 347 |
| 348 /** |
| 349 * @param {number} entryIndex |
| 350 * @return {?{startTimeOffset: number, endTimeOffset: number}} |
| 351 */ |
| 352 highlightTimeRange: function(entryIndex) |
| 353 { |
| 354 return null; |
| 355 }, |
| 356 |
| 357 /** |
| 358 * @return {number} |
| 359 */ |
| 360 paddingLeft: function() |
| 361 { |
| 362 return 0; |
| 363 }, |
| 364 |
| 365 /** |
| 366 * @param {number} entryIndex |
| 367 * @return {!string} |
| 368 */ |
| 369 textColor: function(entryIndex) |
| 370 { |
| 371 return "white"; |
| 372 }, |
| 373 |
| 374 /** |
| 375 * @param {string} title |
| 376 * @param {!WebInspector.TracingModel.Event} record |
| 377 */ |
| 378 _appendHeaderRecord: function(title, record) |
| 379 { |
| 380 var index = this._records.length; |
| 381 this._records.push(record); |
| 382 this._timelineData.entryLevels[index] = this._currentLevel++; |
| 383 this._timelineData.entryTotalTimes[index] = this.totalTime(); |
| 384 this._timelineData.entryOffsets[index] = this._toTimelineTime(0); |
| 385 this._headerTitles[index] = title; |
| 386 }, |
| 387 |
| 388 /** |
| 389 * @param {!WebInspector.TracingModel.Event} record |
| 390 */ |
| 391 _appendRecord: function(record) |
| 392 { |
| 393 var index = this._records.length; |
| 394 this._records.push(record); |
| 395 this._timelineData.entryLevels[index] = this._currentLevel + record.leve
l; |
| 396 this._timelineData.entryTotalTimes[index] = this._toTimelineTime(record.
duration); |
| 397 this._timelineData.entryOffsets[index] = this._toTimelineTime(record.sta
rtTime - this._zeroTime); |
| 398 }, |
| 399 |
| 400 /** |
| 401 * @param {number} time |
| 402 * @return {number} |
| 403 */ |
| 404 _toTimelineTime: function(time) |
| 405 { |
| 406 return time / 1000; |
| 407 } |
| 408 } |
| 409 |
| 410 // The below logic is shamelessly stolen from https://code.google.com/p/trace-vi
ewer/source/browse/trunk/trace_viewer/tracing/color_scheme.js |
| 411 |
| 412 /** |
| 413 * @constructor |
| 414 */ |
| 415 WebInspector.TraceViewPalette = function() |
| 416 { |
| 417 this._palette = WebInspector.TraceViewPalette._paletteBase.map(WebInspector.
TraceViewPalette._rgbToString); |
| 418 } |
| 419 |
| 420 WebInspector.TraceViewPalette._paletteBase = [ |
| 421 [138, 113, 152], |
| 422 [175, 112, 133], |
| 423 [127, 135, 225], |
| 424 [93, 81, 137], |
| 425 [116, 143, 119], |
| 426 [178, 214, 122], |
| 427 [87, 109, 147], |
| 428 [119, 155, 95], |
| 429 [114, 180, 160], |
| 430 [132, 85, 103], |
| 431 [157, 210, 150], |
| 432 [148, 94, 86], |
| 433 [164, 108, 138], |
| 434 [139, 191, 150], |
| 435 [110, 99, 145], |
| 436 [80, 129, 109], |
| 437 [125, 140, 149], |
| 438 [93, 124, 132], |
| 439 [140, 85, 140], |
| 440 [104, 163, 162], |
| 441 [132, 141, 178], |
| 442 [131, 105, 147], |
| 443 [135, 183, 98], |
| 444 [152, 134, 177], |
| 445 [141, 188, 141], |
| 446 [133, 160, 210], |
| 447 [126, 186, 148], |
| 448 [112, 198, 205], |
| 449 [180, 122, 195], |
| 450 [203, 144, 152] |
| 451 ]; |
| 452 |
| 453 /** |
| 454 * @param {string} string |
| 455 * @return {number} |
| 456 */ |
| 457 WebInspector.TraceViewPalette._stringHash = function(string) |
| 458 { |
| 459 var hash = 0; |
| 460 for (var i = 0; i < string.length; ++i) |
| 461 hash = (hash + 37 * hash + 11 * string.charCodeAt(i)) % 0xFFFFFFFF; |
| 462 return hash; |
| 463 } |
| 464 |
| 465 /** |
| 466 * @param {!Array.<number>} rgb |
| 467 * @return {string} |
| 468 */ |
| 469 WebInspector.TraceViewPalette._rgbToString = function(rgb) |
| 470 { |
| 471 return "rgb(" + rgb.join(",") + ")"; |
| 472 } |
| 473 |
| 474 WebInspector.TraceViewPalette.prototype = { |
| 475 /** |
| 476 * @param {string} string |
| 477 * @return {string} |
| 478 */ |
| 479 colorForString: function(string) |
| 480 { |
| 481 var hash = WebInspector.TraceViewPalette._stringHash(string); |
| 482 return this._palette[hash % this._palette.length]; |
| 483 } |
| 484 }; |
OLD | NEW |