1 // Copyright (c) 2013 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.requireStylesheet('tcmalloc.heap_instance_track'); 8 9 base.require('base.sorted_array_utils'); 10 base.require('tracing.tracks.heading_track'); 11 base.require('tracing.tracks.object_instance_track'); 12 base.require('tracing.color_scheme'); 13 base.require('ui'); 14 15 base.exportTo('tcmalloc', function() { 16 17 var palette = tracing.getColorPalette(); 18 var highlightIdBoost = tracing.getColorPaletteHighlightIdBoost(); 19 20 /** 21 * A track that displays heap memory data. 22 * @constructor 23 * @extends {HeadingTrack} 24 */ 25 26 var HeapInstanceTrack = ui.define( 27 'heap-instance-track', tracing.tracks.HeadingTrack); 28 29 HeapInstanceTrack.prototype = { 30 31 __proto__: tracing.tracks.HeadingTrack.prototype, 32 33 decorate: function(viewport) { 34 tracing.tracks.HeadingTrack.prototype.decorate.call(this, viewport); 35 this.classList.add('heap-instance-track'); 36 this.objectInstance_ = null; 37 }, 38 39 set objectInstances(objectInstances) { 40 if (!objectInstances) { 41 this.objectInstance_ = []; 42 return; 43 } 44 if (objectInstances.length != 1) 45 throw new Error('Bad object instance count.'); 46 this.objectInstance_ = objectInstances[0]; 47 this.maxBytes_ = this.computeMaxBytes_( 48 this.objectInstance_.snapshots); 49 }, 50 51 computeMaxBytes_: function(snapshots) { 52 var maxBytes = 0; 53 for (var i = 0; i < snapshots.length; i++) { 54 var snapshot = snapshots[i]; 55 // Sum all the current allocations in this snapshot. 56 var traceNames = Object.keys(snapshot.heap_.children); 57 var sumBytes = 0; 58 for (var j = 0; j < traceNames.length; j++) { 59 sumBytes += snapshot.heap_.children[traceNames[j]].currentBytes; 60 } 61 // Keep track of the maximum across all snapshots. 62 if (sumBytes > maxBytes) 63 maxBytes = sumBytes; 64 } 65 return maxBytes; 66 }, 67 68 get height() { 69 return window.getComputedStyle(this).height; 70 }, 71 72 set height(height) { 73 this.style.height = height; 74 }, 75 76 draw: function(type, viewLWorld, viewRWorld) { 77 switch (type) { 78 case tracing.tracks.DrawType.SLICE: 79 this.drawSlices_(viewLWorld, viewRWorld); 80 break; 81 } 82 }, 83 84 drawSlices_: function(viewLWorld, viewRWorld) { 85 var ctx = this.context(); 86 var pixelRatio = window.devicePixelRatio || 1; 87 88 var bounds = this.getBoundingClientRect(); 89 var width = bounds.width * pixelRatio; 90 var height = bounds.height * pixelRatio; 91 92 // Culling parameters. 93 var vp = this.viewport; 94 95 // Scale by the size of the largest snapshot. 96 var maxBytes = this.maxBytes_; 97 98 var objectSnapshots = this.objectInstance_.snapshots; 99 var lowIndex = base.findLowIndexInSortedArray( 100 objectSnapshots, 101 function(snapshot) { 102 return snapshot.ts; 103 }, 104 viewLWorld); 105 // Assure that the stack with the left edge off screen still gets drawn 106 if (lowIndex > 0) 107 lowIndex -= 1; 108 109 for (var i = lowIndex; i < objectSnapshots.length; ++i) { 110 var snapshot = objectSnapshots[i]; 111 112 var left = snapshot.ts; 113 if (left > viewRWorld) 114 break; 115 var leftView = vp.xWorldToView(left); 116 if (leftView < 0) 117 leftView = 0; 118 119 // Compute the edges for the column graph bar. 120 var right; 121 if (i < objectSnapshots.length - 1) 122 right = objectSnapshots[i + 1].ts; 123 else 124 right = objectSnapshots[objectSnapshots.length - 1].ts + 5000; 125 var rightView = vp.xWorldToView(right); 126 if (rightView > width) 127 rightView = width; 128 129 // Floor the bounds to avoid a small gap between stacks. 130 leftView = Math.floor(leftView); 131 rightView = Math.floor(rightView); 132 133 // Draw a stacked bar graph. Largest item is stored first in the 134 // heap data structure, so iterate backwards. Likewise draw from 135 // the bottom of the bar upwards. 136 var currentY = height; 137 var keys = Object.keys(snapshot.heap_.children); 138 for (var k = keys.length - 1; k >= 0; k--) { 139 var trace = snapshot.heap_.children[keys[k]]; 140 if (this.objectInstance_.selectedTraces && 141 this.objectInstance_.selectedTraces.length > 0 && 142 this.objectInstance_.selectedTraces[0] == keys[k]) { 143 // A trace selected in the analysis view is bright yellow. 144 ctx.fillStyle = 'rgb(239, 248, 206)'; 145 } else { 146 // Selected snapshots get a lighter color. 147 var colorId = snapshot.selected ? 148 snapshot.objectInstance.colorId + highlightIdBoost : 149 snapshot.objectInstance.colorId; 150 ctx.fillStyle = palette[colorId + k]; 151 } 152 153 var barHeight = height * trace.currentBytes / maxBytes; 154 ctx.fillRect(leftView, currentY - barHeight, 155 Math.max(rightView - leftView, 1), barHeight); 156 currentY -= barHeight; 157 } 158 } 159 ctx.lineWidth = 1; 160 }, 161 162 /** 163 * Used to hit-test clicks in the graph. 164 */ 165 addIntersectingItemsInRangeToSelectionInWorldSpace: function( 166 loWX, hiWX, viewPixWidthWorld, selection) { 167 var that = this; 168 function onSnapshotHit(snapshot) { 169 selection.addObjectSnapshot(that, snapshot); 170 } 171 base.iterateOverIntersectingIntervals( 172 this.objectInstance_.snapshots, 173 function(x) { return x.ts; }, 174 function(x) { return 5000; }, 175 loWX, hiWX, 176 onSnapshotHit); 177 }, 178 179 /** 180 * Add the item to the left or right of the provided hit, if any, to the 181 * selection. 182 * @param {slice} The current slice. 183 * @param {Number} offset Number of slices away from the hit to look. 184 * @param {Selection} selection The selection to add a hit to, 185 * if found. 186 * @return {boolean} Whether a hit was found. 187 * @private 188 */ 189 addItemNearToProvidedHitToSelection: function(hit, offset, selection) { 190 if (hit instanceof tracing.SelectionObjectSnapshotHit) { 191 var objectSnapshots = this.objectInstance_.snapshots; 192 var index = objectSnapshots.indexOf(hit.objectSnapshot); 193 var newIndex = index + offset; 194 if (newIndex >= 0 && newIndex < objectSnapshots.length) { 195 selection.addObjectSnapshot(this, objectSnapshots[newIndex]); 196 return true; 197 } 198 } else { 199 throw new Error('Unrecognized hit'); 200 } 201 return false; 202 }, 203 204 addAllObjectsMatchingFilterToSelection: function(filter, selection) { 205 } 206 }; 207 208 tracing.tracks.ObjectInstanceTrack.register( 209 'memory::Heap', HeapInstanceTrack); 210 211 return { 212 HeapInstanceTrack: HeapInstanceTrack 213 }; 214 }); 215