summaryrefslogtreecommitdiffstats
path: root/devtools/server/actors/performance-recording.js
blob: ef590749535c2dda4461ff5a1c7550a10063a4d4 (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 { Actor, ActorClassWithSpec } = require("devtools/shared/protocol");
const { performanceRecordingSpec } = require("devtools/shared/specs/performance-recording");

loader.lazyRequireGetter(this, "merge", "sdk/util/object", true);
loader.lazyRequireGetter(this, "RecordingUtils",
  "devtools/shared/performance/recording-utils");
loader.lazyRequireGetter(this, "PerformanceRecordingCommon",
  "devtools/shared/performance/recording-common", true);

/**
 * This actor wraps the Performance module at devtools/shared/shared/performance.js
 * and provides RDP definitions.
 *
 * @see devtools/shared/shared/performance.js for documentation.
 */
const PerformanceRecordingActor = ActorClassWithSpec(performanceRecordingSpec, merge({
  form: function (detail) {
    if (detail === "actorid") {
      return this.actorID;
    }

    let form = {
      actor: this.actorID,  // actorID is set when this is added to a pool
      configuration: this._configuration,
      startingBufferStatus: this._startingBufferStatus,
      console: this._console,
      label: this._label,
      startTime: this._startTime,
      localStartTime: this._localStartTime,
      recording: this._recording,
      completed: this._completed,
      duration: this._duration,
    };

    // Only send profiler data once it exists and it has
    // not yet been sent
    if (this._profile && !this._sentFinalizedData) {
      form.finalizedData = true;
      form.profile = this.getProfile();
      form.systemHost = this.getHostSystemInfo();
      form.systemClient = this.getClientSystemInfo();
      this._sentFinalizedData = true;
    }

    return form;
  },

  /**
   * @param {object} conn
   * @param {object} options
   *        A hash of features that this recording is utilizing.
   * @param {object} meta
   *        A hash of temporary metadata for a recording that is recording
   *        (as opposed to an imported recording).
   */
  initialize: function (conn, options, meta) {
    Actor.prototype.initialize.call(this, conn);
    this._configuration = {
      withMarkers: options.withMarkers || false,
      withTicks: options.withTicks || false,
      withMemory: options.withMemory || false,
      withAllocations: options.withAllocations || false,
      allocationsSampleProbability: options.allocationsSampleProbability || 0,
      allocationsMaxLogLength: options.allocationsMaxLogLength || 0,
      bufferSize: options.bufferSize || 0,
      sampleFrequency: options.sampleFrequency || 1
    };

    this._console = !!options.console;
    this._label = options.label || "";

    if (meta) {
      // Store the start time roughly with Date.now() so when we
      // are checking the duration during a recording, we can get close
      // to the approximate duration to render elements without
      // making a real request
      this._localStartTime = Date.now();

      this._startTime = meta.startTime;
      this._startingBufferStatus = {
        position: meta.position,
        totalSize: meta.totalSize,
        generation: meta.generation
      };

      this._recording = true;
      this._markers = [];
      this._frames = [];
      this._memory = [];
      this._ticks = [];
      this._allocations = { sites: [], timestamps: [], frames: [], sizes: [] };

      this._systemHost = meta.systemHost || {};
      this._systemClient = meta.systemClient || {};
    }
  },

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

  /**
   * Internal utility called by the PerformanceActor and PerformanceFront on state changes
   * to update the internal state of the PerformanceRecording.
   *
   * @param {string} state
   * @param {object} extraData
   */
  _setState: function (state, extraData) {
    switch (state) {
      case "recording-started": {
        this._recording = true;
        break;
      }
      case "recording-stopping": {
        this._recording = false;
        break;
      }
      case "recording-stopped": {
        this._profile = extraData.profile;
        this._duration = extraData.duration;

        // We filter out all samples that fall out of current profile's range
        // since the profiler is continuously running. Because of this, sample
        // times are not guaranteed to have a zero epoch, so offset the
        // timestamps.
        RecordingUtils.offsetSampleTimes(this._profile, this._startTime);

        // Markers need to be sorted ascending by time, to be properly displayed
        // in a waterfall view.
        this._markers = this._markers.sort((a, b) => (a.start > b.start));

        this._completed = true;
        break;
      }
    }
  },

}, PerformanceRecordingCommon));

exports.PerformanceRecordingActor = PerformanceRecordingActor;