Home | History | Annotate | Download | only in trace_model
      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 Thread class.
      9  */
     10 base.require('base.guid');
     11 base.require('base.range');
     12 base.require('tracing.trace_model.slice');
     13 base.require('tracing.trace_model.slice_group');
     14 base.require('tracing.trace_model.async_slice_group');
     15 base.require('tracing.trace_model.sample');
     16 
     17 base.exportTo('tracing.trace_model', function() {
     18 
     19   var Slice = tracing.trace_model.Slice;
     20   var SliceGroup = tracing.trace_model.SliceGroup;
     21   var AsyncSlice = tracing.trace_model.AsyncSlice;
     22   var AsyncSliceGroup = tracing.trace_model.AsyncSliceGroup;
     23 
     24   /**
     25    * A ThreadSlice represents an interval of time on a thread resource
     26    * with associated nestinged slice information.
     27    *
     28    * ThreadSlices are typically associated with a specific trace event pair on a
     29    * specific thread.
     30    * For example,
     31    *   TRACE_EVENT_BEGIN1("x","myArg", 7) at time=0.1ms
     32    *   TRACE_EVENT_END0()                 at time=0.3ms
     33    * This results in a single slice from 0.1 with duration 0.2 on a
     34    * specific thread.
     35    *
     36    * @constructor
     37    */
     38   function ThreadSlice(cat, title, colorId, start, args, opt_duration) {
     39     Slice.call(this, cat, title, colorId, start, args, opt_duration);
     40     // Do not modify this directly.
     41     // subSlices is configured by SliceGroup.rebuildSubRows_.
     42     this.subSlices = [];
     43   }
     44 
     45   ThreadSlice.prototype = {
     46     __proto__: Slice.prototype
     47   };
     48 
     49   /**
     50    * A Thread stores all the trace events collected for a particular
     51    * thread. We organize the synchronous slices on a thread by "subrows," where
     52    * subrow 0 has all the root slices, subrow 1 those nested 1 deep, and so on.
     53    * The asynchronous slices are stored in an AsyncSliceGroup object.
     54    *
     55    * The slices stored on a Thread should be instances of
     56    * ThreadSlice.
     57    *
     58    * @constructor
     59    */
     60   function Thread(parent, tid) {
     61     this.guid_ = base.GUID.allocate();
     62     if (!parent)
     63       throw new Error('Parent must be provided.');
     64     this.parent = parent;
     65     this.sortIndex = 0;
     66     this.tid = tid;
     67     this.sliceGroup = new SliceGroup(ThreadSlice);
     68     this.cpuSlices = undefined;
     69     this.samples_ = [];
     70     this.kernelSliceGroup = new SliceGroup();
     71     this.asyncSliceGroup = new AsyncSliceGroup();
     72     this.bounds = new base.Range();
     73     this.ephemeralSettings = {};
     74   }
     75 
     76   Thread.prototype = {
     77 
     78     /*
     79      * @return {Number} A globally unique identifier for this counter.
     80      */
     81     get guid() {
     82       return this.guid_;
     83     },
     84 
     85     compareTo: function(that) {
     86       return Thread.compare(this, that);
     87     },
     88 
     89     toJSON: function() {
     90       var obj = new Object();
     91       var keys = Object.keys(this);
     92       for (var i = 0; i < keys.length; i++) {
     93         var key = keys[i];
     94         if (typeof this[key] == 'function')
     95           continue;
     96         if (key == 'parent') {
     97           obj[key] = this[key].guid;
     98           continue;
     99         }
    100         obj[key] = this[key];
    101       }
    102       return obj;
    103     },
    104 
    105     /**
    106      * Adds a new sample in the thread's samples.
    107      *
    108      * Calls to addSample must be made with non-monotonically-decreasing
    109      * timestamps.
    110      *
    111      * @param {String} category Category of the sample to add.
    112      * @param {String} title Title of the sample to add.
    113      * @param {Number} ts The timetsamp of the sample, in milliseconds.
    114      * @param {Object.<string, Object>=} opt_args Arguments associated with
    115      * the sample.
    116      */
    117     addSample: function(category, title, ts, opt_args) {
    118       if (this.samples_.length) {
    119         var lastSample = this.samples_[this.samples_.length - 1];
    120         if (ts < lastSample.start) {
    121           throw new
    122               Error('Samples must be added in increasing timestamp order.');
    123         }
    124       }
    125       var colorId = tracing.getStringColorId(title);
    126       var sample = new tracing.trace_model.Sample(category, title, colorId, ts,
    127                                                   opt_args ? opt_args : {});
    128       this.samples_.push(sample);
    129       return sample;
    130     },
    131 
    132     /**
    133      * Returns the array of samples added to this thread. If no samples
    134      * have been added, an empty array is returned.
    135      *
    136      * @return {Array<Sample>} array of samples.
    137      */
    138     get samples() {
    139       return this.samples_;
    140     },
    141 
    142     /**
    143      * Name of the thread, if present.
    144      */
    145     name: undefined,
    146 
    147     /**
    148      * Shifts all the timestamps inside this thread forward by the amount
    149      * specified.
    150      */
    151     shiftTimestampsForward: function(amount) {
    152       this.sliceGroup.shiftTimestampsForward(amount);
    153 
    154       if (this.cpuSlices) {
    155         for (var i = 0; i < this.cpuSlices.length; i++) {
    156           var slice = this.cpuSlices[i];
    157           slice.start += amount;
    158         }
    159       }
    160 
    161       if (this.samples_.length) {
    162         for (var i = 0; i < this.samples_.length; i++) {
    163           var sample = this.samples_[i];
    164           sample.start += amount;
    165         }
    166       }
    167 
    168       this.kernelSliceGroup.shiftTimestampsForward(amount);
    169       this.asyncSliceGroup.shiftTimestampsForward(amount);
    170     },
    171 
    172     /**
    173      * Determins whether this thread is empty. If true, it usually implies
    174      * that it should be pruned from the model.
    175      */
    176     get isEmpty() {
    177       if (this.sliceGroup.length)
    178         return false;
    179       if (this.sliceGroup.openSliceCount)
    180         return false;
    181       if (this.cpuSlices && this.cpuSlices.length)
    182         return false;
    183       if (this.kernelSliceGroup.length)
    184         return false;
    185       if (this.asyncSliceGroup.length)
    186         return false;
    187       if (this.samples_.length)
    188         return false;
    189       return true;
    190     },
    191 
    192     /**
    193      * Updates the bounds based on the
    194      * current objects associated with the thread.
    195      */
    196     updateBounds: function() {
    197       this.bounds.reset();
    198 
    199       this.sliceGroup.updateBounds();
    200       this.bounds.addRange(this.sliceGroup.bounds);
    201 
    202       this.kernelSliceGroup.updateBounds();
    203       this.bounds.addRange(this.kernelSliceGroup.bounds);
    204 
    205       this.asyncSliceGroup.updateBounds();
    206       this.bounds.addRange(this.asyncSliceGroup.bounds);
    207 
    208       if (this.cpuSlices && this.cpuSlices.length) {
    209         this.bounds.addValue(this.cpuSlices[0].start);
    210         this.bounds.addValue(
    211             this.cpuSlices[this.cpuSlices.length - 1].end);
    212       }
    213       if (this.samples_.length) {
    214         this.bounds.addValue(this.samples_[0].start);
    215         this.bounds.addValue(
    216             this.samples_[this.samples_.length - 1].end);
    217       }
    218     },
    219 
    220     addCategoriesToDict: function(categoriesDict) {
    221       for (var i = 0; i < this.sliceGroup.length; i++)
    222         categoriesDict[this.sliceGroup.slices[i].category] = true;
    223       for (var i = 0; i < this.kernelSliceGroup.length; i++)
    224         categoriesDict[this.kernelSliceGroup.slices[i].category] = true;
    225       for (var i = 0; i < this.asyncSliceGroup.length; i++)
    226         categoriesDict[this.asyncSliceGroup.slices[i].category] = true;
    227       for (var i = 0; i < this.samples_.length; i++)
    228         categoriesDict[this.samples_[i].category] = true;
    229     },
    230 
    231     autoCloseOpenSlices: function(opt_maxTimestamp) {
    232       this.sliceGroup.autoCloseOpenSlices(opt_maxTimestamp);
    233       this.kernelSliceGroup.autoCloseOpenSlices(opt_maxTimestamp);
    234     },
    235 
    236     mergeKernelWithUserland: function() {
    237       if (this.kernelSliceGroup.length > 0) {
    238         var newSlices = SliceGroup.merge(
    239             this.sliceGroup, this.kernelSliceGroup);
    240         this.sliceGroup.slices = newSlices.slices;
    241         this.kernelSliceGroup = new SliceGroup();
    242         this.updateBounds();
    243       }
    244     },
    245 
    246     /**
    247      * @return {String} A user-friendly name for this thread.
    248      */
    249     get userFriendlyName() {
    250       return this.name || this.tid;
    251     },
    252 
    253     /**
    254      * @return {String} User friendly details about this thread.
    255      */
    256     get userFriendlyDetails() {
    257       return 'tid: ' + this.tid +
    258           (this.name ? ', name: ' + this.name : '');
    259     },
    260 
    261     getSettingsKey: function() {
    262       if (!this.name)
    263         return undefined;
    264       var parentKey = this.parent.getSettingsKey();
    265       if (!parentKey)
    266         return undefined;
    267       return parentKey + '.' + this.name;
    268     }
    269   };
    270 
    271   /**
    272    * Comparison between threads that orders first by parent.compareTo,
    273    * then by names, then by tid.
    274    */
    275   Thread.compare = function(x, y) {
    276     var tmp = x.parent.compareTo(y.parent);
    277     if (tmp)
    278       return tmp;
    279 
    280     tmp = x.sortIndex - y.sortIndex;
    281     if (tmp)
    282       return tmp;
    283 
    284     tmp = base.comparePossiblyUndefinedValues(
    285         x.name, y.name,
    286         function(x, y) { return x.localeCompare(y); });
    287     if (tmp)
    288       return tmp;
    289 
    290     return x.tid - y.tid;
    291   };
    292 
    293   return {
    294     ThreadSlice: ThreadSlice,
    295     Thread: Thread
    296   };
    297 });
    298