6.6.0.0 -> 6.9.0.0 perf regression & Enabling the Gecko profiler/DevTools Performance panel on a UXP build Topic is solved

Discussions about the development and maturation of the platform code (UXP).
Warning: may contain highly-technical topics.

Moderators: trava90, athenian200

User avatar
DZMBA
Newbie
Newbie
Posts: 4
Joined: 2026-07-15, 20:24

6.6.0.0 -> 6.9.0.0 perf regression & Enabling the Gecko profiler/DevTools Performance panel on a UXP build

Post by DZMBA » 2026-07-15, 21:26

We ship a commercial desktop app from 2007 built on XULRunner. XULRunner runs our application code/UI, for everything else I've embedded CEF into XULRunner.
The June 2024 6.6.0.0 was a literal miracle I can't thank you guys enough for.

Things were great until recently due to ORCA scans & InfoSec upset about using a 2yr old version of UXP/XulRunner.
To pass ORCA scans I've pulled UXP out of tree & have the CI container clone RB_20240528 then apply a few small git patches (crypto on windows compatibility mainly) before building.
But, InfoSec still insist we use a more recent version. I've upgraded to `UXP tag RC_20260623 (6.9.0.0)` but have a few issues:
  1. Regression:
    New version is extremely slow. Navigation between the various tabs/pages multiple seconds.
    6.6.0.0 -> 6.9.0.0, same app, prefs, arch, and hardware. UI got slower across the board: interaction, redraw, scroll.
    Ruled out:
    • Compositing is D3D11 (nsIGfxInfo layerManagerType), high-end discrete GPU. Not Basic/software.
    • gfx.direct2d.disabled=true (CPU content raster), set identically on both builds.
    • /GS + -Qspectre: A/B with both off, negligible delta.
  2. Profiler / Performance panel over remote debugging:
    For debugging we use Pale Moon's Remote DevTools. But the "Performance" tab I crucially need is unavailable.
    Findings:
    • Cc["@mozilla.org/tools/profiler;1"] is undefined in the target; the component is not registered.
    • --enable-profiling sets MOZ_PROFILING only, not MOZ_GECKO_PROFILER.
    • MOZ_GECKO_PROFILER is not set anywhere in *.configure or old-configure.in; tools/profiler ships only GeckoProfiler.h, IPDL, tasktracer, and tests. The implementation (platform backend, nsProfiler component, core sampler) looks gone.
Our build is stuck on 32-bit x86 by design. We load NPAPI plugins (32-bit only) and legacy binary/XPCOM extensions that are non-trivial to rebuild.

Is there any known perf regressions or changed defaults (layout, style, JS/GC, content backend) between the two or prefs worth looking at? Is there a better tag I should try?

Previous build (the fast one)
  • UXP tag RB_20240528 (upstream 6.6.0.0).
  • 32-bit x86. MozillaBuild 3.4, (MSVC 14.26), Windows 10 SDK.
  • mozconfig same as below, but with -GS- (canaries off) and no -Qspectre.
New Slow Build
  • UXP tag RC_20260623 (6.9.0.0).
  • 32-bit x86. MozillaBuild 3.4, Visual Studio 2022 (MSVC 14.44), Windows 11 SDK 26100.
  • --enable-application=xulrunner, --enable-optimize="-O2 -Qspectre -utf-8" (/GS on, -Qspectre on), --enable-jemalloc, --enable-devtools, --enable-devtools-server, --enable-strip.

User avatar
Moonchild
Project founder
Project founder
Posts: 39713
Joined: 2011-08-28, 17:27
Location: Sweden

Re: 6.6.0.0 -> 6.9.0.0 perf regression & Enabling the Gecko profiler/DevTools Performance panel on a UXP build

Post by Moonchild » 2026-07-15, 21:39

Unfortunately there have been a metric ton of changes between the two versions. It would help greatly if you could narrow the regression window down using RB_* tags so there's a better handle on where this performance regression happened for your application.
"Sales hates anything that can't be turned into a confident sentence." - anonymous warehouse worker
"Why debate someone you fundamentally don't trust?" - Dario Amodei
"Seek wisdom, not knowledge. Knowledge is of the past; wisdom is of the future." -- Native American proverb
"Linux makes everything difficult." -- Lyceus Anubite

User avatar
DZMBA
Newbie
Newbie
Posts: 4
Joined: 2026-07-15, 20:24

Re: 6.6.0.0 -> 6.9.0.0 perf regression & Enabling the Gecko profiler/DevTools Performance panel on a UXP build

Post by DZMBA » 2026-07-16, 02:42

Tomorrow I'll likely begin the slow process of doing a bisect starting from a 1yr old tag until I narrow it down.
I've been assuming RB_* was beta & RC_* was release candidate, is it actually canary? Should I be using RB_* tags?

Is my other assumption about MOZ_GECKO_PROFILER/performance profiling not possible in UXP true?

User avatar
jobbautista9
Board Warrior
Board Warrior
Posts: 1249
Joined: 2020-11-03, 06:47
Location: Philippines

Re: 6.6.0.0 -> 6.9.0.0 perf regression & Enabling the Gecko profiler/DevTools Performance panel on a UXP build

