1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 'use strict'; 6 7 /** 8 * @fileoverview Provides the TimelineThread class. 9 */ 10 base.require('timeline_slice'); 11 base.require('timeline_slice_group'); 12 base.require('timeline_async_slice_group'); 13 base.exportTo('tracing', function() { 14 15 var TimelineSlice = tracing.TimelineSlice; 16 var TimelineSliceGroup = tracing.TimelineSliceGroup; 17 var TimelineAsyncSlice = tracing.TimelineAsyncSlice; 18 var TimelineAsyncSliceGroup = tracing.TimelineAsyncSliceGroup; 19 20 /** 21 * A TimelineThreadSlice represents an interval of time on a thread resource 22 * with associated nestinged slice information. 23 * 24 * ThreadSlices are typically associated with a specific trace event pair on a 25 * specific thread. 26 * For example, 27 * TRACE_EVENT_BEGIN1("x","myArg", 7) at time=0.1ms 28 * TRACE_EVENT_END0() at time=0.3ms 29 * This results in a single timeline slice from 0.1 with duration 0.2 on a 30 * specific thread. 31 * 32 * @constructor 33 */ 34 function TimelineThreadSlice(cat, title, colorId, start, args, opt_duration) { 35 TimelineSlice.call(this, cat, title, colorId, start, args, opt_duration); 36 // Do not modify this directly. 37 // subSlices is configured by TimelineSliceGroup.rebuildSubRows_. 38 this.subSlices = []; 39 } 40 41 TimelineThreadSlice.prototype = { 42 __proto__: TimelineSlice.prototype 43 }; 44 45 /** 46 * A TimelineThread stores all the trace events collected for a particular 47 * thread. We organize the synchronous slices on a thread by "subrows," where 48 * subrow 0 has all the root slices, subrow 1 those nested 1 deep, and so on. 49 * The asynchronous slices are stored in an TimelineAsyncSliceGroup object. 50 * 51 * The slices stored on a TimelineThread should be instances of 52 * TimelineThreadSlice. 53 * 54 * @constructor 55 */ 56 function TimelineThread(parent, tid) { 57 TimelineSliceGroup.call(this, TimelineThreadSlice); 58 if (!parent) 59 throw new Error('Parent must be provided.'); 60 this.pid = parent.pid; 61 this.tid = tid; 62 this.cpuSlices = undefined; 63 this.asyncSlices = new TimelineAsyncSliceGroup(this.ptid); 64 } 65 66 var ptidMap = {}; 67 68 /** 69 * @return {String} A string that can be used as a unique key for a specific 70 * thread within a process. 71 */ 72 TimelineThread.getPTIDFromPidAndTid = function(pid, tid) { 73 if (!ptidMap[pid]) 74 ptidMap[pid] = {}; 75 if (!ptidMap[pid][tid]) 76 ptidMap[pid][tid] = pid + ':' + tid; 77 return ptidMap[pid][tid]; 78 } 79 80 TimelineThread.prototype = { 81 82 __proto__: TimelineSliceGroup.prototype, 83 84 /** 85 * Name of the thread, if present. 86 */ 87 name: undefined, 88 89 /** 90 * @return {string} A concatenation of the pid and the thread's 91 * tid. Can be used to uniquely identify a thread. 92 */ 93 get ptid() { 94 return TimelineThread.getPTIDFromPidAndTid(this.tid, this.pid); 95 }, 96 97 /** 98 * Shifts all the timestamps inside this thread forward by the amount 99 * specified. 100 */ 101 shiftTimestampsForward: function(amount) { 102 TimelineSliceGroup.prototype.shiftTimestampsForward.call(this, amount); 103 104 if (this.cpuSlices) { 105 for (var i = 0; i < this.cpuSlices.length; i++) { 106 var slice = this.cpuSlices[i]; 107 slice.start += amount; 108 } 109 } 110 111 this.asyncSlices.shiftTimestampsForward(amount); 112 }, 113 114 /** 115 * Determins whether this thread is empty. If true, it usually implies 116 * that it should be pruned from the model. 117 */ 118 get isEmpty() { 119 if (this.slices.length) 120 return false; 121 if (this.openSliceCount) 122 return false; 123 if (this.cpuSlices && this.cpuSlices.length) 124 return false; 125 if (this.asyncSlices.length) 126 return false; 127 return true; 128 }, 129 130 /** 131 * Updates the minTimestamp and maxTimestamp fields based on the 132 * current objects associated with the thread. 133 */ 134 updateBounds: function() { 135 TimelineSliceGroup.prototype.updateBounds.call(this); 136 var values = []; 137 if (this.minTimestamp !== undefined) 138 values.push(this.minTimestamp, this.maxTimestamp); 139 140 if (this.asyncSlices.slices.length) { 141 this.asyncSlices.updateBounds(); 142 values.push(this.asyncSlices.minTimestamp); 143 values.push(this.asyncSlices.maxTimestamp); 144 } 145 146 if (this.cpuSlices && this.cpuSlices.length) { 147 values.push(this.cpuSlices[0].start); 148 values.push(this.cpuSlices[this.cpuSlices.length - 1].end); 149 } 150 151 if (values.length) { 152 this.minTimestamp = Math.min.apply(Math, values); 153 this.maxTimestamp = Math.max.apply(Math, values); 154 } else { 155 this.minTimestamp = undefined; 156 this.maxTimestamp = undefined; 157 } 158 }, 159 160 /** 161 * @return {String} A user-friendly name for this thread. 162 */ 163 get userFriendlyName() { 164 var tname = this.name || this.tid; 165 return this.pid + ': ' + tname; 166 }, 167 168 /** 169 * @return {String} User friendly details about this thread. 170 */ 171 get userFriendlyDetails() { 172 return 'pid: ' + this.pid + 173 ', tid: ' + this.tid + 174 (this.name ? ', name: ' + this.name : ''); 175 } 176 }; 177 178 /** 179 * Comparison between threads that orders first by pid, 180 * then by names, then by tid. 181 */ 182 TimelineThread.compare = function(x, y) { 183 if (x.pid != y.pid) 184 return x.pid - y.pid; 185 186 if (x.name && y.name) { 187 var tmp = x.name.localeCompare(y.name); 188 if (tmp == 0) 189 return x.tid - y.tid; 190 return tmp; 191 } else if (x.name) { 192 return -1; 193 } else if (y.name) { 194 return 1; 195 } else { 196 return x.tid - y.tid; 197 } 198 }; 199 200 return { 201 TimelineThreadSlice: TimelineThreadSlice, 202 TimelineThread: TimelineThread 203 }; 204 }); 205