Home | History | Annotate | Download | only in src
      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