Post by jobbautista9 » 2026-07-16, 06:38

RB_ means "release base", so those are the tags end-users should be using yes.

It should be noted too that our XULrunner is currently unmaintained unfortunately, so there might've been some bitrot. You could try using a copy of the installation directory of your Pale Moon or Basilisk as your application's xulrunner directory to make bisecting between UXP versions easier, since PM and Basilisk are really just private xulrunner applications.
Image

Tired of creating stuff!

Avatar artwork by Shinki669: https://www.pixiv.net/artworks/113645617

XUL add-ons developer. You can find a list of add-ons I manage at http://rw.rs/~job/software.html.

User avatar
DZMBA
Newbie
Newbie
Posts: 4
Joined: 2026-07-15, 20:24

Re: 6.6.0.0 -> 6.9.0.0 perf regression & Enabling the Gecko profiler/DevTools Performance panel on a UXP build

Post by DZMBA » 2026-07-16, 22:33

I've not had any luck getting it to work using official build.

c:/Derek/Downloads/palemoon-34.3.1.win32/palemoon/palemoon.exe -app ./semcat/application.ini -profile ./semcat/profile -no-remote -jsconsole -repo .

Code: Select all

1784241038200 addons.xpi ERROR startup failed: [Exception ... "Component returned failure code: 0x80040111 (NS_ERROR_NOT_AVAILABLE) [nsIXPCComponents_Utils.isModuleLoaded]" nsresult: "0x80040111
(NS_ERROR_NOT_AVAILABLE)" location: "JS frame :: resource://gre/modules/addons/XPIProvider.jsm :: XPI_startup :: line 2034" data: no] Stack trace: XPI_startup0@resource://gre/modules/addons/XPIProvider.jsm:2034 <
callProvider(@resource://gre/modules/AddonManager.jsm:193 <_ startProvider(@resource://gre/modules/AddonManager.jsm:725 < startup0@resource://gre/modules/AddonManager.jsm:861 < startup0@resource://gre
/modules/AddonManager.jsm:2433 < observe0@jar:file:///C:/Derek/Downloads/palemoon-34.3.1.win32/palemoon/palemoon.res!/components/addonManager.js:58
resource://gre/modules/Log.jsm
Even if it did work, I think I'd run into crypto issues without this patch:

Code: Select all

diff --git a/toolkit/components/passwordmgr/crypto-SDR.js b/toolkit/components/passwordmgr/crypto-SDR.js
index b0916eb291..f9f10ea816 100644
--- a/toolkit/components/passwordmgr/crypto-SDR.js
+++ b/toolkit/components/passwordmgr/crypto-SDR.js
@@ -31,9 +31,15 @@ LoginManagerCrypto_SDR.prototype = {
 
   __decoderRing : null,  // nsSecretDecoderRing service
   get _decoderRing() {
-    if (!this.__decoderRing)
-      this.__decoderRing = Cc["@mozilla.org/security/sdr;1"].
-                           getService(Ci.nsISecretDecoderRing);
+    if (!this.__decoderRing) {
+      const ns = {};
+      Services.scriptloader.loadSubScript("resource://gre/modules/OSCrypto_win.js", ns);
+      const osCrypto = new ns.OSCrypto;
+      this.__decoderRing = Object.defineProperties(osCrypto, Object.getOwnPropertyDescriptors({
+        get encryptString() { return this.encryptData; },
+        get decryptString() { return this.decryptData; }
+      }));
+    }
     return this.__decoderRing;
   },
 
Trying older tags also hasn't gone well either. Keep fighting with python2. To quote the bot, most recently:
RB_20250801 is py2-era and keeps hitting py2-vs-modern-Windows build bugs (mach py2 [fixed], js/src case [fixed via patch], now a py2 gyp DEPTH-path bug, likely more)
Currently only also tested RC_20260526 (earliest with python3), the issue is present there as well.

EDIT: got old tags building. Still nailing down when things got slow. Here are git diff patches to make it work in case someone in the future stumbles across this post.

gyp-relativepath-drivecase.patch

Code: Select all

diff --git a/media/webrtc/trunk/tools/gyp/pylib/gyp/common.py b/media/webrtc/trunk/tools/gyp/pylib/gyp/common.py
index a1e1db5f12..de269265fb 100644
--- a/media/webrtc/trunk/tools/gyp/pylib/gyp/common.py
+++ b/media/webrtc/trunk/tools/gyp/pylib/gyp/common.py
@@ -158,8 +158,17 @@ def RelativePath(path, relative_to, follow_path_symlink=True):
   path_split = path.split(os.path.sep)
   relative_to_split = relative_to.split(os.path.sep)
 
-  # Determine how much of the prefix the two paths share.
-  prefix_len = len(os.path.commonprefix([path_split, relative_to_split]))
+  # Determine how much of the prefix the two paths share. Compare
+  # case-insensitively on Windows: the drive letter's case can differ between
+  # |path| (from gyp.__file__, lowercase d:) and |relative_to| (from mozbuild,
+  # uppercase D:). A case-sensitive commonprefix would then find nothing in
+  # common, leaving the drive in the "relative" result and mangling it to a
+  # drive-relative path (d:foo) via os.path.join.
+  if sys.platform == 'win32':
+    prefix_len = len(os.path.commonprefix(
+        [[c.lower() for c in path_split], [c.lower() for c in relative_to_split]]))
+  else:
+    prefix_len = len(os.path.commonprefix([path_split, relative_to_split]))
 
   # Put enough ".." components to back up out of relative_to to the common
   # prefix, and then append the part of path_split after the common prefix.
ipdl-parser-normcase.patch

Code: Select all

diff --git a/ipc/ipdl/ipdl/parser.py b/ipc/ipdl/ipdl/parser.py
index 38c46dc73a..a0c3ef2578 100644
--- a/ipc/ipdl/ipdl/parser.py
+++ b/ipc/ipdl/ipdl/parser.py
@@ -57,8 +57,15 @@ class Parser:
     def parse(self, input, filename, includedirs, errout):
         assert os.path.isabs(filename)
 
-        if filename in Parser.parsed:
-            return Parser.parsed[filename].tu
+        # Case-normalize the cache key. On Windows the same .ipdl reaches us both
+        # via the top-level source list (uppercase drive `D:`) and via an
+        # `include protocol` resolved through an objdir includedir (lowercase
+        # `d:`) -- equal paths, but distinct dict keys. Without normcase the file
+        # is parsed twice, yielding two Protocol objects and spurious
+        # manager/manages "does not match" errors (e.g. PBrowser/PDocAccessible).
+        cachekey = os.path.normcase(filename)
+        if cachekey in Parser.parsed:
+            return Parser.parsed[cachekey].tu
 
         self.lexer = lex.lex(debug=self.debug,
                              optimize=not self.debug,
@@ -71,7 +78,7 @@ class Parser:
         self.tu.filename = filename
         self.errout = errout
 
-        Parser.parsed[filename] = self
+        Parser.parsed[cachekey] = self
         Parser.parseStack.append(Parser.current)
         Parser.current = self
 
process-define-files-normcase.patch

Code: Select all

diff --git a/python/mozbuild/mozbuild/action/process_define_files.py b/python/mozbuild/mozbuild/action/process_define_files.py
index f6d0c1695a..b7ee4062d8 100644
--- a/python/mozbuild/mozbuild/action/process_define_files.py
+++ b/python/mozbuild/mozbuild/action/process_define_files.py
@@ -33,8 +33,11 @@ def process_define_file(output, input):
     config = ConfigEnvironment.from_config_status(
         mozpath.join(topobjdir, 'config.status'))

-    if mozpath.basedir(path,
-                       [mozpath.join(config.topsrcdir, 'js/src')]) and \
+    # Case-insensitive compare: on Windows the top configure yields an uppercase
+    # drive (D:) while the js/src subconfigure yields lowercase (d:) via msys, so
+    # this otherwise case-sensitive check must ignore drive-letter case.
+    if mozpath.basedir(path.lower(),
+                       [mozpath.join(config.topsrcdir, 'js/src').lower()]) and \
             not config.substs.get('JS_STANDALONE'):
         config = ConfigEnvironment.from_config_status(
             mozpath.join(topobjdir, 'js', 'src', 'config.status'))
reader-normcase.patch

Code: Select all

diff --git a/python/mozbuild/mozbuild/frontend/reader.py b/python/mozbuild/mozbuild/frontend/reader.py
index 165a8b7119..66993dbe7b 100644
--- a/python/mozbuild/mozbuild/frontend/reader.py
+++ b/python/mozbuild/mozbuild/frontend/reader.py
@@ -152,7 +152,10 @@ def is_read_allowed(path, config):
     path = mozpath.normpath(path)
     topsrcdir = mozpath.normpath(config.topsrcdir)
 
-    if mozpath.basedir(path, [topsrcdir]):
+    # Case-insensitive compare: on Windows the top configure yields an uppercase
+    # drive (D:) while the js/src subconfigure yields lowercase (d:) via msys, so
+    # this otherwise case-sensitive check must ignore drive-letter case.
+    if mozpath.basedir(path.lower(), [topsrcdir.lower()]):
         return True
 
     if config.external_source_dir:

User avatar
DZMBA
Newbie
Newbie
Posts: 4
Joined: 2026-07-15, 20:24

Re: 6.6.0.0 -> 6.9.0.0 perf regression & Enabling the Gecko profiler/DevTools Performance panel on a UXP build

Post by DZMBA » 2026-07-17, 03:10

The regression happens after RB_20260430 and begins with RC_20260526.
Which is an unfortunately massive changeset with no tags between.
https://repo.palemoon.org/MoonchildProd ... C_20260526

RB_20260430 is lightning fast. I don't have any measurements, but just clicking around the UI seems faster than on the old RB_20240528. I was really looking forward to RC_20260526 & beyond after noticing things like PolyFill 'withResolvers' already exists on [object Function] (Promise) popping up in the log. RB_20260430 will satisfy InfoSec for now though... and hopefully 2 years from now it's magically fixed :D