summaryrefslogtreecommitdiffstats
path: root/devtools/client/performance/modules/logic/telemetry.js
blob: b8e32217059fb77bbea437916ddf1d55a573a085 (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
/* 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 Telemetry = require("devtools/client/shared/telemetry");
const flags = require("devtools/shared/flags");
const EVENTS = require("devtools/client/performance/events");

const EVENT_MAP_FLAGS = new Map([
  [EVENTS.RECORDING_IMPORTED, "DEVTOOLS_PERFTOOLS_RECORDING_IMPORT_FLAG"],
  [EVENTS.RECORDING_EXPORTED, "DEVTOOLS_PERFTOOLS_RECORDING_EXPORT_FLAG"],
]);

const RECORDING_FEATURES = [
  "withMarkers", "withTicks", "withMemory", "withAllocations"
];

const SELECTED_VIEW_HISTOGRAM_NAME = "DEVTOOLS_PERFTOOLS_SELECTED_VIEW_MS";

function PerformanceTelemetry(emitter) {
  this._emitter = emitter;
  this._telemetry = new Telemetry();
  this.onFlagEvent = this.onFlagEvent.bind(this);
  this.onRecordingStateChange = this.onRecordingStateChange.bind(this);
  this.onViewSelected = this.onViewSelected.bind(this);

  for (let [event] of EVENT_MAP_FLAGS) {
    this._emitter.on(event, this.onFlagEvent);
  }

  this._emitter.on(EVENTS.RECORDING_STATE_CHANGE, this.onRecordingStateChange);
  this._emitter.on(EVENTS.UI_DETAILS_VIEW_SELECTED, this.onViewSelected);

  if (flags.testing) {
    this.recordLogs();
  }
}

PerformanceTelemetry.prototype.destroy = function () {
  if (this._previousView) {
    this._telemetry.stopTimer(SELECTED_VIEW_HISTOGRAM_NAME, this._previousView);
  }

  this._telemetry.destroy();
  for (let [event] of EVENT_MAP_FLAGS) {
    this._emitter.off(event, this.onFlagEvent);
  }
  this._emitter.off(EVENTS.RECORDING_STATE_CHANGE, this.onRecordingStateChange);
  this._emitter.off(EVENTS.UI_DETAILS_VIEW_SELECTED, this.onViewSelected);
  this._emitter = null;
};

PerformanceTelemetry.prototype.onFlagEvent = function (eventName, ...data) {
  this._telemetry.log(EVENT_MAP_FLAGS.get(eventName), true);
};

PerformanceTelemetry.prototype.onRecordingStateChange = function (_, status, model) {
  if (status != "recording-stopped") {
    return;
  }

  if (model.isConsole()) {
    this._telemetry.log("DEVTOOLS_PERFTOOLS_CONSOLE_RECORDING_COUNT", true);
  } else {
    this._telemetry.log("DEVTOOLS_PERFTOOLS_RECORDING_COUNT", true);
  }

  this._telemetry.log("DEVTOOLS_PERFTOOLS_RECORDING_DURATION_MS", model.getDuration());

  let config = model.getConfiguration();
  for (let k in config) {
    if (RECORDING_FEATURES.indexOf(k) !== -1) {
      this._telemetry.logKeyed("DEVTOOLS_PERFTOOLS_RECORDING_FEATURES_USED", k,
                               config[k]);
    }
  }
};

PerformanceTelemetry.prototype.onViewSelected = function (_, viewName) {
  if (this._previousView) {
    this._telemetry.stopTimer(SELECTED_VIEW_HISTOGRAM_NAME, this._previousView);
  }
  this._previousView = viewName;
  this._telemetry.startTimer(SELECTED_VIEW_HISTOGRAM_NAME);
};

/**
 * Utility to record histogram calls to this instance.
 * Should only be used in testing mode; throws otherwise.
 */
PerformanceTelemetry.prototype.recordLogs = function () {
  if (!flags.testing) {
    throw new Error("Can only record telemetry logs in tests.");
  }

  let originalLog = this._telemetry.log;
  let originalLogKeyed = this._telemetry.logKeyed;
  this._log = {};

  this._telemetry.log = (function (histo, data) {
    let results = this._log[histo] = this._log[histo] || [];
    results.push(data);
    originalLog(histo, data);
  }).bind(this);

  this._telemetry.logKeyed = (function (histo, key, data) {
    let results = this._log[histo] = this._log[histo] || [];
    results.push([key, data]);
    originalLogKeyed(histo, key, data);
  }).bind(this);
};

PerformanceTelemetry.prototype.getLogs = function () {
  if (!flags.testing) {
    throw new Error("Can only get telemetry logs in tests.");
  }

  return this._log;
};

exports.PerformanceTelemetry = PerformanceTelemetry;