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 base.require('tcmalloc.heap_instance_track'); 8 base.require('tracing.analysis.object_snapshot_view'); 9 base.require('tracing.analysis.object_instance_view'); 10 base.require('tracing.tracks.container_track'); 11 base.require('tracing.tracks.counter_track'); 12 base.require('tracing.tracks.object_instance_track'); 13 base.require('tracing.tracks.spacing_track'); 14 base.require('tracing.tracks.thread_track'); 15 base.require('tracing.trace_model_settings'); 16 base.require('tracing.filter'); 17 base.require('ui'); 18 base.require('ui.dom_helpers'); 19 20 base.requireStylesheet('tracing.tracks.process_track_base'); 21 22 base.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