Backed out 9 changesets (bug 1796585, bug 1796152, bug 1797284, bug 1796554, bug 1800076, bug 1802349, bug 1797497) for causing mochitest failures in xpcom/base/nsTraceRefcnt.cpp. CLOSED TREE

Backed out changeset 6002e93434a2 (bug 1802349)
Backed out changeset 000cd664c157 (bug 1800076)
Backed out changeset e3534e639e2c (bug 1796554)
Backed out changeset 1d22428d764c (bug 1797497)
Backed out changeset b0dbe35d48be (bug 1797284)
Backed out changeset b415d4b85ab6 (bug 1796585)
Backed out changeset dc1c2ffbc38e (bug 1796554)
Backed out changeset c44bae1f03f5 (bug 1796152)
Backed out changeset 3eab5e71b1c7 (bug 1796152)
This commit is contained in:
Sandor Molnar 2022-12-13 01:52:06 +02:00
parent e8693c6f13
commit 18b90e2c5a
10 changed files with 199 additions and 1443 deletions

View File

@ -110,7 +110,6 @@ export type RecordingState =
export type PageContext =
| "devtools"
| "devtools-remote"
| "aboutlogging"
| "aboutprofiling"
| "aboutprofiling-remote";

View File

@ -452,7 +452,6 @@ function getPrefPostfix(pageContext) {
switch (pageContext) {
case "devtools":
case "aboutprofiling":
case "aboutlogging":
// Don't use any postfix on the prefs.
return "";
case "devtools-remote":

View File

@ -2,90 +2,48 @@
- 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/. -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Security-Policy" content="default-src chrome:; object-src 'none'">
<meta name="color-scheme" content="light dark">
<title data-l10n-id="about-logging-title"></title>
<link rel="stylesheet" href="chrome://global/skin/aboutLogging.css">
<link rel="stylesheet" href="chrome://global/skin/in-content/info-pages.css">
<script src="chrome://global/content/aboutLogging.js"></script>
<link rel="localization" href="toolkit/about/aboutLogging.ftl">
</head>
<body id="body">
<div class="main-content">
<div id="logging">
<h1 id="title" data-l10n-id="about-logging-page-title"></h1>
<div class=page-section>
<div hidden id=error class=info-box>
<div class=info-box-label data-l10n-id="about-logging-error"></div>
<div id=error-description></div>
<div data-l10n-id="about-logging-configuration-url-ignored"></div>
</div>
<div hidden id=some-elements-unavailable class=info-box>
<div class=info-box-label data-l10n-id="about-logging-info"></div>
<div data-l10n-id="about-logging-some-elements-disabled"></div>
</div>
</div>
<button id="toggle-logging-button" data-l10n-id="about-logging-start-logging"></button>
<div id=log-module-selection class=page-section>
<h2 data-l10n-id="about-logging-log-modules-selection"></h2>
<div>
<label for="current-log-modules" data-l10n-id="about-logging-currently-enabled-log-modules"></label>
<div id="current-log-modules"></div>
<span id="no-log-modules" data-l10n-id="about-logging-no-log-modules"></span>
</div>
<div>
<label for="log-modules" data-l10n-id="about-logging-new-log-modules"></label>
<input type="text" name="log-modules" id="log-modules" value="">
<button id="set-log-modules-button" data-l10n-id="about-logging-set-log-modules"></button>
</div>
<div id=preset-selector-section>
<label for="logging-preset-selector" data-l10n-id="about-logging-logging-preset-selector-text"></label>
<select name="logging-preset-selector" id=logging-preset-dropdown></select>
<div id="logging-preset-description"></div>
</div>
</div>
<div id=logging-output class=page-section>
<div>
<span hidden id="buttons-disabled" data-l10n-id="about-logging-buttons-disabled"></span>
</div>
<h2 data-l10n-id="about-logging-logging-output-selection"></h2>
<div id=logging-output-profiler class="radio-entry">
<input type="radio" id="radio-logging-profiler" name="logging-output" value="profiler" checked>
<label for="profiler" data-l10n-id="about-logging-logging-to-profiler"></label>
</div>
<div id=logging-output-file class="radio-entry">
<input type="radio" id="radio-logging-file" name="logging-output" value="file">
<label for="file" data-l10n-id="about-logging-logging-to-file"></label>
<div>
<span hidden id="buttons-disabled" data-l10n-id="about-logging-buttons-disabled"></span>
</div>
<div>
<div id="log-file-configuration">
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Security-Policy" content="default-src chrome:; object-src 'none'">
<meta name="color-scheme" content="light dark">
<title data-l10n-id="about-logging-title"></title>
<link rel="stylesheet" href="chrome://global/skin/aboutNetworking.css">
<script src="chrome://global/content/aboutLogging.js"></script>
<link rel="localization" href="toolkit/about/aboutLogging.ftl">
</head>
<body id="body">
<div class="main-content">
<div id="logging">
<div>
<label for="current-log-file" data-l10n-id="about-logging-current-log-file"></label>
<div id="current-log-file"></div>
<span id="no-log-file" data-l10n-id="about-logging-no-log-file"></span>
<button id="start-logging-button" data-l10n-id="about-logging-start-logging"></button>
<button id="stop-logging-button" data-l10n-id="about-logging-stop-logging"></button>
</div>
<br>
<br>
<div>
<label for="log-file" data-l10n-id="about-logging-new-log-file"></label>
<label data-l10n-id="about-logging-current-log-file"></label>
<div id="current-log-file"></div><br>
<input type="text" name="log-file" id="log-file">
<button id="open-log-file-button" data-l10n-id="about-logging-open-log-file-dir"></button>
<button id="set-log-file-button" data-l10n-id="about-logging-set-log-file"></button>
</div>
<div>
<label data-l10n-id="about-logging-current-log-modules"></label>
<div id="current-log-modules"></div><br>
<input type="text" name="log-modules" id="log-modules" value="timestamp,sync,nsHttp:5,cache2:5,nsSocketTransport:5,nsHostResolver:5">
<button id="set-log-modules-button" data-l10n-id="about-logging-set-log-modules"></button>
</div>
<br>
<br>
<p id="log-tutorial" data-l10n-id="about-logging-log-tutorial">
<a data-l10n-name="logging" href="https://firefox-source-docs.mozilla.org/networking/http/logging.html"></a>
</p>
</div>
</div>
</div>
<div>
<p id="log-tutorial" data-l10n-id="about-logging-log-tutorial">
<a data-l10n-name="logging" href="https://firefox-source-docs.mozilla.org/networking/http/logging.html"></a>
</p>
</div>
</div>
</div>
</div>
</body>
</body>
</html>

View File

@ -3,9 +3,6 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { XPCOMUtils } = ChromeUtils.importESModule(
"resource://gre/modules/XPCOMUtils.sys.mjs"
);
const gDashboard = Cc["@mozilla.org/network/dashboard;1"].getService(
Ci.nsIDashboard
);
@ -13,295 +10,11 @@ const gDirServ = Cc["@mozilla.org/file/directory_service;1"].getService(
Ci.nsIDirectoryServiceProvider
);
const { ProfilerMenuButton } = ChromeUtils.import(
"resource://devtools/client/performance-new/popup/menu-button.jsm.js"
);
const { CustomizableUI } = ChromeUtils.import(
"resource:///modules/CustomizableUI.jsm"
);
XPCOMUtils.defineLazyGetter(this, "ProfilerPopupBackground", function() {
return ChromeUtils.import(
"resource://devtools/client/performance-new/popup/background.jsm.js"
);
});
const $ = document.querySelector.bind(document);
const $$ = document.querySelectorAll.bind(document);
function fileEnvVarPresent() {
return Services.env.get("MOZ_LOG_FILE") || Services.env.get("NSPR_LOG_FILE");
}
function moduleEnvVarPresent() {
return Services.env.get("MOZ_LOG") || Services.env.get("NSPR_LOG");
}
/**
* All the information associated with a logging presets:
* - `modules` is the list of log modules and option, the same that would have
* been set as a MOZ_LOG environment variable
* - l10nIds.label and l10nIds.description are the Ids of the strings that
* appear in the dropdown selector, and a one-liner describing the purpose of
* a particular logging preset
* - profilerPreset is the name of a Firefox Profiler preset [1]. In general,
* the profiler preset will have the correct set of threads for a particular
* logging preset, so that all logging statements are recorded in the profile
* as markers.
*
* [1]: The keys of https://searchfox.org/mozilla-central/rev/88f285c5163f73abd209d4f73cfa476660351982/devtools/client/performance-new/popup/background.jsm.js#119
*/
const gLoggingPresets = {
networking: {
modules:
"timestamp,sync,nsHttp:5,cache2:5,nsSocketTransport:5,nsHostResolver:5",
l10nIds: {
label: "about-logging-preset-networking-label",
description: "about-logging-preset-networking-description",
},
profilerPreset: "networking",
},
"media-playback": {
modules:
"cubeb:5,PlatformDecoderModule:5,AudioSink:5,AudioSinkWrapper:5,MediaDecoderStateMachine:4,MediaDecoder:4",
l10nIds: {
label: "about-logging-preset-media-playback-label",
description: "about-logging-preset-media-playback-description",
},
profilerPreset: "media",
},
custom: {
modules: "",
l10nIds: {
label: "about-logging-preset-custom-label",
description: "about-logging-preset-custom-description",
},
},
};
const gLoggingSettings = {
// Possible values: "profiler" and "file".
loggingOutputType: "profiler",
running: false,
// If non-null, the profiler preset to use. If null, the preset selected in
// the dropdown is going to be used. It is also possible to use a "custom"
// preset and an explicit list of modules.
loggingPreset: null,
// If non-null, the profiler preset to use. If a logging preset is being used,
// and this is null, the profiler preset associated to the logging preset is
// going to be used. Otherwise, a generic profiler preset is going to be used
// ("firefox-platform").
profilerPreset: null,
// If non-null, the threads that will be recorded by the Firefox Profiler. If
// null, the threads from the profiler presets are going to be used.
profilerThreads: null,
};
// When the profiler has been started, this holds the promise the
// Services.profiler.StartProfiler returns, to ensure the profiler has
// effectively started.
let gProfilerPromise = null;
function presets() {
return gLoggingPresets;
}
function settings() {
return gLoggingSettings;
}
function profilerPromise() {
return gProfilerPromise;
}
function populatePresets() {
let dropdown = $("#logging-preset-dropdown");
for (let presetName in gLoggingPresets) {
let preset = gLoggingPresets[presetName];
let option = document.createElement("option");
document.l10n.setAttributes(option, preset.l10nIds.label);
option.value = presetName;
dropdown.appendChild(option);
if (option.value === gLoggingSettings.loggingPreset) {
option.setAttribute("selected", true);
}
}
function setPresetAndDescription(preset) {
document.l10n.setAttributes(
$("#logging-preset-description"),
gLoggingPresets[preset].l10nIds.description
);
gLoggingSettings.loggingPreset = preset;
}
dropdown.onchange = function() {
// When switching to custom, leave the existing module list, to allow
// editing.
if (dropdown.value != "custom") {
$("#log-modules").value = gLoggingPresets[dropdown.value].modules;
}
setPresetAndDescription(dropdown.value);
setLogModules();
Services.prefs.setCharPref("logging.config.preset", dropdown.value);
};
$("#log-modules").value = gLoggingPresets[dropdown.value].modules;
setPresetAndDescription(dropdown.value);
// When changing the list switch to custom.
$("#log-modules").oninput = e => {
dropdown.value = "custom";
};
}
function updateLoggingOutputType(profilerOutputType) {
gLoggingSettings.loggingOutputType = profilerOutputType;
if (gLoggingSettings.loggingOutputType === "profiler") {
// hide options related to file output for clarity
$("#log-file-configuration").hidden = true;
} else if (gLoggingSettings.loggingOutputType === "file") {
$("#log-file-configuration").hidden = false;
$("#no-log-file").hidden = !!$("#current-log-file").innerText.length;
}
Services.prefs.setCharPref(
"logging.config.output_type",
gLoggingSettings.loggingOutputType
);
}
function displayErrorMessage(error) {
var err = $("#error");
err.hidden = false;
var errorDescription = $("#error-description");
document.l10n.setAttributes(errorDescription, error.l10nId, {
k: error.key,
v: error.value,
});
}
class ParseError extends Error {
constructor(l10nId, key, value) {
super(name);
this.l10nId = l10nId;
this.key = key;
this.value = value;
}
name = "ParseError";
l10nId;
key;
value;
}
function parseURL() {
let options = new URL(document.location.href).searchParams;
if (!options) {
return;
}
let modulesOverriden = null,
outputTypeOverriden = null,
loggingPresetOverriden = null,
threadsOverriden = null,
profilerPresetOverriden = null;
try {
for (let [k, v] of options) {
switch (k) {
case "modules":
case "module":
modulesOverriden = v;
break;
case "output":
case "output-type":
if (v !== "profiler" && v !== "file") {
throw new ParseError("about-logging-invalid-output", k, v);
}
outputTypeOverriden = v;
break;
case "preset":
case "logging-preset":
if (!Object.keys(gLoggingPresets).includes(v)) {
throw new ParseError("about-logging-unknown-logging-preset", k, v);
}
loggingPresetOverriden = v;
break;
case "threads":
case "thread":
threadsOverriden = v;
break;
case "profiler-preset":
if (!Object.keys(ProfilerPopupBackground.presets).includes(v)) {
throw new Error(["about-logging-unknown-profiler-preset", k, v]);
}
profilerPresetOverriden = v;
break;
default:
throw new ParseError("about-logging-unknown-option", k, v);
}
}
} catch (e) {
displayErrorMessage(e);
return;
}
// Detect combinations that don't make sense
if (
(profilerPresetOverriden || threadsOverriden) &&
outputTypeOverriden == "file"
) {
displayErrorMessage(
new ParseError("about-logging-file-and-profiler-override")
);
return;
}
// Configuration is deemed at least somewhat valid, override each setting in
// turn
let someElementsDisabled = false;
if (modulesOverriden || loggingPresetOverriden) {
// Don't allow changing those if set by the URL
let logModules = $("#log-modules");
var dropdown = $("#logging-preset-dropdown");
if (loggingPresetOverriden) {
dropdown.value = loggingPresetOverriden;
dropdown.onchange();
}
if (modulesOverriden) {
logModules.value = modulesOverriden;
dropdown.value = "custom";
dropdown.onchange();
dropdown.disabled = true;
someElementsDisabled = true;
}
logModules.disabled = true;
$("#set-log-modules-button").disabled = true;
$("#logging-preset-dropdown").disabled = true;
someElementsDisabled = true;
setLogModules();
updateLogModules();
}
if (outputTypeOverriden) {
$$("input[type=radio]").forEach(e => {
e.setAttribute("disabled", true);
someElementsDisabled = true;
e.checked = e.value == outputTypeOverriden;
});
}
if (loggingPresetOverriden) {
gLoggingSettings.loggingPreset = loggingPresetOverriden;
}
if (profilerPresetOverriden) {
gLoggingSettings.profilerPreset = profilerPresetOverriden;
}
if (threadsOverriden) {
gLoggingSettings.profilerThreads = threadsOverriden;
}
$("#some-elements-unavailable").hidden = !someElementsDisabled;
function col(element) {
let col = document.createElement("td");
let content = document.createTextNode(element);
col.appendChild(content);
return col;
}
let gInited = false;
@ -312,53 +25,22 @@ function init() {
gInited = true;
gDashboard.enableLogging = true;
populatePresets();
parseURL();
let setLogButton = $("#set-log-file-button");
let setLogButton = document.getElementById("set-log-file-button");
setLogButton.addEventListener("click", setLogFile);
let setModulesButton = $("#set-log-modules-button");
let setModulesButton = document.getElementById("set-log-modules-button");
setModulesButton.addEventListener("click", setLogModules);
let toggleLoggingButton = $("#toggle-logging-button");
toggleLoggingButton.addEventListener("click", startStopLogging);
let startLoggingButton = document.getElementById("start-logging-button");
startLoggingButton.addEventListener("click", startLogging);
$$("input[type=radio]").forEach(radio => {
radio.onchange = e => {
updateLoggingOutputType(e.target.value);
};
});
try {
let loggingOutputType = Services.prefs.getCharPref(
"logging.config.output_type"
);
if (loggingOutputType.length) {
updateLoggingOutputType(loggingOutputType);
}
} catch {
updateLoggingOutputType("profiler");
}
try {
let loggingPreset = Services.prefs.getCharPref("logging.config.preset");
gLoggingSettings.loggingPreset = loggingPreset;
} catch {}
try {
let running = Services.prefs.getBoolPref("logging.config.running");
gLoggingSettings.running = running;
$("#toggle-logging-button").setAttribute(
"data-l10n-id",
`about-logging-${gLoggingSettings.running ? "stop" : "start"}-logging`
);
} catch {}
let stopLoggingButton = document.getElementById("stop-logging-button");
stopLoggingButton.addEventListener("click", stopLogging);
try {
let file = gDirServ.getFile("TmpD", {});
file.append("log.txt");
$("#log-file").value = file.path;
document.getElementById("log-file").value = file.path;
} catch (e) {
console.error(e);
}
@ -371,23 +53,20 @@ function init() {
// If we can't set the file and the modules at runtime,
// the start and stop buttons wouldn't really do anything.
if (
(setLogButton.disabled || setModulesButton.disabled) &&
moduleEnvVarPresent()
) {
$("#buttons-disabled").hidden = false;
toggleLoggingButton.disabled = true;
if (setLogButton.disabled && setModulesButton.disabled) {
startLoggingButton.disabled = true;
stopLoggingButton.disabled = true;
}
}
function updateLogFile(file) {
function updateLogFile() {
let logPath = "";
// Try to get the environment variable for the log file
logPath =
Services.env.get("MOZ_LOG_FILE") || Services.env.get("NSPR_LOG_FILE");
let currentLogFile = $("#current-log-file");
let setLogFileButton = $("#set-log-file-button");
let currentLogFile = document.getElementById("current-log-file");
let setLogFileButton = document.getElementById("set-log-file-button");
// If the log file was set from an env var, we disable the ability to set it
// at runtime.
@ -397,21 +76,19 @@ function updateLogFile(file) {
} else if (gDashboard.getLogPath() != ".moz_log") {
// There may be a value set by a pref.
currentLogFile.innerText = gDashboard.getLogPath();
} else if (file !== undefined) {
currentLogFile.innerText = file;
} else {
try {
let file = gDirServ.getFile("TmpD", {});
file.append("log.txt");
$("#log-file").value = file.path;
document.getElementById("log-file").value = file.path;
} catch (e) {
console.error(e);
}
// Fall back to the temp dir
currentLogFile.innerText = $("#log-file").value;
currentLogFile.innerText = document.getElementById("log-file").value;
}
let openLogFileButton = $("#open-log-file-button");
let openLogFileButton = document.getElementById("open-log-file-button");
openLogFileButton.disabled = true;
if (currentLogFile.innerText.length) {
@ -425,8 +102,6 @@ function updateLogFile(file) {
};
}
}
$("#no-log-file").hidden = !!currentLogFile.innerText.length;
$("#current-log-file").hidden = !currentLogFile.innerText.length;
}
function updateLogModules() {
@ -435,8 +110,8 @@ function updateLogModules() {
Services.env.get("MOZ_LOG") ||
Services.env.get("MOZ_LOG_MODULES") ||
Services.env.get("NSPR_LOG_MODULES");
let currentLogModules = $("#current-log-modules");
let setLogModulesButton = $("#set-log-modules-button");
let currentLogModules = document.getElementById("current-log-modules");
let setLogModulesButton = document.getElementById("set-log-modules-button");
if (logModules.length) {
currentLogModules.innerText = logModules;
// If the log modules are set by an environment variable at startup, do not
@ -477,27 +152,19 @@ function updateLogModules() {
}
}
if (activeLogModules.length !== 0) {
currentLogModules.innerText = activeLogModules.join(",");
currentLogModules.hidden = false;
$("#no-log-modules").hidden = true;
} else {
currentLogModules.innerText = "";
currentLogModules.hidden = true;
$("#no-log-modules").hidden = false;
}
currentLogModules.innerText = activeLogModules.join(",");
}
}
function setLogFile() {
let setLogButton = $("#set-log-file-button");
let setLogButton = document.getElementById("set-log-file-button");
if (setLogButton.disabled) {
// There's no point trying since it wouldn't work anyway.
return;
}
let logFile = $("#log-file").value.trim();
let logFile = document.getElementById("log-file").value.trim();
Services.prefs.setCharPref("logging.config.LOG_FILE", logFile);
updateLogFile(logFile);
updateLogFile();
}
function clearLogModules() {
@ -514,149 +181,50 @@ function clearLogModules() {
}
function setLogModules() {
if (moduleEnvVarPresent()) {
let setLogModulesButton = document.getElementById("set-log-modules-button");
if (setLogModulesButton.disabled) {
// The modules were set via env var, so we shouldn't try to change them.
return;
}
let modules = $("#log-modules").value.trim();
let modules = document.getElementById("log-modules").value.trim();
// Clear previously set log modules.
clearLogModules();
if (modules.length !== 0) {
let logModules = modules.split(",");
for (let module of logModules) {
if (module == "timestamp") {
Services.prefs.setBoolPref("logging.config.add_timestamp", true);
} else if (module == "rotate") {
// XXX: rotate is not yet supported.
} else if (module == "append") {
// XXX: append is not yet supported.
} else if (module == "sync") {
Services.prefs.setBoolPref("logging.config.sync", true);
} else if (module == "profilerstacks") {
Services.prefs.setBoolPref("logging.config.profilerstacks", true);
} else {
let lastColon = module.lastIndexOf(":");
let key = module.slice(0, lastColon);
let value = parseInt(module.slice(lastColon + 1), 10);
Services.prefs.setIntPref(`logging.${key}`, value);
}
let logModules = modules.split(",");
for (let module of logModules) {
if (module == "timestamp") {
Services.prefs.setBoolPref("logging.config.add_timestamp", true);
} else if (module == "rotate") {
// XXX: rotate is not yet supported.
} else if (module == "append") {
// XXX: append is not yet supported.
} else if (module == "sync") {
Services.prefs.setBoolPref("logging.config.sync", true);
} else if (module == "profilerstacks") {
Services.prefs.setBoolPref("logging.config.profilerstacks", true);
} else {
let lastColon = module.lastIndexOf(":");
let key = module.slice(0, lastColon);
let value = parseInt(module.slice(lastColon + 1), 10);
Services.prefs.setIntPref(`logging.${key}`, value);
}
}
updateLogModules();
}
function isLogging() {
try {
return Services.prefs.getBoolPref("logging.config.running");
} catch {
return false;
}
}
function startStopLogging() {
if (isLogging()) {
document.l10n.setAttributes(
$("#toggle-logging-button"),
"about-logging-start-logging"
);
stopLogging();
} else {
document.l10n.setAttributes(
$("#toggle-logging-button"),
"about-logging-stop-logging"
);
startLogging();
}
}
function startLogging() {
setLogFile();
setLogModules();
if (gLoggingSettings.loggingOutputType === "profiler") {
const pageContext = "aboutlogging";
const supportedFeatures = Services.profiler.GetFeatures();
if (gLoggingSettings.loggingPreset != "custom") {
// Change the preset before starting the profiler, so that the
// underlying profiler code picks up the right configuration.
const profilerPreset =
gLoggingPresets[gLoggingSettings.loggingPreset].profilerPreset;
ProfilerPopupBackground.changePreset(
"aboutlogging",
profilerPreset,
supportedFeatures
);
} else {
// a baseline set of threads, and possibly others, overriden by the URL
ProfilerPopupBackground.changePreset(
"aboutlogging",
"firefox-platform",
supportedFeatures
);
}
let {
entries,
interval,
features,
threads,
duration,
} = ProfilerPopupBackground.getRecordingSettings(
pageContext,
Services.profiler.GetFeatures()
);
if (gLoggingSettings.profilerThreads) {
threads.push(...gLoggingSettings.profilerThreads.split(","));
// Small hack: if cubeb is being logged, it's almost always necessary (and
// never harmful) to enable audio callback tracing, otherwise, no log
// statements will be recorded from real-time threads.
if (gLoggingSettings.profilerThreads.includes("cubeb")) {
features.push("audiocallbacktracing");
}
}
const win = Services.wm.getMostRecentWindow("navigator:browser");
const windowid = win?.gBrowser?.selectedBrowser?.browsingContext?.browserId;
// Force displaying the profiler button in the navbar if not preset, so
// that there is a visual indication profiling is in progress.
if (!ProfilerMenuButton.isInNavbar()) {
// Ensure the widget is enabled.
Services.prefs.setBoolPref(
"devtools.performance.popup.feature-flag",
true
);
// Enable the profiler menu button.
ProfilerMenuButton.addToNavbar();
// Dispatch the change event manually, so that the shortcuts will also be
// added.
CustomizableUI.dispatchToolboxEvent("customizationchange");
}
gProfilerPromise = Services.profiler.StartProfiler(
entries,
interval,
features,
threads,
windowid,
duration
);
} else {
setLogFile();
}
Services.prefs.setBoolPref("logging.config.running", true);
}
async function stopLogging() {
if (gLoggingSettings.loggingOutputType === "profiler") {
await ProfilerPopupBackground.captureProfile("aboutlogging");
} else {
Services.prefs.clearUserPref("logging.config.LOG_FILE");
updateLogFile();
}
Services.prefs.setBoolPref("logging.config.running", false);
function stopLogging() {
clearLogModules();
// clear the log file as well
Services.prefs.clearUserPref("logging.config.LOG_FILE");
updateLogFile();
}
// We use the pageshow event instead of onload. This is needed because sometimes

View File

@ -152,6 +152,3 @@ skip-if =
debug # Bug 1388959
[browser_about_networking.js]
skip-if = socketprocess_networking
[browser_about_logging.js]
skip-if =
tsan # Bug 1804081

View File

@ -1,395 +0,0 @@
const PAGE = "about:logging";
function clearLoggingPrefs() {
for (let pref of Services.prefs.getBranch("logging.").getChildList("")) {
info(`Clearing: ${pref}`);
Services.prefs.clearUserPref(pref);
}
}
// Before running, save any MOZ_LOG environment variable that might be preset,
// and restore them at the end of this test.
add_setup(async function saveRestoreLogModules() {
let savedLogModules = Services.env.get("MOZ_LOG");
Services.env.set("MOZ_LOG", "");
registerCleanupFunction(() => {
info(" -- Restoring log modules: " + savedLogModules);
clearLoggingPrefs();
for (let pref of savedLogModules.split(",")) {
let [logModule, level] = pref.split(":");
Services.prefs.setIntPref("logging." + logModule, parseInt(level));
}
});
});
// Test that some UI elements are disabled in some cirumstances.
add_task(async function testElementsDisabled() {
// This test needs a MOZ_LOG env var set.
Services.env.set("MOZ_LOG", "example:4");
await BrowserTestUtils.withNewTab(PAGE, async browser => {
await SpecialPowers.spawn(browser, [], async () => {
let $ = content.document.querySelector.bind(content.document);
Assert.ok(
$("#set-log-modules-button").disabled,
"Because a MOZ_LOG env var is set by the harness, it should be impossible to set new log modules."
);
});
});
Services.env.set("MOZ_LOG", "");
await BrowserTestUtils.withNewTab(
PAGE + "?modules=example:5&output=profiler",
async browser => {
await SpecialPowers.spawn(browser, [], async () => {
let $ = content.document.querySelector.bind(content.document);
Assert.ok(
!$("#some-elements-unavailable").hidden,
"If a log modules are configured via URL params, a warning should be visible."
);
Assert.ok(
$("#set-log-modules-button").disabled,
"If a log modules are configured via URL params, some in-page elements should be disabled (button)."
);
Assert.ok(
$("#log-modules").disabled,
"If a log modules are configured via URL params, some in-page elements should be disabled (input)."
);
Assert.ok(
$("#logging-preset-dropdown").disabled,
"If a log modules are configured via URL params, some in-page elements should be disabled (dropdown)."
);
Assert.ok(
$("#radio-logging-profiler").disabled &&
$("#radio-logging-file").disabled,
"If the ouptut type is configured via URL param, the radio buttons should be disabled."
);
});
}
);
await BrowserTestUtils.withNewTab(
PAGE + "?preset=media-playback",
async browser => {
await SpecialPowers.spawn(browser, [], async () => {
let $ = content.document.querySelector.bind(content.document);
Assert.ok(
!$("#some-elements-unavailable").hidden,
"If a preset is selected via URL, a warning should be displayed."
);
Assert.ok(
$("#set-log-modules-button").disabled,
"If a preset is selected via URL, some in-page elements should be disabled (button)."
);
Assert.ok(
$("#log-modules").disabled,
"If a preset is selected via URL, some in-page elements should be disabled (input)."
);
Assert.ok(
$("#logging-preset-dropdown").disabled,
"If a preset is selected via URL, some in-page elements should be disabled (dropdown)."
);
});
}
);
clearLoggingPrefs();
});
// Test URL parameters
const modulesInURL = "example:4,otherexample:5";
const presetInURL = "media-playback";
const threadsInURL = "example,otherexample";
const profilerPresetInURL = "media";
add_task(async function testURLParameters() {
await BrowserTestUtils.withNewTab(
PAGE + "?modules=" + modulesInURL,
async browser => {
await SpecialPowers.spawn(browser, [modulesInURL], async modulesInURL => {
let $ = content.document.querySelector.bind(content.document);
Assert.ok(
!$("#some-elements-unavailable").hidden,
"If modules are selected via URL, a warning should be displayed."
);
var inPageSorted = $("#current-log-modules")
.innerText.split(",")
.sort()
.join(",");
var inURLSorted = modulesInURL
.split(",")
.sort()
.join(",");
Assert.equal(
inPageSorted,
inURLSorted,
"When selecting modules via URL params, the same modules are reflected in the page."
);
});
}
);
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: PAGE + "?preset=" + presetInURL,
},
async browser => {
await SpecialPowers.spawn(browser, [presetInURL], async presetInURL => {
let $ = content.document.querySelector.bind(content.document);
Assert.ok(
!$("#some-elements-unavailable").hidden,
"If a preset is selected via URL, a warning should be displayed."
);
var inPageSorted = $("#current-log-modules")
.innerText.split(",")
.sort()
.join(",");
var presetSorted = content
.presets()
[presetInURL].modules.split(",")
.sort()
.join(",");
Assert.equal(
inPageSorted,
presetSorted,
"When selecting a preset via URL params, the correct log modules are reflected in the page."
);
});
}
);
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: PAGE + "?profiler-preset=" + profilerPresetInURL,
},
async browser => {
await SpecialPowers.spawn(browser, [profilerPresetInURL], async inURL => {
let $ = content.document.querySelector.bind(content.document);
// Threads override doesn't have a UI element, the warning shouldn't
// be displayed.
Assert.ok(
$("#some-elements-unavailable").hidden,
"When overriding the profiler preset, no warning is displayed on the page."
);
var inSettings = content.settings().profilerPreset;
Assert.equal(
inSettings,
inURL,
"When overriding the profiler preset via URL param, the correct preset is set in the logging manager settings."
);
});
}
);
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: PAGE + "?invalid-param",
},
async browser => {
await SpecialPowers.spawn(browser, [profilerPresetInURL], async inURL => {
let $ = content.document.querySelector.bind(content.document);
Assert.ok(
!$("#error").hidden,
"When an invalid URL param is passed in, the page displays a warning."
);
});
}
);
clearLoggingPrefs();
});
// Test various things related to presets: that it's populated correctly, that
// setting presets work in terms of UI, but also that it sets the logging.*
// prefs correctly.
add_task(async function testAboutLoggingPresets() {
await BrowserTestUtils.withNewTab(PAGE, async browser => {
await SpecialPowers.spawn(browser, [], async () => {
let $ = content.document.querySelector.bind(content.document);
let presetsDropdown = $("#logging-preset-dropdown");
Assert.equal(
Object.keys(content.presets()).length,
presetsDropdown.childNodes.length,
"Presets populated."
);
Assert.equal(presetsDropdown.value, "networking");
$("#set-log-modules-button").click();
Assert.ok(
$("#no-log-modules").hidden && !$("#current-log-modules").hidden,
"When log modules are set, they are visible."
);
var lengthModuleListNetworking = $("#log-modules").value.length;
var lengthCurrentModuleListNetworking = $("#current-log-modules")
.innerText.length;
Assert.notEqual(
lengthModuleListNetworking,
0,
"When setting a profiler preset, the module string is non-empty (input)."
);
Assert.notEqual(
lengthCurrentModuleListNetworking,
0,
"When setting a profiler preset, the module string is non-empty (selected modules)."
);
// Change preset
presetsDropdown.value = "media-playback";
presetsDropdown.dispatchEvent(new content.Event("change"));
// Check the following after "onchange".
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
await new Promise(resolve => content.setTimeout(resolve, 0));
Assert.equal(
presetsDropdown.value,
"media-playback",
"Selecting another preset is reflected in the page"
);
$("#set-log-modules-button").click();
Assert.ok(
$("#no-log-modules").hidden && !$("#current-log-modules").hidden,
"When other log modules are set, they are still visible"
);
Assert.notEqual(
$("#log-modules").value.length,
0,
"When setting a profiler preset, the module string is non-empty (input)."
);
Assert.notEqual(
$("#current-log-modules").innerText.length,
0,
"When setting a profiler preset, the module string is non-empty (selected modules)."
);
Assert.notEqual(
$("#log-modules").value.length,
lengthModuleListNetworking,
"When setting another profiler preset, the module string changes (input)."
);
let currentLogModulesString = $("#current-log-modules").innerText;
Assert.notEqual(
currentLogModulesString.length,
lengthCurrentModuleListNetworking,
"When setting another profiler preset, the module string changes (selected modules)."
);
// After setting some log modules via the preset dropdown, verify
// that they have been reflected to logging.* preferences.
var activeLogModules = [];
let children = Services.prefs.getBranch("logging.").getChildList("");
for (let pref of children) {
if (pref.startsWith("config.")) {
continue;
}
try {
let value = Services.prefs.getIntPref(`logging.${pref}`);
activeLogModules.push(`${pref}:${value}`);
} catch (e) {
console.error(e);
}
}
let mod;
while ((mod = activeLogModules.pop())) {
Assert.ok(
currentLogModulesString.includes(mod),
`${mod} was effectively set`
);
}
});
});
clearLoggingPrefs();
});
// Here we test that starting and stopping log collection to the Firefox
// Profiler opens a new tab. We don't actually check the content of the profile.
add_task(async function testProfilerOpens() {
await BrowserTestUtils.withNewTab(PAGE, async browser => {
let profilerOpenedPromise = BrowserTestUtils.waitForNewTab(
gBrowser,
"https://example.com/",
false
);
SpecialPowers.spawn(browser, [], async savedLogModules => {
let $ = content.document.querySelector.bind(content.document);
// Override the URL the profiler uses to avoid hitting external
// resources (and crash).
await SpecialPowers.pushPrefEnv({
set: [
["devtools.performance.recording.ui-base-url", "https://example.com"],
["devtools.performance.recording.ui-base-url-path", "/"],
],
});
$("#radio-logging-file").click();
$("#radio-logging-profiler").click();
$("#logging-preset-dropdown").value = "networking";
$("#logging-preset-dropdown").dispatchEvent(new content.Event("change"));
$("#set-log-modules-button").click();
$("#toggle-logging-button").click();
// Wait for the profiler to start. This can be very slow.
await content.profilerPromise();
// Wait for some time for good measure while the profiler collects some
// data. We don't really care about the data itself.
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
await new Promise(resolve => content.setTimeout(resolve, 1000));
$("#toggle-logging-button").click();
});
let tab = await profilerOpenedPromise;
Assert.ok(true, "Profiler tab opened after profiling");
await BrowserTestUtils.removeTab(tab);
});
clearLoggingPrefs();
});
// Same test, outputing to a file, with network logging, while opening and
// closing a tab. We only check that the file exists and has a non-zero size.
add_task(async function testLogFileFound() {
await BrowserTestUtils.withNewTab(PAGE, async browser => {
await SpecialPowers.spawn(browser, [], async () => {
// Clear any previous log file.
let $ = content.document.querySelector.bind(content.document);
$("#radio-logging-file").click();
$("#log-file").value = "";
$("#log-file").dispatchEvent(new content.Event("change"));
$("#set-log-file-button").click();
Assert.ok(
!$("#no-log-file").hidden,
"When a log file hasn't been set, it's indicated as such."
);
});
});
await BrowserTestUtils.withNewTab(PAGE, async browser => {
let logPath = await SpecialPowers.spawn(browser, [], async () => {
let $ = content.document.querySelector.bind(content.document);
$("#radio-logging-file").click();
// Set the log file (use the default path)
$("#set-log-file-button").click();
var logPath = $("#current-log-file").innerText;
// Set log modules for networking
$("#logging-preset-dropdown").value = "networking";
$("#logging-preset-dropdown").dispatchEvent(new content.Event("change"));
$("#set-log-modules-button").click();
return logPath;
});
// No need to start or stop logging when logging to a file. Just open
// a tab, any URL will do. Wait for this tab to be loaded so we're sure
// something (anything) has happened in necko.
let tab = await BrowserTestUtils.openNewForegroundTab(
gBrowser,
"https://example.com",
true /* waitForLoad */
);
await BrowserTestUtils.removeTab(tab);
let logDirectory = PathUtils.parent(logPath);
let logBasename = PathUtils.filename(logPath);
let entries = await IOUtils.getChildren(logDirectory);
let foundNonEmptyLogFile = false;
for (let entry of entries) {
if (entry.includes(logBasename)) {
info("Log file found: " + entry);
let fileinfo = await IOUtils.stat(entry);
foundNonEmptyLogFile |= fileinfo.size > 0;
}
}
Assert.ok(foundNonEmptyLogFile, "Found at least one non-empty log file.");
});
clearLoggingPrefs();
});

View File

@ -2,26 +2,10 @@
# 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/.
## The following feature name must be treated as a brand.
##
## They cannot be:
## - Transliterated.
## - Translated.
##
## Declension should be avoided where possible, leaving the original
## brand unaltered in prominent UI positions.
##
## For further details, consult:
## https://mozilla-l10n.github.io/styleguides/mozilla_general/#brands-copyright-and-trademark
-profiler-brand-name = Firefox Profiler
# This is the title of the page
about-logging-title = About Logging
about-logging-page-title = Logging manager
about-logging-current-log-file = Current log file:
about-logging-new-log-file = New log file:
about-logging-currently-enabled-log-modules = Currently enabled log modules:
about-logging-current-log-file = Current Log File:
about-logging-current-log-modules = Current Log Modules:
about-logging-log-tutorial =
See <a data-l10n-name="logging">HTTP Logging</a>
for instructions on how to use this tool.
@ -31,39 +15,3 @@ about-logging-set-log-file = Set Log File
about-logging-set-log-modules = Set Log Modules
about-logging-start-logging = Start Logging
about-logging-stop-logging = Stop Logging
about-logging-buttons-disabled = Logging configured via environment variables, dynamic configuration unavailable.
about-logging-some-elements-disabled = Logging configured via URL, some configuration options are unavailable
about-logging-info = Info:
about-logging-log-modules-selection = Log module selection
about-logging-new-log-modules = New log modules:
about-logging-logging-output-selection = Logging output
about-logging-logging-to-file = Logging to a file
about-logging-logging-to-profiler = Logging to the { -profiler-brand-name }
about-logging-no-log-modules = None
about-logging-no-log-file = None
about-logging-logging-preset-selector-text = Logging preset:
## Logging presets
about-logging-preset-networking-label = Networking
about-logging-preset-networking-description = Log modules to diagnose networking issues
about-logging-preset-media-playback-label = Media playback
about-logging-preset-media-playback-description = Log modules to diagnose media playback issues (not video-conferencing issues)
about-logging-preset-custom-label = Custom
about-logging-preset-custom-description = Log modules manually selected
# Error handling
about-logging-error = Error:
## Variables:
## $k (String) - Variable name
## $v (String) - Variable value
about-logging-invalid-output = Invalid value “{ $v }“ for key “{ $k }“
about-logging-unknown-logging-preset = Unknown logging preset “{ $v }“
about-logging-unknown-profiler-preset = Unknown profiler preset “{ $v }“
about-logging-unknown-option = Unknown about:logging option “{ $k }“
about-logging-configuration-url-ignored = Configuration URL ignored
about-logging-file-and-profiler-override = Cant force file output and override profiler options at the same time
about-logging-configured-via-url = Option configured via URL

View File

@ -1,83 +0,0 @@
/* 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/. */
@import url("chrome://global/skin/in-content/common.css");
html {
height: 100%;
}
body {
height: 100%;
}
/** Content area **/
.main-content {
flex: 1;
}
#logging {
padding: 1em;
}
div.page-section {
padding: 0.5em;
}
div.page-section > div {
padding-inline-start: 0.5em;
padding-bottom: 2em;
}
div.page-section > div.radio-entry {
padding-bottom: 0;
}
#current-log-modules,
#no-log-modules {
font-family: monospace;
padding-inline-start: 8px;
margin-bottom: 1em;
}
#current-log-file,
#no-log-file {
font-family: monospace;
padding-inline-start: 8px;
}
#logging-preset-description {
padding-inline-start: 8px;
}
input[type=text] {
width: 100%;
font-family: monospace;
padding-inline-start: 0;
}
input[type=radio] {
vertical-align: middle;
}
.info-box {
width: 100%;
padding: 0.3em 1em;
border-radius: 4px;
}
.info-box-label {
font-weight: 600;
}
#error {
background-color: rgba(240, 40, 40, 0.5);
border: 1px solid rgba(240, 40, 40, 0.6);
}
#some-elements-unavailable {
background-color: var(--in-content-box-info-background);
border-color: var(--in-content-box-border-color);
}

