| <!DOCTYPE html> |
| <!-- |
| Copyright (c) 2013 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. |
| --> |
| |
| <link rel="stylesheet" href="/extras/tcmalloc/heap_instance_track.css"> |
| <link rel="import" href="/core/tracks/stacked_bars_track.html"> |
| <link rel="import" href="/core/tracks/object_instance_track.html"> |
| <link rel="import" href="/core/event_presenter.html"> |
| <link rel="import" href="/base/sorted_array_utils.html"> |
| <link rel="import" href="/base/ui.html"> |
| |
| <script> |
| 'use strict'; |
| |
| tr.exportTo('tr.e.tcmalloc', function() { |
| var EventPresenter = tr.c.EventPresenter; |
| |
| /** |
| * A track that displays heap memory data. |
| * @constructor |
| * @extends {StackedBarsTrack} |
| */ |
| |
| var HeapInstanceTrack = tr.b.ui.define( |
| 'heap-instance-track', tr.c.tracks.StackedBarsTrack); |
| |
| HeapInstanceTrack.prototype = { |
| |
| __proto__: tr.c.tracks.StackedBarsTrack.prototype, |
| |
| decorate: function(viewport) { |
| tr.c.tracks.StackedBarsTrack.prototype.decorate.call(this, viewport); |
| this.classList.add('heap-instance-track'); |
| this.objectInstance_ = null; |
| }, |
| |
| set objectInstances(objectInstances) { |
| if (!objectInstances) { |
| this.objectInstance_ = []; |
| return; |
| } |
| if (objectInstances.length != 1) |
| throw new Error('Bad object instance count.'); |
| this.objectInstance_ = objectInstances[0]; |
| this.maxBytes_ = this.computeMaxBytes_( |
| this.objectInstance_.snapshots); |
| }, |
| |
| computeMaxBytes_: function(snapshots) { |
| var maxBytes = 0; |
| for (var i = 0; i < snapshots.length; i++) { |
| var snapshot = snapshots[i]; |
| // Sum all the current allocations in this snapshot. |
| var traceNames = Object.keys(snapshot.heap_.children); |
| var sumBytes = 0; |
| for (var j = 0; j < traceNames.length; j++) { |
| sumBytes += snapshot.heap_.children[traceNames[j]].currentBytes; |
| } |
| // Keep track of the maximum across all snapshots. |
| if (sumBytes > maxBytes) |
| maxBytes = sumBytes; |
| } |
| return maxBytes; |
| }, |
| |
| get height() { |
| return window.getComputedStyle(this).height; |
| }, |
| |
| set height(height) { |
| this.style.height = height; |
| }, |
| |
| draw: function(type, viewLWorld, viewRWorld) { |
| switch (type) { |
| case tr.c.tracks.DrawType.GENERAL_EVENT: |
| this.drawEvents_(viewLWorld, viewRWorld); |
| break; |
| } |
| }, |
| |
| drawEvents_: function(viewLWorld, viewRWorld) { |
| var ctx = this.context(); |
| var pixelRatio = window.devicePixelRatio || 1; |
| |
| var bounds = this.getBoundingClientRect(); |
| var width = bounds.width * pixelRatio; |
| var height = bounds.height * pixelRatio; |
| |
| // Culling parameters. |
| var dt = this.viewport.currentDisplayTransform; |
| |
| // Scale by the size of the largest snapshot. |
| var maxBytes = this.maxBytes_; |
| |
| var objectSnapshots = this.objectInstance_.snapshots; |
| var lowIndex = tr.b.findLowIndexInSortedArray( |
| objectSnapshots, |
| function(snapshot) { |
| return snapshot.ts; |
| }, |
| viewLWorld); |
| // Assure that the stack with the left edge off screen still gets drawn |
| if (lowIndex > 0) |
| lowIndex -= 1; |
| |
| for (var i = lowIndex; i < objectSnapshots.length; ++i) { |
| var snapshot = objectSnapshots[i]; |
| |
| var left = snapshot.ts; |
| if (left > viewRWorld) |
| break; |
| var leftView = dt.xWorldToView(left); |
| if (leftView < 0) |
| leftView = 0; |
| |
| // Compute the edges for the column graph bar. |
| var right; |
| if (i != objectSnapshots.length - 1) { |
| right = objectSnapshots[i + 1].ts; |
| } else { |
| // If this is the last snaphot of multiple snapshots, use the width of |
| // the previous snapshot for the width. |
| if (objectSnapshots.length > 1) |
| right = objectSnapshots[i].ts + (objectSnapshots[i].ts - |
| objectSnapshots[i - 1].ts); |
| else |
| // If there's only one snapshot, use max bounds as the width. |
| right = this.objectInstance_.parent.model.bounds.max; |
| } |
| |
| var rightView = dt.xWorldToView(right); |
| if (rightView > width) |
| rightView = width; |
| |
| // Floor the bounds to avoid a small gap between stacks. |
| leftView = Math.floor(leftView); |
| rightView = Math.floor(rightView); |
| |
| // Draw a stacked bar graph. Largest item is stored first in the |
| // heap data structure, so iterate backwards. Likewise draw from |
| // the bottom of the bar upwards. |
| var currentY = height; |
| var keys = Object.keys(snapshot.heap_.children); |
| for (var k = keys.length - 1; k >= 0; k--) { |
| var trace = snapshot.heap_.children[keys[k]]; |
| if (this.objectInstance_.selectedTraces && |
| this.objectInstance_.selectedTraces.length > 0 && |
| this.objectInstance_.selectedTraces[0] == keys[k]) { |
| // A trace selected in the analysis view is bright yellow. |
| ctx.fillStyle = 'rgb(239, 248, 206)'; |
| } else |
| ctx.fillStyle = EventPresenter.getBarSnapshotColor(snapshot, k); |
| |
| var barHeight = height * trace.currentBytes / maxBytes; |
| ctx.fillRect(leftView, currentY - barHeight, |
| Math.max(rightView - leftView, 1), barHeight); |
| currentY -= barHeight; |
| } |
| } |
| ctx.lineWidth = 1; |
| } |
| }; |
| |
| tr.c.tracks.ObjectInstanceTrack.register( |
| HeapInstanceTrack, |
| {typeName: 'memory::Heap'}); |
| |
| return { |
| HeapInstanceTrack: HeapInstanceTrack |
| }; |
| }); |
| </script> |