mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-26 14:22:01 +00:00
Bug 1122058 - Add telemetry hooks to various performance tools actions. r=vp,mratcliffe
This commit is contained in:
parent
8ee72d869e
commit
615f6de19b
106
browser/devtools/performance/events.js
Normal file
106
browser/devtools/performance/events.js
Normal file
@ -0,0 +1,106 @@
|
||||
/* 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";
|
||||
|
||||
module.exports = {
|
||||
// Fired by the PerformanceController and OptionsView when a pref changes.
|
||||
PREF_CHANGED: "Performance:PrefChanged",
|
||||
|
||||
// Fired by the PerformanceController when the devtools theme changes.
|
||||
THEME_CHANGED: "Performance:ThemeChanged",
|
||||
|
||||
// Emitted by the PerformanceView when the state (display mode) changes,
|
||||
// for example when switching between "empty", "recording" or "recorded".
|
||||
// This causes certain panels to be hidden or visible.
|
||||
UI_STATE_CHANGED: "Performance:UI:StateChanged",
|
||||
|
||||
// Emitted by the PerformanceView on clear button click
|
||||
UI_CLEAR_RECORDINGS: "Performance:UI:ClearRecordings",
|
||||
|
||||
// Emitted by the PerformanceView on record button click
|
||||
UI_START_RECORDING: "Performance:UI:StartRecording",
|
||||
UI_STOP_RECORDING: "Performance:UI:StopRecording",
|
||||
|
||||
// Emitted by the PerformanceView on import button click
|
||||
UI_IMPORT_RECORDING: "Performance:UI:ImportRecording",
|
||||
// Emitted by the RecordingsView on export button click
|
||||
UI_EXPORT_RECORDING: "Performance:UI:ExportRecording",
|
||||
|
||||
// When a new recording is being tracked in the panel.
|
||||
NEW_RECORDING: "Performance:NewRecording",
|
||||
|
||||
// When a recording is started or stopped or stopping via the PerformanceController
|
||||
RECORDING_STATE_CHANGE: "Performance:RecordingStateChange",
|
||||
|
||||
// Emitted by the PerformanceController or RecordingView
|
||||
// when a recording model is selected
|
||||
RECORDING_SELECTED: "Performance:RecordingSelected",
|
||||
|
||||
// When recordings have been cleared out
|
||||
RECORDINGS_CLEARED: "Performance:RecordingsCleared",
|
||||
|
||||
// When a recording is exported via the PerformanceController
|
||||
RECORDING_EXPORTED: "Performance:RecordingExported",
|
||||
|
||||
// Emitted by the PerformanceController when a recording is imported.
|
||||
// Unless you're interested in specifically imported recordings, like in tests
|
||||
// or telemetry, you should probably use the normal RECORDING_STATE_CHANGE in the UI.
|
||||
RECORDING_IMPORTED: "Performance:RecordingImported",
|
||||
|
||||
// When the front has updated information on the profiler's circular buffer
|
||||
PROFILER_STATUS_UPDATED: "Performance:BufferUpdated",
|
||||
|
||||
// When the PerformanceView updates the display of the buffer status
|
||||
UI_BUFFER_STATUS_UPDATED: "Performance:UI:BufferUpdated",
|
||||
|
||||
// Emitted by the OptimizationsListView when it renders new optimization
|
||||
// data and clears the optimization data
|
||||
OPTIMIZATIONS_RESET: "Performance:UI:OptimizationsReset",
|
||||
OPTIMIZATIONS_RENDERED: "Performance:UI:OptimizationsRendered",
|
||||
|
||||
// Emitted by the OverviewView when more data has been rendered
|
||||
OVERVIEW_RENDERED: "Performance:UI:OverviewRendered",
|
||||
FRAMERATE_GRAPH_RENDERED: "Performance:UI:OverviewFramerateRendered",
|
||||
MARKERS_GRAPH_RENDERED: "Performance:UI:OverviewMarkersRendered",
|
||||
MEMORY_GRAPH_RENDERED: "Performance:UI:OverviewMemoryRendered",
|
||||
|
||||
// Emitted by the OverviewView when a range has been selected in the graphs
|
||||
OVERVIEW_RANGE_SELECTED: "Performance:UI:OverviewRangeSelected",
|
||||
|
||||
// Emitted by the DetailsView when a subview is selected
|
||||
DETAILS_VIEW_SELECTED: "Performance:UI:DetailsViewSelected",
|
||||
|
||||
// Emitted by the WaterfallView when it has been rendered
|
||||
WATERFALL_RENDERED: "Performance:UI:WaterfallRendered",
|
||||
|
||||
// Emitted by the JsCallTreeView when a call tree has been rendered
|
||||
JS_CALL_TREE_RENDERED: "Performance:UI:JsCallTreeRendered",
|
||||
|
||||
// Emitted by the JsFlameGraphView when it has been rendered
|
||||
JS_FLAMEGRAPH_RENDERED: "Performance:UI:JsFlameGraphRendered",
|
||||
|
||||
// Emitted by the MemoryCallTreeView when a call tree has been rendered
|
||||
MEMORY_CALL_TREE_RENDERED: "Performance:UI:MemoryCallTreeRendered",
|
||||
|
||||
// Emitted by the MemoryFlameGraphView when it has been rendered
|
||||
MEMORY_FLAMEGRAPH_RENDERED: "Performance:UI:MemoryFlameGraphRendered",
|
||||
|
||||
// When a source is shown in the JavaScript Debugger at a specific location.
|
||||
SOURCE_SHOWN_IN_JS_DEBUGGER: "Performance:UI:SourceShownInJsDebugger",
|
||||
SOURCE_NOT_FOUND_IN_JS_DEBUGGER: "Performance:UI:SourceNotFoundInJsDebugger",
|
||||
|
||||
// These are short hands for the RECORDING_STATE_CHANGE event to make refactoring
|
||||
// tests easier and in rare cases (telemetry). UI components should use
|
||||
// RECORDING_STATE_CHANGE in almost all cases,
|
||||
RECORDING_STARTED: "Performance:RecordingStarted",
|
||||
RECORDING_WILL_STOP: "Performance:RecordingWillStop",
|
||||
RECORDING_STOPPED: "Performance:RecordingStopped",
|
||||
|
||||
// Fired by the PerformanceController when `populateWithRecordings` is finished.
|
||||
RECORDINGS_SEEDED: "Performance:RecordingsSeeded",
|
||||
|
||||
// Emitted by the PerformanceController when `PerformanceController.stopRecording()`
|
||||
// is completed; used in tests, to know when a manual UI click is finished.
|
||||
CONTROLLER_STOPPED_RECORDING: "Performance:Controller:StoppedRecording",
|
||||
};
|
122
browser/devtools/performance/modules/logic/telemetry.js
Normal file
122
browser/devtools/performance/modules/logic/telemetry.js
Normal file
@ -0,0 +1,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";
|
||||
|
||||
loader.lazyRequireGetter(this, "Telemetry",
|
||||
"devtools/shared/telemetry");
|
||||
loader.lazyRequireGetter(this, "Services",
|
||||
"resource://gre/modules/Services.jsm", true);
|
||||
loader.lazyRequireGetter(this, "DevToolsUtils",
|
||||
"devtools/toolkit/DevToolsUtils");
|
||||
loader.lazyRequireGetter(this, "EVENTS",
|
||||
"devtools/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", "withJITOptimizations"
|
||||
];
|
||||
|
||||
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.onRecordingStopped = this.onRecordingStopped.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_STOPPED, this.onRecordingStopped);
|
||||
this._emitter.on(EVENTS.DETAILS_VIEW_SELECTED, this.onViewSelected);
|
||||
|
||||
if (DevToolsUtils.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_STOPPED, this.onRecordingStopped);
|
||||
this._emitter.off(EVENTS.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.onRecordingStopped = function (_, model) {
|
||||
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 (!DevToolsUtils.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 (!DevToolsUtils.testing) {
|
||||
throw new Error("Can only get telemetry logs in tests.");
|
||||
}
|
||||
|
||||
return this._log;
|
||||
};
|
||||
|
||||
exports.PerformanceTelemetry = PerformanceTelemetry;
|
@ -4,10 +4,12 @@
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
EXTRA_JS_MODULES.devtools.performance += [
|
||||
'events.js',
|
||||
'modules/global.js',
|
||||
'modules/logic/frame-utils.js',
|
||||
'modules/logic/jit.js',
|
||||
'modules/logic/marker-utils.js',
|
||||
'modules/logic/telemetry.js',
|
||||
'modules/logic/tree-model.js',
|
||||
'modules/logic/waterfall-utils.js',
|
||||
'modules/markers.js',
|
||||
|
@ -9,6 +9,9 @@ const { loader, require } = Cu.import("resource://gre/modules/devtools/Loader.js
|
||||
const { Task } = require("resource://gre/modules/Task.jsm");
|
||||
const { Heritage, ViewHelpers, WidgetMethods } = require("resource:///modules/devtools/ViewHelpers.jsm");
|
||||
|
||||
// Events emitted by various objects in the panel.
|
||||
const EVENTS = require("devtools/performance/events");
|
||||
|
||||
loader.lazyRequireGetter(this, "Services");
|
||||
loader.lazyRequireGetter(this, "promise");
|
||||
loader.lazyRequireGetter(this, "EventEmitter",
|
||||
@ -22,6 +25,8 @@ loader.lazyRequireGetter(this, "system",
|
||||
|
||||
loader.lazyRequireGetter(this, "L10N",
|
||||
"devtools/performance/global", true);
|
||||
loader.lazyRequireGetter(this, "PerformanceTelemetry",
|
||||
"devtools/performance/telemetry", true);
|
||||
loader.lazyRequireGetter(this, "TIMELINE_BLUEPRINT",
|
||||
"devtools/performance/markers", true);
|
||||
loader.lazyRequireGetter(this, "RecordingUtils",
|
||||
@ -69,108 +74,6 @@ loader.lazyImporter(this, "PluralForm",
|
||||
|
||||
const BRANCH_NAME = "devtools.performance.ui.";
|
||||
|
||||
// Events emitted by various objects in the panel.
|
||||
const EVENTS = {
|
||||
// Fired by the PerformanceController and OptionsView when a pref changes.
|
||||
PREF_CHANGED: "Performance:PrefChanged",
|
||||
|
||||
// Fired by the PerformanceController when the devtools theme changes.
|
||||
THEME_CHANGED: "Performance:ThemeChanged",
|
||||
|
||||
// Emitted by the PerformanceView when the state (display mode) changes,
|
||||
// for example when switching between "empty", "recording" or "recorded".
|
||||
// This causes certain panels to be hidden or visible.
|
||||
UI_STATE_CHANGED: "Performance:UI:StateChanged",
|
||||
|
||||
// Emitted by the PerformanceView on clear button click
|
||||
UI_CLEAR_RECORDINGS: "Performance:UI:ClearRecordings",
|
||||
|
||||
// Emitted by the PerformanceView on record button click
|
||||
UI_START_RECORDING: "Performance:UI:StartRecording",
|
||||
UI_STOP_RECORDING: "Performance:UI:StopRecording",
|
||||
|
||||
// Emitted by the PerformanceView on import button click
|
||||
UI_IMPORT_RECORDING: "Performance:UI:ImportRecording",
|
||||
// Emitted by the RecordingsView on export button click
|
||||
UI_EXPORT_RECORDING: "Performance:UI:ExportRecording",
|
||||
|
||||
// When a new recording is being tracked in the panel.
|
||||
NEW_RECORDING: "Performance:NewRecording",
|
||||
|
||||
// When a recording is started or stopped or stopping via the PerformanceController
|
||||
RECORDING_STATE_CHANGE: "Performance:RecordingStateChange",
|
||||
|
||||
// Emitted by the PerformanceController or RecordingView
|
||||
// when a recording model is selected
|
||||
RECORDING_SELECTED: "Performance:RecordingSelected",
|
||||
|
||||
// When recordings have been cleared out
|
||||
RECORDINGS_CLEARED: "Performance:RecordingsCleared",
|
||||
|
||||
// When a recording is exported via the PerformanceController
|
||||
RECORDING_EXPORTED: "Performance:RecordingExported",
|
||||
|
||||
// When the front has updated information on the profiler's circular buffer
|
||||
PROFILER_STATUS_UPDATED: "Performance:BufferUpdated",
|
||||
|
||||
// When the PerformanceView updates the display of the buffer status
|
||||
UI_BUFFER_STATUS_UPDATED: "Performance:UI:BufferUpdated",
|
||||
|
||||
// Emitted by the OptimizationsListView when it renders new optimization
|
||||
// data and clears the optimization data
|
||||
OPTIMIZATIONS_RESET: "Performance:UI:OptimizationsReset",
|
||||
OPTIMIZATIONS_RENDERED: "Performance:UI:OptimizationsRendered",
|
||||
|
||||
// Emitted by the OverviewView when more data has been rendered
|
||||
OVERVIEW_RENDERED: "Performance:UI:OverviewRendered",
|
||||
FRAMERATE_GRAPH_RENDERED: "Performance:UI:OverviewFramerateRendered",
|
||||
MARKERS_GRAPH_RENDERED: "Performance:UI:OverviewMarkersRendered",
|
||||
MEMORY_GRAPH_RENDERED: "Performance:UI:OverviewMemoryRendered",
|
||||
|
||||
// Emitted by the OverviewView when a range has been selected in the graphs
|
||||
OVERVIEW_RANGE_SELECTED: "Performance:UI:OverviewRangeSelected",
|
||||
|
||||
// Emitted by the DetailsView when a subview is selected
|
||||
DETAILS_VIEW_SELECTED: "Performance:UI:DetailsViewSelected",
|
||||
|
||||
// Emitted by the WaterfallView when it has been rendered
|
||||
WATERFALL_RENDERED: "Performance:UI:WaterfallRendered",
|
||||
|
||||
// Emitted by the JsCallTreeView when a call tree has been rendered
|
||||
JS_CALL_TREE_RENDERED: "Performance:UI:JsCallTreeRendered",
|
||||
|
||||
// Emitted by the JsFlameGraphView when it has been rendered
|
||||
JS_FLAMEGRAPH_RENDERED: "Performance:UI:JsFlameGraphRendered",
|
||||
|
||||
// Emitted by the MemoryCallTreeView when a call tree has been rendered
|
||||
MEMORY_CALL_TREE_RENDERED: "Performance:UI:MemoryCallTreeRendered",
|
||||
|
||||
// Emitted by the MemoryFlameGraphView when it has been rendered
|
||||
MEMORY_FLAMEGRAPH_RENDERED: "Performance:UI:MemoryFlameGraphRendered",
|
||||
|
||||
// When a source is shown in the JavaScript Debugger at a specific location.
|
||||
SOURCE_SHOWN_IN_JS_DEBUGGER: "Performance:UI:SourceShownInJsDebugger",
|
||||
SOURCE_NOT_FOUND_IN_JS_DEBUGGER: "Performance:UI:SourceNotFoundInJsDebugger",
|
||||
|
||||
// These are short hands for the RECORDING_STATE_CHANGE event to make refactoring
|
||||
// tests easier. UI components should use RECORDING_STATE_CHANGE, and these are
|
||||
// deprecated for test usage only.
|
||||
RECORDING_STARTED: "Performance:RecordingStarted",
|
||||
RECORDING_WILL_STOP: "Performance:RecordingWillStop",
|
||||
RECORDING_STOPPED: "Performance:RecordingStopped",
|
||||
|
||||
// Fired by the PerformanceController when `populateWithRecordings` is finished.
|
||||
RECORDINGS_SEEDED: "Performance:RecordingsSeeded",
|
||||
|
||||
// Emitted by the PerformanceController when `PerformanceController.stopRecording()`
|
||||
// is completed; used in tests, to know when a manual UI click is finished.
|
||||
CONTROLLER_STOPPED_RECORDING: "Performance:Controller:StoppedRecording",
|
||||
|
||||
// Emitted by the PerformanceController when a recording is imported. Used
|
||||
// only in tests. Should use the normal RECORDING_STATE_CHANGE in the UI.
|
||||
RECORDING_IMPORTED: "Performance:ImportedRecording",
|
||||
};
|
||||
|
||||
/**
|
||||
* The current target, toolbox and PerformanceFront, set by this tool's host.
|
||||
*/
|
||||
@ -205,6 +108,7 @@ let PerformanceController = {
|
||||
* main UI events.
|
||||
*/
|
||||
initialize: Task.async(function* () {
|
||||
this._telemetry = new PerformanceTelemetry(this);
|
||||
this.startRecording = this.startRecording.bind(this);
|
||||
this.stopRecording = this.stopRecording.bind(this);
|
||||
this.importRecording = this.importRecording.bind(this);
|
||||
@ -214,6 +118,7 @@ let PerformanceController = {
|
||||
this._onPrefChanged = this._onPrefChanged.bind(this);
|
||||
this._onThemeChanged = this._onThemeChanged.bind(this);
|
||||
this._onFrontEvent = this._onFrontEvent.bind(this);
|
||||
this._pipe = this._pipe.bind(this);
|
||||
|
||||
// Store data regarding if e10s is enabled.
|
||||
this._e10s = Services.appinfo.browserTabsRemoteAutostart;
|
||||
@ -230,6 +135,7 @@ let PerformanceController = {
|
||||
PerformanceView.on(EVENTS.UI_CLEAR_RECORDINGS, this.clearRecordings);
|
||||
RecordingsView.on(EVENTS.UI_EXPORT_RECORDING, this.exportRecording);
|
||||
RecordingsView.on(EVENTS.RECORDING_SELECTED, this._onRecordingSelectFromView);
|
||||
DetailsView.on(EVENTS.DETAILS_VIEW_SELECTED, this._pipe);
|
||||
|
||||
gDevTools.on("pref-changed", this._onThemeChanged);
|
||||
}),
|
||||
@ -238,6 +144,7 @@ let PerformanceController = {
|
||||
* Remove events handled by the PerformanceController
|
||||
*/
|
||||
destroy: function() {
|
||||
this._telemetry.destroy();
|
||||
this._prefs.off("pref-changed", this._onPrefChanged);
|
||||
|
||||
gFront.off("*", this._onFrontEvent);
|
||||
@ -248,6 +155,7 @@ let PerformanceController = {
|
||||
PerformanceView.off(EVENTS.UI_CLEAR_RECORDINGS, this.clearRecordings);
|
||||
RecordingsView.off(EVENTS.UI_EXPORT_RECORDING, this.exportRecording);
|
||||
RecordingsView.off(EVENTS.RECORDING_SELECTED, this._onRecordingSelectFromView);
|
||||
DetailsView.off(EVENTS.DETAILS_VIEW_SELECTED, this._pipe);
|
||||
|
||||
gDevTools.off("pref-changed", this._onThemeChanged);
|
||||
},
|
||||
@ -372,12 +280,7 @@ let PerformanceController = {
|
||||
let recording = yield gFront.importRecording(file);
|
||||
this._addNewRecording(recording);
|
||||
|
||||
// Only emit in tests for legacy purposes for shorthand --
|
||||
// other things in UI should handle the generic NEW_RECORDING
|
||||
// event to handle lazy recordings.
|
||||
if (DevToolsUtils.testing) {
|
||||
this.emit(EVENTS.RECORDING_IMPORTED, recording);
|
||||
}
|
||||
this.emit(EVENTS.RECORDING_IMPORTED, recording);
|
||||
}),
|
||||
|
||||
/**
|
||||
@ -491,21 +394,19 @@ let PerformanceController = {
|
||||
|
||||
// Emit the state specific events for tests that I'm too
|
||||
// lazy and frusterated to change right now. These events
|
||||
// should only be used in tests, as the rest of the UI should
|
||||
// react to general RECORDING_STATE_CHANGE events and NEW_RECORDING
|
||||
// events to handle lazy recordings.
|
||||
if (DevToolsUtils.testing) {
|
||||
switch (state) {
|
||||
case "recording-started":
|
||||
this.emit(EVENTS.RECORDING_STARTED, model);
|
||||
break;
|
||||
case "recording-stopping":
|
||||
this.emit(EVENTS.RECORDING_WILL_STOP, model);
|
||||
break;
|
||||
case "recording-stopped":
|
||||
this.emit(EVENTS.RECORDING_STOPPED, model);
|
||||
break;
|
||||
}
|
||||
// should only be used in tests and specific rare cases (telemetry),
|
||||
// as the rest of the UI should react to general RECORDING_STATE_CHANGE
|
||||
// events and NEW_RECORDING events to handle lazy recordings.
|
||||
switch (state) {
|
||||
case "recording-started":
|
||||
this.emit(EVENTS.RECORDING_STARTED, model);
|
||||
break;
|
||||
case "recording-stopping":
|
||||
this.emit(EVENTS.RECORDING_WILL_STOP, model);
|
||||
break;
|
||||
case "recording-stopped":
|
||||
this.emit(EVENTS.RECORDING_STOPPED, model);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
@ -635,6 +536,13 @@ let PerformanceController = {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Pipes an event from some source to the PerformanceController.
|
||||
*/
|
||||
_pipe: function (eventName, ...data) {
|
||||
this.emit(eventName, ...data);
|
||||
},
|
||||
|
||||
toString: () => "[object PerformanceController]"
|
||||
};
|
||||
|
||||
|
85
browser/devtools/performance/test/browser_perf-telemetry.js
Normal file
85
browser/devtools/performance/test/browser_perf-telemetry.js
Normal file
@ -0,0 +1,85 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests that the performance telemetry module records events at appropriate times.
|
||||
*/
|
||||
|
||||
function* spawnTest() {
|
||||
PMM_loadFrameScripts(gBrowser);
|
||||
let { panel } = yield initPerformance(SIMPLE_URL);
|
||||
let { EVENTS, PerformanceController, OverviewView, DetailsView, WaterfallView, JsCallTreeView, JsFlameGraphView } = panel.panelWin;
|
||||
|
||||
Services.prefs.setBoolPref(MEMORY_PREF, false);
|
||||
let DURATION = "DEVTOOLS_PERFTOOLS_RECORDING_DURATION_MS";
|
||||
let COUNT = "DEVTOOLS_PERFTOOLS_RECORDING_COUNT";
|
||||
let CONSOLE_COUNT = "DEVTOOLS_PERFTOOLS_CONSOLE_RECORDING_COUNT";
|
||||
let FEATURES = "DEVTOOLS_PERFTOOLS_RECORDING_FEATURES_USED";
|
||||
let VIEWS = "DEVTOOLS_PERFTOOLS_SELECTED_VIEW_MS";
|
||||
let EXPORTED = "DEVTOOLS_PERFTOOLS_RECORDING_EXPORT_FLAG";
|
||||
let IMPORTED = "DEVTOOLS_PERFTOOLS_RECORDING_IMPORT_FLAG";
|
||||
|
||||
let telemetry = PerformanceController._telemetry;
|
||||
let logs = telemetry.getLogs();
|
||||
|
||||
yield startRecording(panel);
|
||||
yield stopRecording(panel);
|
||||
|
||||
Services.prefs.setBoolPref(MEMORY_PREF, true);
|
||||
|
||||
yield startRecording(panel);
|
||||
yield stopRecording(panel);
|
||||
|
||||
is(logs[DURATION].length, 2, `two entry for ${DURATION}`);
|
||||
ok(logs[DURATION].every(d => typeof d === "number"), `every ${DURATION} entry is a number`);
|
||||
is(logs[COUNT].length, 2, `two entry for ${COUNT}`);
|
||||
is(logs[CONSOLE_COUNT], void 0, `no entries for ${CONSOLE_COUNT}`);
|
||||
is(logs[FEATURES].length, 10, `two recordings worth of entries for ${FEATURES}`);
|
||||
|
||||
ok(logs[FEATURES].find(r => r[0] === "withMemory" && r[1] === true), "one feature entry for memory enabled");
|
||||
ok(logs[FEATURES].find(r => r[0] === "withMemory" && r[1] === false), "one feature entry for memory disabled");
|
||||
|
||||
let calltreeRendered = once(JsCallTreeView, EVENTS.JS_CALL_TREE_RENDERED);
|
||||
let flamegraphRendered = once(JsFlameGraphView, EVENTS.JS_FLAMEGRAPH_RENDERED);
|
||||
|
||||
// Go through some views to check later
|
||||
DetailsView.selectView("js-calltree");
|
||||
yield calltreeRendered;
|
||||
DetailsView.selectView("js-flamegraph");
|
||||
yield flamegraphRendered;
|
||||
|
||||
let file = FileUtils.getFile("TmpD", ["tmpprofile.json"]);
|
||||
file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, parseInt("666", 8));
|
||||
let exported = once(PerformanceController, EVENTS.RECORDING_EXPORTED);
|
||||
yield PerformanceController.exportRecording("", PerformanceController.getCurrentRecording(), file);
|
||||
yield exported;
|
||||
|
||||
ok(logs[EXPORTED], `a telemetry entry for ${EXPORTED} exists after exporting`);
|
||||
|
||||
let imported = once(PerformanceController, EVENTS.RECORDING_IMPORTED);
|
||||
yield PerformanceController.importRecording(null, file);
|
||||
yield imported;
|
||||
|
||||
ok(logs[IMPORTED], `a telemetry entry for ${IMPORTED} exists after importing`);
|
||||
|
||||
yield consoleProfile(panel.panelWin, "rust");
|
||||
yield consoleProfileEnd(panel.panelWin, "rust");
|
||||
|
||||
info("Performed a console recording.");
|
||||
|
||||
is(logs[DURATION].length, 3, `three entry for ${DURATION}`);
|
||||
ok(logs[DURATION].every(d => typeof d === "number"), `every ${DURATION} entry is a number`);
|
||||
is(logs[COUNT].length, 2, `two entry for ${COUNT}`);
|
||||
is(logs[CONSOLE_COUNT].length, 1, `one entry for ${CONSOLE_COUNT}`);
|
||||
is(logs[FEATURES].length, 15, `two recordings worth of entries for ${FEATURES}`);
|
||||
|
||||
yield teardown(panel);
|
||||
|
||||
// Check views after destruction to ensure `js-flamegraph` gets called with a time
|
||||
// during destruction
|
||||
ok(logs[VIEWS].find(r => r[0] === "waterfall" && typeof r[1] === "number"), `${VIEWS} for waterfall view and time.`);
|
||||
ok(logs[VIEWS].find(r => r[0] === "js-calltree" && typeof r[1] === "number"), `${VIEWS} for js-calltree view and time.`);
|
||||
ok(logs[VIEWS].find(r => r[0] === "js-flamegraph" && typeof r[1] === "number"), `${VIEWS} for js-flamegraph view and time.`);
|
||||
|
||||
finish();
|
||||
};
|
@ -276,12 +276,18 @@ Telemetry.prototype = {
|
||||
*
|
||||
* @param String histogramId
|
||||
* Histogram in which the data is to be stored.
|
||||
* @param String key [optional]
|
||||
* Optional key for a keyed histogram.
|
||||
*/
|
||||
stopTimer: function(histogramId) {
|
||||
stopTimer: function(histogramId, key) {
|
||||
let startTime = this._timers.get(histogramId);
|
||||
if (startTime) {
|
||||
let time = (new Date() - startTime) / 1000;
|
||||
this.log(histogramId, time);
|
||||
if (!key) {
|
||||
this.log(histogramId, time);
|
||||
} else {
|
||||
this.logKeyed(histogramId, key, time);
|
||||
}
|
||||
this._timers.delete(histogramId);
|
||||
}
|
||||
},
|
||||
|
@ -7276,6 +7276,47 @@
|
||||
"n_buckets": "10000",
|
||||
"description": "The number of edges serialized into a heap snapshot."
|
||||
},
|
||||
"DEVTOOLS_PERFTOOLS_RECORDING_COUNT": {
|
||||
"expires_in_version": "never",
|
||||
"kind": "count",
|
||||
"description": "Incremented whenever a performance tool recording is completed."
|
||||
},
|
||||
"DEVTOOLS_PERFTOOLS_CONSOLE_RECORDING_COUNT": {
|
||||
"expires_in_version": "never",
|
||||
"kind": "count",
|
||||
"description": "Incremented whenever a performance tool recording is completed that was initiated via console.profile."
|
||||
},
|
||||
"DEVTOOLS_PERFTOOLS_RECORDING_IMPORT_FLAG": {
|
||||
"expires_in_version": "never",
|
||||
"kind": "flag",
|
||||
"description": "When a user imports a recording in the performance tool."
|
||||
},
|
||||
"DEVTOOLS_PERFTOOLS_RECORDING_EXPORT_FLAG": {
|
||||
"expires_in_version": "never",
|
||||
"kind": "flag",
|
||||
"description": "When a user imports a recording in the performance tool."
|
||||
},
|
||||
"DEVTOOLS_PERFTOOLS_RECORDING_FEATURES_USED": {
|
||||
"expires_in_version": "never",
|
||||
"kind": "boolean",
|
||||
"keyed": true,
|
||||
"description": "When a user starts a recording with specific recording options, keyed by feature name (withMarkers, withAllocations, etc.)."
|
||||
},
|
||||
"DEVTOOLS_PERFTOOLS_RECORDING_DURATION_MS": {
|
||||
"expires_in_version": "never",
|
||||
"kind": "exponential",
|
||||
"high": "600000",
|
||||
"n_buckets": 20,
|
||||
"description": "The length of a duration in MS of a performance tool recording."
|
||||
},
|
||||
"DEVTOOLS_PERFTOOLS_SELECTED_VIEW_MS": {
|
||||
"expires_in_version": "never",
|
||||
"kind": "exponential",
|
||||
"keyed": true,
|
||||
"high": "600000",
|
||||
"n_buckets": 20,
|
||||
"description": "The amount of time spent in a specific performance tool view, keyed by view name (waterfall, js-calltree, js-flamegraph, etc)."
|
||||
},
|
||||
"BROWSER_IS_USER_DEFAULT": {
|
||||
"expires_in_version": "never",
|
||||
"kind": "boolean",
|
||||
|
Loading…
Reference in New Issue
Block a user