1<!DOCTYPE html> 2<!-- 3Copyright (c) 2013 The Chromium Authors. All rights reserved. 4Use of this source code is governed by a BSD-style license that can be 5found in the LICENSE file. 6--> 7 8<link rel="import" href="/tracing/base/range.html"> 9<link rel="import" href="/tracing/model/counter.html"> 10<link rel="import" href="/tracing/model/cpu_slice.html"> 11<link rel="import" href="/tracing/model/thread_time_slice.html"> 12 13<script> 14'use strict'; 15 16/** 17 * @fileoverview Provides the Cpu class. 18 */ 19tr.exportTo('tr.model', function() { 20 21 var ColorScheme = tr.b.ColorScheme; 22 var Counter = tr.model.Counter; 23 var CpuSlice = tr.model.CpuSlice; 24 var Slice = tr.model.Slice; 25 26 /** 27 * The Cpu represents a Cpu from the kernel's point of view. 28 * @constructor 29 */ 30 function Cpu(kernel, number) { 31 if (kernel === undefined || number === undefined) 32 throw new Error('Missing arguments'); 33 this.kernel = kernel; 34 this.cpuNumber = number; 35 this.slices = []; 36 this.counters = {}; 37 this.bounds = new tr.b.Range(); 38 this.samples_ = undefined; // Set during createSubSlices 39 40 // Start timestamp of the last active thread. 41 this.lastActiveTimestamp_ = undefined; 42 43 // Identifier of the last active thread. On Linux, it's a pid while on 44 // Windows it's a thread id. 45 this.lastActiveThread_ = undefined; 46 47 // Name and arguments of the last active thread. 48 this.lastActiveName_ = undefined; 49 this.lastActiveArgs_ = undefined; 50 }; 51 52 Cpu.prototype = { 53 get samples() { 54 return this.samples_; 55 }, 56 57 get userFriendlyName() { 58 return 'CPU ' + this.cpuNumber; 59 }, 60 61 iterateAllEventsInThisContainer: function(eventTypePredicate, 62 callback, opt_this) { 63 if (eventTypePredicate.call(opt_this, tr.model.CpuSlice)) 64 this.slices.forEach(callback, opt_this); 65 66 if (this.samples_) { 67 if (eventTypePredicate.call(opt_this, tr.model.Sample)) 68 this.samples_.forEach(callback, opt_this); 69 } 70 }, 71 72 iterateAllChildEventContainers: function(callback, opt_this) { 73 for (var id in this.counters) 74 callback.call(opt_this, this.counters[id]); 75 }, 76 77 /** 78 * @return {Counter} The counter on this CPU with the given category/name 79 * combination, creating it if it doesn't exist. 80 */ 81 getOrCreateCounter: function(cat, name) { 82 var id = cat + '.' + name; 83 if (!this.counters[id]) 84 this.counters[id] = new Counter(this, id, cat, name); 85 return this.counters[id]; 86 }, 87 88 /** 89 * @return {Counter} the counter on this CPU with the given category/name 90 * combination, or undefined if it doesn't exist. 91 */ 92 getCounter: function(cat, name) { 93 var id = cat + '.' + name; 94 if (!this.counters[id]) 95 return undefined; 96 return this.counters[id]; 97 }, 98 99 /** 100 * Shifts all the timestamps inside this CPU forward by the amount 101 * specified. 102 */ 103 shiftTimestampsForward: function(amount) { 104 for (var sI = 0; sI < this.slices.length; sI++) 105 this.slices[sI].start = (this.slices[sI].start + amount); 106 for (var id in this.counters) 107 this.counters[id].shiftTimestampsForward(amount); 108 }, 109 110 /** 111 * Updates the range based on the current slices attached to the cpu. 112 */ 113 updateBounds: function() { 114 this.bounds.reset(); 115 if (this.slices.length) { 116 this.bounds.addValue(this.slices[0].start); 117 this.bounds.addValue(this.slices[this.slices.length - 1].end); 118 } 119 for (var id in this.counters) { 120 this.counters[id].updateBounds(); 121 this.bounds.addRange(this.counters[id].bounds); 122 } 123 if (this.samples_ && this.samples_.length) { 124 this.bounds.addValue(this.samples_[0].start); 125 this.bounds.addValue( 126 this.samples_[this.samples_.length - 1].end); 127 } 128 }, 129 130 createSubSlices: function() { 131 this.samples_ = this.kernel.model.samples.filter(function(sample) { 132 return sample.cpu == this; 133 }, this); 134 }, 135 136 addCategoriesToDict: function(categoriesDict) { 137 for (var i = 0; i < this.slices.length; i++) 138 categoriesDict[this.slices[i].category] = true; 139 for (var id in this.counters) 140 categoriesDict[this.counters[id].category] = true; 141 for (var i = 0; i < this.samples_.length; i++) 142 categoriesDict[this.samples_[i].category] = true; 143 }, 144 145 146 147 /* 148 * Returns the index of the slice in the CPU's slices, or undefined. 149 */ 150 indexOf: function(cpuSlice) { 151 var i = tr.b.findLowIndexInSortedArray( 152 this.slices, 153 function(slice) { return slice.start; }, 154 cpuSlice.start); 155 if (this.slices[i] !== cpuSlice) 156 return undefined; 157 return i; 158 }, 159 160 /** 161 * Closes the thread running on the CPU. |end_timestamp| is the timestamp 162 * at which the thread was unscheduled. |args| is merged with the arguments 163 * specified when the thread was initially scheduled. 164 */ 165 closeActiveThread: function(end_timestamp, args) { 166 // Don't generate a slice if the last active thread is the idle task. 167 if (this.lastActiveThread_ == undefined || this.lastActiveThread_ == 0) 168 return; 169 170 if (end_timestamp < this.lastActiveTimestamp_) { 171 throw new Error('The end timestamp of a thread running on CPU ' + 172 this.cpuNumber + ' is before its start timestamp.'); 173 } 174 175 // Merge |args| with |this.lastActiveArgs_|. If a key is in both 176 // dictionaries, the value from |args| is used. 177 for (var key in args) { 178 this.lastActiveArgs_[key] = args[key]; 179 } 180 181 var duration = end_timestamp - this.lastActiveTimestamp_; 182 var slice = new tr.model.CpuSlice( 183 '', this.lastActiveName_, 184 ColorScheme.getColorIdForGeneralPurposeString(this.lastActiveName_), 185 this.lastActiveTimestamp_, 186 this.lastActiveArgs_, 187 duration); 188 slice.cpu = this; 189 this.slices.push(slice); 190 191 // Clear the last state. 192 this.lastActiveTimestamp_ = undefined; 193 this.lastActiveThread_ = undefined; 194 this.lastActiveName_ = undefined; 195 this.lastActiveArgs_ = undefined; 196 }, 197 198 switchActiveThread: function(timestamp, old_thread_args, new_thread_id, 199 new_thread_name, new_thread_args) { 200 // Close the previous active thread and generate a slice. 201 this.closeActiveThread(timestamp, old_thread_args); 202 203 // Keep track of the new thread. 204 this.lastActiveTimestamp_ = timestamp; 205 this.lastActiveThread_ = new_thread_id; 206 this.lastActiveName_ = new_thread_name; 207 this.lastActiveArgs_ = new_thread_args; 208 }, 209 210 /** 211 * Returns the frequency statistics for this CPU; 212 * the returned object contains the frequencies as keys, 213 * and the duration at this frequency in milliseconds as the value, 214 * for the range that was specified. 215 */ 216 getFreqStatsForRange: function(range) { 217 var stats = {}; 218 219 function addStatsForFreq(freqSample, index) { 220 // Counters don't have an explicit end or duration; 221 // calculate the end by looking at the starting point 222 // of the next value in the series, or if that doesn't 223 // exist, assume this frequency is held until the end. 224 var freqEnd = (index < freqSample.series_.length - 1) ? 225 freqSample.series_.samples_[index + 1].timestamp : range.max; 226 227 var freqRange = tr.b.Range.fromExplicitRange(freqSample.timestamp, 228 freqEnd); 229 var intersection = freqRange.findIntersection(range); 230 if (!(freqSample.value in stats)) 231 stats[freqSample.value] = 0; 232 stats[freqSample.value] += intersection.duration; 233 } 234 235 var freqCounter = this.getCounter('', 'Clock Frequency'); 236 if (freqCounter !== undefined) { 237 var freqSeries = freqCounter.getSeries(0); 238 if (!freqSeries) 239 return; 240 241 tr.b.iterateOverIntersectingIntervals(freqSeries.samples_, 242 function(x) { return x.timestamp; }, 243 function(x, index) { return index < freqSeries.length - 1 ? 244 freqSeries.samples_[index + 1].timestamp : 245 range.max; }, 246 range.min, 247 range.max, 248 addStatsForFreq); 249 } 250 251 return stats; 252 } 253 }; 254 255 /** 256 * Comparison between processes that orders by cpuNumber. 257 */ 258 Cpu.compare = function(x, y) { 259 return x.cpuNumber - y.cpuNumber; 260 }; 261 262 263 return { 264 Cpu: Cpu 265 }; 266}); 267</script> 268