| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| 'use strict'; |
| |
| /** |
| * @fileoverview Provides the TimelineThread class. |
| */ |
| base.require('timeline_slice'); |
| base.require('timeline_slice_group'); |
| base.require('timeline_async_slice_group'); |
| base.exportTo('tracing', function() { |
| |
| var TimelineSlice = tracing.TimelineSlice; |
| var TimelineSliceGroup = tracing.TimelineSliceGroup; |
| var TimelineAsyncSlice = tracing.TimelineAsyncSlice; |
| var TimelineAsyncSliceGroup = tracing.TimelineAsyncSliceGroup; |
| |
| /** |
| * A TimelineThreadSlice represents an interval of time on a thread resource |
| * with associated nestinged slice information. |
| * |
| * ThreadSlices are typically associated with a specific trace event pair on a |
| * specific thread. |
| * For example, |
| * TRACE_EVENT_BEGIN1("x","myArg", 7) at time=0.1ms |
| * TRACE_EVENT_END0() at time=0.3ms |
| * This results in a single timeline slice from 0.1 with duration 0.2 on a |
| * specific thread. |
| * |
| * @constructor |
| */ |
| function TimelineThreadSlice(cat, title, colorId, start, args, opt_duration) { |
| TimelineSlice.call(this, cat, title, colorId, start, args, opt_duration); |
| // Do not modify this directly. |
| // subSlices is configured by TimelineSliceGroup.rebuildSubRows_. |
| this.subSlices = []; |
| } |
| |
| TimelineThreadSlice.prototype = { |
| __proto__: TimelineSlice.prototype |
| }; |
| |
| /** |
| * A TimelineThread stores all the trace events collected for a particular |
| * thread. We organize the synchronous slices on a thread by "subrows," where |
| * subrow 0 has all the root slices, subrow 1 those nested 1 deep, and so on. |
| * The asynchronous slices are stored in an TimelineAsyncSliceGroup object. |
| * |
| * The slices stored on a TimelineThread should be instances of |
| * TimelineThreadSlice. |
| * |
| * @constructor |
| */ |
| function TimelineThread(parent, tid) { |
| TimelineSliceGroup.call(this, TimelineThreadSlice); |
| if (!parent) |
| throw new Error('Parent must be provided.'); |
| this.pid = parent.pid; |
| this.tid = tid; |
| this.cpuSlices = undefined; |
| this.asyncSlices = new TimelineAsyncSliceGroup(this.ptid); |
| } |
| |
| var ptidMap = {}; |
| |
| /** |
| * @return {String} A string that can be used as a unique key for a specific |
| * thread within a process. |
| */ |
| TimelineThread.getPTIDFromPidAndTid = function(pid, tid) { |
| if (!ptidMap[pid]) |
| ptidMap[pid] = {}; |
| if (!ptidMap[pid][tid]) |
| ptidMap[pid][tid] = pid + ':' + tid; |
| return ptidMap[pid][tid]; |
| } |
| |
| TimelineThread.prototype = { |
| |
| __proto__: TimelineSliceGroup.prototype, |
| |
| /** |
| * Name of the thread, if present. |
| */ |
| name: undefined, |
| |
| /** |
| * @return {string} A concatenation of the pid and the thread's |
| * tid. Can be used to uniquely identify a thread. |
| */ |
| get ptid() { |
| return TimelineThread.getPTIDFromPidAndTid(this.tid, this.pid); |
| }, |
| |
| /** |
| * Shifts all the timestamps inside this thread forward by the amount |
| * specified. |
| */ |
| shiftTimestampsForward: function(amount) { |
| TimelineSliceGroup.prototype.shiftTimestampsForward.call(this, amount); |
| |
| if (this.cpuSlices) { |
| for (var i = 0; i < this.cpuSlices.length; i++) { |
| var slice = this.cpuSlices[i]; |
| slice.start += amount; |
| } |
| } |
| |
| this.asyncSlices.shiftTimestampsForward(amount); |
| }, |
| |
| /** |
| * Determins whether this thread is empty. If true, it usually implies |
| * that it should be pruned from the model. |
| */ |
| get isEmpty() { |
| if (this.slices.length) |
| return false; |
| if (this.openSliceCount) |
| return false; |
| if (this.cpuSlices && this.cpuSlices.length) |
| return false; |
| if (this.asyncSlices.length) |
| return false; |
| return true; |
| }, |
| |
| /** |
| * Updates the minTimestamp and maxTimestamp fields based on the |
| * current objects associated with the thread. |
| */ |
| updateBounds: function() { |
| TimelineSliceGroup.prototype.updateBounds.call(this); |
| var values = []; |
| if (this.minTimestamp !== undefined) |
| values.push(this.minTimestamp, this.maxTimestamp); |
| |
| if (this.asyncSlices.slices.length) { |
| this.asyncSlices.updateBounds(); |
| values.push(this.asyncSlices.minTimestamp); |
| values.push(this.asyncSlices.maxTimestamp); |
| } |
| |
| if (this.cpuSlices && this.cpuSlices.length) { |
| values.push(this.cpuSlices[0].start); |
| values.push(this.cpuSlices[this.cpuSlices.length - 1].end); |
| } |
| |
| if (values.length) { |
| this.minTimestamp = Math.min.apply(Math, values); |
| this.maxTimestamp = Math.max.apply(Math, values); |
| } else { |
| this.minTimestamp = undefined; |
| this.maxTimestamp = undefined; |
| } |
| }, |
| |
| /** |
| * @return {String} A user-friendly name for this thread. |
| */ |
| get userFriendlyName() { |
| var tname = this.name || this.tid; |
| return this.pid + ': ' + tname; |
| }, |
| |
| /** |
| * @return {String} User friendly details about this thread. |
| */ |
| get userFriendlyDetails() { |
| return 'pid: ' + this.pid + |
| ', tid: ' + this.tid + |
| (this.name ? ', name: ' + this.name : ''); |
| } |
| }; |
| |
| /** |
| * Comparison between threads that orders first by pid, |
| * then by names, then by tid. |
| */ |
| TimelineThread.compare = function(x, y) { |
| if (x.pid != y.pid) |
| return x.pid - y.pid; |
| |
| if (x.name && y.name) { |
| var tmp = x.name.localeCompare(y.name); |
| if (tmp == 0) |
| return x.tid - y.tid; |
| return tmp; |
| } else if (x.name) { |
| return -1; |
| } else if (y.name) { |
| return 1; |
| } else { |
| return x.tid - y.tid; |
| } |
| }; |
| |
| return { |
| TimelineThreadSlice: TimelineThreadSlice, |
| TimelineThread: TimelineThread |
| }; |
| }); |