• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
7base.require('tcmalloc.heap_instance_track');
8base.require('tracing.analysis.object_snapshot_view');
9base.require('tracing.analysis.object_instance_view');
10base.require('tracing.tracks.container_track');
11base.require('tracing.tracks.counter_track');
12base.require('tracing.tracks.object_instance_track');
13base.require('tracing.tracks.spacing_track');
14base.require('tracing.tracks.thread_track');
15base.require('tracing.trace_model_settings');
16base.require('tracing.filter');
17base.require('ui');
18base.require('ui.dom_helpers');
19
20base.requireStylesheet('tracing.tracks.process_track_base');
21
22base.exportTo('tracing.tracks', function() {
23
24  var ObjectSnapshotView = tracing.analysis.ObjectSnapshotView;
25  var ObjectInstanceView = tracing.analysis.ObjectInstanceView;
26  var TraceModelSettings = tracing.TraceModelSettings;
27  var SpacingTrack = tracing.tracks.SpacingTrack;
28
29  /**
30   * Visualizes a Process by building ThreadTracks and CounterTracks.
31   * @constructor
32   */
33  var ProcessTrackBase =
34      ui.define('process-track-base', tracing.tracks.ContainerTrack);
35
36  ProcessTrackBase.prototype = {
37
38    __proto__: tracing.tracks.ContainerTrack.prototype,
39
40    decorate: function(viewport) {
41      tracing.tracks.ContainerTrack.prototype.decorate.call(this, viewport);
42
43      this.processBase_ = undefined;
44
45      this.classList.add('process-track-base');
46      this.classList.add('expanded');
47
48      this.expandEl_ = document.createElement('expand-button');
49      this.expandEl_.classList.add('expand-button-expanded');
50
51      this.processNameEl_ = ui.createSpan();
52
53      this.headerEl_ = ui.createDiv({className: 'process-track-header'});
54      this.headerEl_.appendChild(this.expandEl_);
55      this.headerEl_.appendChild(this.processNameEl_);
56      this.headerEl_.addEventListener('click', this.onHeaderClick_.bind(this));
57
58      this.appendChild(this.headerEl_);
59    },
60
61    get processBase() {
62      return this.processBase_;
63    },
64
65    set processBase(processBase) {
66      this.processBase_ = processBase;
67
68      if (this.processBase_) {
69        var modelSettings = new TraceModelSettings(this.processBase_.model);
70        this.expanded = modelSettings.getSettingFor(
71            this.processBase_, 'expanded', true);
72      }
73
74      this.updateContents_();
75    },
76
77    get expanded() {
78      return this.expandEl_.classList.contains('expand-button-expanded');
79    },
80
81    set expanded(expanded) {
82      expanded = !!expanded;
83
84      var wasExpanded = this.expandEl_.classList.contains(
85          'expand-button-expanded');
86      if (wasExpanded === expanded)
87        return;
88
89      if (expanded) {
90        this.classList.add('expanded');
91        this.expandEl_.classList.add('expand-button-expanded');
92      } else {
93        this.classList.remove('expanded');
94        this.expandEl_.classList.remove('expand-button-expanded');
95      }
96
97      // Expanding and collapsing tracks is, essentially, growing and shrinking
98      // the viewport. We dispatch a change event to trigger any processing
99      // to happen.
100      this.viewport_.dispatchChangeEvent();
101
102      if (!this.processBase_)
103        return;
104
105      var modelSettings = new TraceModelSettings(this.processBase_.model);
106      modelSettings.setSettingFor(this.processBase_, 'expanded', expanded);
107    },
108
109    get hasVisibleContent() {
110      if (this.expanded)
111        return this.children.length > 1;
112      return true;
113    },
114
115    onHeaderClick_: function(e) {
116      e.stopPropagation();
117      e.preventDefault();
118      this.expanded = !this.expanded;
119    },
120
121    updateContents_: function() {
122      this.tracks_.forEach(function(track) {
123        this.removeChild(track);
124      }, this);
125
126      if (!this.processBase_)
127        return;
128
129      this.processNameEl_.textContent = this.processBase_.userFriendlyName;
130      this.headerEl_.title = this.processBase_.userFriendlyDetails;
131
132      // Create the object instance tracks for this process.
133      this.willAppendTracks_();
134      this.appendObjectInstanceTracks_();
135      this.appendCounterTracks_();
136      this.appendThreadTracks_();
137      this.didAppendTracks_();
138    },
139
140    willAppendTracks_: function() {
141    },
142
143    didAppendTracks_: function() {
144    },
145
146    appendObjectInstanceTracks_: function() {
147      var instancesByTypeName =
148          this.processBase_.objects.getAllInstancesByTypeName();
149      var instanceTypeNames = base.dictionaryKeys(instancesByTypeName);
150      instanceTypeNames.sort();
151
152      var didAppendAtLeastOneTrack = false;
153      instanceTypeNames.forEach(function(typeName) {
154        var allInstances = instancesByTypeName[typeName];
155
156        // If a object snapshot has a viewer it will be shown,
157        // unless the viewer asked for it to not be shown.
158        var instanceViewInfo = ObjectInstanceView.getViewInfo(typeName);
159        var snapshotViewInfo = ObjectSnapshotView.getViewInfo(typeName);
160        if (instanceViewInfo && !instanceViewInfo.options.showInTrackView)
161          instanceViewInfo = undefined;
162        if (snapshotViewInfo && !snapshotViewInfo.options.showInTrackView)
163          snapshotViewInfo = undefined;
164        var hasViewInfo = instanceViewInfo || snapshotViewInfo;
165
166        // There are some instances that don't merit their own track in
167        // the UI. Filter them out.
168        var visibleInstances = [];
169        for (var i = 0; i < allInstances.length; i++) {
170          var instance = allInstances[i];
171
172          // Do not create tracks for instances that have no snapshots.
173          if (instance.snapshots.length === 0)
174            continue;
175
176          // Do not create tracks for instances that have implicit snapshots
177          // and don't have a viewer.
178          if (instance.hasImplicitSnapshots && !hasViewInfo)
179            continue;
180
181          visibleInstances.push(instance);
182        }
183        if (visibleInstances.length === 0)
184          return;
185
186        // Look up the constructor for this track, or use the default
187        // constructor if none exists.
188        var trackConstructor =
189            tracing.tracks.ObjectInstanceTrack.getTrackConstructor(typeName);
190        if (!trackConstructor)
191          trackConstructor = tracing.tracks.ObjectInstanceTrack;
192        var track = new trackConstructor(this.viewport);
193        track.categoryFilter = this.categoryFilter_;
194        track.objectInstances = visibleInstances;
195        this.appendChild(track);
196        didAppendAtLeastOneTrack = true;
197      }, this);
198      if (didAppendAtLeastOneTrack)
199        this.appendChild(new SpacingTrack(this.viewport));
200    },
201
202    appendCounterTracks_: function() {
203      // Add counter tracks for this process.
204      var counters = base.dictionaryValues(this.processBase.counters).
205          filter(this.categoryFilter.matchCounter, this.categoryFilter);
206      counters.sort(tracing.trace_model.Counter.compare);
207
208      // Create the counters for this process.
209      counters.forEach(function(counter) {
210        var track = new tracing.tracks.CounterTrack(this.viewport);
211        track.categoryFilter = this.categoryFilter_;
212        track.counter = counter;
213        this.appendChild(track);
214        this.appendChild(new SpacingTrack(this.viewport));
215      }.bind(this));
216    },
217
218    appendThreadTracks_: function() {
219      // Get a sorted list of threads.
220      var threads = base.dictionaryValues(this.processBase.threads).
221          filter(function(thread) {
222            return this.categoryFilter_.matchThread(thread);
223          }, this);
224      threads.sort(tracing.trace_model.Thread.compare);
225
226      // Create the threads.
227      threads.forEach(function(thread) {
228        var track = new tracing.tracks.ThreadTrack(this.viewport);
229        track.categoryFilter = this.categoryFilter_;
230        track.thread = thread;
231        if (!track.hasVisibleContent)
232          return;
233        this.appendChild(track);
234        this.appendChild(new SpacingTrack(this.viewport));
235      }.bind(this));
236    }
237  };
238
239  return {
240    ProcessTrackBase: ProcessTrackBase
241  };
242});
243