Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 | 5 |
| 6 /** | 6 /** |
| 7 * @fileoverview TimelineModel is a parsed representation of the | 7 * @fileoverview TimelineModel is a parsed representation of the |
| 8 * TraceEvents obtained from base/trace_event in which the begin-end | 8 * TraceEvents obtained from base/trace_event in which the begin-end |
| 9 * tokens are converted into a hierarchy of processes, threads, | 9 * tokens are converted into a hierarchy of processes, threads, |
| 10 * subrows, and slices. | 10 * subrows, and slices. |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 23 /** | 23 /** |
| 24 * A TimelineSlice represents an interval of time on a given thread | 24 * A TimelineSlice represents an interval of time on a given thread |
| 25 * associated with a specific trace event. For example, | 25 * associated with a specific trace event. For example, |
| 26 * TRACE_EVENT_BEGIN1("x","myArg", 7) at time=0.1ms | 26 * TRACE_EVENT_BEGIN1("x","myArg", 7) at time=0.1ms |
| 27 * TRACE_EVENT_END() at time=0.3ms | 27 * TRACE_EVENT_END() at time=0.3ms |
| 28 * Results in a single timeline slice from 0.1 with duration 0.2. | 28 * Results in a single timeline slice from 0.1 with duration 0.2. |
| 29 * | 29 * |
| 30 * All time units are stored in milliseconds. | 30 * All time units are stored in milliseconds. |
| 31 * @constructor | 31 * @constructor |
| 32 */ | 32 */ |
| 33 function TimelineSlice(title, colorId, start, args) { | 33 function TimelineSlice(title, colorId, start, args, opt_duration) { |
| 34 this.title = title; | 34 this.title = title; |
| 35 this.start = start; | 35 this.start = start; |
| 36 this.colorId = colorId; | 36 this.colorId = colorId; |
| 37 this.args = args; | 37 this.args = args; |
| 38 this.didNotFinish = false; | 38 this.didNotFinish = false; |
| 39 this.subSlices = []; | 39 this.subSlices = []; |
| 40 if (opt_duration !== undefined) | |
| 41 this.duration = opt_duration; | |
| 40 } | 42 } |
| 41 | 43 |
| 42 TimelineSlice.prototype = { | 44 TimelineSlice.prototype = { |
| 43 selected: false, | 45 selected: false, |
| 44 | 46 |
| 45 duration: undefined, | 47 duration: undefined, |
| 46 | 48 |
| 47 get end() { | 49 get end() { |
| 48 return this.start + this.duration; | 50 return this.start + this.duration; |
| 49 } | 51 } |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 105 values.push(slices[0].start); | 107 values.push(slices[0].start); |
| 106 values.push(slices[slices.length - 1].end); | 108 values.push(slices[slices.length - 1].end); |
| 107 } | 109 } |
| 108 if (values.length) { | 110 if (values.length) { |
| 109 this.minTimestamp = Math.min.apply(Math, values); | 111 this.minTimestamp = Math.min.apply(Math, values); |
| 110 this.maxTimestamp = Math.max.apply(Math, values); | 112 this.maxTimestamp = Math.max.apply(Math, values); |
| 111 } else { | 113 } else { |
| 112 this.minTimestamp = undefined; | 114 this.minTimestamp = undefined; |
| 113 this.maxTimestamp = undefined; | 115 this.maxTimestamp = undefined; |
| 114 } | 116 } |
| 117 }, | |
| 118 | |
| 119 /** | |
| 120 * @return {String} A user-friendly name for this thread. | |
| 121 */ | |
| 122 get userFriendlyName() { | |
| 123 var tname = this.name || this.tid; | |
| 124 return this.parent.pid + ': ' + tname; | |
| 125 }, | |
| 126 | |
| 127 /** | |
| 128 * @return {String} User friendly details about this thread. | |
| 129 */ | |
| 130 get userFriendlyDetials() { | |
| 131 return 'pid: ' + this.parent.pid + | |
| 132 ', tid: ' + this.tid + | |
| 133 (this.name ? ', name: ' + this.name : ''); | |
| 115 } | 134 } |
| 116 | 135 |
| 117 }; | 136 }; |
| 118 | 137 |
| 119 /** | 138 /** |
| 120 * Comparison between threads that orders first by pid, | 139 * Comparison between threads that orders first by pid, |
| 121 * then by names, then by tid. | 140 * then by names, then by tid. |
| 122 */ | 141 */ |
| 123 TimelineThread.compare = function(x, y) { | 142 TimelineThread.compare = function(x, y) { |
| 124 if (x.parent.pid != y.parent.pid) { | 143 if (x.parent.pid != y.parent.pid) { |
| 125 return x.parent.pid - y.parent.pid; | 144 return TimelineProcess.compare(x.parent, y.parent.pid); |
| 126 } | 145 } |
| 127 | 146 |
| 128 if (x.name && y.name) { | 147 if (x.name && y.name) { |
| 129 var tmp = x.name.localeCompare(y.name); | 148 var tmp = x.name.localeCompare(y.name); |
| 130 if (tmp == 0) | 149 if (tmp == 0) |
| 131 return x.tid - y.tid; | 150 return x.tid - y.tid; |
| 132 return tmp; | 151 return tmp; |
| 133 } else if (x.name) { | 152 } else if (x.name) { |
| 134 return -1; | 153 return -1; |
| 135 } else if (y.name) { | 154 } else if (y.name) { |
| 136 return 1; | 155 return 1; |
| 137 } else { | 156 } else { |
| 138 return x.tid - y.tid; | 157 return x.tid - y.tid; |
| 139 } | 158 } |
| 140 }; | 159 }; |
| 141 | 160 |
| 161 /** | |
| 162 * Stores all the samples for a given counter. | |
| 163 * @constructor | |
| 164 */ | |
| 165 function TimelineCounter(parent, name) { | |
| 166 this.parent = parent; | |
| 167 this.name = name; | |
| 168 this.seriesNames = []; | |
| 169 this.seriesColors = []; | |
| 170 this.timestamps = []; | |
| 171 this.samples = []; | |
| 172 } | |
| 173 | |
| 174 TimelineCounter.prototype = { | |
| 175 __proto__: Object.prototype, | |
| 176 | |
| 177 get numSeries() { | |
| 178 return this.seriesNames.length; | |
| 179 }, | |
| 180 | |
| 181 get numSamples() { | |
| 182 return this.timestamps.length; | |
| 183 }, | |
| 184 | |
| 185 /** | |
| 186 * Updates the bounds for this counter based on the samples it contains. | |
| 187 */ | |
| 188 updateBounds: function() { | |
| 189 if (this.seriesNames.length != this.seriesColors.length) | |
| 190 throw 'seriesNames.length must match seriesColors.length'; | |
| 191 if (this.numSeries * this.numSamples != this.samples.length) | |
| 192 throw 'samples.length must be a multiple of numSamples.'; | |
| 193 | |
| 194 this.totals = []; | |
| 195 if (this.samples.length == 0) { | |
| 196 this.minTimestamp = undefined; | |
| 197 this.maxTimestamp = undefined; | |
| 198 this.maxTotal = 0; | |
| 199 return; | |
| 200 } | |
| 201 this.minTimestamp = this.timestamps[0]; | |
| 202 this.maxTimestamp = this.timestamps[this.timestamps.length - 1]; | |
| 203 | |
| 204 var numSeries = this.numSeries; | |
| 205 var maxTotal = -Infinity; | |
| 206 for (var i = 0; i < this.timestamps.length; i++) { | |
| 207 var total = 0; | |
| 208 for (var j = 0; j < numSeries; j++) { | |
| 209 total += this.samples[i * numSeries + j]; | |
| 210 this.totals.push(total); | |
| 211 } | |
| 212 if (total > maxTotal) | |
| 213 maxTotal = total; | |
| 214 } | |
| 215 this.maxTotal = maxTotal; | |
| 216 } | |
| 217 | |
| 218 }; | |
| 219 | |
| 220 /** | |
| 221 * Comparison between counters that orders by pid, then name. | |
| 222 */ | |
| 223 TimelineCounter.compare = function(x, y) { | |
| 224 if (x.parent.pid != y.parent.pid) { | |
| 225 return TimelineProcess.compare(x.parent, y.parent.pid); | |
| 226 } | |
| 227 var tmp = x.name.localeCompare(y.name); | |
| 228 if (tmp == 0) | |
| 229 return x.tid - y.tid; | |
| 230 return tmp; | |
| 231 }; | |
| 142 | 232 |
| 143 /** | 233 /** |
| 144 * The TimelineProcess represents a single process in the | 234 * The TimelineProcess represents a single process in the |
| 145 * trace. Right now, we keep this around purely for bookkeeping | 235 * trace. Right now, we keep this around purely for bookkeeping |
| 146 * reasons. | 236 * reasons. |
| 147 * @constructor | 237 * @constructor |
| 148 */ | 238 */ |
| 149 function TimelineProcess(pid) { | 239 function TimelineProcess(pid) { |
| 150 this.pid = pid; | 240 this.pid = pid; |
| 151 this.threads = {}; | 241 this.threads = {}; |
| 242 this.counters = {}; | |
| 152 }; | 243 }; |
| 153 | 244 |
| 154 TimelineProcess.prototype = { | 245 TimelineProcess.prototype = { |
| 155 get numThreads() { | 246 get numThreads() { |
| 156 var n = 0; | 247 var n = 0; |
| 157 for (var p in this.threads) { | 248 for (var p in this.threads) { |
| 158 n++; | 249 n++; |
| 159 } | 250 } |
| 160 return n; | 251 return n; |
| 161 }, | 252 }, |
| 162 | 253 |
| 254 /** | |
| 255 * @return {TimlineThread} The thread identified by tid on this process, | |
| 256 * creating it if it doesn't exist. | |
| 257 */ | |
| 163 getOrCreateThread: function(tid) { | 258 getOrCreateThread: function(tid) { |
| 164 if (!this.threads[tid]) | 259 if (!this.threads[tid]) |
| 165 this.threads[tid] = new TimelineThread(this, tid); | 260 this.threads[tid] = new TimelineThread(this, tid); |
| 166 return this.threads[tid]; | 261 return this.threads[tid]; |
| 262 }, | |
| 263 | |
| 264 /** | |
| 265 * @return {TimlineCounter} The counter on this process named 'name', | |
| 266 * creating it if it doesn't exist. | |
| 267 */ | |
| 268 getOrCreateCounter: function(name) { | |
| 269 if (!this.counters[name]) | |
| 270 this.counters[name] = new TimelineCounter(this, name); | |
| 271 return this.counters[name]; | |
| 167 } | 272 } |
| 168 }; | 273 }; |
| 169 | 274 |
| 170 /** | 275 /** |
| 276 * Comparison between processes that orders by pid. | |
| 277 */ | |
| 278 TimelineProcess.compare = function(x, y) { | |
| 279 return x.pid - y.pid; | |
| 280 }; | |
| 281 | |
| 282 /** | |
| 171 * Computes a simplistic hashcode of the provide name. Used to chose colors | 283 * Computes a simplistic hashcode of the provide name. Used to chose colors |
| 172 * for slices. | 284 * for slices. |
| 173 * @param {string} name The string to hash. | 285 * @param {string} name The string to hash. |
| 174 */ | 286 */ |
| 175 function getStringHash(name) { | 287 function getStringHash(name) { |
| 176 var hash = 0; | 288 var hash = 0; |
| 177 for (var i = 0; i < name.length; ++i) | 289 for (var i = 0; i < name.length; ++i) |
| 178 hash = (hash + 37 * hash + 11 * name.charCodeAt(i)) % 0xFFFFFFFF; | 290 hash = (hash + 37 * hash + 11 * name.charCodeAt(i)) % 0xFFFFFFFF; |
| 179 return hash; | 291 return hash; |
| 180 } | 292 } |
| 181 | 293 |
| 182 /** | 294 /** |
| 295 * The number of color IDs that getStringColorId can choose from. | |
| 296 */ | |
| 297 const numColorIds = 30; | |
| 298 | |
| 299 /** | |
| 300 * @return {Number} A color ID that is stably associated to the provided via | |
| 301 * the getStringHash method. | |
| 302 */ | |
| 303 function getStringColorId(string) { | |
| 304 var hash = getStringHash(string); | |
| 305 return hash % numColorIds; | |
| 306 } | |
| 307 | |
| 308 /** | |
| 183 * Builds a model from an array of TraceEvent objects. | 309 * Builds a model from an array of TraceEvent objects. |
| 184 * @param {Array} events An array of TraceEvents created by | 310 * @param {Array} events An array of TraceEvents created by |
| 185 * TraceEvent.ToJSON(). | 311 * TraceEvent.ToJSON(). |
| 186 * @constructor | 312 * @constructor |
| 187 */ | 313 */ |
| 188 function TimelineModel(events) { | 314 function TimelineModel(events) { |
| 189 this.processes = {}; | 315 this.processes = {}; |
| 190 this.importErrors = []; | 316 this.importErrors = []; |
| 191 | 317 |
| 192 if (events) | 318 if (events) |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 211 | 337 |
| 212 /** | 338 /** |
| 213 * The import takes an array of json-ified TraceEvents and adds them into | 339 * The import takes an array of json-ified TraceEvents and adds them into |
| 214 * the TimelineModel as processes, threads, and slices. | 340 * the TimelineModel as processes, threads, and slices. |
| 215 */ | 341 */ |
| 216 importEvents: function(events) { | 342 importEvents: function(events) { |
| 217 // A ptid is a pid and tid joined together x:y fashion, eg 1024:130 | 343 // A ptid is a pid and tid joined together x:y fashion, eg 1024:130 |
| 218 // The ptid is a unique key for a thread in the trace. | 344 // The ptid is a unique key for a thread in the trace. |
| 219 this.importErrors = []; | 345 this.importErrors = []; |
| 220 | 346 |
| 221 // Threadstate | 347 // Threadstate. |
| 222 const numColorIds = 30; | |
| 223 function ThreadState(tid) { | 348 function ThreadState(tid) { |
| 224 this.openSlices = []; | 349 this.openSlices = []; |
| 225 this.openNonNestedSlices = {}; | 350 this.openNonNestedSlices = {}; |
| 226 } | 351 } |
| 227 var threadStateByPTID = {}; | 352 var threadStateByPTID = {}; |
| 228 | 353 |
| 229 var nameToColorMap = {}; | 354 var nameToColorMap = {}; |
| 230 function getColor(name) { | 355 function getColor(name) { |
| 231 if (!(name in nameToColorMap)) { | 356 if (!(name in nameToColorMap)) { |
| 232 var hash = getStringHash(name); | 357 nameToColorMap[name] = getStringColorId(name); |
| 233 nameToColorMap[name] = hash % numColorIds; | |
| 234 } | 358 } |
| 235 return nameToColorMap[name]; | 359 return nameToColorMap[name]; |
| 236 } | 360 } |
| 237 | 361 |
| 238 var self = this; | 362 var self = this; |
| 239 | 363 |
| 240 /** | 364 /** |
| 241 * Helper to process a 'begin' event (e.g. initiate a slice). | 365 * Helper to process a 'begin' event (e.g. initiate a slice). |
| 242 * @param {ThreadState} state Thread state (holds slices). | 366 * @param {ThreadState} state Thread state (holds slices). |
| 243 * @param {Object} event The current trace event. | 367 * @param {Object} event The current trace event. |
| (...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 321 | 445 |
| 322 if (event.ph == 'B') { | 446 if (event.ph == 'B') { |
| 323 processBegin(state, event); | 447 processBegin(state, event); |
| 324 } else if (event.ph == 'E') { | 448 } else if (event.ph == 'E') { |
| 325 processEnd(state, event); | 449 processEnd(state, event); |
| 326 } else if (event.ph == 'I') { | 450 } else if (event.ph == 'I') { |
| 327 // Treat an Instant event as a duration 0 slice. | 451 // Treat an Instant event as a duration 0 slice. |
| 328 // TimelineSliceTrack's redraw() knows how to handle this. | 452 // TimelineSliceTrack's redraw() knows how to handle this. |
| 329 processBegin(state, event); | 453 processBegin(state, event); |
| 330 processEnd(state, event); | 454 processEnd(state, event); |
| 455 } else if (event.ph == 'C') { | |
| 456 var ctr = this.getOrCreateProcess(event.pid) | |
| 457 .getOrCreateCounter(event.name); | |
|
jbates
2011/11/16 23:25:39
Do we care that ("cat1", "name1") is the same coun
nduca
2011/11/18 08:58:19
ooo great point.
On 2011/11/16 23:25:39, jbates wr
| |
| 458 // Initialize the counter's series fields if needed. | |
| 459 if (ctr.numSeries == 0) { | |
| 460 for (var seriesName in event.args) { | |
| 461 ctr.seriesNames.push(seriesName); | |
| 462 ctr.seriesColors.push(getStringHash(ctr.name + '.' + seriesName)); | |
| 463 } | |
| 464 if (ctr.numSeries == 0) { | |
| 465 this.importErrors.push('Expected counter ' + event.name + | |
| 466 ' to have at least one argument to use as a value.'); | |
| 467 // Drop the counter. | |
| 468 delete ctr.parent.counters[ctr.name]; | |
| 469 continue; | |
| 470 } | |
| 471 } | |
| 472 | |
| 473 // Add the sample values. | |
| 474 ctr.timestamps.push(event.ts); | |
| 475 for (var i = 0; i < ctr.numSeries; i++) { | |
| 476 var seriesName = ctr.seriesNames[i]; | |
| 477 if (event.args[seriesName] === undefined) { | |
| 478 ctr.samples.push(0); | |
| 479 continue; | |
| 480 } | |
| 481 ctr.samples.push(event.args[seriesName]); | |
| 482 } | |
| 483 | |
| 331 } else if (event.ph == 'M') { | 484 } else if (event.ph == 'M') { |
| 332 if (event.name == 'thread_name') { | 485 if (event.name == 'thread_name') { |
| 333 var thread = this.getOrCreateProcess(event.pid) | 486 var thread = this.getOrCreateProcess(event.pid) |
| 334 .getOrCreateThread(event.tid); | 487 .getOrCreateThread(event.tid); |
| 335 thread.name = event.args.name; | 488 thread.name = event.args.name; |
| 336 } else { | 489 } else { |
| 337 this.importErrors.push('Unrecognized metadata name: ' + event.name); | 490 this.importErrors.push('Unrecognized metadata name: ' + event.name); |
| 338 } | 491 } |
| 339 } else { | 492 } else { |
| 340 this.importErrors.push('Unrecognized event phase: ' + event.ph + | 493 this.importErrors.push('Unrecognized event phase: ' + event.ph + |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 398 var threads = this.getAllThreads(); | 551 var threads = this.getAllThreads(); |
| 399 for (var tI = 0; tI < threads.length; tI++) { | 552 for (var tI = 0; tI < threads.length; tI++) { |
| 400 var thread = threads[tI]; | 553 var thread = threads[tI]; |
| 401 thread.updateBounds(); | 554 thread.updateBounds(); |
| 402 if (thread.minTimestamp != undefined && | 555 if (thread.minTimestamp != undefined && |
| 403 thread.maxTimestamp != undefined) { | 556 thread.maxTimestamp != undefined) { |
| 404 wmin = Math.min(wmin, thread.minTimestamp); | 557 wmin = Math.min(wmin, thread.minTimestamp); |
| 405 wmax = Math.max(wmax, thread.maxTimestamp); | 558 wmax = Math.max(wmax, thread.maxTimestamp); |
| 406 } | 559 } |
| 407 } | 560 } |
| 561 var counters = this.getAllCounters(); | |
| 562 for (var tI = 0; tI < counters.length; tI++) { | |
| 563 var counter = counters[tI]; | |
| 564 counter.updateBounds(); | |
| 565 if (counter.minTimestamp != undefined && | |
| 566 counter.maxTimestamp != undefined) { | |
| 567 wmin = Math.min(wmin, counter.minTimestamp); | |
| 568 wmax = Math.max(wmax, counter.maxTimestamp); | |
| 569 } | |
| 570 } | |
| 408 this.minTimestamp = wmin; | 571 this.minTimestamp = wmin; |
| 409 this.maxTimestamp = wmax; | 572 this.maxTimestamp = wmax; |
| 410 }, | 573 }, |
| 411 | 574 |
| 412 shiftWorldToMicroseconds: function() { | 575 shiftWorldToMicroseconds: function() { |
| 413 var timeBase = this.minTimestamp; | 576 var timeBase = this.minTimestamp; |
| 414 var threads = this.getAllThreads(); | 577 var threads = this.getAllThreads(); |
| 415 for (var tI = 0; tI < threads.length; tI++) { | 578 for (var tI = 0; tI < threads.length; tI++) { |
| 416 var thread = threads[tI]; | 579 var thread = threads[tI]; |
| 417 var shiftSubRow = function(subRow) { | 580 var shiftSubRow = function(subRow) { |
| 418 for (var tS = 0; tS < subRow.length; tS++) { | 581 for (var tS = 0; tS < subRow.length; tS++) { |
| 419 var slice = subRow[tS]; | 582 var slice = subRow[tS]; |
| 420 slice.start = (slice.start - timeBase) / 1000; | 583 slice.start = (slice.start - timeBase) / 1000; |
| 421 slice.duration /= 1000; | 584 slice.duration /= 1000; |
| 422 if (slice.startInUserTime) | 585 if (slice.startInUserTime) |
| 423 slice.startInUserTime /= 1000; | 586 slice.startInUserTime /= 1000; |
| 424 if (slice.durationInUserTime) | 587 if (slice.durationInUserTime) |
| 425 slice.durationInUserTime /= 1000; | 588 slice.durationInUserTime /= 1000; |
| 426 } | 589 } |
| 427 }; | 590 }; |
| 428 for (var tSR = 0; tSR < thread.subRows.length; tSR++) { | 591 for (var tSR = 0; tSR < thread.subRows.length; tSR++) { |
| 429 shiftSubRow(thread.subRows[tSR]); | 592 shiftSubRow(thread.subRows[tSR]); |
| 430 } | 593 } |
| 431 for (var tSR = 0; tSR < thread.nonNestedSubRows.length; tSR++) { | 594 for (var tSR = 0; tSR < thread.nonNestedSubRows.length; tSR++) { |
| 432 shiftSubRow(thread.nonNestedSubRows[tSR]); | 595 shiftSubRow(thread.nonNestedSubRows[tSR]); |
| 433 } | 596 } |
| 434 } | 597 } |
| 435 | 598 var counters = this.getAllCounters(); |
| 599 for (var tI = 0; tI < counters.length; tI++) { | |
| 600 var counter = counters[tI]; | |
| 601 for (var sI = 0; sI < counter.timestamps.length; sI++) | |
| 602 counter.timestamps[sI] = (counter.timestamps[sI] - timeBase) / 1000; | |
| 603 } | |
| 436 this.updateBounds(); | 604 this.updateBounds(); |
| 437 }, | 605 }, |
| 438 | 606 |
| 439 getAllThreads: function() { | 607 getAllThreads: function() { |
| 440 var threads = []; | 608 var threads = []; |
| 441 for (var pid in this.processes) { | 609 for (var pid in this.processes) { |
| 442 var process = this.processes[pid]; | 610 var process = this.processes[pid]; |
| 443 for (var tid in process.threads) { | 611 for (var tid in process.threads) { |
| 444 threads.push(process.threads[tid]); | 612 threads.push(process.threads[tid]); |
| 445 } | 613 } |
| 446 } | 614 } |
| 447 return threads; | 615 return threads; |
| 616 }, | |
| 617 | |
| 618 /** | |
| 619 * @return {Array} An array of all the counters in the model. | |
| 620 */ | |
| 621 getAllCounters: function() { | |
| 622 var counters = []; | |
| 623 for (var pid in this.processes) { | |
| 624 var process = this.processes[pid]; | |
| 625 for (var tid in process.counters) { | |
| 626 counters.push(process.counters[tid]); | |
| 627 } | |
| 628 } | |
| 629 return counters; | |
| 448 } | 630 } |
| 449 | 631 |
| 450 }; | 632 }; |
| 451 | 633 |
| 452 return { | 634 return { |
| 453 getStringHash: getStringHash, | 635 getStringHash: getStringHash, |
| 636 getStringColorId: getStringColorId, | |
| 454 TimelineSlice: TimelineSlice, | 637 TimelineSlice: TimelineSlice, |
| 455 TimelineThread: TimelineThread, | 638 TimelineThread: TimelineThread, |
| 639 TimelineCounter: TimelineCounter, | |
| 456 TimelineProcess: TimelineProcess, | 640 TimelineProcess: TimelineProcess, |
| 457 TimelineModel: TimelineModel | 641 TimelineModel: TimelineModel |
| 458 }; | 642 }; |
| 459 }); | 643 }); |
| OLD | NEW |