Bug 2065408 - Add profiler markers for shutdown phases. r=xpcom-reviewers,profiler-reviewers,nika,canova

AdvanceShutdownPhaseInternal emits no profiler markers, so shutdown phases are
only indirectly visible through the NotifyObservers marker of the topic each
phase fires. XPCOMShutdownFinal and CCPostLastCycleCollection have no observer
topic at all, and the notification covers only a part of the phase advance: the
two NS_ProcessPendingEvents drains and KillClearOnShutdown run outside it, so
runnables processed there show up with no enclosing context.

Add an interval marker over the whole advance, plus one for each drain and for
KillClearOnShutdown, all carrying the phase name.

Differential Revision: https://phabricator.services.mozilla.com/D320446
This commit is contained in:
Jens Stutte
2026-08-25 21:50:30 +00:00
committed by jstutte@mozilla.com
parent f827f59b17
commit 15522dafef
+52 -3
View File
@@ -18,6 +18,7 @@
#include "mozilla/LateWriteChecks.h"
#include "mozilla/PoisonIOInterposer.h"
#include "mozilla/Printf.h"
#include "mozilla/ProfilerMarkers.h"
#include "mozilla/Services.h"
#include "mozilla/SpinEventLoopUntil.h"
#include "mozilla/StartupTimeline.h"
@@ -354,6 +355,37 @@ bool AppShutdown::IsNoOrLegalShutdownTopic(const char* aTopic) {
}
#endif
// AUTO_PROFILER_MARKER_TEXT only adds its marker once the scope ends, so a
// profile dumped from inside that scope, as AsyncShutdown does when a blocker
// times out, loses it. Emitting the two halves separately survives that.
class MOZ_RAII AutoShutdownPhaseMarker {
public:
AutoShutdownPhaseMarker(const char* aMarkerName, const char* aPhaseName)
: mMarkerName(aMarkerName), mPhaseName(aPhaseName) {
if (profiler_is_active_and_unpaused()) {
Emit(MarkerTiming::IntervalStart());
}
}
~AutoShutdownPhaseMarker() {
if (profiler_is_active_and_unpaused()) {
Emit(MarkerTiming::IntervalEnd());
}
}
private:
// Only to be called while the profiler is active, MarkerTiming's factories
// take a TimeStamp::Now unconditionally.
void Emit(MarkerTiming aTiming) {
PROFILER_MARKER_TEXT(
ProfilerString8View::WrapNullTerminatedString(mMarkerName), OTHER,
MarkerOptions(std::move(aTiming)), nsDependentCString(mPhaseName));
}
const char* mMarkerName;
const char* mPhaseName;
};
void AppShutdown::AdvanceShutdownPhaseInternal(
ShutdownPhase aPhase, bool doNotify, const char16_t* aNotificationData,
const nsCOMPtr<nsISupports>& aNotificationSubject) {
@@ -373,6 +405,9 @@ void AppShutdown::AdvanceShutdownPhaseInternal(
return;
}
const char* phaseName = AppShutdown::GetShutdownPhaseName(aPhase);
AutoShutdownPhaseMarker phaseMarker("AdvanceShutdownPhase", phaseName);
// In case we missed the earlier Init (in the parent) or notification (in
// content processes), we ensure the flag is set from now.
// This should only ever be needed in some test environments, but it's cheap
@@ -402,6 +437,7 @@ void AppShutdown::AdvanceShutdownPhaseInternal(
// way of ensuring shutdown processing remains to have an async shutdown
// blocker.
if (mayProcessPending && thread) {
AutoShutdownPhaseMarker drainMarker("ShutdownDrainBeforePhase", phaseName);
NS_ProcessPendingEvents(thread);
}
@@ -420,10 +456,15 @@ void AppShutdown::AdvanceShutdownPhaseInternal(
// Note that we keep the old order here to avoid breakage, so be aware that
// the notifications fired below will find these already cleared in case
// you expected the opposite.
mozilla::KillClearOnShutdown(aPhase);
{
AutoShutdownPhaseMarker killMarker("KillClearOnShutdown", phaseName);
mozilla::KillClearOnShutdown(aPhase);
}
// Empty our MT event queue to process any side effects thereof.
if (mayProcessPending && thread) {
AutoShutdownPhaseMarker drainMarker("ShutdownDrainAfterKillClearOnShutdown",
phaseName);
NS_ProcessPendingEvents(thread);
}
@@ -437,10 +478,18 @@ void AppShutdown::AdvanceShutdownPhaseInternal(
sNotifyingShutdownObservers = true;
auto reset = MakeScopeExit([] { sNotifyingShutdownObservers = false; });
#endif
obsService->NotifyObservers(aNotificationSubject, aTopic,
aNotificationData);
{
// nsObserverService emits its own marker with the topic, but that
// one is scope bound and vanishes if we hang in here.
AutoShutdownPhaseMarker notifyMarker("ShutdownNotifyObservers",
phaseName);
obsService->NotifyObservers(aNotificationSubject, aTopic,
aNotificationData);
}
// Empty our MT event queue again after the notification has finished
if (mayProcessPending && thread) {
AutoShutdownPhaseMarker drainMarker("ShutdownDrainAfterNotification",
phaseName);
NS_ProcessPendingEvents(thread);
}
}