// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. 'use strict'; base.require('tcmalloc.heap_instance_track'); base.require('tracing.analysis.object_snapshot_view'); base.require('tracing.analysis.object_instance_view'); base.require('tracing.tracks.container_track'); base.require('tracing.tracks.counter_track'); base.require('tracing.tracks.object_instance_track'); base.require('tracing.tracks.spacing_track'); base.require('tracing.tracks.thread_track'); base.require('tracing.trace_model_settings'); base.require('tracing.filter'); base.require('ui'); base.require('ui.dom_helpers'); base.requireStylesheet('tracing.tracks.process_track_base'); base.exportTo('tracing.tracks', function() { var ObjectSnapshotView = tracing.analysis.ObjectSnapshotView; var ObjectInstanceView = tracing.analysis.ObjectInstanceView; var TraceModelSettings = tracing.TraceModelSettings; var SpacingTrack = tracing.tracks.SpacingTrack; /** * Visualizes a Process by building ThreadTracks and CounterTracks. * @constructor */ var ProcessTrackBase = ui.define('process-track-base', tracing.tracks.ContainerTrack); ProcessTrackBase.prototype = { __proto__: tracing.tracks.ContainerTrack.prototype, decorate: function(viewport) { tracing.tracks.ContainerTrack.prototype.decorate.call(this, viewport); this.processBase_ = undefined; this.classList.add('process-track-base'); this.classList.add('expanded'); this.expandEl_ = document.createElement('expand-button'); this.expandEl_.classList.add('expand-button-expanded'); this.processNameEl_ = ui.createSpan(); this.headerEl_ = ui.createDiv({className: 'process-track-header'}); this.headerEl_.appendChild(this.expandEl_); this.headerEl_.appendChild(this.processNameEl_); this.headerEl_.addEventListener('click', this.onHeaderClick_.bind(this)); this.appendChild(this.headerEl_); }, get processBase() { return this.processBase_; }, set processBase(processBase) { this.processBase_ = processBase; if (this.processBase_) { var modelSettings = new TraceModelSettings(this.processBase_.model); this.expanded = modelSettings.getSettingFor( this.processBase_, 'expanded', true); } this.updateContents_(); }, get expanded() { return this.expandEl_.classList.contains('expand-button-expanded'); }, set expanded(expanded) { expanded = !!expanded; var wasExpanded = this.expandEl_.classList.contains( 'expand-button-expanded'); if (wasExpanded === expanded) return; if (expanded) { this.classList.add('expanded'); this.expandEl_.classList.add('expand-button-expanded'); } else { this.classList.remove('expanded'); this.expandEl_.classList.remove('expand-button-expanded'); } // Expanding and collapsing tracks is, essentially, growing and shrinking // the viewport. We dispatch a change event to trigger any processing // to happen. this.viewport_.dispatchChangeEvent(); if (!this.processBase_) return; var modelSettings = new TraceModelSettings(this.processBase_.model); modelSettings.setSettingFor(this.processBase_, 'expanded', expanded); }, get hasVisibleContent() { if (this.expanded) return this.children.length > 1; return true; }, onHeaderClick_: function(e) { e.stopPropagation(); e.preventDefault(); this.expanded = !this.expanded; }, updateContents_: function() { this.tracks_.forEach(function(track) { this.removeChild(track); }, this); if (!this.processBase_) return; this.processNameEl_.textContent = this.processBase_.userFriendlyName; this.headerEl_.title = this.processBase_.userFriendlyDetails; // Create the object instance tracks for this process. this.willAppendTracks_(); this.appendObjectInstanceTracks_(); this.appendCounterTracks_(); this.appendThreadTracks_(); this.didAppendTracks_(); }, willAppendTracks_: function() { }, didAppendTracks_: function() { }, appendObjectInstanceTracks_: function() { var instancesByTypeName = this.processBase_.objects.getAllInstancesByTypeName(); var instanceTypeNames = base.dictionaryKeys(instancesByTypeName); instanceTypeNames.sort(); var didAppendAtLeastOneTrack = false; instanceTypeNames.forEach(function(typeName) { var allInstances = instancesByTypeName[typeName]; // If a object snapshot has a viewer it will be shown, // unless the viewer asked for it to not be shown. var instanceViewInfo = ObjectInstanceView.getViewInfo(typeName); var snapshotViewInfo = ObjectSnapshotView.getViewInfo(typeName); if (instanceViewInfo && !instanceViewInfo.options.showInTrackView) instanceViewInfo = undefined; if (snapshotViewInfo && !snapshotViewInfo.options.showInTrackView) snapshotViewInfo = undefined; var hasViewInfo = instanceViewInfo || snapshotViewInfo; // There are some instances that don't merit their own track in // the UI. Filter them out. var visibleInstances = []; for (var i = 0; i < allInstances.length; i++) { var instance = allInstances[i]; // Do not create tracks for instances that have no snapshots. if (instance.snapshots.length === 0) continue; // Do not create tracks for instances that have implicit snapshots // and don't have a viewer. if (instance.hasImplicitSnapshots && !hasViewInfo) continue; visibleInstances.push(instance); } if (visibleInstances.length === 0) return; // Look up the constructor for this track, or use the default // constructor if none exists. var trackConstructor = tracing.tracks.ObjectInstanceTrack.getTrackConstructor(typeName); if (!trackConstructor) trackConstructor = tracing.tracks.ObjectInstanceTrack; var track = new trackConstructor(this.viewport); track.categoryFilter = this.categoryFilter_; track.objectInstances = visibleInstances; this.appendChild(track); didAppendAtLeastOneTrack = true; }, this); if (didAppendAtLeastOneTrack) this.appendChild(new SpacingTrack(this.viewport)); }, appendCounterTracks_: function() { // Add counter tracks for this process. var counters = base.dictionaryValues(this.processBase.counters). filter(this.categoryFilter.matchCounter, this.categoryFilter); counters.sort(tracing.trace_model.Counter.compare); // Create the counters for this process. counters.forEach(function(counter) { var track = new tracing.tracks.CounterTrack(this.viewport); track.categoryFilter = this.categoryFilter_; track.counter = counter; this.appendChild(track); this.appendChild(new SpacingTrack(this.viewport)); }.bind(this)); }, appendThreadTracks_: function() { // Get a sorted list of threads. var threads = base.dictionaryValues(this.processBase.threads). filter(function(thread) { return this.categoryFilter_.matchThread(thread); }, this); threads.sort(tracing.trace_model.Thread.compare); // Create the threads. threads.forEach(function(thread) { var track = new tracing.tracks.ThreadTrack(this.viewport); track.categoryFilter = this.categoryFilter_; track.thread = thread; if (!track.hasVisibleContent) return; this.appendChild(track); this.appendChild(new SpacingTrack(this.viewport)); }.bind(this)); } }; return { ProcessTrackBase: ProcessTrackBase }; });