commit 237a6bb3f6de5cb41e7b12f85e1e5108b522337b
parent 11f2a88ae4ad882f5bfd93d4723eaa9e710a5944
Author: Nazım Can Altınova <canaltinova@gmail.com>
Date: Wed, 3 Dec 2025 19:24:24 +0000
Bug 2002982 - Ensure profiler script sources are re-registered on consecutive profiler runs r=iain
Some script sources were missing from profiles captured in the second or
third profiler run. This happened because ensureProfileString would return
early if profileString_ was already set, and it would skip the
insertScriptSource call.
The issue is that profileString_ persists across profiler stop/start, but
the profiler's ScriptSources hashset is cleared when the profiler stops.
On the next profiler run, ensureProfileString would see profileString_
already populated and return early without re-registering the script
source.
This patch extracts the script source registration into a separate
ensureProfilerScriptSource method and calls it independently in
ToggleBaselineProfiling, to ensurie sources are registered every time
the profiler is enabled.
Differential Revision: https://phabricator.services.mozilla.com/D274448
Diffstat:
4 files changed, 51 insertions(+), 0 deletions(-)
diff --git a/js/src/jit-test/tests/profiler/bug2002982.js b/js/src/jit-test/tests/profiler/bug2002982.js
@@ -0,0 +1,43 @@
+// Test that script sources are properly registered on consecutive profiler runs.
+// Bug 2002982: When the profiler is disabled and re-enabled, script sources
+// need to be re-registered because the profiler's ScriptSources hashset is
+// cleared on stop. Previously, ensureProfileString would return early if
+// profileString_ was already set. And that was causing us to never re-insert
+// the script source.
+
+// Set a low warmup threshold to trigger baseline compilation.
+setJitCompilerOption("baseline.warmup.trigger", 5);
+
+// A simple function that will be JIT'ed.
+function compute(x) {
+ return x * x;
+}
+
+enableGeckoProfiling();
+
+// Warm up with the profiler enabled. It creates JitScripts and registers
+// their sources.
+for (let i = 0; i < 10; i++) {
+ compute(i);
+}
+
+// Get how many sources are registered so far.
+const countBefore = getGeckoProfilingScriptSourcesCount();
+assertEq(countBefore > 0, true,
+ "Expected to have at least one registered script source");
+
+disableGeckoProfiling();
+enableGeckoProfiling();
+
+// Run the same code
+for (let i = 0; i < 10; i++) {
+ compute(i);
+}
+
+// Get how many sources are registered so far again, and compare the before and
+// after values. The numbers have to be the same.
+const countAfter = getGeckoProfilingScriptSourcesCount();
+assertEq(countAfter, countBefore,
+ `Expected the same amount of script sources to be registered. Before: ${countBefore}, After: ${countAfter}`);
+
+disableGeckoProfiling();
diff --git a/js/src/jit/BaselineJIT.cpp b/js/src/jit/BaselineJIT.cpp
@@ -1277,6 +1277,7 @@ void jit::ToggleBaselineProfiling(JSContext* cx, bool enable) {
JSScript* script = jitScript->owningScript();
if (enable) {
jitScript->ensureProfileString(cx, script);
+ jitScript->ensureProfilerScriptSource(cx, script);
}
if (script->hasBaselineScript()) {
AutoWritableJitCode awjc(script->baselineScript()->method());
diff --git a/js/src/jit/JitScript.cpp b/js/src/jit/JitScript.cpp
@@ -355,6 +355,12 @@ void JitScript::ensureProfileString(JSContext* cx, JSScript* script) {
if (!profileString_) {
oomUnsafe.crash("Failed to allocate profile string");
}
+}
+
+void JitScript::ensureProfilerScriptSource(JSContext* cx, JSScript* script) {
+ MOZ_ASSERT(cx->runtime()->geckoProfiler().enabled());
+
+ AutoEnterOOMUnsafeRegion oomUnsafe;
if (!cx->runtime()->geckoProfiler().insertScriptSource(
script->scriptSource())) {
oomUnsafe.crash("Failed to insert profiled script source");
diff --git a/js/src/jit/JitScript.h b/js/src/jit/JitScript.h
@@ -414,6 +414,7 @@ class alignas(uintptr_t) JitScript final
void resetAllActiveFlags();
void ensureProfileString(JSContext* cx, JSScript* script);
+ void ensureProfilerScriptSource(JSContext* cx, JSScript* script);
const char* profileString() const {
MOZ_ASSERT(profileString_);