summaryrefslogtreecommitdiffstats
path: root/devtools/client/performance/views/details-memory-call-tree.js
blob: 883d92e63e42066b29700b7c8c1479f26bb5f0fe (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */
/* import-globals-from ../performance-controller.js */
/* import-globals-from ../performance-view.js */
/* globals DetailsSubview */
"use strict";

/**
 * CallTree view containing memory allocation sites, controlled by DetailsView.
 */
var MemoryCallTreeView = Heritage.extend(DetailsSubview, {

  rerenderPrefs: [
    "invert-call-tree"
  ],

  // Units are in milliseconds.
  rangeChangeDebounceTime: 100,

  /**
   * Sets up the view with event binding.
   */
  initialize: function () {
    DetailsSubview.initialize.call(this);

    this._onLink = this._onLink.bind(this);

    this.container = $("#memory-calltree-view > .call-tree-cells-container");
  },

  /**
   * Unbinds events.
   */
  destroy: function () {
    DetailsSubview.destroy.call(this);
  },

  /**
   * Method for handling all the set up for rendering a new call tree.
   *
   * @param object interval [optional]
   *        The { startTime, endTime }, in milliseconds.
   */
  render: function (interval = {}) {
    let options = {
      invertTree: PerformanceController.getOption("invert-call-tree")
    };
    let recording = PerformanceController.getCurrentRecording();
    let allocations = recording.getAllocations();
    let threadNode = this._prepareCallTree(allocations, interval, options);
    this._populateCallTree(threadNode, options);
    this.emit(EVENTS.UI_MEMORY_CALL_TREE_RENDERED);
  },

  /**
   * Fired on the "link" event for the call tree in this container.
   */
  _onLink: function (_, treeItem) {
    let { url, line } = treeItem.frame.getInfo();
    gToolbox.viewSourceInDebugger(url, line).then(success => {
      if (success) {
        this.emit(EVENTS.SOURCE_SHOWN_IN_JS_DEBUGGER);
      } else {
        this.emit(EVENTS.SOURCE_NOT_FOUND_IN_JS_DEBUGGER);
      }
    });
  },

  /**
   * Called when the recording is stopped and prepares data to
   * populate the call tree.
   */
  _prepareCallTree: function (allocations, { startTime, endTime }, options) {
    let thread = RecordingUtils.getProfileThreadFromAllocations(allocations);
    let { invertTree } = options;

    return new ThreadNode(thread, { startTime, endTime, invertTree });
  },

  /**
   * Renders the call tree.
   */
  _populateCallTree: function (frameNode, options = {}) {
    // If we have an empty profile (no samples), then don't invert the tree, as
    // it would hide the root node and a completely blank call tree space can be
    // mis-interpreted as an error.
    let inverted = options.invertTree && frameNode.samples > 0;

    let root = new CallView({
      frame: frameNode,
      inverted: inverted,
      // Root nodes are hidden in inverted call trees.
      hidden: inverted,
      // Call trees should only auto-expand when not inverted. Passing undefined
      // will default to the CALL_TREE_AUTO_EXPAND depth.
      autoExpandDepth: inverted ? 0 : undefined,
      // Some cells like the time duration and cost percentage don't make sense
      // for a memory allocations call tree.
      visibleCells: {
        selfCount: true,
        count: true,
        selfSize: true,
        size: true,
        selfCountPercentage: true,
        countPercentage: true,
        selfSizePercentage: true,
        sizePercentage: true,
        function: true
      }
    });

    // Bind events.
    root.on("link", this._onLink);

    // Pipe "focus" events to the view, mostly for tests
    root.on("focus", () => this.emit("focus"));

    // Clear out other call trees.
    this.container.innerHTML = "";
    root.attachTo(this.container);

    // Memory allocation samples don't contain cateogry labels.
    root.toggleCategories(false);
  },

  toString: () => "[object MemoryCallTreeView]"
});

EventEmitter.decorate(MemoryCallTreeView);