Bug 1122058 - Add telemetry hooks to various performance tools actions. r=vp,mratcliffe

This commit is contained in:
Jordan Santell 2015-09-09 15:20:12 -07:00
parent 8ee72d869e
commit 615f6de19b
7 changed files with 395 additions and 125 deletions

View 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",
};

View 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;

View File

@ -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',

View File

@ -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]"
};

View 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();
};

View File

@ -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);
}
},

View File

@ -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",