tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 95e81323519c31c407a0eb4adfdfe948f30f787c
parent c1654ddf391dd0ff7263ca2deccc5219c9433267
Author: Matthew Gaudet <mgaudet@mozilla.com>
Date:   Tue, 14 Oct 2025 21:29:31 +0000

Bug 1990870 - Fix flow markers for JS owned microtasks r=arai,profiler-reviewers,jrmuizel,canaltinova

Differential Revision: https://phabricator.services.mozilla.com/D268185

Diffstat:
Mjs/public/friend/MicroTask.h | 6++++++
Mjs/src/builtin/Promise.cpp | 27+++++++++++++++++++++++++++
Mxpcom/base/CycleCollectedJSContext.cpp | 22+++++++++++++++++-----
3 files changed, 50 insertions(+), 5 deletions(-)

diff --git a/js/public/friend/MicroTask.h b/js/public/friend/MicroTask.h @@ -158,5 +158,11 @@ JS_PUBLIC_API JSObject* MaybeGetHostDefinedGlobalFromJSMicroTask( JS_PUBLIC_API JSObject* MaybeGetPromiseFromJSMicroTask(const MicroTask& entry); +// Get the flow ID from a JS microtask for profiler markers. +// This only returns false if entry has become a dead wrapper, +// in which case the microtask doesn't run anyhow. +JS_PUBLIC_API bool GetFlowIdFromJSMicroTask(const MicroTask& entry, + uint64_t* uid); + } // namespace JS #endif /* js_friend_MicroTask_h */ diff --git a/js/src/builtin/Promise.cpp b/js/src/builtin/Promise.cpp @@ -35,6 +35,7 @@ #include "vm/Warnings.h" // js::WarnNumberASCII #include "debugger/DebugAPI-inl.h" +#include "gc/StableCellHasher-inl.h" #include "vm/Compartment-inl.h" #include "vm/ErrorObject-inl.h" #include "vm/JSContext-inl.h" // JSContext::check @@ -1679,6 +1680,15 @@ static bool ResolvePromiseFunction(JSContext* cx, unsigned argc, Value* vp) { static bool EnqueueJob(JSContext* cx, JS::MicroTask&& job) { MOZ_ASSERT(cx->realm()); + GeckoProfilerRuntime& profiler = cx->runtime()->geckoProfiler(); + if (profiler.enabled()) { + // Emit a flow start marker here. + uint64_t uid = 0; + if (JS::GetFlowIdFromJSMicroTask(job, &uid)) { + profiler.markFlow("JS::EnqueueJob", uid, + JS::ProfilingCategoryPair::OTHER); + } + } // Only check if we need to use the debug queue when we're not on main thread. if (MOZ_UNLIKELY(!cx->runtime()->isMainRuntime() && @@ -7794,6 +7804,23 @@ JS_PUBLIC_API JSObject* JS::MaybeGetPromiseFromJSMicroTask( return nullptr; } +JS_PUBLIC_API bool JS::GetFlowIdFromJSMicroTask(const MicroTask& entry, + uint64_t* uid) { + MOZ_RELEASE_ASSERT(entry.isObject(), "Only use on JSMicroTasks"); + + // We want to make sure we get the flow id from the target object + // not the wrapper. + JSObject* unwrapped = UncheckedUnwrap(&entry.toObject()); + if (JS_IsDeadWrapper(unwrapped)) { + return false; + } + + MOZ_ASSERT(unwrapped->is<MicroTaskEntry>(), "Only use on JSMicroTasks"); + + *uid = js::gc::GetUniqueIdInfallible(unwrapped); + return true; +} + JS_PUBLIC_API bool JS::IsJSMicroTask(Handle<JS::Value> hv) { if (!hv.isObject()) { return false; diff --git a/xpcom/base/CycleCollectedJSContext.cpp b/xpcom/base/CycleCollectedJSContext.cpp @@ -981,11 +981,28 @@ static bool MOZ_CAN_RUN_SCRIPT RunMicroTask(JSContext* aCx, JS::MutableHandle<JS::MicroTask> task) { if (RefPtr<MicroTaskRunnable> runnable = MaybeUnwrapTaskToOwnedRunnable(task)) { + AUTO_PROFILER_TERMINATING_FLOW_MARKER_FLOW_ONLY( + "RunMicroTaskRunnable", OTHER, Flow::FromPointer(runnable.get())); AutoSlowOperation aso; runnable->Run(aso); return true; } + // Avoid the overhead of GetFlowIdFromJSMicroTask in the common case + // of not having the profiler enabled. + mozilla::Maybe<AutoProfilerTerminatingFlowMarkerFlowOnly> terminatingMarker; + if (profiler_is_active_and_unpaused() && + profiler_feature_active(ProfilerFeature::Flows)) { + uint64_t flowId = 0; + // Since this only returns false when the microtask won't run (dead wrapper) + // we can elide the marker if it does fail. + if (JS::GetFlowIdFromJSMicroTask(task.get(), &flowId)) { + terminatingMarker.emplace("RunMicroTask", + mozilla::baseprofiler::category::OTHER, + Flow::ProcessScoped(flowId)); + } + } + JS::Rooted<JSObject*> maybePromise(aCx, JS::MaybeGetPromiseFromJSMicroTask(task)); auto state = maybePromise @@ -1190,11 +1207,6 @@ bool CycleCollectedJSContext::PerformMicroTaskCheckPoint(bool aForce) { // Bug 1991164: Need to support LogMicroTaskQueue Entry for // LogMicroTaskQueueEntry::Run log(job.get().get()); - // Bug 1990870: Need to support flow markers with JS Micro Tasks. - // AUTO_PROFILER_TERMINATING_FLOW_MARKER_FLOW_ONLY( - // "CycleCollectedJSContext::PerformDebuggerMicroTaskCheckpoint", - // OTHER, Flow::FromPointer(runnable.get())); - // Note: We're dropping the return value on the floor here. This is // consistent with the previous implementation, which left the // exception if it was there pending on the context, but likely should