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