no bug - Bumping Firefox l10n changesets r=release a=l10n-bump DONTBUILD CLOSED TREE
[gecko.git] / toolkit / content / aboutLogging.js
blobdaff10fbec6be4d218dff7c6e832ac5e5ffe324f
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 "use strict";
6 const { XPCOMUtils } = ChromeUtils.importESModule(
7   "resource://gre/modules/XPCOMUtils.sys.mjs"
8 );
9 const gDashboard = Cc["@mozilla.org/network/dashboard;1"].getService(
10   Ci.nsIDashboard
12 const gDirServ = Cc["@mozilla.org/file/directory_service;1"].getService(
13   Ci.nsIDirectoryServiceProvider
16 const { ProfilerMenuButton } = ChromeUtils.importESModule(
17   "resource://devtools/client/performance-new/popup/menu-button.sys.mjs"
19 const { CustomizableUI } = ChromeUtils.importESModule(
20   "resource:///modules/CustomizableUI.sys.mjs"
23 ChromeUtils.defineLazyGetter(this, "ProfilerPopupBackground", function () {
24   return ChromeUtils.importESModule(
25     "resource://devtools/client/performance-new/shared/background.sys.mjs"
26   );
27 });
29 const $ = document.querySelector.bind(document);
30 const $$ = document.querySelectorAll.bind(document);
32 function fileEnvVarPresent() {
33   return Services.env.get("MOZ_LOG_FILE") || Services.env.get("NSPR_LOG_FILE");
36 function moduleEnvVarPresent() {
37   return Services.env.get("MOZ_LOG") || Services.env.get("NSPR_LOG");
40 /**
41  * All the information associated with a logging presets:
42  * - `modules` is the list of log modules and option, the same that would have
43  *   been set as a MOZ_LOG environment variable
44  * - l10nIds.label and l10nIds.description are the Ids of the strings that
45  *   appear in the dropdown selector, and a one-liner describing the purpose of
46  *   a particular logging preset
47  * - profilerPreset is the name of a Firefox Profiler preset [1]. In general,
48  *   the profiler preset will have the correct set of threads for a particular
49  *   logging preset, so that all logging statements are recorded in the profile
50  *   as markers.
51  *
52  * [1]: The keys of the `presets` object defined in
53  * https://searchfox.org/mozilla-central/source/devtools/client/performance-new/shared/background.sys.mjs
54  */
56 const gOsSpecificLoggingPresets = (() => {
57   // Microsoft Windows
58   if (navigator.platform.startsWith("Win")) {
59     return {
60       windows: {
61         modules:
62           "timestamp,sync,Widget:5,BaseWidget:5,WindowsEvent:4,TaskbarConcealer:5,FileDialog:5",
63         l10nIds: {
64           label: "about-logging-preset-windows-label",
65           description: "about-logging-preset-windows-description",
66         },
67       },
68     };
69   }
71   return {};
72 })();
74 const gLoggingPresets = {
75   networking: {
76     modules:
77       "timestamp,sync,nsHttp:5,cache2:5,nsSocketTransport:5,nsHostResolver:5,EarlyHint:5",
78     l10nIds: {
79       label: "about-logging-preset-networking-label",
80       description: "about-logging-preset-networking-description",
81     },
82     profilerPreset: "networking",
83   },
84   cookie: {
85     modules: "timestamp,sync,nsHttp:5,cache2:5,cookie:5",
86     l10nIds: {
87       label: "about-logging-preset-networking-cookie-label",
88       description: "about-logging-preset-networking-cookie-description",
89     },
90   },
91   websocket: {
92     modules:
93       "timestamp,sync,nsHttp:5,nsWebSocket:5,nsSocketTransport:5,nsHostResolver:5",
94     l10nIds: {
95       label: "about-logging-preset-networking-websocket-label",
96       description: "about-logging-preset-networking-websocket-description",
97     },
98   },
99   http3: {
100     modules:
101       "timestamp,sync,nsHttp:5,nsSocketTransport:5,nsHostResolver:5,neqo_http3::*:5,neqo_transport::*:5",
102     l10nIds: {
103       label: "about-logging-preset-networking-http3-label",
104       description: "about-logging-preset-networking-http3-description",
105     },
106   },
107   "http3-upload-speed": {
108     modules: "timestamp,neqo_transport::*:3",
109     l10nIds: {
110       label: "about-logging-preset-networking-http3-upload-speed-label",
111       description:
112         "about-logging-preset-networking-http3-upload-speed-description",
113     },
114   },
115   "media-playback": {
116     modules:
117       "HTMLMediaElement:4,HTMLMediaElementEvents:4,cubeb:5,PlatformDecoderModule:5,AudioSink:5,AudioSinkWrapper:5,MediaDecoderStateMachine:4,MediaDecoder:4,MediaFormatReader:5,GMP:5",
118     l10nIds: {
119       label: "about-logging-preset-media-playback-label",
120       description: "about-logging-preset-media-playback-description",
121     },
122     profilerPreset: "media",
123   },
124   webrtc: {
125     modules:
126       "jsep:5,sdp:5,signaling:5,mtransport:5,RTCRtpReceiver:5,RTCRtpSender:5,RTCDMTFSender:5,VideoFrameConverter:5,WebrtcTCPSocket:5,CamerasChild:5,CamerasParent:5,VideoEngine:5,ShmemPool:5,TabShare:5,MediaChild:5,MediaParent:5,MediaManager:5,MediaTrackGraph:5,cubeb:5,MediaStream:5,MediaStreamTrack:5,DriftCompensator:5,ForwardInputTrack:5,MediaRecorder:5,MediaEncoder:5,TrackEncoder:5,VP8TrackEncoder:5,Muxer:5,GetUserMedia:5,MediaPipeline:5,PeerConnectionImpl:5,WebAudioAPI:5,webrtc_trace:5,RTCRtpTransceiver:5,ForwardedInputTrack:5,HTMLMediaElement:5,HTMLMediaElementEvents:5",
127     l10nIds: {
128       label: "about-logging-preset-webrtc-label",
129       description: "about-logging-preset-webrtc-description",
130     },
131     profilerPreset: "media",
132   },
133   webgpu: {
134     modules:
135       "wgpu_core::*:5,wgpu_hal::*:5,wgpu_types::*:5,naga::*:5,wgpu_bindings::*:5,WebGPU:5",
136     l10nIds: {
137       label: "about-logging-preset-webgpu-label",
138       description: "about-logging-preset-webgpu-description",
139     },
140   },
141   gfx: {
142     modules:
143       "webrender::*:5,webrender_bindings::*:5,webrender_types::*:5,gfx2d:5,WebRenderBridgeParent:5,DcompSurface:5,apz.displayport:5,layout:5,dl.content:5,dl.parent:5,nsRefreshDriver:5,fontlist:5,fontinit:5,textrun:5,textrunui:5,textperf:5",
144     l10nIds: {
145       label: "about-logging-preset-gfx-label",
146       description: "about-logging-preset-gfx-description",
147     },
148     // The graphics profiler preset enables the threads we want but loses the screenshots.
149     // We could add an extra preset for that if we miss it.
150     profilerPreset: "graphics",
151   },
152   ...gOsSpecificLoggingPresets,
153   custom: {
154     modules: "",
155     l10nIds: {
156       label: "about-logging-preset-custom-label",
157       description: "about-logging-preset-custom-description",
158     },
159   },
162 const gLoggingSettings = {
163   // Possible values: "profiler" and "file".
164   loggingOutputType: "profiler",
165   running: false,
166   // If non-null, the profiler preset to use. If null, the preset selected in
167   // the dropdown is going to be used. It is also possible to use a "custom"
168   // preset and an explicit list of modules.
169   loggingPreset: null,
170   // If non-null, the profiler preset to use. If a logging preset is being used,
171   // and this is null, the profiler preset associated to the logging preset is
172   // going to be used. Otherwise, a generic profiler preset is going to be used
173   // ("firefox-platform").
174   profilerPreset: null,
175   // If non-null, the threads that will be recorded by the Firefox Profiler. If
176   // null, the threads from the profiler presets are going to be used.
177   profilerThreads: null,
178   // If non-null, stack traces will be recorded for MOZ_LOG profiler markers.
179   // This is set only when coming from the URL, not when the user changes the UI.
180   profilerStacks: null,
183 // When the profiler has been started, this holds the promise the
184 // Services.profiler.StartProfiler returns, to ensure the profiler has
185 // effectively started.
186 let gProfilerPromise = null;
188 // Used in tests
189 function presets() {
190   return gLoggingPresets;
193 // Used in tests
194 function settings() {
195   return gLoggingSettings;
198 // Used in tests
199 function profilerPromise() {
200   return gProfilerPromise;
203 function populatePresets() {
204   let dropdown = $("#logging-preset-dropdown");
205   for (let presetName in gLoggingPresets) {
206     let preset = gLoggingPresets[presetName];
207     let option = document.createElement("option");
208     document.l10n.setAttributes(option, preset.l10nIds.label);
209     option.value = presetName;
210     dropdown.appendChild(option);
211     if (option.value === gLoggingSettings.loggingPreset) {
212       option.setAttribute("selected", true);
213     }
214   }
216   function setPresetAndDescription(preset) {
217     document.l10n.setAttributes(
218       $("#logging-preset-description"),
219       gLoggingPresets[preset].l10nIds.description
220     );
221     gLoggingSettings.loggingPreset = preset;
222   }
224   dropdown.onchange = function () {
225     // When switching to custom, leave the existing module list, to allow
226     // editing.
227     if (dropdown.value != "custom") {
228       $("#log-modules").value = gLoggingPresets[dropdown.value].modules;
229     }
230     setPresetAndDescription(dropdown.value);
231     Services.prefs.setCharPref("logging.config.preset", dropdown.value);
232   };
234   $("#log-modules").value = gLoggingPresets[dropdown.value].modules;
235   setPresetAndDescription(dropdown.value);
236   // When changing the list switch to custom.
237   $("#log-modules").oninput = e => {
238     dropdown.value = "custom";
239   };
242 function updateLoggingOutputType(profilerOutputType) {
243   gLoggingSettings.loggingOutputType = profilerOutputType;
244   Services.prefs.setCharPref("logging.config.output_type", profilerOutputType);
245   $(`input[type=radio][value=${profilerOutputType}]`).checked = true;
247   switch (profilerOutputType) {
248     case "profiler":
249       if (!gLoggingSettings.profilerStacks) {
250         // If this value is set from the URL, do not allow to change it.
251         $("#with-profiler-stacks-checkbox").disabled = false;
252       }
253       // hide options related to file output for clarity
254       $("#log-file-configuration").hidden = true;
255       break;
256     case "file":
257       $("#with-profiler-stacks-checkbox").disabled = true;
258       $("#log-file-configuration").hidden = false;
259       $("#no-log-file").hidden = !!$("#current-log-file").innerText.length;
260       break;
261   }
264 function displayErrorMessage(error) {
265   var err = $("#error");
266   err.hidden = false;
268   var errorDescription = $("#error-description");
269   document.l10n.setAttributes(errorDescription, error.l10nId, {
270     k: error.key,
271     v: error.value,
272   });
275 class ParseError extends Error {
276   constructor(l10nId, key, value) {
277     super(name);
278     this.l10nId = l10nId;
279     this.key = key;
280     this.value = value;
281   }
282   name = "ParseError";
283   l10nId;
284   key;
285   value;
288 function parseURL() {
289   let options = new URL(document.location.href).searchParams;
291   if (!options) {
292     return;
293   }
295   let modulesOverriden = null,
296     outputTypeOverriden = null,
297     loggingPresetOverriden = null,
298     threadsOverriden = null,
299     profilerPresetOverriden = null,
300     profilerStacksOverriden = null;
301   try {
302     for (let [k, v] of options) {
303       switch (k) {
304         case "modules":
305         case "module":
306           modulesOverriden = v;
307           break;
308         case "output":
309         case "output-type":
310           if (v !== "profiler" && v !== "file") {
311             throw new ParseError("about-logging-invalid-output", k, v);
312           }
313           outputTypeOverriden = v;
314           break;
315         case "preset":
316         case "logging-preset":
317           if (!Object.keys(gLoggingPresets).includes(v)) {
318             throw new ParseError("about-logging-unknown-logging-preset", k, v);
319           }
320           loggingPresetOverriden = v;
321           break;
322         case "threads":
323         case "thread":
324           threadsOverriden = v;
325           break;
326         case "profiler-preset":
327           if (!Object.keys(ProfilerPopupBackground.presets).includes(v)) {
328             throw new Error(["about-logging-unknown-profiler-preset", k, v]);
329           }
330           profilerPresetOverriden = v;
331           break;
332         case "profilerstacks":
333           profilerStacksOverriden = true;
334           break;
335         default:
336           throw new ParseError("about-logging-unknown-option", k, v);
337       }
338     }
339   } catch (e) {
340     displayErrorMessage(e);
341     return;
342   }
344   // Detect combinations that don't make sense
345   if (
346     (profilerPresetOverriden || threadsOverriden) &&
347     outputTypeOverriden == "file"
348   ) {
349     displayErrorMessage(
350       new ParseError("about-logging-file-and-profiler-override")
351     );
352     return;
353   }
355   // Configuration is deemed at least somewhat valid, override each setting in
356   // turn
357   let someElementsDisabled = false;
359   if (modulesOverriden || loggingPresetOverriden) {
360     // Don't allow changing those if set by the URL
361     let logModules = $("#log-modules");
362     var dropdown = $("#logging-preset-dropdown");
363     if (loggingPresetOverriden) {
364       dropdown.value = loggingPresetOverriden;
365       dropdown.onchange();
366     }
367     if (modulesOverriden) {
368       logModules.value = modulesOverriden;
369       dropdown.value = "custom";
370       dropdown.onchange();
371       dropdown.disabled = true;
372       someElementsDisabled = true;
373     }
374     logModules.disabled = true;
375     $("#set-log-modules-button").disabled = true;
376     $("#logging-preset-dropdown").disabled = true;
377     someElementsDisabled = true;
378     updateLogModules();
379   }
380   if (outputTypeOverriden) {
381     $$("input[type=radio]").forEach(e => (e.disabled = true));
382     someElementsDisabled = true;
383     updateLoggingOutputType(outputTypeOverriden);
384   }
385   if (profilerStacksOverriden) {
386     const checkbox = $("#with-profiler-stacks-checkbox");
387     checkbox.disabled = true;
388     someElementsDisabled = true;
389     Services.prefs.setBoolPref("logging.config.profilerstacks", true);
390     gLoggingSettings.profilerStacks = true;
391   }
393   if (loggingPresetOverriden) {
394     gLoggingSettings.loggingPreset = loggingPresetOverriden;
395   }
396   if (profilerPresetOverriden) {
397     gLoggingSettings.profilerPreset = profilerPresetOverriden;
398   }
399   if (threadsOverriden) {
400     gLoggingSettings.profilerThreads = threadsOverriden;
401   }
403   $("#some-elements-unavailable").hidden = !someElementsDisabled;
406 let gInited = false;
407 function init() {
408   if (gInited) {
409     return;
410   }
411   gInited = true;
412   gDashboard.enableLogging = true;
414   populatePresets();
415   parseURL();
417   $("#log-file-configuration").addEventListener("submit", e => {
418     e.preventDefault();
419     setLogFile();
420   });
422   $("#log-modules-form").addEventListener("submit", e => {
423     e.preventDefault();
424     setLogModules();
425   });
427   let toggleLoggingButton = $("#toggle-logging-button");
428   toggleLoggingButton.addEventListener("click", startStopLogging);
430   $$("input[type=radio]").forEach(radio => {
431     radio.onchange = e => {
432       updateLoggingOutputType(e.target.value);
433     };
434   });
436   $("#with-profiler-stacks-checkbox").addEventListener("change", e => {
437     Services.prefs.setBoolPref(
438       "logging.config.profilerstacks",
439       e.target.checked
440     );
441     updateLogModules();
442   });
444   let loggingOutputType = Services.prefs.getCharPref(
445     "logging.config.output_type",
446     "profiler"
447   );
448   if (loggingOutputType.length) {
449     updateLoggingOutputType(loggingOutputType);
450   }
452   $("#with-profiler-stacks-checkbox").checked = Services.prefs.getBoolPref(
453     "logging.config.profilerstacks",
454     false
455   );
457   try {
458     let loggingPreset = Services.prefs.getCharPref("logging.config.preset");
459     gLoggingSettings.loggingPreset = loggingPreset;
460   } catch {}
462   try {
463     let running = Services.prefs.getBoolPref("logging.config.running");
464     gLoggingSettings.running = running;
465     document.l10n.setAttributes(
466       $("#toggle-logging-button"),
467       `about-logging-${gLoggingSettings.running ? "stop" : "start"}-logging`
468     );
469   } catch {}
471   try {
472     let file = gDirServ.getFile("TmpD", {});
473     file.append("log.txt");
474     $("#log-file").value = file.path;
475   } catch (e) {
476     console.error(e);
477   }
479   // Update the value of the log file.
480   updateLogFile();
482   // Update the active log modules
483   updateLogModules();
485   // If we can't set the file and the modules at runtime,
486   // the start and stop buttons wouldn't really do anything.
487   if (
488     ($("#set-log-file-button").disabled ||
489       $("#set-log-modules-button").disabled) &&
490     moduleEnvVarPresent()
491   ) {
492     $("#buttons-disabled").hidden = false;
493     toggleLoggingButton.disabled = true;
494   }
497 function updateLogFile(file) {
498   let logPath = "";
500   // Try to get the environment variable for the log file
501   logPath =
502     Services.env.get("MOZ_LOG_FILE") || Services.env.get("NSPR_LOG_FILE");
503   let currentLogFile = $("#current-log-file");
504   let setLogFileButton = $("#set-log-file-button");
506   // If the log file was set from an env var, we disable the ability to set it
507   // at runtime.
508   if (logPath.length) {
509     currentLogFile.innerText = logPath;
510     setLogFileButton.disabled = true;
511   } else if (gDashboard.getLogPath() != ".moz_log") {
512     // There may be a value set by a pref.
513     currentLogFile.innerText = gDashboard.getLogPath();
514   } else if (file !== undefined) {
515     currentLogFile.innerText = file;
516   } else {
517     try {
518       let file = gDirServ.getFile("TmpD", {});
519       file.append("log.txt");
520       $("#log-file").value = file.path;
521     } catch (e) {
522       console.error(e);
523     }
524     // Fall back to the temp dir
525     currentLogFile.innerText = $("#log-file").value;
526   }
528   let openLogFileButton = $("#open-log-file-button");
529   openLogFileButton.disabled = true;
531   if (currentLogFile.innerText.length) {
532     let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
533     file.initWithPath(currentLogFile.innerText);
535     if (file.exists()) {
536       openLogFileButton.disabled = false;
537       openLogFileButton.onclick = function (e) {
538         file.reveal();
539       };
540     }
541   }
542   $("#no-log-file").hidden = !!currentLogFile.innerText.length;
543   $("#current-log-file").hidden = !currentLogFile.innerText.length;
546 function updateLogModules() {
547   // Try to get the environment variable for the log file
548   let logModules =
549     Services.env.get("MOZ_LOG") ||
550     Services.env.get("MOZ_LOG_MODULES") ||
551     Services.env.get("NSPR_LOG_MODULES");
552   let currentLogModules = $("#current-log-modules");
553   let setLogModulesButton = $("#set-log-modules-button");
554   if (logModules.length) {
555     currentLogModules.innerText = logModules;
556     // If the log modules are set by an environment variable at startup, do not
557     // allow changing them throught a pref. It would be difficult to figure out
558     // which ones are enabled and which ones are not. The user probably knows
559     // what he they are doing.
560     setLogModulesButton.disabled = true;
561   } else {
562     let activeLogModules = [];
563     let children = Services.prefs.getBranch("logging.").getChildList("");
565     for (let pref of children) {
566       if (pref.startsWith("config.")) {
567         continue;
568       }
570       try {
571         let value = Services.prefs.getIntPref(`logging.${pref}`);
572         activeLogModules.push(`${pref}:${value}`);
573       } catch (e) {
574         console.error(e);
575       }
576     }
578     if (activeLogModules.length) {
579       // Add some options only if some modules are present.
580       if (Services.prefs.getBoolPref("logging.config.add_timestamp", false)) {
581         activeLogModules.push("timestamp");
582       }
583       if (Services.prefs.getBoolPref("logging.config.sync", false)) {
584         activeLogModules.push("sync");
585       }
586       if (Services.prefs.getBoolPref("logging.config.profilerstacks", false)) {
587         activeLogModules.push("profilerstacks");
588       }
589     }
591     if (activeLogModules.length !== 0) {
592       currentLogModules.innerText = activeLogModules.join(",");
593       currentLogModules.hidden = false;
594       $("#no-log-modules").hidden = true;
595     } else {
596       currentLogModules.innerText = "";
597       currentLogModules.hidden = true;
598       $("#no-log-modules").hidden = false;
599     }
600   }
603 function setLogFile() {
604   let setLogButton = $("#set-log-file-button");
605   if (setLogButton.disabled) {
606     // There's no point trying since it wouldn't work anyway.
607     return;
608   }
609   let logFile = $("#log-file").value.trim();
610   Services.prefs.setCharPref("logging.config.LOG_FILE", logFile);
611   updateLogFile(logFile);
614 function clearLogModules() {
615   // Turn off all the modules.
616   let children = Services.prefs.getBranch("logging.").getChildList("");
617   for (let pref of children) {
618     if (!pref.startsWith("config.")) {
619       Services.prefs.clearUserPref(`logging.${pref}`);
620     }
621   }
622   Services.prefs.clearUserPref("logging.config.add_timestamp");
623   Services.prefs.clearUserPref("logging.config.sync");
624   updateLogModules();
627 function setLogModules() {
628   if (moduleEnvVarPresent()) {
629     // The modules were set via env var, so we shouldn't try to change them.
630     return;
631   }
633   let modules = $("#log-modules").value.trim();
635   // Clear previously set log modules.
636   clearLogModules();
638   if (modules.length !== 0) {
639     let logModules = modules.split(",");
640     for (let module of logModules) {
641       if (module == "timestamp") {
642         Services.prefs.setBoolPref("logging.config.add_timestamp", true);
643       } else if (module == "rotate") {
644         // XXX: rotate is not yet supported.
645       } else if (module == "append") {
646         // XXX: append is not yet supported.
647       } else if (module == "sync") {
648         Services.prefs.setBoolPref("logging.config.sync", true);
649       } else if (module == "profilerstacks") {
650         Services.prefs.setBoolPref("logging.config.profilerstacks", true);
651       } else {
652         let lastColon = module.lastIndexOf(":");
653         let key = module.slice(0, lastColon);
654         let value = parseInt(module.slice(lastColon + 1), 10);
655         Services.prefs.setIntPref(`logging.${key}`, value);
656       }
657     }
658   }
660   updateLogModules();
663 function isLogging() {
664   try {
665     return Services.prefs.getBoolPref("logging.config.running");
666   } catch {
667     return false;
668   }
671 function startStopLogging() {
672   if (isLogging()) {
673     document.l10n.setAttributes(
674       $("#toggle-logging-button"),
675       "about-logging-start-logging"
676     );
677     stopLogging();
678   } else {
679     document.l10n.setAttributes(
680       $("#toggle-logging-button"),
681       "about-logging-stop-logging"
682     );
683     startLogging();
684   }
687 function startLogging() {
688   setLogModules();
689   if (gLoggingSettings.loggingOutputType === "profiler") {
690     const pageContext = "aboutlogging";
691     const supportedFeatures = Services.profiler.GetFeatures();
692     if (gLoggingSettings.loggingPreset != "custom") {
693       // Change the preset before starting the profiler, so that the
694       // underlying profiler code picks up the right configuration.
695       const profilerPreset =
696         gLoggingPresets[gLoggingSettings.loggingPreset].profilerPreset;
697       ProfilerPopupBackground.changePreset(
698         "aboutlogging",
699         profilerPreset,
700         supportedFeatures
701       );
702     } else {
703       // a baseline set of threads, and possibly others, overriden by the URL
704       ProfilerPopupBackground.changePreset(
705         "aboutlogging",
706         "firefox-platform",
707         supportedFeatures
708       );
709     }
710     let { entries, interval, features, threads, duration } =
711       ProfilerPopupBackground.getRecordingSettings(
712         pageContext,
713         Services.profiler.GetFeatures()
714       );
716     if (gLoggingSettings.profilerThreads) {
717       threads.push(...gLoggingSettings.profilerThreads.split(","));
718       // Small hack: if cubeb is being logged, it's almost always necessary (and
719       // never harmful) to enable audio callback tracing, otherwise, no log
720       // statements will be recorded from real-time threads.
721       if (gLoggingSettings.profilerThreads.includes("cubeb")) {
722         features.push("audiocallbacktracing");
723       }
724     }
725     const win = Services.wm.getMostRecentWindow("navigator:browser");
726     const windowid = win?.gBrowser?.selectedBrowser?.browsingContext?.browserId;
728     // Force displaying the profiler button in the navbar if not preset, so
729     // that there is a visual indication profiling is in progress.
730     if (!ProfilerMenuButton.isInNavbar()) {
731       // Ensure the widget is enabled.
732       Services.prefs.setBoolPref(
733         "devtools.performance.popup.feature-flag",
734         true
735       );
736       // Enable the profiler menu button.
737       ProfilerMenuButton.addToNavbar();
738       // Dispatch the change event manually, so that the shortcuts will also be
739       // added.
740       CustomizableUI.dispatchToolboxEvent("customizationchange");
741     }
743     gProfilerPromise = Services.profiler.StartProfiler(
744       entries,
745       interval,
746       features,
747       threads,
748       windowid,
749       duration
750     );
751   } else {
752     setLogFile();
753   }
754   Services.prefs.setBoolPref("logging.config.running", true);
757 async function stopLogging() {
758   if (gLoggingSettings.loggingOutputType === "profiler") {
759     await ProfilerPopupBackground.captureProfile("aboutlogging");
760   } else {
761     Services.prefs.clearUserPref("logging.config.LOG_FILE");
762     updateLogFile();
763   }
764   Services.prefs.setBoolPref("logging.config.running", false);
765   clearLogModules();
768 // We use the pageshow event instead of onload. This is needed because sometimes
769 // the page is loaded via session-restore/bfcache. In such cases we need to call
770 // init() to keep the page behaviour consistent with the ticked checkboxes.
771 // Mostly the issue is with the autorefresh checkbox.
772 window.addEventListener("pageshow", function () {
773   init();