• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
7base.requireStylesheet('tcmalloc.tcmalloc_snapshot_view');
8
9base.require('tracing.analysis.object_snapshot_view');
10base.require('tracing.analysis.util');
11
12base.exportTo('tcmalloc', function() {
13
14  var tsRound = tracing.analysis.tsRound;
15
16  /*
17   * Displays a heap memory snapshot in a human readable form.
18   * @constructor
19   */
20  var TcmallocSnapshotView = ui.define(
21      'heap-snapshot-view',
22      tracing.analysis.ObjectSnapshotView);
23
24  TcmallocSnapshotView.prototype = {
25    __proto__: tracing.analysis.ObjectSnapshotView.prototype,
26
27    decorate: function() {
28      this.classList.add('tcmalloc-snapshot-view');
29    },
30
31    updateContents: function() {
32      var snapshot = this.objectSnapshot_;
33      if (!snapshot || !snapshot.heap_) {
34        this.textContent = 'No heap found.';
35        return;
36      }
37      // Clear old snapshot view.
38      this.textContent = '';
39
40      // Note: "total" may actually be less than the largest allocation bin.
41      // This might happen if one stack is doing a lot of allocation, then
42      // passing off to another stack for deallocation.  That stack will
43      // have a high "current bytes" count and the other one might be
44      // negative or zero. So "total" may be smaller than the largest trace.
45      var subhead = document.createElement('div');
46      subhead.textContent = 'Retaining ' +
47          this.getByteString_(snapshot.total_.currentBytes) + ' in ' +
48          snapshot.total_.currentAllocs +
49          ' allocations. Showing > 0.1 MB.';
50      subhead.className = 'subhead';
51      this.appendChild(subhead);
52
53      // Build a nested tree-view of allocations
54      var myList = this.buildAllocList_(snapshot.heap_, false);
55      this.appendChild(myList);
56    },
57
58    /**
59     * Creates a nested list with clickable entries.
60     * @param {Object} heapEntry The current trace heap entry.
61     * @param {boolean} hide Whether this list is hidden by default.
62     * @return {Element} A <ul> list element.
63     */
64    buildAllocList_: function(heapEntry, hide) {
65      var myList = document.createElement('ul');
66      myList.hidden = hide;
67      var keys = Object.keys(heapEntry.children);
68      keys.sort(function(a, b) {
69        // Sort from large to small.
70        return heapEntry.children[b].currentBytes -
71            heapEntry.children[a].currentBytes;
72      });
73      for (var i = 0; i < keys.length; i++) {
74        var traceName = keys[i];
75        var trace = heapEntry.children[traceName];
76        // Don't show small nodes - they make things harder to see.
77        if (trace.currentBytes < 100 * 1024)
78          continue;
79        var childCount = Object.keys(trace.children).length;
80        var isLeaf = childCount == 0;
81        var myItem = this.buildItem_(
82            traceName, isLeaf, trace.currentBytes, trace.currentAllocs);
83        myList.appendChild(myItem);
84        // Build a nested <ul> list of my children.
85        if (childCount > 0)
86          myItem.appendChild(this.buildAllocList_(trace, true));
87      }
88      return myList;
89    },
90
91    /*
92     * Returns a <li> for an allocation traceName of size bytes.
93     */
94    buildItem_: function(traceName, isLeaf, bytes, allocs) {
95      var myItem = document.createElement('li');
96      myItem.className = 'trace-item';
97      myItem.id = traceName;
98
99      var byteDiv = document.createElement('div');
100      byteDiv.textContent = this.getByteString_(bytes);
101      byteDiv.className = 'trace-bytes';
102      myItem.appendChild(byteDiv);
103
104      if (traceName.length == 0) {
105        // The empty trace name indicates that the allocations occurred at
106        // this trace level, not in a sub-trace. This looks weird as the
107        // empty string, so replace it with something non-empty and don't
108        // give that line an expander.
109        traceName = '(here)';
110      } else if (traceName.indexOf('..') == 0) {
111        // Tasks in RunTask have special handling. They show the path of the
112        // filename. Convert '../../foo.cc' into 'RunTask from foo.cc'.
113        var lastSlash = traceName.lastIndexOf('/');
114        if (lastSlash != -1)
115          traceName = 'Task from ' + traceName.substr(lastSlash + 1);
116      }
117      var traceDiv = document.createElement('div');
118      traceDiv.textContent = traceName;
119      traceDiv.className = 'trace-name';
120      myItem.appendChild(traceDiv);
121
122      // Don't allow leaf nodes to be expanded.
123      if (isLeaf)
124        return myItem;
125
126      // Expand the element when it is clicked.
127      var self = this;
128      myItem.addEventListener('click', function(event) {
129        // Allow click on the +/- image (li) or child divs.
130        if (this == event.target || this == event.target.parentElement) {
131          this.classList.toggle('expanded');
132          var child = this.querySelector('ul');
133          child.hidden = !child.hidden;
134          // Highlight this stack trace in the timeline view.
135          self.onItemClicked_(this);
136        }
137      });
138      myItem.classList.add('collapsed');
139      return myItem;
140    },
141
142    onItemClicked_: function(traceItem) {
143      // Compute the full stack trace the user just clicked.
144      var traces = [];
145      while (traceItem.classList.contains('trace-item')) {
146        var traceNameDiv = traceItem.firstElementChild.nextElementSibling;
147        traces.unshift(traceNameDiv.textContent);
148        var traceNameUl = traceItem.parentElement;
149        traceItem = traceNameUl.parentElement;
150      }
151      // Tell the instance that this stack trace is selected.
152      var instance = this.objectSnapshot_.objectInstance;
153      instance.selectedTraces = traces;
154      // Invalid the viewport to cause a redraw.
155      var trackView = document.querySelector('.timeline-track-view');
156      trackView.viewport_.dispatchChangeEvent();
157    },
158
159    /*
160     * Returns a human readable string for a size in bytes.
161     */
162    getByteString_: function(bytes) {
163      var mb = bytes / 1024 / 1024;
164      return mb.toFixed(1) + ' MB';
165    }
166  };
167
168  tracing.analysis.ObjectSnapshotView.register(
169      'memory::Heap', TcmallocSnapshotView);
170
171  return {
172    TcmallocSnapshotView: TcmallocSnapshotView
173  };
174
175});
176