tor-browser

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

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

Bug 1990870 - Add flow marker support to GeckoProfilerRuntime r=sfink,canaltinova,profiler-reviewers

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

Diffstat:
Mjs/public/ProfilingStack.h | 11+++++++----
Mjs/src/shell/js.cpp | 19+++++++++++++++++--
Mjs/src/vm/GeckoProfiler.cpp | 54+++++++++++++++++++++++++++++++++++++++++++++++-------
Mjs/src/vm/GeckoProfiler.h | 18++++++++++++++++++
Mtools/profiler/core/ProfilerThreadRegistrationData.cpp | 35+++++++++++++++++++++++++++++++++--
5 files changed, 122 insertions(+), 15 deletions(-)

diff --git a/js/public/ProfilingStack.h b/js/public/ProfilingStack.h @@ -364,11 +364,14 @@ JS_PUBLIC_API void SetContextProfilingStack(JSContext* cx, JS_PUBLIC_API void EnableContextProfilingStack(JSContext* cx, bool enabled); -JS_PUBLIC_API void RegisterContextProfilingEventMarker( +JS_PUBLIC_API void RegisterContextProfilerMarkers( JSContext* cx, - void (*mark)(mozilla::MarkerCategory, const char*, const char*), - void (*interval)(mozilla::MarkerCategory, const char*, mozilla::TimeStamp, - const char*)); + void (*eventMarker)(mozilla::MarkerCategory, const char*, const char*), + void (*intervalMarker)(mozilla::MarkerCategory, const char*, + mozilla::TimeStamp, const char*), + void (*flowMarker)(mozilla::MarkerCategory, const char*, uint64_t), + void (*terminatingFlowMarker)(mozilla::MarkerCategory, const char*, + uint64_t)); } // namespace js diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp @@ -7759,11 +7759,26 @@ static void PrintProfilerIntervals_Callback(mozilla::MarkerCategory, (TimeStamp::Now() - start).ToMilliseconds(), msg, details); } +static void PrintProfilerFlow_Callback(mozilla::MarkerCategory, + const char* markerName, + uint64_t flowId) { + fprintf(stderr, "PROFILER FLOW: %s (flowId=%" PRIu64 ")\n", markerName, + flowId); +} + +static void PrintProfilerTerminatingFlow_Callback(mozilla::MarkerCategory, + const char* markerName, + uint64_t flowId) { + fprintf(stderr, "PROFILER TERMINATING FLOW: %s (flowId=%" PRIu64 ")\n", + markerName, flowId); +} + static bool PrintProfilerEvents(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); if (cx->runtime()->geckoProfiler().enabled()) { - js::RegisterContextProfilingEventMarker(cx, &PrintProfilerEvents_Callback, - &PrintProfilerIntervals_Callback); + js::RegisterContextProfilerMarkers( + cx, &PrintProfilerEvents_Callback, &PrintProfilerIntervals_Callback, + &PrintProfilerFlow_Callback, &PrintProfilerTerminatingFlow_Callback); } args.rval().setUndefined(); return true; diff --git a/js/src/vm/GeckoProfiler.cpp b/js/src/vm/GeckoProfiler.cpp @@ -34,7 +34,9 @@ GeckoProfilerRuntime::GeckoProfilerRuntime(JSRuntime* rt) slowAssertions(false), enabled_(false), eventMarker_(nullptr), - intervalMarker_(nullptr) { + intervalMarker_(nullptr), + flowMarker_(nullptr), + terminatingFlowMarker_(nullptr) { MOZ_ASSERT(rt != nullptr); } @@ -55,6 +57,16 @@ void GeckoProfilerRuntime::setIntervalMarker(void (*fn)( intervalMarker_ = fn; } +void GeckoProfilerRuntime::setFlowMarker(void (*fn)(mozilla::MarkerCategory, + const char*, uint64_t)) { + flowMarker_ = fn; +} + +void GeckoProfilerRuntime::setTerminatingFlowMarker( + void (*fn)(mozilla::MarkerCategory, const char*, uint64_t)) { + terminatingFlowMarker_ = fn; +} + // Get a pointer to the top-most profiling frame, given the exit frame pointer. static jit::JitFrameLayout* GetTopProfilingJitFrame(jit::JitActivation* act) { // If there is no exit frame set, just return. @@ -214,6 +226,28 @@ void GeckoProfilerRuntime::markInterval(const char* event, } } +void GeckoProfilerRuntime::markFlow(const char* markerName, uint64_t flowId, + JS::ProfilingCategoryPair jsPair) { + MOZ_ASSERT(enabled()); + if (flowMarker_) { + JS::AutoSuppressGCAnalysis nogc; + mozilla::MarkerCategory category( + static_cast<mozilla::baseprofiler::ProfilingCategoryPair>(jsPair)); + flowMarker_(category, markerName, flowId); + } +} + +void GeckoProfilerRuntime::markTerminatingFlow( + const char* markerName, uint64_t flowId, JS::ProfilingCategoryPair jsPair) { + MOZ_ASSERT(enabled()); + if (terminatingFlowMarker_) { + JS::AutoSuppressGCAnalysis nogc; + mozilla::MarkerCategory category( + static_cast<mozilla::baseprofiler::ProfilingCategoryPair>(jsPair)); + terminatingFlowMarker_(category, markerName, flowId); + } +} + bool GeckoProfilerThread::enter(JSContext* cx, JSScript* script) { const char* dynamicString = cx->runtime()->geckoProfiler().profileString(cx, script); @@ -510,14 +544,20 @@ JS_PUBLIC_API void js::EnableContextProfilingStack(JSContext* cx, cx->runtime()->geckoProfiler().enable(enabled); } -JS_PUBLIC_API void js::RegisterContextProfilingEventMarker( +JS_PUBLIC_API void js::RegisterContextProfilerMarkers( JSContext* cx, - void (*mark)(mozilla::MarkerCategory, const char*, const char*), - void (*interval)(mozilla::MarkerCategory, const char*, mozilla::TimeStamp, - const char*)) { + void (*eventMarker)(mozilla::MarkerCategory, const char*, const char*), + void (*intervalMarker)(mozilla::MarkerCategory, const char*, + mozilla::TimeStamp, const char*), + void (*flowMarker)(mozilla::MarkerCategory, const char*, uint64_t), + void (*terminatingFlowMarker)(mozilla::MarkerCategory, const char*, + uint64_t)) { MOZ_ASSERT(cx->runtime()->geckoProfiler().enabled()); - cx->runtime()->geckoProfiler().setEventMarker(mark); - cx->runtime()->geckoProfiler().setIntervalMarker(interval); + cx->runtime()->geckoProfiler().setEventMarker(eventMarker); + cx->runtime()->geckoProfiler().setIntervalMarker(intervalMarker); + cx->runtime()->geckoProfiler().setFlowMarker(flowMarker); + cx->runtime()->geckoProfiler().setTerminatingFlowMarker( + terminatingFlowMarker); } AutoSuppressProfilerSampling::AutoSuppressProfilerSampling(JSContext* cx) diff --git a/js/src/vm/GeckoProfiler.h b/js/src/vm/GeckoProfiler.h @@ -127,6 +127,9 @@ class GeckoProfilerRuntime { void (*eventMarker_)(mozilla::MarkerCategory, const char*, const char*); void (*intervalMarker_)(mozilla::MarkerCategory, const char*, mozilla::TimeStamp, const char*); + void (*flowMarker_)(mozilla::MarkerCategory, const char*, uint64_t); + void (*terminatingFlowMarker_)(mozilla::MarkerCategory, const char*, + uint64_t); public: explicit GeckoProfilerRuntime(JSRuntime* rt); @@ -141,6 +144,10 @@ class GeckoProfilerRuntime { const char*)); void setIntervalMarker(void (*fn)(mozilla::MarkerCategory, const char*, mozilla::TimeStamp, const char*)); + void setFlowMarker(void (*fn)(mozilla::MarkerCategory, const char*, + uint64_t)); + void setTerminatingFlowMarker(void (*fn)(mozilla::MarkerCategory, const char*, + uint64_t)); static JS::UniqueChars allocProfileString(JSContext* cx, BaseScript* script); const char* profileString(JSContext* cx, BaseScript* script); @@ -155,6 +162,17 @@ class GeckoProfilerRuntime { const char* event, mozilla::TimeStamp start, const char* details, JS::ProfilingCategoryPair jsPair = JS::ProfilingCategoryPair::JS); + // Note that flowId will be added as a process-scoped id for both + // markFlow and markTerminatingFlow. + // + // See baseprofiler/public/Flow.h + void markFlow( + const char* markerName, uint64_t flowId, + JS::ProfilingCategoryPair jsPair = JS::ProfilingCategoryPair::JS); + void markTerminatingFlow( + const char* markerName, uint64_t flowId, + JS::ProfilingCategoryPair jsPair = JS::ProfilingCategoryPair::JS); + ProfileStringMap& strings() { return strings_.ref(); } /* meant to be used for testing, not recommended to call in normal code */ diff --git a/tools/profiler/core/ProfilerThreadRegistrationData.cpp b/tools/profiler/core/ProfilerThreadRegistrationData.cpp @@ -7,6 +7,7 @@ #include "mozilla/ProfilerThreadRegistrationData.h" #include "mozilla/CycleCollectedJSContext.h" +#include "mozilla/FlowMarkers.h" #include "mozilla/FOGIPC.h" #include "mozilla/ProfilerMarkers.h" #include "js/AllocationRecording.h" @@ -102,6 +103,35 @@ static void profiler_add_js_interval(mozilla::MarkerCategory aCategory, #endif } +static void profiler_add_js_flow(mozilla::MarkerCategory aCategory, + const char* aMarkerName, uint64_t aFlowId) { +#ifdef MOZ_GECKO_PROFILER + if (!profiler_feature_active(ProfilerFeature::Flows)) { + return; + } + AUTO_PROFILER_STATS(js_flow); + profiler_add_marker( + mozilla::ProfilerString8View::WrapNullTerminatedString(aMarkerName), + aCategory, {}, ::geckoprofiler::markers::FlowMarker{}, + Flow::ProcessScoped(aFlowId)); +#endif +} + +static void profiler_add_js_terminating_flow(mozilla::MarkerCategory aCategory, + const char* aMarkerName, + uint64_t aFlowId) { +#ifdef MOZ_GECKO_PROFILER + if (!profiler_feature_active(ProfilerFeature::Flows)) { + return; + } + AUTO_PROFILER_STATS(js_terminating_flow); + profiler_add_marker( + mozilla::ProfilerString8View::WrapNullTerminatedString(aMarkerName), + aCategory, {}, ::geckoprofiler::markers::TerminatingFlowMarker{}, + Flow::ProcessScoped(aFlowId)); +#endif +} + static void profiler_add_js_allocation_marker(JS::RecordAllocationInfo&& info) { if (!profiler_thread_is_being_profiled_for_markers()) { return; @@ -261,8 +291,9 @@ void ThreadRegistrationLockedRWOnThread::PollJSSampling() { JS::EnableRecordingAllocations(cx, profiler_add_js_allocation_marker, 0.01); } - js::RegisterContextProfilingEventMarker(cx, profiler_add_js_marker, - profiler_add_js_interval); + js::RegisterContextProfilerMarkers( + cx, profiler_add_js_marker, profiler_add_js_interval, + profiler_add_js_flow, profiler_add_js_terminating_flow); } else if (mJSSampling == INACTIVE_REQUESTED) { mJSSampling = INACTIVE;