View File

@ -16,7 +16,6 @@
skin/classic/global/aboutMemory.css (../../shared/aboutMemory.css)
skin/classic/global/aboutNetError.css (../../shared/aboutNetError.css)
skin/classic/global/aboutNetworking.css (../../shared/aboutNetworking.css)
skin/classic/global/aboutLogging.css (../../shared/aboutLogging.css)
skin/classic/global/aboutReader.css (../../shared/aboutReader.css)
skin/classic/global/aboutReaderPocket.css (../../shared/aboutReaderPocket.css)
skin/classic/global/aboutRights.css (../../shared/aboutRights.css)

View File

@ -1,352 +1,7 @@
Gecko Logging
=============
A minimal C++ logging framework is provided for use in core Gecko code. It is
enabled for all builds and is thread-safe.
This page covers enabling logging for particular logging module, configuring
the logging output, and how to use the logging facilities in native code.
Enabling and configuring logging
++++++++++++++++++++++++++++++++
Caveat: sandboxing when logging to a file
-----------------------------------------
A sandboxed content process cannot write to ``stderr`` or any file. The easiest
way to log these processes is to disable the content sandbox by setting the
preference ``security.sandbox.content.level`` to ``0``, or setting the environment
variable ``MOZ_DISABLE_CONTENT_SANDBOX`` to ``1``.
On Windows, you can still see child process messages by using DOS (not the
``MOZ_LOG_FILE`` variable defined below) to redirect output to a file. For
example: ``MOZ_LOG="CameraChild:5" mach run >& my_log_file.txt`` will include
debug messages from the camera's child actor that lives in a (sandboxed) content
process.
Logging to the Firefox Profiler
-------------------------------
When a log statement is logged on a thread and the `Firefox Profiler
<https://profiler.firefox.com>`_ is profiling that thread, the log statements is
recorded as a profiler marker.
This allows getting logs alongside profiler markers and lots of performance
and contextual informations, in a way that doesn't require disabling the
sandbox, and works accross all processes.
The profile can be downloaded and shared e.g. via Bugzilla or email, or
uploaded, and the logging statements will be visible either in the marker chart
or the marker table.
While it is possible to manually configure logging module and start the profiler
with the right set of threads to profile, ``about:logging`` makes this task a lot
simpler and error-proof.
The ``MOZ_LOG`` syntax
----------------------
Logging is configured using a special but simple syntax: which module should be
enabled, at which level, and what logging options should be enabled or disabled.
The syntax is a list of terms, separated by commas. There are two types of
terms:
- A log module and its level, separated by a colon (``:``), such as
``example_module:5`` to enable the module ``example_module`` at logging level
``5`` (verbose). This `searchfox query
<https://searchfox.org/mozilla-central/search?q=LazyLogModule+.*%5C%28%22&path=&case=true&regexp=true>`_
returns the complete list of modules available.
- A special string in the following table, to configure the logging behaviour.
Some configuration switch take an integer parameter, in which case it's
separated from the string by a colon (``:``). Most switches only apply in a
specific output context, noted in the **Context** column.
+----------------------+---------+-------------------------------------------------------------------------------------------+
| Special module name | Context | Action |
+======================+=========+===========================================================================================+
| append | File | Append new logs to existing log file. |
+----------------------+---------+-------------------------------------------------------------------------------------------+
| sync | File | Print each log synchronously, this is useful to check behavior in real time or get logs |
| | | immediately before crash. |
+----------------------+---------+-------------------------------------------------------------------------------------------+
| raw | File | Print exactly what has been specified in the format string, without the |
| | | process/thread/timestamp, etc. prefix. |
+----------------------+---------+-------------------------------------------------------------------------------------------+
| timestamp | File | Insert timestamp at start of each log line. |
+----------------------+---------+-------------------------------------------------------------------------------------------+
| rotate:**N** | File | | This limits the produced log files' size. Only most recent **N megabytes** of log data |
| | | | is saved. We rotate four log files with .0, .1, .2, .3 extensions. Note: this option |
| | | | disables 'append' and forces 'timestamp'. |
+----------------------+---------+-------------------------------------------------------------------------------------------+
| maxsize:**N** | File | Limit the log to **N** MB. Only work in append mode. |
+----------------------+---------+-------------------------------------------------------------------------------------------+
| prependheader | File | Prepend a simple header while distinguishing logging. Useful in append mode. |
+----------------------+---------+-------------------------------------------------------------------------------------------+
| profilerstacks | Profiler| | When profiling with the Firefox Profiler and log modules are enabled, capture the call |
| | | | stack for each log statement. |
+----------------------+---------+-------------------------------------------------------------------------------------------+
This syntax is used for most methods of enabling logging, with the exception of
settings preferences directly, see :ref:`this section <Enabling logging using preferences>` for directions.
Enabling Logging
----------------
Enabling logging can be done in a variety of ways:
- via environment variables
- via command line switches
- using ``about:config`` preferences
- using ``about:logging``
The first two allow logging from the start of the application and are also
useful in case of a crash (when ``sync`` output is requested, this can also be
done with ``about:config`` as well to a certain extent). The last two
allow enabling and disabling logging at runtime and don't require using the
command-line.
By default all logging output is disabled.
Enabling logging using ``about:logging``
''''''''''''''''''''''''''''''''''''''''
``about:logging`` allows enabling logging by entering a ``MOZ_LOG`` string in the
text input, and validating.
Options allow logging to a file or using the Firefox Profiler, that can be
started and stopped right from the page.
Logging presets for common scenarios are available in a drop-down. They can be
associated with a profiler preset.
It is possible, via URL parameters, to select a particular logging
configuration, or to override certain parameters in a preset. This is useful to
ask a user to gather logs efficiently without having to fiddle with prefs and/or
environment variable.
URL parameters are described in the following table:
+---------------------+---------------------------------------------------------------------------------------------+
| Parameter | Description |
+=====================+=============================================================================================+
| ``preset`` | a `logging preset <https://searchfox.org/mozilla-central/search?q=gLoggingPresets>`_ |
+---------------------+---------------------------------------------------------------------------------------------+
| ``logging-preset`` | alias for ``preset`` |
+---------------------+---------------------------------------------------------------------------------------------+
| ``modules`` | a string in ``MOZ_LOG`` syntax |
+---------------------+---------------------------------------------------------------------------------------------+
| ``module`` | alias for ``modules`` |
+---------------------+---------------------------------------------------------------------------------------------+
| ``threads`` | a list of threads to profile, overrides what a profiler preset would have picked |
+---------------------+---------------------------------------------------------------------------------------------+
| ``thread`` | alias for ``threads`` |
+---------------------+---------------------------------------------------------------------------------------------+
| ``output`` | either ``profiler`` or ``file`` |
+---------------------+---------------------------------------------------------------------------------------------+
| ``output-type`` | alias for ``output`` |
+---------------------+---------------------------------------------------------------------------------------------+
| ``profiler-preset`` | a `profiler preset <https://searchfox.org/mozilla-central/search?q=%40type+{Presets}>`_ |
+---------------------+---------------------------------------------------------------------------------------------+
If a preset is selected, then ``threads`` or ``modules`` can be used to override the
profiled threads or logging modules enabled, but keeping other aspects of the
preset. If no preset is selected, then a generic profiling preset is used,
``firefox-platform``. For example:
::
about:logging?output=profiler&preset=media-playback&modules=cubeb:4,AudioSinkWrapper:4:AudioSink:4
will profile the threads in the ``Media`` profiler preset, but will only log
specific log modules (instead of the `long list
<https://searchfox.org/mozilla-central/search?q="media-playback"&path=toolkit%2Fcontent%2FaboutLogging.js>`_
in the ``media-playback`` preset). In addition, it disallows logging to a file.
Enabling logging using environment variables
''''''''''''''''''''''''''''''''''''''''''''
On UNIX, setting and environment variable can be done in a variety of ways
::
set MOZ_LOG="example_logger:3"
export MOZ_LOG="example_logger:3"
MOZ_LOG="example_logger:3" ./mach run
In the Windows Command Prompt (``cmd.exe``), don't use quotes:
::
set MOZ_LOG=example_logger:3
If you want this on GeckoView example, use the following adb command to launch process:
::
adb shell am start -n org.mozilla.geckoview_example/.GeckoViewActivity --es env0 "MOZ_LOG=example_logger:3"
There are special module names to change logging behavior. You can specify one or more special module names without logging level.
For example, if you want to specify ``sync``, ``timestamp`` and ``rotate``:
::
set MOZ_LOG="example_logger:3,timestamp,sync,rotate:10"
Enabling logging usually outputs the logging statements to the terminal. To
have the logs written to a file instead (one file per process), the environment
variable ``MOZ_LOG_FILE`` can be used. Logs will be written at this path
(either relative or absolute), suffixed by a process type and its PID.
``MOZ_LOG`` files are text files and have the extension ``.moz_log``.
For example, setting:
::
set MOZ_LOG_FILE="firefox-logs"
can create a number of files like so:
::
firefox-log-main.96353.moz_log
firefox-log-child.96354.moz_log
respectively for a parent process of PID 96353 and a child process of PID
96354.
Enabling logging using command-line flags
'''''''''''''''''''''''''''''''''''''''''
The ``MOZ_LOG`` syntax can be used with the command line switch on the same
name, and specifying a file with ``MOZ_LOG_FILE`` works in the same way:
::
./mach run -MOZ_LOG=timestamp,rotate:200,example_module:5 -MOZ_LOG_FILE=%TEMP%\firefox-logs
will enable verbose (``5``) logging for the module ``example_module``, with
timestamp prepended to each line, rotate the logs with 4 files of each 50MB
(for a total of 200MB), and write the output to the temporary directory on
Windows, with name starting with ``firefox-logs``.
.. _Enabling logging using preferences:
Enabling logging using preferences
''''''''''''''''''''''''''''''''''
To adjust the logging after Firefox has started, you can set prefs under the
`logging.` prefix. For example, setting `logging.foo` to `3` will set the log
module `foo` to start logging at level 3. A number of special prefs can be set,
described in the table below:
+-------------------------------------+------------+-------------------------------+--------------------------------------------------------+
| Preference name | Preference | Preference value | Description |
+=====================================+============+===============================+========================================================+
| ``logging.config.clear_on_startup`` | bool | -- | Whether to clear all prefs under ``logging.`` |
+-------------------------------------+------------+-------------------------------+--------------------------------------------------------+
| ``logging.config.LOG_FILE`` | string | A path (relative or absolute) | The path to which the log files will be written. |
+-------------------------------------+------------+-------------------------------+--------------------------------------------------------+
| ``logging.config.add_timestamp`` | bool | -- | Whether to prefix all lines by a timestamp. |
+-------------------------------------+------------+-------------------------------+--------------------------------------------------------+
| ``logging.config.sync`` | bool | -- | Whether to flush the stream after each log statements. |
+-------------------------------------+------------+-------------------------------+--------------------------------------------------------+
| ``logging.config.profilerstacks`` | bool | -- | | When logging to the Firefox Profiler, whether to |
| | | | | include the call stack in each logging statement. |
+-------------------------------------+------------+-------------------------------+--------------------------------------------------------+
Enabling logging in Rust code
-----------------------------
We're gradually adding more Rust code to Gecko, and Rust crates typically use a
different approach to logging. Many Rust libraries use the `log
<https://docs.rs/log>`_ crate to log messages, which works together with
`env_logger <https://docs.rs/env_logger>`_ at the application level to control
what's actually printed via `RUST_LOG`.
You can set an overall logging level, though it could be quite verbose:
::
set RUST_LOG="debug"
You can also target individual modules by path:
::
set RUST_LOG="style::style_resolver=debug"
.. note::
For Linux/MacOS users, you need to use `export` rather than `set`.
.. note::
Sometimes it can be useful to only log child processes and ignore the parent
process. In Firefox 57 and later, you can use `RUST_LOG_CHILD` instead of
`RUST_LOG` to specify log settings that will only apply to child processes.
The `log` crate lists the available `log levels <https://docs.rs/log/0.3.8/log/enum.LogLevel.html>`_:
+-----------+---------------------------------------------------------------------------------------------------------+
| Log Level | Purpose |
+===========+=========================================================================================================+
| error | Designates very serious errors. |
+-----------+---------------------------------------------------------------------------------------------------------+
| warn | Designates hazardous situations. |
+-----------+---------------------------------------------------------------------------------------------------------+
| info | Designates useful information. |
+-----------+---------------------------------------------------------------------------------------------------------+
| debug | Designates lower priority information. |
+-----------+---------------------------------------------------------------------------------------------------------+
| trace | Designates very low priority, often extremely verbose, information. |
+-----------+---------------------------------------------------------------------------------------------------------+
It is common for debug and trace to be disabled at compile time in release builds, so you may need a debug build if you want logs from those levels.
Check the `env_logger <https://docs.rs/env_logger>`_ docs for more details on logging options.
Additionally, a mapping from `RUST_LOG` is available. When using the `MOZ_LOG`
syntax, it is possible to enable logging in rust crate using a similar syntax:
::
MOZ_LOG=rust_crate_name::*:4
will enable `debug` logging for all log statements in the crate
``rust_crate_name``.
`*` can be replaced by a series of modules if more specificity is needed:
::
MOZ_LOG=rust_crate_name::module::submodule:4
will enable `debug` logging for all log statements in the sub-module
``submodule`` of the module ``module`` of the crate ``rust_crate_name``.
A table mapping Rust log levels to `MOZ_LOG` log level is available below:
+----------------+---------------+-----------------+
| Rust log level | MOZ_LOG level | Numerical value |
+================+===============+=================+
| off | Disabled | 0 |
+----------------+---------------+-----------------+
| error | Error | 1 |
+----------------+---------------+-----------------+
| warn | Warning | 2 |
+----------------+---------------+-----------------+
| info | Info | 3 |
+----------------+---------------+-----------------+
| debug | Debug | 4 |
+----------------+---------------+-----------------+
| trace | Verbose | 5 |
+----------------+---------------+-----------------+
Working with ``MOZ_LOG`` in the code
++++++++++++++++++++++++++++++++++++
A minimal C++ logging framework is provided for use in core Gecko code. It is enabled for all builds and is thread-safe.
Declaring a Log Module
----------------------
@ -433,3 +88,114 @@ Example Usage
MOZ_LOG(sLogger, LogLevel::Error, ("i should be 10!"));
}
}
Enabling Logging
----------------
The log level for a module is controlled by setting an environment variable before launching the application. It can also be adjusted by setting prefs. By default all logging output is disabled.
::
set MOZ_LOG="example_logger:3"
In the Windows Command Prompt (`cmd.exe`), don't use quotes:
::
set MOZ_LOG=example_logger:3
If you want this on GeckoView example, use the following adb command to launch process:
::
adb shell am start -n org.mozilla.geckoview_example/.GeckoViewActivity --es env0 "MOZ_LOG=example_logger:3"
There are special module names to change logging behavior. You can specify one or more special module names without logging level.
+-------------------------+-------------------------------------------------------------------------------------------+
| Special module name | Action |
+=========================+===========================================================================================+
| append | Append new logs to existing log file. |
+-------------------------+-------------------------------------------------------------------------------------------+
| sync | Print each log synchronously, this is useful to check behavior in real time or get logs |
| | immediately before crash. |
+-------------------------+-------------------------------------------------------------------------------------------+
| raw | Print exactly what has been specified in the format string, without the |
| | process/thread/timestamp, etc. prefix. |
+-------------------------+-------------------------------------------------------------------------------------------+
| timestamp | Insert timestamp at start of each log line. |
+-------------------------+-------------------------------------------------------------------------------------------+
| rotate: **N** | | This limits the produced log files' size. Only most recent **N megabytes** of log data |
| | | is saved. We rotate four log files with .0, .1, .2, .3 extensions. Note: this option |
| | | disables 'append' and forces 'timestamp'. |
+-------------------------+-------------------------------------------------------------------------------------------+
For example, if you want to specify `sync`, `timestamp` and `rotate`:
::
set MOZ_LOG="example_logger:3,timestamp,sync,rotate:10"
To adjust the logging after Firefox has started, you can set prefs under the `logging.` prefix. For example, setting `logging.foo` to `3` will set the log module `foo` to start logging at level 3. The special boolean prefs `logging.config.sync` and `logging.config.add_timestamp` can be used to control the `sync` and `timestamp` properties described above.
.. warning::
A sandboxed content process cannot write to stderr or any file. The easiest way to log these processes is to disable the content sandbox by setting the preference `security.sandbox.content.level` to `0`. On Windows, you can still see child process messages by using DOS (not the `MOZ_LOG_FILE` variable defined below) to redirect output to a file. For example: `MOZ_LOG="CameraChild:5" mach run >& my_log_file.txt` will include debug messages from the camera's child actor that lives in a (sandboxed) content process.
Redirecting logging output to a file
------------------------------------
Logging output can be redirected to a file by passing its path via an environment variable.
.. note::
By default logging output goes to `stderr`.
::
set MOZ_LOG_FILE="log.txt"
The `rotate` and `append` options described above only apply when logging to a file.
The special pref `logging.config.LOG_FILE` can be set at runtime to change the log file being output to.
Logging Rust
------------
We're gradually adding more Rust code to Gecko, and Rust crates typically use a different approach to logging. Many Rust libraries use the `log <https://docs.rs/log>`_ crate to log messages, which works together with `env_logger <https://docs.rs/env_logger>`_ at the application level to control what's actually printed via `RUST_LOG`.
You can set an overall logging level, though it could be quite verbose:
::
set RUST_LOG="debug"
You can also target individual modules by path:
::
set RUST_LOG="style::style_resolver=debug"
.. note::
For Linux/MacOS users, you need to use `export` rather than `set`.
.. note::
Sometimes it can be useful to only log child processes and ignore the parent process. In Firefox 57 and later, you can use `RUST_LOG_CHILD` instead of `RUST_LOG` to specify log settings that will only apply to child processes.
The `log` crate lists the available `log levels <https://docs.rs/log/0.3.8/log/enum.LogLevel.html>`_:
+-----------+---------------------------------------------------------------------------------------------------------+
| Log Level | Purpose |
+===========+=========================================================================================================+
| error | Designates very serious errors. |
+-----------+---------------------------------------------------------------------------------------------------------+
| warn | Designates hazardous situations. |
+-----------+---------------------------------------------------------------------------------------------------------+
| info | Designates useful information. |
+-----------+---------------------------------------------------------------------------------------------------------+
| debug | Designates lower priority information. |
+-----------+---------------------------------------------------------------------------------------------------------+
| trace | Designates very low priority, often extremely verbose, information. |
+-----------+---------------------------------------------------------------------------------------------------------+
It is common for debug and trace to be disabled at compile time in release builds, so you may need a debug build if you want logs from those levels.
Check the `env_logger <https://docs.rs/env_logger>`_ docs for more details on logging options.