summaryrefslogtreecommitdiffstats
path: root/devtools/shared/fronts/performance.js
blob: da5a9ffb0706ae816c32ef25af6f32ec49f7e943 (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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
/* 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/. */
"use strict";

const { Cu } = require("chrome");
const { Front, FrontClassWithSpec, custom, preEvent } = require("devtools/shared/protocol");
const { PerformanceRecordingFront } = require("devtools/shared/fronts/performance-recording");
const { performanceSpec } = require("devtools/shared/specs/performance");
const { Task } = require("devtools/shared/task");

loader.lazyRequireGetter(this, "PerformanceIO",
  "devtools/client/performance/modules/io");
loader.lazyRequireGetter(this, "LegacyPerformanceFront",
  "devtools/client/performance/legacy/front", true);
loader.lazyRequireGetter(this, "getSystemInfo",
  "devtools/shared/system", true);

const PerformanceFront = FrontClassWithSpec(performanceSpec, {
  initialize: function (client, form) {
    Front.prototype.initialize.call(this, client, form);
    this.actorID = form.performanceActor;
    this.manage(this);
  },

  destroy: function () {
    Front.prototype.destroy.call(this);
  },

  /**
   * Conenct to the server, and handle once-off tasks like storing traits
   * or system info.
   */
  connect: custom(Task.async(function* () {
    let systemClient = yield getSystemInfo();
    let { traits } = yield this._connect({ systemClient });
    this._traits = traits;

    return this._traits;
  }), {
    impl: "_connect"
  }),

  get traits() {
    if (!this._traits) {
      Cu.reportError("Cannot access traits of PerformanceFront before " +
                     "calling `connect()`.");
    }
    return this._traits;
  },

  /**
   * Pass in a PerformanceRecording and get a normalized value from 0 to 1 of how much
   * of this recording's lifetime remains without being overwritten.
   *
   * @param {PerformanceRecording} recording
   * @return {number?}
   */
  getBufferUsageForRecording: function (recording) {
    if (!recording.isRecording()) {
      return void 0;
    }
    let {
      position: currentPosition,
      totalSize,
      generation: currentGeneration
    } = this._currentBufferStatus;
    let {
      position: origPosition,
      generation: origGeneration
    } = recording.getStartingBufferStatus();

    let normalizedCurrent = (totalSize * (currentGeneration - origGeneration)) +
                            currentPosition;
    let percent = (normalizedCurrent - origPosition) / totalSize;

    // Clamp between 0 and 1; can get negative percentage values when a new
    // recording starts and the currentBufferStatus has not yet been updated. Rather
    // than fetching another status update, just clamp to 0, and this will be updated
    // on the next profiler-status event.
    if (percent < 0) {
      return 0;
    } else if (percent > 1) {
      return 1;
    }

    return percent;
  },

  /**
   * Loads a recording from a file.
   *
   * @param {nsILocalFile} file
   *        The file to import the data from.
   * @return {Promise<PerformanceRecordingFront>}
   */
  importRecording: function (file) {
    return PerformanceIO.loadRecordingFromFile(file).then(recordingData => {
      let model = new PerformanceRecordingFront();
      model._imported = true;
      model._label = recordingData.label || "";
      model._duration = recordingData.duration;
      model._markers = recordingData.markers;
      model._frames = recordingData.frames;
      model._memory = recordingData.memory;
      model._ticks = recordingData.ticks;
      model._allocations = recordingData.allocations;
      model._profile = recordingData.profile;
      model._configuration = recordingData.configuration || {};
      model._systemHost = recordingData.systemHost;
      model._systemClient = recordingData.systemClient;
      return model;
    });
  },

  /**
   * Store profiler status when the position has been update so we can
   * calculate recording's buffer percentage usage after emitting the event.
   */
  _onProfilerStatus: preEvent("profiler-status", function (data) {
    this._currentBufferStatus = data;
  }),

  /**
   * For all PerformanceRecordings that are recording, and needing realtime markers,
   * apply the timeline data to the front PerformanceRecording (so we only have one event
   * for each timeline data chunk as they could be shared amongst several recordings).
   */
  _onTimelineEvent: preEvent("timeline-data", function (type, data, recordings) {
    for (let recording of recordings) {
      recording._addTimelineData(type, data);
    }
  }),
});

exports.PerformanceFront = PerformanceFront;

exports.createPerformanceFront = function createPerformanceFront(target) {
  // If we force legacy mode, or the server does not have a performance actor (< Fx42),
  // use our LegacyPerformanceFront which will handle
  // the communication over RDP to other underlying actors.
  if (target.TEST_PERFORMANCE_LEGACY_FRONT || !target.form.performanceActor) {
    return new LegacyPerformanceFront(target);
  }
  // If our server has a PerformanceActor implementation, set this
  // up like a normal front.
  return new PerformanceFront(target.client, target.form);
};