Home | History | Annotate | Download | only in tcmalloc
      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