commit ac9af230cee6b77f4ab0457e446bc7b4a05488ec
parent ce82072e19be84ce102516b501e40bc50ddf7b94
Author: Nazım Can Altınova <canaltinova@gmail.com>
Date: Tue, 21 Oct 2025 23:59:05 +0000
Bug 1959977 - Insert ScriptSource RefPtr to keep it alive until the end of the profiling session r=iain,mstange
We need this API because we need to make sure that the sources of the JS
interpreter, JIT, and the JS execution tracing frames are kept alive until
the serialization.
The implementation is done similarly to `GeckoProfilerRuntime::strings_`.
One difference is that, strings_ is main thread only. But we need to
make sure that the scriptSources_ can be accessed from other threads
since the profile serialization happen in a background thread.
Differential Revision: https://phabricator.services.mozilla.com/D259257
Diffstat:
4 files changed, 37 insertions(+), 0 deletions(-)
diff --git a/js/src/jit/JitScript.cpp b/js/src/jit/JitScript.cpp
@@ -90,6 +90,11 @@ bool JSScript::createJitScript(JSContext* cx) {
if (!profileString) {
return false;
}
+
+ if (!cx->runtime()->geckoProfiler().insertScriptSource(scriptSource())) {
+ ReportOutOfMemory(cx);
+ return false;
+ }
}
static_assert(sizeof(JitScript) % sizeof(uintptr_t) == 0,
@@ -347,6 +352,10 @@ void JitScript::ensureProfileString(JSContext* cx, JSScript* script) {
if (!profileString_) {
oomUnsafe.crash("Failed to allocate profile string");
}
+ if (!cx->runtime()->geckoProfiler().insertScriptSource(
+ script->scriptSource())) {
+ oomUnsafe.crash("Failed to insert profiled script source");
+ }
}
/* static */
diff --git a/js/src/vm/GeckoProfiler.cpp b/js/src/vm/GeckoProfiler.cpp
@@ -20,6 +20,7 @@
#include "vm/FrameIter.h" // js::OnlyJSJitFrameIter
#include "vm/JitActivation.h"
#include "vm/JSScript.h"
+#include "vm/MutexIDs.h"
#include "gc/Marking-inl.h"
#include "jit/JSJitFrameIter-inl.h"
@@ -31,6 +32,7 @@ GeckoProfilerThread::GeckoProfilerThread()
GeckoProfilerRuntime::GeckoProfilerRuntime(JSRuntime* rt)
: rt(rt),
+ scriptSources_(mutexid::GeckoProfilerScriptSources),
slowAssertions(false),
enabled_(false),
eventMarker_(nullptr),
@@ -122,6 +124,8 @@ void GeckoProfilerRuntime::enable(bool enabled) {
enabled_ = enabled;
+ scriptSources_.writeLock()->clear();
+
/* Toggle Gecko Profiler-related jumps on baseline jitcode.
* The call to |ReleaseAllJITCode| above will release most baseline jitcode,
* but not jitcode for scripts with active frames on the stack. These scripts
@@ -255,6 +259,12 @@ bool GeckoProfilerThread::enter(JSContext* cx, JSScript* script) {
return false;
}
+ if (!cx->runtime()->geckoProfiler().insertScriptSource(
+ script->scriptSource())) {
+ ReportOutOfMemory(cx);
+ return false;
+ }
+
#ifdef DEBUG
// In debug builds, assert the JS profiling stack frames already on the
// stack have a non-null pc. Only look at the top frames to avoid quadratic
diff --git a/js/src/vm/GeckoProfiler.h b/js/src/vm/GeckoProfiler.h
@@ -20,8 +20,10 @@
#include "js/AllocPolicy.h"
#include "js/HashTable.h"
#include "js/ProfilingCategory.h"
+#include "js/ProfilingSources.h"
#include "js/TypeDecls.h"
#include "js/Utility.h"
+#include "threading/ExclusiveData.h"
#include "threading/ProtectedData.h"
/*
@@ -112,6 +114,7 @@ namespace js {
class BaseScript;
class GeckoProfilerThread;
+class ScriptSource;
// The `ProfileStringMap` weakly holds its `BaseScript*` keys and owns its
// string values. Entries are removed when the `BaseScript` is finalized; see
@@ -119,9 +122,14 @@ class GeckoProfilerThread;
using ProfileStringMap = HashMap<BaseScript*, JS::UniqueChars,
DefaultHasher<BaseScript*>, SystemAllocPolicy>;
+using ProfilerScriptSourceSet =
+ HashSet<RefPtr<ScriptSource>, PointerHasher<ScriptSource*>,
+ SystemAllocPolicy>;
+
class GeckoProfilerRuntime {
JSRuntime* rt;
MainThreadData<ProfileStringMap> strings_;
+ RWExclusiveData<ProfilerScriptSourceSet> scriptSources_;
bool slowAssertions;
uint32_t enabled_;
void (*eventMarker_)(mozilla::MarkerCategory, const char*, const char*);
@@ -179,6 +187,15 @@ class GeckoProfilerRuntime {
size_t stringsCount();
void stringsReset();
+ bool insertScriptSource(ScriptSource* scriptSource) {
+ auto guard = scriptSources_.writeLock();
+ if (!enabled_) {
+ return true;
+ }
+
+ return guard->put(scriptSource);
+ }
+
const uint32_t* addressOfEnabled() const { return &enabled_; }
void fixupStringsMapAfterMovingGC();
diff --git a/js/src/vm/MutexIDs.h b/js/src/vm/MutexIDs.h
@@ -62,6 +62,7 @@
_(SourceCompression, 500) \
_(GCDelayedMarkingLock, 500) \
_(BufferAllocator, 500) \
+ _(GeckoProfilerScriptSources, 500) \
\
_(SharedImmutableStringsCache, 600) \
_(IrregexpLazyStatic, 600) \