--- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -239,16 +239,20 @@ # include "mozIPlacesPendingOperation.h" #endif #if NS_PRINT_PREVIEW # include "nsIDocumentViewerPrint.h" # include "nsIWebBrowserPrint.h" #endif +#ifdef MOZ_GECKO_PROFILER +# include "ProfilerMarkerPayload.h" +#endif + using namespace mozilla; using namespace mozilla::dom; using namespace mozilla::net; using mozilla::ipc::Endpoint; // Threshold value in ms for META refresh based redirects #define REFRESH_REDIRECT_TIMER 15000
--- a/dom/base/ChromeUtils.cpp +++ b/dom/base/ChromeUtils.cpp @@ -46,16 +46,19 @@ #include "mozilla/ipc/GeckoChildProcessHost.h" #include "mozilla/net/UrlClassifierFeatureFactory.h" #include "mozilla/net/SocketProcessHost.h" #include "IOActivityMonitor.h" #include "nsIOService.h" #include "nsThreadUtils.h" #include "mozJSComponentLoader.h" #include "GeckoProfiler.h" +#ifdef MOZ_GECKO_PROFILER +# include "ProfilerMarkerPayload.h" +#endif #include "nsIException.h" namespace mozilla::dom { /* static */ void ChromeUtils::NondeterministicGetWeakMapKeys( GlobalObject& aGlobal, JS::Handle<JS::Value> aMap, JS::MutableHandle<JS::Value> aRetval, ErrorResult& aRv) {
--- a/dom/base/TimeoutManager.cpp +++ b/dom/base/TimeoutManager.cpp @@ -17,16 +17,19 @@ #include "mozilla/dom/DocGroup.h" #include "mozilla/dom/PopupBlocker.h" #include "mozilla/dom/ContentChild.h" #include "mozilla/dom/TimeoutHandler.h" #include "TimeoutExecutor.h" #include "TimeoutBudgetManager.h" #include "mozilla/net/WebSocketEventService.h" #include "mozilla/MediaManager.h" +#ifdef MOZ_GECKO_PROFILER +# include "ProfilerMarkerPayload.h" +#endif using namespace mozilla; using namespace mozilla::dom; LazyLogModule gTimeoutLog("Timeout"); static int32_t gRunningTimeoutDepth = 0; @@ -146,23 +149,20 @@ void TimeoutManager::MoveIdleToActive() TimeDuration target = timeout->When() - timeout->SubmitTime(); TimeDuration delta = now - timeout->When(); nsPrintfCString marker( "Releasing deferred setTimeout() for %dms (original target time was " "%dms (%dms delta))", int(elapsed.ToMilliseconds()), int(target.ToMilliseconds()), int(delta.ToMilliseconds())); // don't have end before start... - PROFILER_MARKER_TEXT( - "setTimeout deferred release", DOM, - MarkerOptions( - MarkerTiming::Interval( - delta.ToMilliseconds() >= 0 ? timeout->When() : now, now), - MarkerInnerWindowId(mWindow.WindowID())), - marker); + PROFILER_ADD_MARKER_WITH_PAYLOAD( + "setTimeout deferred release", DOM, TextMarkerPayload, + (marker, delta.ToMilliseconds() >= 0 ? timeout->When() : now, now, + Some(mWindow.WindowID()))); } #endif num++; } if (num > 0) { MOZ_ALWAYS_SUCCEEDS(MaybeSchedule(when)); mIdleExecutor->Cancel(); } @@ -900,23 +900,20 @@ void TimeoutManager::RunTimeout(const Ti nsPrintfCString marker( "%sset%s() for %dms (original target time was %dms (%dms " "delta)); runtime = %dms", aProcessIdle ? "Deferred " : "", timeout->mIsInterval ? "Interval" : "Timeout", int(elapsed.ToMilliseconds()), int(target.ToMilliseconds()), int(delta.ToMilliseconds()), int(runtime.ToMilliseconds())); // don't have end before start... - PROFILER_MARKER_TEXT( - "setTimeout", DOM, - MarkerOptions( - MarkerTiming::Interval( - delta.ToMilliseconds() >= 0 ? timeout->When() : now, now), - MarkerInnerWindowId(mWindow.WindowID())), - marker); + PROFILER_ADD_MARKER_WITH_PAYLOAD( + "setTimeout", DOM, TextMarkerPayload, + (marker, delta.ToMilliseconds() >= 0 ? timeout->When() : now, now, + Some(mWindow.WindowID()))); } #endif MOZ_LOG(gTimeoutLog, LogLevel::Debug, ("Run%s(TimeoutManager=%p, timeout=%p) returned %d\n", timeout->mIsInterval ? "Interval" : "Timeout", this, timeout.get(), !!timeout_was_cleared));
--- a/dom/base/nsDOMNavigationTiming.cpp +++ b/dom/base/nsDOMNavigationTiming.cpp @@ -20,17 +20,17 @@ #include "nsContentUtils.h" #include "nsDocShell.h" #include "nsHttp.h" #include "nsIScriptSecurityManager.h" #include "nsIURI.h" #include "nsPrintfCString.h" #include "prtime.h" #ifdef MOZ_GECKO_PROFILER -# include "mozilla/ProfilerMarkerTypes.h" +# include "ProfilerMarkerPayload.h" #endif using namespace mozilla; using namespace mozilla::dom; namespace mozilla { LazyLogModule gPageLoadLog("PageLoad"); @@ -111,40 +111,34 @@ void nsDOMNavigationTiming::NotifyBefore void nsDOMNavigationTiming::NotifyUnloadAccepted(nsIURI* aOldURI) { mUnloadStart = mBeforeUnloadStart; mUnloadedURI = aOldURI; } void nsDOMNavigationTiming::NotifyUnloadEventStart() { mUnloadStart = TimeStamp::Now(); - PROFILER_MARKER("Unload", NETWORK, - MarkerOptions(MarkerTiming::IntervalStart(), - MarkerInnerWindowIdFromDocShell(mDocShell)), - Tracing, "Navigation"); + PROFILER_TRACING_MARKER_DOCSHELL("Navigation", "Unload", NETWORK, + TRACING_INTERVAL_START, mDocShell); } void nsDOMNavigationTiming::NotifyUnloadEventEnd() { mUnloadEnd = TimeStamp::Now(); - PROFILER_MARKER("Unload", NETWORK, - MarkerOptions(MarkerTiming::IntervalEnd(), - MarkerInnerWindowIdFromDocShell(mDocShell)), - Tracing, "Navigation"); + PROFILER_TRACING_MARKER_DOCSHELL("Navigation", "Unload", NETWORK, + TRACING_INTERVAL_END, mDocShell); } void nsDOMNavigationTiming::NotifyLoadEventStart() { if (!mLoadEventStart.IsNull()) { return; } mLoadEventStart = TimeStamp::Now(); - PROFILER_MARKER("Load", NETWORK, - MarkerOptions(MarkerTiming::IntervalStart(), - MarkerInnerWindowIdFromDocShell(mDocShell)), - Tracing, "Navigation"); + PROFILER_TRACING_MARKER_DOCSHELL("Navigation", "Load", NETWORK, + TRACING_INTERVAL_START, mDocShell); if (IsTopLevelContentDocumentInContentProcess()) { mLoadEventStartForTelemetry = TimeStamp::Now(); Telemetry::AccumulateTimeDelta(Telemetry::TIME_TO_LOAD_EVENT_START_MS, mNavigationStart, mLoadEventStartForTelemetry); @@ -164,41 +158,36 @@ void nsDOMNavigationTiming::NotifyLoadEv } void nsDOMNavigationTiming::NotifyLoadEventEnd() { if (!mLoadEventEnd.IsNull()) { return; } mLoadEventEnd = TimeStamp::Now(); - PROFILER_MARKER("Load", NETWORK, - MarkerOptions(MarkerTiming::IntervalEnd(), - MarkerInnerWindowIdFromDocShell(mDocShell)), - Tracing, "Navigation"); + PROFILER_TRACING_MARKER_DOCSHELL("Navigation", "Load", NETWORK, + TRACING_INTERVAL_END, mDocShell); if (IsTopLevelContentDocumentInContentProcess()) { #ifdef MOZ_GECKO_PROFILER if (profiler_can_accept_markers() || PAGELOAD_LOG_ENABLED()) { TimeDuration elapsed = mLoadEventEnd - mNavigationStart; TimeDuration duration = mLoadEventEnd - mLoadEventStart; nsAutoCString spec; if (mLoadedURI) { mLoadedURI->GetSpec(spec); } nsPrintfCString marker( "Document %s loaded after %dms, load event duration %dms", spec.get(), int(elapsed.ToMilliseconds()), int(duration.ToMilliseconds())); PAGELOAD_LOG(("%s", marker.get())); - PROFILER_MARKER_TEXT( - "DocumentLoad", DOM, - MarkerOptions( - MarkerTiming::Interval(mNavigationStart, mLoadEventEnd), - MarkerInnerWindowId( - profiler_get_inner_window_id_from_docshell(mDocShell))), - marker); + PROFILER_ADD_MARKER_WITH_PAYLOAD( + "DocumentLoad", DOM, TextMarkerPayload, + (marker, mNavigationStart, mLoadEventEnd, + profiler_get_inner_window_id_from_docshell(mDocShell))); } #endif TimeStamp loadEventEnd = TimeStamp::Now(); Telemetry::AccumulateTimeDelta(Telemetry::TIME_TO_LOAD_EVENT_END_MS, mNavigationStart, loadEventEnd); MaybeSubmitTimeToLoadEventPreloadTelemetry(loadEventEnd); @@ -247,20 +236,18 @@ void nsDOMNavigationTiming::NotifyDOMCom void nsDOMNavigationTiming::NotifyDOMContentLoadedStart(nsIURI* aURI) { if (!mDOMContentLoadedEventStart.IsNull()) { return; } mLoadedURI = aURI; mDOMContentLoadedEventStart = TimeStamp::Now(); - PROFILER_MARKER("DOMContentLoaded", NETWORK, - MarkerOptions(MarkerTiming::IntervalStart(), - MarkerInnerWindowIdFromDocShell(mDocShell)), - Tracing, "Navigation"); + PROFILER_TRACING_MARKER_DOCSHELL("Navigation", "DOMContentLoaded", NETWORK, + TRACING_INTERVAL_START, mDocShell); if (IsTopLevelContentDocumentInContentProcess()) { TimeStamp now = TimeStamp::Now(); Telemetry::AccumulateTimeDelta( Telemetry::TIME_TO_DOM_CONTENT_LOADED_START_MS, mNavigationStart, now); if (mDocShellHasBeenActiveSinceNavigationStart) { @@ -281,20 +268,18 @@ void nsDOMNavigationTiming::NotifyDOMCon void nsDOMNavigationTiming::NotifyDOMContentLoadedEnd(nsIURI* aURI) { if (!mDOMContentLoadedEventEnd.IsNull()) { return; } mLoadedURI = aURI; mDOMContentLoadedEventEnd = TimeStamp::Now(); - PROFILER_MARKER("DOMContentLoaded", NETWORK, - MarkerOptions(MarkerTiming::IntervalEnd(), - MarkerInnerWindowIdFromDocShell(mDocShell)), - Tracing, "Navigation"); + PROFILER_TRACING_MARKER_DOCSHELL("Navigation", "DOMContentLoaded", NETWORK, + TRACING_INTERVAL_END, mDocShell); if (IsTopLevelContentDocumentInContentProcess()) { Telemetry::AccumulateTimeDelta(Telemetry::TIME_TO_DOM_CONTENT_LOADED_END_MS, mNavigationStart); } } // static @@ -387,23 +372,20 @@ void nsDOMNavigationTiming::TTITimeout(n nsAutoCString spec; if (mLoadedURI) { mLoadedURI->GetSpec(spec); } nsPrintfCString marker("TTFI after %dms (LongTask was at %dms) for URL %s", int(elapsed.ToMilliseconds()), int(elapsedLongTask.ToMilliseconds()), spec.get()); - PROFILER_MARKER_TEXT( - "TimeToFirstInteractive (TTFI)", DOM, - MarkerOptions( - MarkerTiming::Interval(mNavigationStart, mTTFI), - MarkerInnerWindowId( - profiler_get_inner_window_id_from_docshell(mDocShell))), - marker); + PROFILER_ADD_MARKER_WITH_PAYLOAD( + "TimeToFirstInteractive (TTFI)", DOM, TextMarkerPayload, + (marker, mNavigationStart, mTTFI, + profiler_get_inner_window_id_from_docshell(mDocShell))); } #endif } void nsDOMNavigationTiming::NotifyNonBlankPaintForRootContentDocument() { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(!mNavigationStart.IsNull()); @@ -423,23 +405,20 @@ void nsDOMNavigationTiming::NotifyNonBla nsPrintfCString marker( "Non-blank paint after %dms for URL %s, %s", int(elapsed.ToMilliseconds()), spec.get(), mDocShellHasBeenActiveSinceNavigationStart ? "foreground tab" : "this tab was inactive some of the time between navigation start " "and first non-blank paint"); PAGELOAD_LOG(("%s", marker.get())); - PROFILER_MARKER_TEXT( - "FirstNonBlankPaint", DOM, - MarkerOptions( - MarkerTiming::Interval(mNavigationStart, mNonBlankPaint), - MarkerInnerWindowId( - profiler_get_inner_window_id_from_docshell(mDocShell))), - marker); + PROFILER_ADD_MARKER_WITH_PAYLOAD( + "FirstNonBlankPaint", DOM, TextMarkerPayload, + (marker, mNavigationStart, mNonBlankPaint, + profiler_get_inner_window_id_from_docshell(mDocShell))); } #endif if (mDocShellHasBeenActiveSinceNavigationStart) { if (net::nsHttp::IsBeforeLastActiveTabLoadOptimization(mNavigationStart)) { Telemetry::AccumulateTimeDelta( Telemetry::TIME_TO_NON_BLANK_PAINT_NETOPT_MS, mNavigationStart, mNonBlankPaint); @@ -475,23 +454,20 @@ void nsDOMNavigationTiming::NotifyConten nsPrintfCString marker( "Contentful paint after %dms for URL %s, %s", int(elapsed.ToMilliseconds()), spec.get(), mDocShellHasBeenActiveSinceNavigationStart ? "foreground tab" : "this tab was inactive some of the time between navigation start " "and first non-blank paint"); PAGELOAD_LOG(("%s", marker.get())); - PROFILER_MARKER_TEXT( - "FirstContentfulPaint", DOM, - MarkerOptions( - MarkerTiming::Interval(mNavigationStart, mContentfulPaint), - MarkerInnerWindowId( - profiler_get_inner_window_id_from_docshell(mDocShell))), - marker); + PROFILER_ADD_MARKER_WITH_PAYLOAD( + "FirstContentfulPaint", DOM, TextMarkerPayload, + (marker, mNavigationStart, mContentfulPaint, + profiler_get_inner_window_id_from_docshell(mDocShell))); } #endif if (!mTTITimer) { mTTITimer = NS_NewTimer(); } // TTI is first checked 5 seconds after the FCP (non-blank-paint is very close @@ -527,23 +503,20 @@ void nsDOMNavigationTiming::NotifyDOMCon nsPrintfCString marker( "DOMContentFlushed after %dms for URL %s, %s", int(elapsed.ToMilliseconds()), spec.get(), mDocShellHasBeenActiveSinceNavigationStart ? "foreground tab" : "this tab was inactive some of the time between navigation start " "and DOMContentFlushed"); PAGELOAD_LOG(("%s", marker.get())); - PROFILER_MARKER_TEXT( - "DOMContentFlushed", DOM, - MarkerOptions( - MarkerTiming::Interval(mNavigationStart, mDOMContentFlushed), - MarkerInnerWindowId( - profiler_get_inner_window_id_from_docshell(mDocShell))), - marker); + PROFILER_ADD_MARKER_WITH_PAYLOAD( + "DOMContentFlushed", DOM, TextMarkerPayload, + (marker, mNavigationStart, mDOMContentFlushed, + profiler_get_inner_window_id_from_docshell(mDocShell))); } #endif } void nsDOMNavigationTiming::NotifyDocShellStateChanged( DocShellState aDocShellState) { mDocShellHasBeenActiveSinceNavigationStart &= (aDocShellState == DocShellState::eActive);
--- a/dom/events/EventDispatcher.cpp +++ b/dom/events/EventDispatcher.cpp @@ -1034,16 +1034,20 @@ nsresult EventDispatcher::Dispatch(nsISu MarkerInnerWindowId innerWindowId; if (nsCOMPtr<nsPIDOMWindowInner> inner = do_QueryInterface(aEvent->mTarget->GetOwnerGlobal())) { innerWindowId = MarkerInnerWindowId{inner->WindowID()}; } struct DOMEventMarker { static constexpr Span<const char> MarkerTypeName() { + // Note: DOMEventMarkerPayload was originally a sub-class of + // TracingMarkerPayload, so it uses the same payload type. + // TODO: Change to its own distinct type, but this will require + // front-end changes. return MakeStringSpan("DOMEvent"); } static void StreamJSONMarkerData( baseprofiler::SpliceableJSONWriter& aWriter, const ProfilerString16View& aEventType, const TimeStamp& aStartTime, const TimeStamp& aEventTimeStamp) { aWriter.StringProperty( "eventType", NS_ConvertUTF16toUTF8(aEventType.Data(),
--- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -16,16 +16,19 @@ #include "chrome/common/process_watcher.h" #include "mozilla/Result.h" #include "nsIBrowserDOMWindow.h" #ifdef ACCESSIBILITY # include "mozilla/a11y/PDocAccessible.h" #endif #include "GeckoProfiler.h" +#ifdef MOZ_GECKO_PROFILER +# include "ProfilerMarkerPayload.h" +#endif #include "GMPServiceParent.h" #include "HandlerServiceParent.h" #include "IHistory.h" #if defined(XP_WIN) && defined(ACCESSIBILITY) # include "mozilla/a11y/AccessibleWrap.h" # include "mozilla/a11y/Compatibility.h" #endif #include <utility> @@ -910,17 +913,19 @@ already_AddRefed<ContentParent> ContentP aMaxContentParents, &index))) { // If the provider returned an existing ContentParent, use that one. if (0 <= index && static_cast<uint32_t>(index) <= aMaxContentParents) { RefPtr<ContentParent> retval = aContentParents[index]; #ifdef MOZ_GECKO_PROFILER if (profiler_thread_is_being_profiled()) { nsPrintfCString marker("Reused process %u", (unsigned int)retval->ChildID()); - PROFILER_MARKER_TEXT("Process", DOM, {}, marker); + TimeStamp now = TimeStamp::Now(); + PROFILER_ADD_MARKER_WITH_PAYLOAD("Process", DOM, TextMarkerPayload, + (marker, now, now)); } #endif MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug, ("GetUsedProcess: Reused process %p (%u) for %s", retval.get(), (unsigned int)retval->ChildID(), PromiseFlatCString(aRemoteType).get())); retval->AssertAlive(); retval->StopRecycling(); @@ -949,17 +954,19 @@ already_AddRefed<ContentParent> ContentP MOZ_DIAGNOSTIC_ASSERT(recycled->GetRemoteType() == DEFAULT_REMOTE_TYPE); recycled->AssertAlive(); recycled->StopRecycling(); #ifdef MOZ_GECKO_PROFILER if (profiler_thread_is_being_profiled()) { nsPrintfCString marker("Recycled process %u (%p)", (unsigned int)recycled->ChildID(), recycled.get()); - PROFILER_MARKER_TEXT("Process", DOM, {}, marker); + TimeStamp now = TimeStamp::Now(); + PROFILER_ADD_MARKER_WITH_PAYLOAD("Process", DOM, TextMarkerPayload, + (marker, now, now)); } #endif MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug, ("Recycled process %p", recycled.get())); return recycled.forget(); } @@ -972,17 +979,19 @@ already_AddRefed<ContentParent> ContentP PREALLOC_REMOTE_TYPE); MOZ_DIAGNOSTIC_ASSERT(sRecycledE10SProcess != preallocated); preallocated->AssertAlive(); #ifdef MOZ_GECKO_PROFILER if (profiler_thread_is_being_profiled()) { nsPrintfCString marker("Assigned preallocated process %u", (unsigned int)preallocated->ChildID()); - PROFILER_MARKER_TEXT("Process", DOM, {}, marker); + TimeStamp now = TimeStamp::Now(); + PROFILER_ADD_MARKER_WITH_PAYLOAD("Process", DOM, TextMarkerPayload, + (marker, now, now)); } #endif MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug, ("Adopted preallocated process %p for type %s", preallocated.get(), PromiseFlatCString(aRemoteType).get())); // Specialize this process for the appropriate remote type, and activate it. preallocated->mActivateTS = TimeStamp::Now(); @@ -2409,20 +2418,19 @@ bool ContentParent::LaunchSubprocessReso mPrefSerializer = nullptr; const auto launchResumeTS = TimeStamp::Now(); #ifdef MOZ_GECKO_PROFILER if (profiler_thread_is_being_profiled()) { nsPrintfCString marker("Process start%s for %u", mIsAPreallocBlocker ? " (immediate)" : "", (unsigned int)ChildID()); - PROFILER_MARKER_TEXT( - mIsAPreallocBlocker ? ProfilerString8View("Process Immediate Launch") - : ProfilerString8View("Process Launch"), - DOM, MarkerTiming::Interval(mLaunchTS, launchResumeTS), marker); + PROFILER_ADD_MARKER_WITH_PAYLOAD( + mIsAPreallocBlocker ? "Process Immediate Launch" : "Process Launch", + DOM, TextMarkerPayload, (marker, mLaunchTS, launchResumeTS)); } #endif if (!sCreatedFirstContentProcess) { nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); obs->NotifyObservers(nullptr, "ipc:first-content-process-created", nullptr); sCreatedFirstContentProcess = true; }
--- a/dom/media/AsyncLogger.h +++ b/dom/media/AsyncLogger.h @@ -11,16 +11,19 @@ #include <thread> #include <cinttypes> #include "mozilla/ArrayUtils.h" #include "mozilla/Attributes.h" #include "mozilla/Logging.h" #include "mozilla/MathAlgorithms.h" #include "mozilla/Sprintf.h" #include "GeckoProfiler.h" +#ifdef MOZ_GECKO_PROFILER +# include "ProfilerMarkerPayload.h" +#endif #include "MPSCQueue.h" #if defined(_WIN32) # include <process.h> # define getpid() _getpid() #else # include <unistd.h> #endif @@ -191,51 +194,34 @@ class AsyncLogger { TextPayload message; while (mMessageQueueLog.Pop(&message) && mRunning) { MOZ_LOG(mLogModule, mozilla::LogLevel::Verbose, ("%s", message.mPayload)); } } #ifdef MOZ_GECKO_PROFILER { - struct Budget { - static constexpr Span<const char> MarkerTypeName() { - return MakeStringSpan("Budget"); - } - static void StreamJSONMarkerData( - baseprofiler::SpliceableJSONWriter& aWriter) {} - static MarkerSchema MarkerTypeDisplay() { - using MS = MarkerSchema; - MS schema{MS::Location::markerChart, MS::Location::markerTable}; - // Nothing outside the defaults. - return schema; - } - }; - TracePayload message; while (mMessageQueueProfiler.Pop(&message) && mRunning) { if (message.mPhase != TracingPhase::COMPLETE) { - profiler_add_marker( - ProfilerString8View::WrapNullTerminatedString(message.mName), - geckoprofiler::category::MEDIA_RT, - {MarkerThreadId(message.mTID), - (message.mPhase == TracingPhase::BEGIN) - ? MarkerTiming::IntervalStart(message.mTimestamp) - : MarkerTiming::IntervalEnd(message.mTimestamp)}, - Budget{}); + TracingKind kind = message.mPhase == TracingPhase::BEGIN + ? TracingKind::TRACING_INTERVAL_START + : TracingKind::TRACING_INTERVAL_END; + TracingMarkerPayload payload("media", kind, message.mTimestamp); + profiler_add_marker_for_thread( + message.mTID, JS::ProfilingCategoryPair::MEDIA_RT, + message.mName, payload); } else { - profiler_add_marker( - ProfilerString8View::WrapNullTerminatedString(message.mName), - geckoprofiler::category::MEDIA_RT, - {MarkerThreadId(message.mTID), - MarkerTiming::Interval( - message.mTimestamp, - message.mTimestamp + TimeDuration::FromMicroseconds( - message.mDurationUs))}, - Budget{}); + mozilla::TimeStamp end = + message.mTimestamp + + TimeDuration::FromMicroseconds(message.mDurationUs); + BudgetMarkerPayload payload(message.mTimestamp, end); + profiler_add_marker_for_thread( + message.mTID, JS::ProfilingCategoryPair::MEDIA_RT, + message.mName, payload); } } } #endif Sleep(); } PROFILER_UNREGISTER_THREAD(); }));
--- a/dom/media/MediaDecoderStateMachine.cpp +++ b/dom/media/MediaDecoderStateMachine.cpp @@ -22,30 +22,38 @@ #include "mozilla/TaskQueue.h" #include "mozilla/Tuple.h" #include "nsIMemoryReporter.h" #include "nsPrintfCString.h" #include "nsTArray.h" #include "AudioSegment.h" #include "DOMMediaStream.h" #include "ImageContainer.h" -#include "GeckoProfiler.h" #include "MediaDecoder.h" #include "MediaDecoderStateMachine.h" #include "MediaShutdownManager.h" #include "MediaTrackGraph.h" #include "MediaTimer.h" #include "ReaderProxy.h" #include "TimeUnits.h" #include "VideoSegment.h" #include "VideoUtils.h" #ifdef MOZ_GECKO_PROFILER -# include "mozilla/ProfilerMarkerTypes.h" -#endif // MOZ_GECKO_PROFILER +# include "ProfilerMarkerPayload.h" +# define MDSM_ERROR_MARKER(tag, error, markerTime) \ + PROFILER_ADD_MARKER_WITH_PAYLOAD(tag, MEDIA_PLAYBACK, TextMarkerPayload, \ + (error, markerTime)) +# define MDSM_SAMPLE_MARKER(tag, startTime, endTime) \ + PROFILER_ADD_MARKER_WITH_PAYLOAD( \ + tag, MEDIA_PLAYBACK, MediaSampleMarkerPayload, (startTime, endTime)) +#else +# define MDSM_ERROR_MARKER(tag, error, markerTime) +# define MDSM_SAMPLE_MARKER(tag, startTime, endTime) +#endif namespace mozilla { using namespace mozilla::media; #define NS_DispatchToMainThread(...) \ CompileError_UseAbstractThreadDispatchInstead @@ -2846,29 +2854,27 @@ bool MediaDecoderStateMachine::HaveEnoug MOZ_ASSERT(OnTaskQueue()); return VideoQueue().GetSize() >= GetAmpleVideoFrames() * mPlaybackRate + 1; } void MediaDecoderStateMachine::PushAudio(AudioData* aSample) { MOZ_ASSERT(OnTaskQueue()); MOZ_ASSERT(aSample); AudioQueue().Push(aSample); - PROFILER_MARKER("MDSM::PushAudio", MEDIA_PLAYBACK, {}, MediaSampleMarker, - aSample->mTime.ToMicroseconds(), - aSample->GetEndTime().ToMicroseconds()); + MDSM_SAMPLE_MARKER("MDSM::PushAudio", aSample->mTime.ToMicroseconds(), + aSample->GetEndTime().ToMicroseconds()); } void MediaDecoderStateMachine::PushVideo(VideoData* aSample) { MOZ_ASSERT(OnTaskQueue()); MOZ_ASSERT(aSample); aSample->mFrameID = ++mCurrentFrameID; VideoQueue().Push(aSample); - PROFILER_MARKER("MDSM::PushVideo", MEDIA_PLAYBACK, {}, MediaSampleMarker, - aSample->mTime.ToMicroseconds(), - aSample->GetEndTime().ToMicroseconds()); + MDSM_SAMPLE_MARKER("MDSM::PushVideo", aSample->mTime.ToMicroseconds(), + aSample->GetEndTime().ToMicroseconds()); } void MediaDecoderStateMachine::OnAudioPopped(const RefPtr<AudioData>& aSample) { MOZ_ASSERT(OnTaskQueue()); mPlaybackOffset = std::max(mPlaybackOffset, aSample->mOffset); } void MediaDecoderStateMachine::OnVideoPopped(const RefPtr<VideoData>& aSample) { @@ -3458,18 +3464,18 @@ bool MediaDecoderStateMachine::HasLowBuf } media::TimeInterval interval(start, end); return !mBuffered.Ref().Contains(interval); } void MediaDecoderStateMachine::DecodeError(const MediaResult& aError) { MOZ_ASSERT(OnTaskQueue()); LOGE("Decode error: %s", aError.Description().get()); - PROFILER_MARKER_TEXT("MDSM::DecodeError", MEDIA_PLAYBACK, {}, - aError.Description()); + MDSM_ERROR_MARKER("MDSM::DecodeError", aError.Description(), + TimeStamp::NowUnfuzzed()); // Notify the decode error and MediaDecoder will shut down MDSM. mOnPlaybackErrorEvent.Notify(aError); } void MediaDecoderStateMachine::EnqueueFirstFrameLoadedEvent() { MOZ_ASSERT(OnTaskQueue()); // Track value of mSentFirstFrameLoadedEvent from before updating it bool firstFrameBeenLoaded = mSentFirstFrameLoadedEvent;
--- a/dom/media/MediaFormatReader.cpp +++ b/dom/media/MediaFormatReader.cpp @@ -37,16 +37,25 @@ mozilla::LazyLogModule gMediaDemuxerLog( #define LOG(arg, ...) \ DDMOZ_LOG(sFormatDecoderLog, mozilla::LogLevel::Debug, "::%s: " arg, \ __func__, ##__VA_ARGS__) #define LOGV(arg, ...) \ DDMOZ_LOG(sFormatDecoderLog, mozilla::LogLevel::Verbose, "::%s: " arg, \ __func__, ##__VA_ARGS__) +#ifdef MOZ_GECKO_PROFILER +# include "ProfilerMarkerPayload.h" +# define MEDIA_FORMAT_READER_STATUS_MARKER(tag, text, markerTime) \ + PROFILER_ADD_MARKER_WITH_PAYLOAD(tag, MEDIA_PLAYBACK, TextMarkerPayload, \ + (text, markerTime)) +#else +# define MEDIA_FORMAT_READER_STATUS_MARKER(tag, text, markerTime) +#endif + #define NS_DispatchToMainThread(...) CompileError_UseAbstractMainThreadInstead namespace mozilla { typedef void* MediaDataDecoderID; /** * This class tracks shutdown promises to ensure all decoders are shut down @@ -1919,17 +1928,18 @@ void MediaFormatReader::HandleDemuxedSam } else if (decoder.HasWaitingPromise()) { decoder.Flush(); } } nsPrintfCString markerString( "%s stream id changed from:%" PRIu32 " to:%" PRIu32, TrackTypeToStr(aTrack), decoder.mLastStreamSourceID, info->GetID()); - PROFILER_MARKER_TEXT("StreamID Change", MEDIA_PLAYBACK, {}, markerString); + MEDIA_FORMAT_READER_STATUS_MARKER("StreamID Change", markerString, + TimeStamp::NowUnfuzzed()); LOG("%s", markerString.get()); if (aTrack == TrackInfo::kVideoTrack) { // We are about to create a new decoder thus the benchmark, // up to this point, is stored. NotifyDecoderBenchmarkStore(); } decoder.mNextStreamSourceID.reset(); @@ -3137,10 +3147,11 @@ void MediaFormatReader::OnFirstDemuxFail MOZ_ASSERT(decoder.mFirstDemuxedSampleTime.isNothing()); decoder.mFirstDemuxedSampleTime.emplace(TimeUnit::FromInfinity()); MaybeResolveMetadataPromise(); } } // namespace mozilla #undef NS_DispatchToMainThread +#undef MEDIA_FORMAT_READER_STATUS_MARKER #undef LOGV #undef LOG
--- a/dom/media/mediasink/AudioSink.cpp +++ b/dom/media/mediasink/AudioSink.cpp @@ -7,21 +7,38 @@ #include "AudioSink.h" #include "AudioConverter.h" #include "AudioDeviceInfo.h" #include "MediaQueue.h" #include "VideoUtils.h" #include "mozilla/CheckedInt.h" #include "mozilla/DebugOnly.h" #include "mozilla/IntegerPrintfMacros.h" -#include "mozilla/ProfilerMarkerTypes.h" #include "mozilla/StaticPrefs_media.h" #include "mozilla/StaticPrefs_dom.h" #include "nsPrintfCString.h" +#ifdef MOZ_GECKO_PROFILER +# include "ProfilerMarkerPayload.h" +# define PROFILER_AUDIO_MARKER(tag, sample) \ + do { \ + uint64_t startTime = (sample)->mTime.ToMicroseconds(); \ + uint64_t endTime = (sample)->GetEndTime().ToMicroseconds(); \ + auto profilerTag = (tag); \ + mOwnerThread->Dispatch(NS_NewRunnableFunction( \ + "AudioSink:AddMarker", [profilerTag, startTime, endTime] { \ + PROFILER_ADD_MARKER_WITH_PAYLOAD(profilerTag, MEDIA_PLAYBACK, \ + MediaSampleMarkerPayload, \ + (startTime, endTime)); \ + })); \ + } while (0) +#else +# define PROFILER_AUDIO_MARKER(tag, sample) +#endif + namespace mozilla { extern LazyLogModule gMediaDecoderLog; #define SINK_LOG(msg, ...) \ MOZ_LOG(gMediaDecoderLog, LogLevel::Debug, \ ("AudioSink=%p " msg, this, ##__VA_ARGS__)) #define SINK_LOG_V(msg, ...) \ MOZ_LOG(gMediaDecoderLog, LogLevel::Verbose, \ @@ -249,26 +266,17 @@ UniquePtr<AudioStream::Chunk> AudioSink: FramesToUsecs(mCurrentData->Frames(), mOutputRate).value(); } auto framesToPop = std::min(aFrames, mCursor->Available()); SINK_LOG_V("playing audio at time=%" PRId64 " offset=%u length=%u", mCurrentData->mTime.ToMicroseconds(), mCurrentData->Frames() - mCursor->Available(), framesToPop); - -#ifdef MOZ_GECKO_PROFILER - mOwnerThread->Dispatch(NS_NewRunnableFunction( - "AudioSink:AddMarker", - [startTime = mCurrentData->mTime.ToMicroseconds(), - endTime = mCurrentData->GetEndTime().ToMicroseconds()] { - PROFILER_MARKER("PlayAudio", MEDIA_PLAYBACK, {}, MediaSampleMarker, - startTime, endTime); - })); -#endif // MOZ_GECKO_PROFILER + PROFILER_AUDIO_MARKER("PlayAudio", mCurrentData); UniquePtr<AudioStream::Chunk> chunk = MakeUnique<Chunk>(mCurrentData, framesToPop, mCursor->Ptr()); { MonitorAutoLock mon(mMonitor); mWritten += framesToPop; mCursor->Advance(framesToPop);
--- a/dom/media/mediasink/VideoSink.cpp +++ b/dom/media/mediasink/VideoSink.cpp @@ -11,30 +11,38 @@ #endif #include "VideoSink.h" #include "MediaQueue.h" #include "VideoUtils.h" #include "mozilla/IntegerPrintfMacros.h" -#include "mozilla/ProfilerMarkerTypes.h" #include "mozilla/StaticPrefs_browser.h" #include "mozilla/StaticPrefs_media.h" extern mozilla::LazyLogModule gMediaDecoderLog; #undef FMT #define FMT(x, ...) "VideoSink=%p " x, this, ##__VA_ARGS__ #define VSINK_LOG(x, ...) \ MOZ_LOG(gMediaDecoderLog, LogLevel::Debug, (FMT(x, ##__VA_ARGS__))) #define VSINK_LOG_V(x, ...) \ MOZ_LOG(gMediaDecoderLog, LogLevel::Verbose, (FMT(x, ##__VA_ARGS__))) +#ifdef MOZ_GECKO_PROFILER +# include "ProfilerMarkerPayload.h" +# define VSINK_ADD_PROFILER_MARKER(tag, startTime, endTime) \ + PROFILER_ADD_MARKER_WITH_PAYLOAD( \ + tag, MEDIA_PLAYBACK, MediaSampleMarkerPayload, (startTime, endTime)) +#else +# define VSINK_ADD_PROFILER_MARKER(tag, startTime, endTime) +#endif + namespace mozilla { using namespace mozilla::layers; // Minimum update frequency is 1/120th of a second, i.e. half the // duration of a 60-fps frame. static const int64_t MIN_UPDATE_INTERVAL_US = 1000000 / (60 * 2); @@ -450,19 +458,18 @@ void VideoSink::RenderVideoFrames(int32_ } img->mFrameID = frame->mFrameID; img->mProducerID = mProducerID; VSINK_LOG_V("playing video frame %" PRId64 " (id=%x) (vq-queued=%zu)", frame->mTime.ToMicroseconds(), frame->mFrameID, VideoQueue().GetSize()); if (!wasSent) { - PROFILER_MARKER("PlayVideo", MEDIA_PLAYBACK, {}, MediaSampleMarker, - frame->mTime.ToMicroseconds(), - frame->GetEndTime().ToMicroseconds()); + VSINK_ADD_PROFILER_MARKER("PlayVideo", frame->mTime.ToMicroseconds(), + frame->GetEndTime().ToMicroseconds()); } } if (images.Length() > 0) { mContainer->SetCurrentFrames(frames[0]->mDisplay, images); if (mSecondaryContainer) { mSecondaryContainer->SetCurrentFrames(frames[0]->mDisplay, images); @@ -491,19 +498,18 @@ void VideoSink::UpdateRenderedVideoFrame lastFrameEndTime = frame->GetEndTime(); if (frame->IsSentToCompositor()) { sentToCompositorCount++; } else { droppedInSink++; VSINK_LOG_V("discarding video frame mTime=%" PRId64 " clock_time=%" PRId64, frame->mTime.ToMicroseconds(), clockTime.ToMicroseconds()); - PROFILER_MARKER("DiscardVideo", MEDIA_PLAYBACK, {}, MediaSampleMarker, - frame->mTime.ToMicroseconds(), - frame->GetEndTime().ToMicroseconds()); + VSINK_ADD_PROFILER_MARKER("DiscardVideo", frame->mTime.ToMicroseconds(), + frame->GetEndTime().ToMicroseconds()); } } if (droppedInSink || sentToCompositorCount) { uint32_t totalCompositorDroppedCount = mContainer->GetDroppedImageCount(); uint32_t droppedInCompositor = totalCompositorDroppedCount - mOldCompositorDroppedCount; if (droppedInCompositor > 0) {
--- a/dom/media/platforms/wmf/WMFDecoderModule.cpp +++ b/dom/media/platforms/wmf/WMFDecoderModule.cpp @@ -22,39 +22,47 @@ #include "WMFVideoMFTManager.h" #include "mozilla/DebugOnly.h" #include "mozilla/Maybe.h" #include "mozilla/StaticMutex.h" #include "mozilla/StaticPrefs_media.h" #include "mozilla/WindowsVersion.h" #include "mozilla/gfx/gfxVars.h" #include "mozilla/mscom/EnsureMTA.h" -#include "mozilla/ProfilerMarkers.h" #include "nsComponentManagerUtils.h" #include "nsIXULRuntime.h" #include "nsIXULRuntime.h" // for BrowserTabsRemoteAutostart #include "nsServiceManagerUtils.h" #include "nsWindowsHelpers.h" #include "prsystem.h" #define LOG(...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, (__VA_ARGS__)) +#ifdef MOZ_GECKO_PROFILER +# include "ProfilerMarkerPayload.h" +# define WFM_DECODER_MODULE_STATUS_MARKER(tag, text, markerTime) \ + PROFILER_ADD_MARKER_WITH_PAYLOAD(tag, MEDIA_PLAYBACK, TextMarkerPayload, \ + (text, markerTime)) +#else +# define WFM_DECODER_MODULE_STATUS_MARKER(tag, text, markerTime) +#endif + extern const GUID CLSID_WebmMfVpxDec; namespace mozilla { // Helper function to add a profile marker and log at the same time. static void MOZ_FORMAT_PRINTF(2, 3) - WmfDeocderModuleMarkerAndLog(const ProfilerString8View& aMarkerTag, - const char* aFormat, ...) { + WmfDeocderModuleMarkerAndLog(const char* aTag, const char* aFormat, ...) { va_list ap; va_start(ap, aFormat); const nsVprintfCString markerString(aFormat, ap); va_end(ap); - PROFILER_MARKER_TEXT(aMarkerTag, MEDIA_PLAYBACK, {}, markerString); + WFM_DECODER_MODULE_STATUS_MARKER(aTag, markerString, + TimeStamp::NowUnfuzzed()); LOG("%s", markerString.get()); } static Atomic<bool> sDXVAEnabled(false); static Atomic<bool> sUsableVPXMFT(false); /* static */ already_AddRefed<PlatformDecoderModule> WMFDecoderModule::Create() {
--- a/dom/media/platforms/wmf/WMFMediaDataDecoder.cpp +++ b/dom/media/platforms/wmf/WMFMediaDataDecoder.cpp @@ -4,24 +4,32 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "WMFMediaDataDecoder.h" #include "VideoUtils.h" #include "WMFUtils.h" #include "mozilla/Logging.h" -#include "mozilla/ProfilerMarkers.h" #include "mozilla/SyncRunnable.h" #include "mozilla/TaskQueue.h" #include "mozilla/Telemetry.h" #include "nsTArray.h" #define LOG(...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, (__VA_ARGS__)) +#ifdef MOZ_GECKO_PROFILER +# include "ProfilerMarkerPayload.h" +# define WFM_MEDIA_DATA_DECODER_STATUS_MARKER(tag, text, markerTime) \ + PROFILER_ADD_MARKER_WITH_PAYLOAD(tag, MEDIA_PLAYBACK, TextMarkerPayload, \ + (text, markerTime)) +#else +# define WFM_MEDIA_DATA_DECODER_STATUS_MARKER(tag, text, markerTime) +#endif + namespace mozilla { WMFMediaDataDecoder::WMFMediaDataDecoder(MFTManager* aMFTManager) : mTaskQueue( new TaskQueue(GetMediaThreadPool(MediaThreadType::PLATFORM_DECODER), "WMFMediaDataDecoder")), mMFTManager(aMFTManager) {} @@ -89,17 +97,18 @@ RefPtr<MediaDataDecoder::DecodePromise> mRecordedError = true; } nsPrintfCString markerString( "WMFMediaDataDecoder::ProcessError for decoder with description %s with " "reason: %s", GetDescriptionName().get(), aReason); LOG(markerString.get()); - PROFILER_MARKER_TEXT("WMFDecoder Error", MEDIA_PLAYBACK, {}, markerString); + WFM_MEDIA_DATA_DECODER_STATUS_MARKER("WMFDecoder Error", markerString, + TimeStamp::NowUnfuzzed()); // TODO: For the error DXGI_ERROR_DEVICE_RESET, we could return // NS_ERROR_DOM_MEDIA_NEED_NEW_DECODER to get the latest device. Maybe retry // up to 3 times. return DecodePromise::CreateAndReject( MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR, RESULT_DETAIL("%s:%x", aReason, aError)), __func__);
--- a/dom/media/platforms/wrappers/MediaChangeMonitor.cpp +++ b/dom/media/platforms/wrappers/MediaChangeMonitor.cpp @@ -8,20 +8,28 @@ #include "AnnexB.h" #include "H264.h" #include "ImageContainer.h" #include "MP4Decoder.h" #include "MediaInfo.h" #include "PDMFactory.h" #include "VPXDecoder.h" -#include "mozilla/ProfilerMarkers.h" #include "mozilla/StaticPrefs_media.h" #include "mozilla/TaskQueue.h" +#ifdef MOZ_GECKO_PROFILER +# include "ProfilerMarkerPayload.h" +# define MEDIA_CHANGE_MONITOR_STATUS_MARKER(tag, text, markerTime) \ + PROFILER_ADD_MARKER_WITH_PAYLOAD(tag, MEDIA_PLAYBACK, TextMarkerPayload, \ + (text, markerTime)) +#else +# define MEDIA_CHANGE_MONITOR_STATUS_MARKER(tag, text, markerTime) +#endif + namespace mozilla { // H264ChangeMonitor is used to ensure that only AVCC or AnnexB is fed to the // underlying MediaDataDecoder. The H264ChangeMonitor allows playback of content // where the SPS NAL may not be provided in the init segment (e.g. AVC3 or Annex // B) H264ChangeMonitor will monitor the input data, and will delay creation of // the MediaDataDecoder until a SPS and PPS NALs have been extracted. @@ -85,19 +93,21 @@ class H264ChangeMonitor : public MediaCh return NS_OK; } // Store the sample's extradata so we don't trigger a false positive // with the out of band test on the next sample. mPreviousExtraData = aSample->mExtraData; UpdateConfigFromExtraData(extra_data); - PROFILER_MARKER_TEXT("H264 Stream Change", MEDIA_PLAYBACK, {}, - "H264ChangeMonitor::CheckForChange has detected a " - "change in the stream and will request a new decoder"); + MEDIA_CHANGE_MONITOR_STATUS_MARKER( + "H264 Stream Change", + "H264ChangeMonitor::CheckForChange has detected a change in the " + "stream and will request a new decoder"_ns, + TimeStamp::NowUnfuzzed()); return NS_ERROR_DOM_MEDIA_NEED_NEW_DECODER; } const TrackInfo& Config() const override { return mCurrentConfig; } MediaResult PrepareSample(MediaDataDecoder::ConversionRequired aConversion, MediaRawData* aSample, bool aNeedKeyFrame) override { @@ -189,20 +199,21 @@ class VPXChangeMonitor : public MediaCha if (mInfo.ref().IsCompatible(info)) { return rv; } mCurrentConfig.mImage = info.mImage; mCurrentConfig.mDisplay = info.mDisplay; mCurrentConfig.SetImageRect( gfx::IntRect(0, 0, info.mImage.width, info.mImage.height)); - PROFILER_MARKER_TEXT( - "VPX Stream Change", MEDIA_PLAYBACK, {}, + MEDIA_CHANGE_MONITOR_STATUS_MARKER( + "VPX Stream Change", "VPXChangeMonitor::CheckForChange has detected a change in the " - "stream and will request a new decoder"); + "stream and will request a new decoder"_ns, + TimeStamp::NowUnfuzzed()); rv = NS_ERROR_DOM_MEDIA_NEED_NEW_DECODER; } mInfo = Some(info); // For the first frame, we leave the mDisplay/mImage untouched as they // contain aspect ratio (AR) information set by the demuxer. // The AR data isn't found in the VP8/VP9 bytestream. mCurrentConfig.mColorDepth = gfx::ColorDepthForBitDepth(info.mBitDepth); mCurrentConfig.mColorSpace = info.ColorSpace(); @@ -739,8 +750,10 @@ void MediaChangeMonitor::FlushThenShutdo return; } mDecodePromise.Reject(aError, __func__); }) ->Track(mFlushRequest); } } // namespace mozilla + +#undef MEDIA_CHANGE_MONITOR_STATUS_MARKER
--- a/dom/performance/Performance.cpp +++ b/dom/performance/Performance.cpp @@ -23,16 +23,20 @@ #include "mozilla/dom/PerformanceNavigationBinding.h" #include "mozilla/dom/PerformanceObserverBinding.h" #include "mozilla/dom/PerformanceNavigationTiming.h" #include "mozilla/IntegerPrintfMacros.h" #include "mozilla/Preferences.h" #include "mozilla/dom/WorkerPrivate.h" #include "mozilla/dom/WorkerRunnable.h" +#ifdef MOZ_GECKO_PROFILER +# include "ProfilerMarkerPayload.h" +#endif + #define PERFLOG(msg, ...) printf_stderr(msg, ##__VA_ARGS__) namespace mozilla::dom { NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Performance) NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) NS_IMPL_CYCLE_COLLECTION_INHERITED(Performance, DOMEventTargetHelper, @@ -207,63 +211,16 @@ void Performance::ClearUserEntries(const return (!aEntryName.WasPassed() || entry->GetName().Equals(aEntryName.Value())) && (aEntryType.IsEmpty() || entry->GetEntryType().Equals(aEntryType)); }); } void Performance::ClearResourceTimings() { mResourceEntries.Clear(); } -#ifdef MOZ_GECKO_PROFILER -struct UserTimingMarker { - static constexpr Span<const char> MarkerTypeName() { - return MakeStringSpan("UserTiming"); - } - static void StreamJSONMarkerData( - baseprofiler::SpliceableJSONWriter& aWriter, - const ProfilerString16View& aName, bool aIsMeasure, - const Maybe<ProfilerString16View>& aStartMark, - const Maybe<ProfilerString16View>& aEndMark) { - aWriter.StringProperty("name", NS_ConvertUTF16toUTF8(aName.Data())); - if (aIsMeasure) { - aWriter.StringProperty("entryType", "measure"); - } else { - aWriter.StringProperty("entryType", "mark"); - } - - if (aStartMark.isSome()) { - aWriter.StringProperty("startMark", - NS_ConvertUTF16toUTF8(aStartMark->Data())); - } else { - aWriter.NullProperty("startMark"); - } - if (aEndMark.isSome()) { - aWriter.StringProperty("endMark", - NS_ConvertUTF16toUTF8(aEndMark->Data())); - } else { - aWriter.NullProperty("endMark"); - } - } - static MarkerSchema MarkerTypeDisplay() { - using MS = MarkerSchema; - MS schema{MS::Location::markerChart, MS::Location::markerTable}; - schema.SetAllLabels("{marker.data.name}"); - schema.AddStaticLabelValue("Marker", "UserTiming"); - schema.AddKeyLabelFormat("entryType", "Entry Type", MS::Format::string); - schema.AddKeyLabelFormat("name", "Name", MS::Format::string); - schema.AddKeyLabelFormat("startMark", "Start Mark", MS::Format::string); - schema.AddKeyLabelFormat("endMark", "End Mark", MS::Format::string); - schema.AddStaticLabelValue("Description", - "UserTimingMeasure is created using the DOM API " - "performance.measure()."); - return schema; - } -}; -#endif - void Performance::Mark(const nsAString& aName, ErrorResult& aRv) { // We add nothing when 'privacy.resistFingerprinting' is on. if (nsContentUtils::ShouldResistFingerprinting()) { return; } if (IsPerformanceTimingAttribute(aName)) { aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); @@ -275,19 +232,18 @@ void Performance::Mark(const nsAString& InsertUserEntry(performanceMark); #ifdef MOZ_GECKO_PROFILER if (profiler_can_accept_markers()) { Maybe<uint64_t> innerWindowId; if (GetOwner()) { innerWindowId = Some(GetOwner()->WindowID()); } - profiler_add_marker("UserTiming", geckoprofiler::category::DOM, - MarkerInnerWindowId(innerWindowId), UserTimingMarker{}, - aName, /* aIsMeasure */ false, Nothing{}, Nothing{}); + PROFILER_ADD_MARKER_WITH_PAYLOAD("UserTiming", DOM, UserTimingMarkerPayload, + (aName, TimeStamp::Now(), innerWindowId)); } #endif } void Performance::ClearMarks(const Optional<nsAString>& aName) { ClearUserEntries(aName, u"mark"_ns); } @@ -371,21 +327,19 @@ void Performance::Measure(const nsAStrin if (aEndMark.WasPassed()) { endMark.emplace(aEndMark.Value()); } Maybe<uint64_t> innerWindowId; if (GetOwner()) { innerWindowId = Some(GetOwner()->WindowID()); } - profiler_add_marker("UserTiming", geckoprofiler::category::DOM, - {MarkerTiming::Interval(startTimeStamp, endTimeStamp), - MarkerInnerWindowId(innerWindowId)}, - UserTimingMarker{}, aName, /* aIsMeasure */ true, - startMark, endMark); + PROFILER_ADD_MARKER_WITH_PAYLOAD("UserTiming", DOM, UserTimingMarkerPayload, + (aName, startMark, endMark, startTimeStamp, + endTimeStamp, innerWindowId)); } #endif } void Performance::ClearMeasures(const Optional<nsAString>& aName) { ClearUserEntries(aName, u"measure"_ns); }
--- a/dom/script/ScriptLoader.cpp +++ b/dom/script/ScriptLoader.cpp @@ -19,17 +19,16 @@ #include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_* #include "js/MemoryFunctions.h" #include "js/Modules.h" // JS::FinishDynamicModuleImport, JS::{G,S}etModuleResolveHook, JS::Get{ModulePrivate,ModuleScript,RequestedModule{s,Specifier,SourcePos}}, JS::SetModule{DynamicImport,Metadata}Hook #include "js/OffThreadScriptCompilation.h" #include "js/Realm.h" #include "js/SourceText.h" #include "js/Utility.h" #include "xpcpublic.h" -#include "GeckoProfiler.h" #include "nsCycleCollectionParticipant.h" #include "nsIContent.h" #include "nsJSUtils.h" #include "mozilla/dom/DocGroup.h" #include "mozilla/dom/Element.h" #include "mozilla/dom/ScriptDecoding.h" // mozilla::dom::ScriptDecoding #include "mozilla/dom/ScriptSettings.h" #include "mozilla/dom/SRILogHelper.h" @@ -79,16 +78,20 @@ #include "mozilla/Telemetry.h" #include "mozilla/TimeStamp.h" #include "mozilla/UniquePtr.h" #include "mozilla/Unused.h" #include "mozilla/Utf8.h" // mozilla::Utf8Unit #include "nsIScriptError.h" #include "nsIAsyncOutputStream.h" +#ifdef MOZ_GECKO_PROFILER +# include "ProfilerMarkerPayload.h" +#endif + using JS::SourceText; using mozilla::Telemetry::LABELS_DOM_SCRIPT_PRELOAD_RESULT; namespace mozilla { namespace dom { LazyLogModule ScriptLoader::gCspPRLog("CSP"); @@ -2172,33 +2175,32 @@ NotifyOffThreadScriptLoadCompletedRunnab // function. RefPtr<ScriptLoadRequest> request = std::move(mRequest); // Runnable pointer should have been cleared in the offthread callback. MOZ_ASSERT(!request->mRunnable); #ifdef MOZ_GECKO_PROFILER if (profiler_is_active()) { - ProfilerString8View scriptSourceString; + const char* scriptSourceString; if (request->IsTextSource()) { scriptSourceString = "ScriptCompileOffThread"; } else if (request->IsBinASTSource()) { scriptSourceString = "BinASTDecodeOffThread"; } else { MOZ_ASSERT(request->IsBytecode()); scriptSourceString = "BytecodeDecodeOffThread"; } nsAutoCString profilerLabelString; GetProfilerLabelForRequest(request, profilerLabelString); - PROFILER_MARKER_TEXT( - scriptSourceString, JS, - MarkerTiming::Interval(request->mOffThreadParseStartTime, - request->mOffThreadParseStopTime), - profilerLabelString); + PROFILER_ADD_MARKER_WITH_PAYLOAD( + scriptSourceString, JS, TextMarkerPayload, + (profilerLabelString, request->mOffThreadParseStartTime, + request->mOffThreadParseStopTime)); } #endif RefPtr<ScriptLoader> loader = std::move(mLoader); // Request was already cancelled at some earlier point. if (!request->mOffThreadToken) { return NS_OK;
--- a/dom/xhr/XMLHttpRequestMainThread.cpp +++ b/dom/xhr/XMLHttpRequestMainThread.cpp @@ -2453,17 +2453,17 @@ nsresult XMLHttpRequestMainThread::Creat NS_ENSURE_SUCCESS(rv, rv); } nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mChannel)); if (httpChannel) { rv = httpChannel->SetRequestMethod(mRequestMethod); NS_ENSURE_SUCCESS(rv, rv); - httpChannel->SetSource(profiler_capture_backtrace()); + httpChannel->SetSource(profiler_get_backtrace()); // Set the initiator type nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(httpChannel)); if (timedChannel) { timedChannel->SetInitiatorType(u"xmlhttprequest"_ns); } } @@ -3235,18 +3235,17 @@ void XMLHttpRequestMainThread::SetMozBac SetMozBackgroundRequest(aMozBackgroundRequest); } void XMLHttpRequestMainThread::SetOriginStack( UniquePtr<SerializedStackHolder> aOriginStack) { mOriginStack = std::move(aOriginStack); } -void XMLHttpRequestMainThread::SetSource( - UniquePtr<ProfileChunkedBuffer> aSource) { +void XMLHttpRequestMainThread::SetSource(UniqueProfilerBacktrace aSource) { if (!mChannel) { return; } nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel); if (httpChannel) { httpChannel->SetSource(std::move(aSource)); }
--- a/dom/xhr/XMLHttpRequestMainThread.h +++ b/dom/xhr/XMLHttpRequestMainThread.h @@ -391,17 +391,17 @@ class XMLHttpRequestMainThread final : p nsresult SetMozBackgroundRequest(bool aMozBackgroundRequest); virtual void SetMozBackgroundRequest(bool aMozBackgroundRequest, ErrorResult& aRv) override; void SetOriginStack(UniquePtr<SerializedStackHolder> aOriginStack); - void SetSource(UniquePtr<ProfileChunkedBuffer> aSource); + void SetSource(UniqueProfilerBacktrace aSource); virtual uint16_t ErrorCode() const override { return static_cast<uint16_t>(mErrorLoad); } virtual bool MozAnon() const override; virtual bool MozSystem() const override;
--- a/dom/xhr/XMLHttpRequestWorker.cpp +++ b/dom/xhr/XMLHttpRequestWorker.cpp @@ -648,28 +648,28 @@ class OpenRunnable final : public Worker const nsString mMimeTypeOverride; // Remember the worker thread's stack when the XHR was opened, so that it can // be passed on to the net monitor. UniquePtr<SerializedStackHolder> mOriginStack; // Remember the worker thread's stack when the XHR was opened for profiling // purposes. - UniquePtr<ProfileChunkedBuffer> mSource; + UniqueProfilerBacktrace mSource; public: OpenRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy, const nsACString& aMethod, const nsAString& aURL, const Optional<nsAString>& aUser, const Optional<nsAString>& aPassword, bool aBackgroundRequest, bool aWithCredentials, uint32_t aTimeout, XMLHttpRequestResponseType aResponseType, const nsString& aMimeTypeOverride, UniquePtr<SerializedStackHolder> aOriginStack, - UniquePtr<ProfileChunkedBuffer> aSource = nullptr) + UniqueProfilerBacktrace aSource = nullptr) : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy), mMethod(aMethod), mURL(aURL), mBackgroundRequest(aBackgroundRequest), mWithCredentials(aWithCredentials), mTimeout(aTimeout), mResponseType(aResponseType), mMimeTypeOverride(aMimeTypeOverride), @@ -1736,17 +1736,17 @@ void XMLHttpRequestWorker::Open(const ns stack = GetCurrentStackForNetMonitor(cx); } } RefPtr<OpenRunnable> runnable = new OpenRunnable( mWorkerPrivate, mProxy, aMethod, aUrl, aUser, aPassword, mBackgroundRequest, mWithCredentials, mTimeout, mResponseType, alsoOverrideMimeType ? mMimeTypeOverride : VoidString(), std::move(stack), - profiler_capture_backtrace()); + profiler_get_backtrace()); ++mProxy->mOpenCount; runnable->Dispatch(Canceling, aRv); if (aRv.Failed()) { if (mProxy && !--mProxy->mOpenCount) { ReleaseProxy(); }
--- a/gfx/layers/ProfilerScreenshots.cpp +++ b/gfx/layers/ProfilerScreenshots.cpp @@ -6,16 +6,19 @@ #include "mozilla/layers/ProfilerScreenshots.h" #include "mozilla/TimeStamp.h" #include "GeckoProfiler.h" #include "gfxUtils.h" #include "nsThreadUtils.h" +#ifdef MOZ_GECKO_PROFILER +# include "ProfilerMarkerPayload.h" +#endif using namespace mozilla; using namespace mozilla::gfx; using namespace mozilla::layers; ProfilerScreenshots::ProfilerScreenshots() : mMutex("ProfilerScreenshots::mMutex"), mLiveSurfaceCount(0) {} @@ -76,42 +79,22 @@ void ProfilerScreenshots::SubmitScreensh // Encode surf to a JPEG data URL. nsCString dataURL; nsresult rv = gfxUtils::EncodeSourceSurface( surf, ImageType::JPEG, u"quality=85"_ns, gfxUtils::eDataURIEncode, nullptr, &dataURL); if (NS_SUCCEEDED(rv)) { // Add a marker with the data URL. - struct ScreenshotMarker { - static constexpr mozilla::Span<const char> MarkerTypeName() { - return mozilla::MakeStringSpan("CompositorScreenshot"); - } - static void StreamJSONMarkerData( - mozilla::baseprofiler::SpliceableJSONWriter& aWriter, - const mozilla::ProfilerString8View& aScreenshotDataURL, - const mozilla::gfx::IntSize& aWindowSize, - uintptr_t aWindowIdentifier) { - aWriter.UniqueStringProperty("url", aScreenshotDataURL); - - char hexWindowID[32]; - SprintfLiteral(hexWindowID, "0x%" PRIXPTR, aWindowIdentifier); - aWriter.StringProperty("windowID", hexWindowID); - aWriter.DoubleProperty("windowWidth", aWindowSize.width); - aWriter.DoubleProperty("windowHeight", aWindowSize.height); - } - static mozilla::MarkerSchema MarkerTypeDisplay() { - return mozilla::MarkerSchema::SpecialFrontendLocation{}; - } - }; - profiler_add_marker( - "CompositorScreenshot", geckoprofiler::category::GRAPHICS, - {MarkerThreadId(sourceThread), - MarkerTiming::InstantAt(timeStamp)}, - ScreenshotMarker{}, dataURL, originalSize, windowIdentifier); + AUTO_PROFILER_STATS(add_marker_with_ScreenshotPayload); + profiler_add_marker_for_thread( + sourceThread, JS::ProfilingCategoryPair::GRAPHICS, + "CompositorScreenshot", + ScreenshotPayload(timeStamp, std::move(dataURL), originalSize, + windowIdentifier)); } } // Return backingSurface back to the surface pool. self->ReturnSurface(backingSurface); })); #endif }
--- a/gfx/layers/composite/ContainerLayerComposite.cpp +++ b/gfx/layers/composite/ContainerLayerComposite.cpp @@ -35,16 +35,20 @@ #include "nsISupportsUtils.h" // for NS_ADDREF, NS_RELEASE #include "nsRegion.h" // for nsIntRegion #include "nsTArray.h" // for AutoTArray #include <stack> #include "TextRenderer.h" // for TextRenderer #include <vector> #include "GeckoProfiler.h" // for GeckoProfiler +#ifdef MOZ_GECKO_PROFILER +# include "ProfilerMarkerPayload.h" // for LayerTranslationMarkerPayload +#endif + static mozilla::LazyLogModule sGfxCullLog("gfx.culling"); #define CULLING_LOG(...) MOZ_LOG(sGfxCullLog, LogLevel::Debug, (__VA_ARGS__)) #define DUMP(...) \ do { \ if (gfxEnv::DumpDebug()) { \ printf_stderr(__VA_ARGS__); \ } \ @@ -94,47 +98,19 @@ static void PrintUniformityInfo(Layer* a } Matrix4x4 transform = aLayer->AsHostLayer()->GetShadowBaseTransform(); if (!transform.Is2D()) { return; } Point translation = transform.As2D().GetTranslation(); - - // Contains the translation applied to a 2d layer so we can track the layer - // position at each frame. - struct LayerTranslationMarker { - static constexpr Span<const char> MarkerTypeName() { - return MakeStringSpan("LayerTranslation"); - } - static void StreamJSONMarkerData( - baseprofiler::SpliceableJSONWriter& aWriter, - ProfileBufferRawPointer<layers::Layer> aLayer, gfx::Point aPoint) { - const size_t bufferSize = 32; - char buffer[bufferSize]; - SprintfLiteral(buffer, "%p", aLayer.mRawPointer); - - aWriter.StringProperty("layer", buffer); - aWriter.IntProperty("x", aPoint.x); - aWriter.IntProperty("y", aPoint.y); - } - static MarkerSchema MarkerTypeDisplay() { - using MS = MarkerSchema; - MS schema{MS::Location::markerChart, MS::Location::markerTable}; - schema.AddKeyLabelFormat("layer", "Layer", MS::Format::string); - schema.AddKeyLabelFormat("x", "X", MS::Format::integer); - schema.AddKeyLabelFormat("y", "Y", MS::Format::integer); - return schema; - } - }; - - profiler_add_marker("LayerTranslation", geckoprofiler::category::GRAPHICS, {}, - LayerTranslationMarker{}, - WrapProfileBufferRawPointer(aLayer), translation); + PROFILER_ADD_MARKER_WITH_PAYLOAD("LayerTranslation", GRAPHICS, + LayerTranslationMarkerPayload, + (aLayer, translation, TimeStamp::Now())); #endif } static Maybe<gfx::Polygon> SelectLayerGeometry( const Maybe<gfx::Polygon>& aParentGeometry, const Maybe<gfx::Polygon>& aChildGeometry) { // Both the parent and the child layer were split. if (aParentGeometry && aChildGeometry) {
--- a/gfx/layers/ipc/CompositorBridgeParent.cpp +++ b/gfx/layers/ipc/CompositorBridgeParent.cpp @@ -85,16 +85,19 @@ #endif #include "GeckoProfiler.h" #include "mozilla/ipc/ProtocolTypes.h" #include "mozilla/Unused.h" #include "mozilla/Hal.h" #include "mozilla/HalTypes.h" #include "mozilla/StaticPtr.h" #include "mozilla/Telemetry.h" +#ifdef MOZ_GECKO_PROFILER +# include "ProfilerMarkerPayload.h" +#endif #include "mozilla/VsyncDispatcher.h" #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) # include "VsyncSource.h" #endif #include "mozilla/widget/CompositorWidget.h" #ifdef MOZ_WIDGET_SUPPORTS_OOP_COMPOSITING # include "mozilla/widget/CompositorWidgetParent.h" #endif @@ -2168,33 +2171,18 @@ already_AddRefed<IAPZCTreeManager> Compo lts->mParent ? lts->mParent->mApzcTreeManager.get() : nullptr; return apzctm.forget(); } #if defined(MOZ_GECKO_PROFILER) static void InsertVsyncProfilerMarker(TimeStamp aVsyncTimestamp) { MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); if (profiler_thread_is_being_profiled()) { - // Tracks when a vsync occurs according to the HardwareComposer. - struct VsyncMarker { - static constexpr mozilla::Span<const char> MarkerTypeName() { - return mozilla::MakeStringSpan("VsyncTimestamp"); - } - static void StreamJSONMarkerData( - baseprofiler::SpliceableJSONWriter& aWriter) {} - static MarkerSchema MarkerTypeDisplay() { - using MS = MarkerSchema; - MS schema{MS::Location::markerChart, MS::Location::markerTable}; - // Nothing outside the defaults. - return schema; - } - }; - profiler_add_marker("VsyncTimestamp", geckoprofiler::category::GRAPHICS, - MarkerTiming::InstantAt(aVsyncTimestamp), - VsyncMarker{}); + PROFILER_ADD_MARKER_WITH_PAYLOAD("VsyncTimestamp", GRAPHICS, + VsyncMarkerPayload, (aVsyncTimestamp)); } } #endif /*static */ void CompositorBridgeParent::PostInsertVsyncProfilerMarker( TimeStamp aVsyncTimestamp) { #if defined(MOZ_GECKO_PROFILER) @@ -2749,33 +2737,52 @@ int32_t RecordContentFrameTime( const TimeDuration& aVsyncRate, bool aContainsSVGGroup, bool aRecordUploadStats, wr::RendererStats* aStats /* = nullptr */) { double latencyMs = (aCompositeEnd - aTxnStart).ToMilliseconds(); double latencyNorm = latencyMs / aVsyncRate.ToMilliseconds(); int32_t fracLatencyNorm = lround(latencyNorm * 100.0); #ifdef MOZ_GECKO_PROFILER if (profiler_can_accept_markers()) { - struct ContentFrameMarker { - static constexpr Span<const char> MarkerTypeName() { - return MakeStringSpan("CONTENT_FRAME_TIME"); + class ContentFramePayload : public ProfilerMarkerPayload { + public: + ContentFramePayload(const mozilla::TimeStamp& aStartTime, + const mozilla::TimeStamp& aEndTime) + : ProfilerMarkerPayload(aStartTime, aEndTime) {} + mozilla::ProfileBufferEntryWriter::Length TagAndSerializationBytes() + const override { + return CommonPropsTagAndSerializationBytes(); + } + void SerializeTagAndPayload( + mozilla::ProfileBufferEntryWriter& aEntryWriter) const override { + static const DeserializerTag tag = TagForDeserializer(Deserialize); + SerializeTagAndCommonProps(tag, aEntryWriter); } - static void StreamJSONMarkerData( - baseprofiler::SpliceableJSONWriter& aWriter) {} - static MarkerSchema MarkerTypeDisplay() { - using MS = MarkerSchema; - MS schema{MS::Location::markerChart, MS::Location::markerTable}; - // Nothing outside the defaults. - return schema; + void StreamPayload(mozilla::baseprofiler::SpliceableJSONWriter& aWriter, + const TimeStamp& aProcessStartTime, + UniqueStacks& aUniqueStacks) const override { + StreamCommonProps("CONTENT_FRAME_TIME", aWriter, aProcessStartTime, + aUniqueStacks); + } + + private: + explicit ContentFramePayload(CommonProps&& aCommonProps) + : ProfilerMarkerPayload(std::move(aCommonProps)) {} + static mozilla::UniquePtr<ProfilerMarkerPayload> Deserialize( + mozilla::ProfileBufferEntryReader& aEntryReader) { + ProfilerMarkerPayload::CommonProps props = + DeserializeCommonProps(aEntryReader); + return UniquePtr<ProfilerMarkerPayload>( + new ContentFramePayload(std::move(props))); } }; - - profiler_add_marker("CONTENT_FRAME_TIME", geckoprofiler::category::GRAPHICS, - MarkerTiming::Interval(aTxnStart, aCompositeEnd), - ContentFrameMarker{}); + AUTO_PROFILER_STATS(add_marker_with_ContentFramePayload); + profiler_add_marker_for_thread( + profiler_current_thread_id(), JS::ProfilingCategoryPair::GRAPHICS, + "CONTENT_FRAME_TIME", ContentFramePayload(aTxnStart, aCompositeEnd)); } #endif Telemetry::Accumulate(Telemetry::CONTENT_FRAME_TIME, fracLatencyNorm); if (!(aTxnId == VsyncId()) && aVsyncStart) { latencyMs = (aCompositeEnd - aVsyncStart).ToMilliseconds(); latencyNorm = latencyMs / aVsyncRate.ToMilliseconds();
--- a/gfx/layers/ipc/ContentCompositorBridgeParent.cpp +++ b/gfx/layers/ipc/ContentCompositorBridgeParent.cpp @@ -34,17 +34,17 @@ #include "mozilla/mozalloc.h" // for operator new, etc #include "nsDebug.h" // for NS_ASSERTION, etc #include "nsTArray.h" // for nsTArray #include "nsXULAppAPI.h" // for XRE_GetIOMessageLoop #include "mozilla/Unused.h" #include "mozilla/StaticPtr.h" #include "mozilla/Telemetry.h" #ifdef MOZ_GECKO_PROFILER -# include "mozilla/BaseProfilerMarkerTypes.h" +# include "ProfilerMarkerPayload.h" #endif namespace mozilla { namespace layers { // defined in CompositorBridgeParent.cpp typedef std::map<LayersId, CompositorBridgeParent::LayerTreeState> LayerTreeMap; @@ -367,20 +367,53 @@ void ContentCompositorBridgeParent::Shad // to reach the widget owning the tab. Unused << state->mParent->SendObserveLayersUpdate( id, aLayerTree->GetChildEpoch(), true); } auto endTime = TimeStamp::Now(); #ifdef MOZ_GECKO_PROFILER if (profiler_can_accept_markers()) { - profiler_add_marker( - "CONTENT_FULL_PAINT_TIME", geckoprofiler::category::GRAPHICS, - MarkerTiming::Interval(aInfo.transactionStart(), endTime), - baseprofiler::markers::ContentBuildMarker{}); + class ContentBuildPayload : public ProfilerMarkerPayload { + public: + ContentBuildPayload(const mozilla::TimeStamp& aStartTime, + const mozilla::TimeStamp& aEndTime) + : ProfilerMarkerPayload(aStartTime, aEndTime) {} + mozilla::ProfileBufferEntryWriter::Length TagAndSerializationBytes() + const override { + return CommonPropsTagAndSerializationBytes(); + } + void SerializeTagAndPayload( + mozilla::ProfileBufferEntryWriter& aEntryWriter) const override { + static const DeserializerTag tag = TagForDeserializer(Deserialize); + SerializeTagAndCommonProps(tag, aEntryWriter); + } + void StreamPayload(mozilla::baseprofiler::SpliceableJSONWriter& aWriter, + const TimeStamp& aProcessStartTime, + UniqueStacks& aUniqueStacks) const override { + StreamCommonProps("CONTENT_FULL_PAINT_TIME", aWriter, aProcessStartTime, + aUniqueStacks); + } + + private: + explicit ContentBuildPayload(CommonProps&& aCommonProps) + : ProfilerMarkerPayload(std::move(aCommonProps)) {} + static mozilla::UniquePtr<ProfilerMarkerPayload> Deserialize( + mozilla::ProfileBufferEntryReader& aEntryReader) { + ProfilerMarkerPayload::CommonProps props = + DeserializeCommonProps(aEntryReader); + return UniquePtr<ProfilerMarkerPayload>( + new ContentBuildPayload(std::move(props))); + } + }; + AUTO_PROFILER_STATS(add_marker_with_ContentBuildPayload); + profiler_add_marker_for_thread( + profiler_current_thread_id(), JS::ProfilingCategoryPair::GRAPHICS, + "CONTENT_FULL_PAINT_TIME", + ContentBuildPayload(aInfo.transactionStart(), endTime)); } #endif Telemetry::Accumulate( Telemetry::CONTENT_FULL_PAINT_TIME, static_cast<uint32_t>( (endTime - aInfo.transactionStart()).ToMilliseconds())); RegisterPayloads(aLayerTree, aInfo.payload());
--- a/gfx/layers/wr/WebRenderBridgeParent.cpp +++ b/gfx/layers/wr/WebRenderBridgeParent.cpp @@ -40,44 +40,50 @@ #include "mozilla/webrender/RenderThread.h" #include "mozilla/widget/CompositorWidget.h" #ifdef XP_WIN # include "mozilla/widget/WinCompositorWidget.h" #endif #ifdef MOZ_GECKO_PROFILER -# include "mozilla/ProfilerMarkerTypes.h" +# include "ProfilerMarkerPayload.h" #endif bool is_in_main_thread() { return NS_IsMainThread(); } bool is_in_compositor_thread() { return mozilla::layers::CompositorThreadHolder::IsInCompositorThread(); } bool is_in_render_thread() { return mozilla::wr::RenderThread::IsInRenderThread(); } void gecko_profiler_start_marker(const char* name) { - PROFILER_MARKER(mozilla::ProfilerString8View::WrapNullTerminatedString(name), - GRAPHICS, mozilla::MarkerTiming::IntervalStart(), Tracing, - "WebRender"); +#ifdef MOZ_GECKO_PROFILER + profiler_tracing_marker("WebRender", name, + JS::ProfilingCategoryPair::GRAPHICS, + TRACING_INTERVAL_START); +#endif } void gecko_profiler_end_marker(const char* name) { - PROFILER_MARKER(mozilla::ProfilerString8View::WrapNullTerminatedString(name), - GRAPHICS, mozilla::MarkerTiming::IntervalEnd(), Tracing, - "WebRender"); +#ifdef MOZ_GECKO_PROFILER + profiler_tracing_marker("WebRender", name, + JS::ProfilingCategoryPair::GRAPHICS, + TRACING_INTERVAL_END); +#endif } void gecko_profiler_event_marker(const char* name) { - PROFILER_MARKER(mozilla::ProfilerString8View::WrapNullTerminatedString(name), - GRAPHICS, {}, Tracing, "WebRender"); +#ifdef MOZ_GECKO_PROFILER + profiler_tracing_marker("WebRender", name, + JS::ProfilingCategoryPair::GRAPHICS, TRACING_EVENT); +#endif } void gecko_profiler_add_text_marker(const char* name, const char* text_bytes, size_t text_len, uint64_t microseconds) { #ifdef MOZ_GECKO_PROFILER if (profiler_thread_is_being_profiled()) { auto now = mozilla::TimeStamp::NowUnfuzzed(); auto start = now - mozilla::TimeDuration::FromMicroseconds(microseconds); @@ -215,20 +221,56 @@ class SceneBuiltNotification : public wr auto startTime = this->mTxnStartTime; RefPtr<WebRenderBridgeParent> parent = mParent; wr::Epoch epoch = mEpoch; CompositorThread()->Dispatch(NS_NewRunnableFunction( "SceneBuiltNotificationRunnable", [parent, epoch, startTime]() { auto endTime = TimeStamp::Now(); #ifdef MOZ_GECKO_PROFILER if (profiler_can_accept_markers()) { - profiler_add_marker("CONTENT_FULL_PAINT_TIME", - geckoprofiler::category::GRAPHICS, - MarkerTiming::Interval(startTime, endTime), - baseprofiler::markers::ContentBuildMarker{}); + class ContentFullPaintPayload : public ProfilerMarkerPayload { + public: + ContentFullPaintPayload(const mozilla::TimeStamp& aStartTime, + const mozilla::TimeStamp& aEndTime) + : ProfilerMarkerPayload(aStartTime, aEndTime) {} + mozilla::ProfileBufferEntryWriter::Length + TagAndSerializationBytes() const override { + return CommonPropsTagAndSerializationBytes(); + } + void SerializeTagAndPayload(mozilla::ProfileBufferEntryWriter& + aEntryWriter) const override { + static const DeserializerTag tag = + TagForDeserializer(Deserialize); + SerializeTagAndCommonProps(tag, aEntryWriter); + } + void StreamPayload( + mozilla::baseprofiler::SpliceableJSONWriter& aWriter, + const TimeStamp& aProcessStartTime, + UniqueStacks& aUniqueStacks) const override { + StreamCommonProps("CONTENT_FULL_PAINT_TIME", aWriter, + aProcessStartTime, aUniqueStacks); + } + + private: + explicit ContentFullPaintPayload(CommonProps&& aCommonProps) + : ProfilerMarkerPayload(std::move(aCommonProps)) {} + static mozilla::UniquePtr<ProfilerMarkerPayload> Deserialize( + mozilla::ProfileBufferEntryReader& aEntryReader) { + ProfilerMarkerPayload::CommonProps props = + DeserializeCommonProps(aEntryReader); + return UniquePtr<ProfilerMarkerPayload>( + new ContentFullPaintPayload(std::move(props))); + } + }; + + AUTO_PROFILER_STATS(add_marker_with_ContentFullPaintPayload); + profiler_add_marker_for_thread( + profiler_current_thread_id(), + JS::ProfilingCategoryPair::GRAPHICS, "CONTENT_FULL_PAINT_TIME", + ContentFullPaintPayload(startTime, endTime)); } #endif Telemetry::Accumulate( Telemetry::CONTENT_FULL_PAINT_TIME, static_cast<uint32_t>((endTime - startTime).ToMilliseconds())); parent->NotifySceneBuiltForEpoch(epoch, endTime); })); }
--- a/gfx/webrender_bindings/RenderCompositorNative.cpp +++ b/gfx/webrender_bindings/RenderCompositorNative.cpp @@ -12,16 +12,20 @@ #include "GLContextProvider.h" #include "mozilla/gfx/gfxVars.h" #include "mozilla/layers/CompositionRecorder.h" #include "mozilla/layers/SurfacePool.h" #include "mozilla/StaticPrefs_gfx.h" #include "mozilla/webrender/RenderThread.h" #include "mozilla/widget/CompositorWidget.h" +#ifdef MOZ_GECKO_PROFILER +# include "ProfilerMarkerPayload.h" +#endif + namespace mozilla { namespace wr { class RenderCompositorRecordedFrame final : public layers::RecordedFrame { public: RenderCompositorRecordedFrame( const TimeStamp& aTimeStamp, RefPtr<layers::profiler_screenshots::AsyncReadbackBuffer>&& aBuffer)
--- a/ipc/chromium/src/chrome/common/ipc_channel_utils.cc +++ b/ipc/chromium/src/chrome/common/ipc_channel_utils.cc @@ -3,32 +3,35 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "chrome/common/ipc_channel_utils.h" #include "chrome/common/ipc_message.h" +#ifdef MOZ_GECKO_PROFILER +# include "ProfilerMarkerPayload.h" +#endif + namespace IPC { void AddIPCProfilerMarker(const Message& aMessage, int32_t aOtherPid, mozilla::ipc::MessageDirection aDirection, mozilla::ipc::MessagePhase aPhase) { #ifdef MOZ_GECKO_PROFILER if (aMessage.routing_id() != MSG_ROUTING_NONE && profiler_feature_active(ProfilerFeature::IPCMessages)) { if (aOtherPid == -1) { DLOG(WARNING) << "Unable to record IPC profile marker, other PID not set"; return; } - // The current timestamp must be given to the `IPCMarker` payload. - const mozilla::TimeStamp now = mozilla::TimeStamp::NowUnfuzzed(); - PROFILER_MARKER("IPC", IPC, mozilla::MarkerTiming::InstantAt(now), - IPCMarker, now, now, aOtherPid, aMessage.seqno(), - aMessage.type(), mozilla::ipc::UnknownSide, aDirection, - aPhase, aMessage.is_sync()); + PROFILER_ADD_MARKER_WITH_PAYLOAD( + "IPC", IPC, IPCMarkerPayload, + (aOtherPid, aMessage.seqno(), aMessage.type(), + mozilla::ipc::UnknownSide, aDirection, aPhase, aMessage.is_sync(), + mozilla::TimeStamp::NowUnfuzzed())); } #endif } } // namespace IPC
--- a/ipc/glue/MessageChannel.cpp +++ b/ipc/glue/MessageChannel.cpp @@ -32,16 +32,20 @@ #include "nsISupportsImpl.h" #include "nsPrintfCString.h" #ifdef MOZ_TASK_TRACER # include "GeckoTaskTracer.h" using namespace mozilla::tasktracer; #endif +#ifdef MOZ_GECKO_PROFILER +# include "ProfilerMarkerPayload.h" +#endif + // Undo the damage done by mozzconf.h #undef compress static mozilla::LazyLogModule sLogModule("ipc"); #define IPC_LOG(...) MOZ_LOG(sLogModule, LogLevel::Debug, (__VA_ARGS__)) /* * IPC design: @@ -2780,21 +2784,21 @@ void MessageChannel::DumpInterruptStack( void MessageChannel::AddProfilerMarker(const IPC::Message& aMessage, MessageDirection aDirection) { mMonitor->AssertCurrentThreadOwns(); #ifdef MOZ_GECKO_PROFILER if (profiler_feature_active(ProfilerFeature::IPCMessages)) { int32_t pid = mListener->OtherPidMaybeInvalid(); if (pid != kInvalidProcessId) { - // The current timestamp must be given to the `IPCMarker` payload. - const TimeStamp now = TimeStamp::NowUnfuzzed(); - PROFILER_MARKER("IPC", IPC, MarkerTiming::InstantAt(now), IPCMarker, now, - now, pid, aMessage.seqno(), aMessage.type(), mSide, - aDirection, MessagePhase::Endpoint, aMessage.is_sync()); + PROFILER_ADD_MARKER_WITH_PAYLOAD( + "IPC", IPC, IPCMarkerPayload, + (pid, aMessage.seqno(), aMessage.type(), mSide, aDirection, + MessagePhase::Endpoint, aMessage.is_sync(), + TimeStamp::NowUnfuzzed())); } } #endif } int32_t MessageChannel::GetTopmostMessageRoutingId() const { AssertWorkerThread();
--- a/ipc/glue/MessageChannel.h +++ b/ipc/glue/MessageChannel.h @@ -24,20 +24,16 @@ #include <functional> #include <map> #include <stack> #include <vector> #include "MessageLink.h" #include "mozilla/ipc/Transport.h" -#ifdef MOZ_GECKO_PROFILER -# include "mozilla/ProfilerMarkersPrerequisites.h" -#endif - class nsIEventTarget; namespace mozilla { namespace ipc { class MessageChannel; class IToplevelProtocol; class ActorLifecycleProxy; @@ -878,79 +874,9 @@ namespace IPC { template <> struct ParamTraits<mozilla::ipc::ResponseRejectReason> : public ContiguousEnumSerializer< mozilla::ipc::ResponseRejectReason, mozilla::ipc::ResponseRejectReason::SendError, mozilla::ipc::ResponseRejectReason::EndGuard_> {}; } // namespace IPC -#ifdef MOZ_GECKO_PROFILER -namespace geckoprofiler::markers { - -struct IPCMarker { - static constexpr mozilla::Span<const char> MarkerTypeName() { - return mozilla::MakeStringSpan("IPC"); - } - static void StreamJSONMarkerData( - mozilla::baseprofiler::SpliceableJSONWriter& aWriter, - mozilla::TimeStamp aStart, mozilla::TimeStamp aEnd, int32_t aOtherPid, - int32_t aMessageSeqno, IPC::Message::msgid_t aMessageType, - mozilla::ipc::Side aSide, mozilla::ipc::MessageDirection aDirection, - mozilla::ipc::MessagePhase aPhase, bool aSync) { - using namespace mozilla::ipc; - // This payload still streams a startTime and endTime property because it - // made the migration to MarkerTiming on the front-end easier. - mozilla::baseprofiler::WritePropertyTime(aWriter, "startTime", aStart); - mozilla::baseprofiler::WritePropertyTime(aWriter, "endTime", aEnd); - - aWriter.IntProperty("otherPid", aOtherPid); - aWriter.IntProperty("messageSeqno", aMessageSeqno); - aWriter.StringProperty( - "messageType", - mozilla::MakeStringSpan(IPC::StringFromIPCMessageType(aMessageType))); - aWriter.StringProperty("side", IPCSideToString(aSide)); - aWriter.StringProperty("direction", - aDirection == MessageDirection::eSending - ? mozilla::MakeStringSpan("sending") - : mozilla::MakeStringSpan("receiving")); - aWriter.StringProperty("phase", IPCPhaseToString(aPhase)); - aWriter.BoolProperty("sync", aSync); - } - static mozilla::MarkerSchema MarkerTypeDisplay() { - return mozilla::MarkerSchema::SpecialFrontendLocation{}; - } - - private: - static mozilla::Span<const char> IPCSideToString(mozilla::ipc::Side aSide) { - switch (aSide) { - case mozilla::ipc::ParentSide: - return mozilla::MakeStringSpan("parent"); - case mozilla::ipc::ChildSide: - return mozilla::MakeStringSpan("child"); - case mozilla::ipc::UnknownSide: - return mozilla::MakeStringSpan("unknown"); - default: - MOZ_ASSERT_UNREACHABLE("Invalid IPC side"); - return mozilla::MakeStringSpan("<invalid IPC side>"); - } - } - - static mozilla::Span<const char> IPCPhaseToString( - mozilla::ipc::MessagePhase aPhase) { - switch (aPhase) { - case mozilla::ipc::MessagePhase::Endpoint: - return mozilla::MakeStringSpan("endpoint"); - case mozilla::ipc::MessagePhase::TransferStart: - return mozilla::MakeStringSpan("transferStart"); - case mozilla::ipc::MessagePhase::TransferEnd: - return mozilla::MakeStringSpan("transferEnd"); - default: - MOZ_ASSERT_UNREACHABLE("Invalid IPC phase"); - return mozilla::MakeStringSpan("<invalid IPC phase>"); - } - } -}; - -} // namespace geckoprofiler::markers -#endif - #endif // ifndef ipc_glue_MessageChannel_h
--- a/ipc/mscom/ProfilerMarkers.cpp +++ b/ipc/mscom/ProfilerMarkers.cpp @@ -18,20 +18,16 @@ #include "nsIObserverService.h" #include "nsISupportsImpl.h" #include "nsString.h" #include "nsXULAppAPI.h" #include <objbase.h> #include <objidlbase.h> -#ifdef MOZ_GECKO_PROFILER -# include "mozilla/ProfilerMarkerTypes.h" -#endif - // {9DBE6B28-E5E7-4FDE-AF00-9404604E74DC} static const GUID GUID_MozProfilerMarkerExtension = { 0x9dbe6b28, 0xe5e7, 0x4fde, {0xaf, 0x0, 0x94, 0x4, 0x60, 0x4e, 0x74, 0xdc}}; namespace { class ProfilerMarkerChannelHook final : public IChannelHook { ~ProfilerMarkerChannelHook() = default; @@ -132,35 +128,35 @@ void ProfilerMarkerChannelHook::BuildMar } void ProfilerMarkerChannelHook::ClientGetSize(REFGUID aExtensionId, REFIID aIid, ULONG* aOutDataSize) { if (aExtensionId == GUID_MozProfilerMarkerExtension) { if (NS_IsMainThread()) { nsAutoCString markerName; BuildMarkerName(aIid, markerName); - PROFILER_MARKER(markerName, IPC, mozilla::MarkerTiming::IntervalStart(), - Tracing, "MSCOM"); + PROFILER_TRACING_MARKER("MSCOM", markerName.get(), IPC, + TRACING_INTERVAL_START); } if (aOutDataSize) { // We don't add any payload data to the channel *aOutDataSize = 0UL; } } } void ProfilerMarkerChannelHook::ClientNotify(REFGUID aExtensionId, REFIID aIid, ULONG aDataSize, void* aDataBuffer, DWORD aDataRep, HRESULT aFault) { if (NS_IsMainThread() && aExtensionId == GUID_MozProfilerMarkerExtension) { nsAutoCString markerName; BuildMarkerName(aIid, markerName); - PROFILER_MARKER(markerName, IPC, mozilla::MarkerTiming::IntervalEnd(), - Tracing, "MSCOM"); + PROFILER_TRACING_MARKER("MSCOM", markerName.get(), IPC, + TRACING_INTERVAL_END); } } } // anonymous namespace static void RegisterChannelHook() { RefPtr<ProfilerMarkerChannelHook> hook(new ProfilerMarkerChannelHook()); mozilla::DebugOnly<HRESULT> hr =
--- a/js/xpconnect/src/XPCJSContext.cpp +++ b/js/xpconnect/src/XPCJSContext.cpp @@ -58,16 +58,19 @@ #include "AccessCheck.h" #include "nsGlobalWindow.h" #include "nsAboutProtocolUtils.h" #include "GeckoProfiler.h" #include "nsIXULRuntime.h" #include "nsJSPrincipals.h" #include "ExpandedPrincipal.h" +#ifdef MOZ_GECKO_PROFILER +# include "ProfilerMarkerPayload.h" +#endif #if defined(XP_LINUX) && !defined(ANDROID) // For getrlimit and min/max. # include <algorithm> # include <sys/resource.h> #endif #ifdef XP_WIN @@ -583,17 +586,19 @@ bool XPCJSContext::InterruptCallback(JSC nsDependentCString filename("unknown file"); JS::AutoFilename scriptFilename; // Computing the line number can be very expensive (see bug 1330231 for // example), so don't request it here. if (JS::DescribeScriptedCaller(cx, &scriptFilename)) { if (const char* file = scriptFilename.get()) { filename.Assign(file, strlen(file)); } - PROFILER_MARKER_TEXT("JS::InterruptCallback", JS, {}, filename); + PROFILER_ADD_MARKER_WITH_PAYLOAD("JS::InterruptCallback", JS, + TextMarkerPayload, + (filename, TimeStamp::Now())); } #endif // Normally we record mSlowScriptCheckpoint when we start to process an // event. However, we can run JS outside of event handlers. This code takes // care of that case. if (self->mSlowScriptCheckpoint.IsNull()) { self->mSlowScriptCheckpoint = TimeStamp::NowLoRes();
--- a/layout/base/AutoProfilerStyleMarker.h +++ b/layout/base/AutoProfilerStyleMarker.h @@ -3,25 +3,26 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef mozilla_AutoProfilerStyleMarker_h #define mozilla_AutoProfilerStyleMarker_h #include "mozilla/Attributes.h" -#include "mozilla/ProfilerMarkers.h" #include "mozilla/ServoTraversalStatistics.h" #include "mozilla/TimeStamp.h" +#include "GeckoProfiler.h" +#include "ProfilerMarkerPayload.h" namespace mozilla { class MOZ_RAII AutoProfilerStyleMarker { public: - explicit AutoProfilerStyleMarker(UniquePtr<ProfileChunkedBuffer> aCause, + explicit AutoProfilerStyleMarker(UniqueProfilerBacktrace aCause, const Maybe<uint64_t>& aInnerWindowID) : mActive(profiler_can_accept_markers()), mStartTime(TimeStamp::Now()), mCause(std::move(aCause)), mInnerWindowID(aInnerWindowID) { if (!mActive) { return; } @@ -30,65 +31,25 @@ class MOZ_RAII AutoProfilerStyleMarker { ServoTraversalStatistics::sSingleton = ServoTraversalStatistics(); ServoTraversalStatistics::sActive = true; } ~AutoProfilerStyleMarker() { if (!mActive) { return; } - - struct StyleMarker { - static constexpr mozilla::Span<const char> MarkerTypeName() { - return mozilla::MakeStringSpan("Styles"); - } - static void StreamJSONMarkerData( - baseprofiler::SpliceableJSONWriter& aWriter, - uint32_t aElementsTraversed, uint32_t aElementsStyled, - uint32_t aElementsMatched, uint32_t aStylesShared, - uint32_t aStylesReused) { - aWriter.IntProperty("elementsTraversed", aElementsTraversed); - aWriter.IntProperty("elementsStyled", aElementsStyled); - aWriter.IntProperty("elementsMatched", aElementsMatched); - aWriter.IntProperty("stylesShared", aStylesShared); - aWriter.IntProperty("stylesReused", aStylesReused); - } - static MarkerSchema MarkerTypeDisplay() { - using MS = MarkerSchema; - MS schema{MS::Location::markerChart, MS::Location::markerTable, - MS::Location::timelineOverview}; - schema.AddKeyLabelFormat("elementsTraversed", "Elements traversed", - MS::Format::integer); - schema.AddKeyLabelFormat("elementsStyled", "Elements styled", - MS::Format::integer); - schema.AddKeyLabelFormat("elementsMatched", "Elements matched", - MS::Format::integer); - schema.AddKeyLabelFormat("stylesShared", "Styles shared", - MS::Format::integer); - schema.AddKeyLabelFormat("stylesReused", "Styles reused", - MS::Format::integer); - return schema; - } - }; - ServoTraversalStatistics::sActive = false; - profiler_add_marker("Styles", geckoprofiler::category::LAYOUT, - {MarkerTiming::IntervalUntilNowFrom(mStartTime), - MarkerStack::TakeBacktrace(std::move(mCause)), - MarkerInnerWindowId(mInnerWindowID)}, - StyleMarker{}, - ServoTraversalStatistics::sSingleton.mElementsTraversed, - ServoTraversalStatistics::sSingleton.mElementsStyled, - ServoTraversalStatistics::sSingleton.mElementsMatched, - ServoTraversalStatistics::sSingleton.mStylesShared, - ServoTraversalStatistics::sSingleton.mStylesReused); + PROFILER_ADD_MARKER_WITH_PAYLOAD( + "Styles", LAYOUT, StyleMarkerPayload, + (mStartTime, TimeStamp::Now(), std::move(mCause), + ServoTraversalStatistics::sSingleton, mInnerWindowID)); } private: bool mActive; TimeStamp mStartTime; - UniquePtr<ProfileChunkedBuffer> mCause; + UniqueProfilerBacktrace mCause; Maybe<uint64_t> mInnerWindowID; }; } // namespace mozilla #endif // mozilla_AutoProfilerStyleMarker_h
--- a/layout/base/PresShell.cpp +++ b/layout/base/PresShell.cpp @@ -9560,17 +9560,17 @@ bool PresShell::DoReflow(nsIFrame* targe } #ifdef MOZ_GECKO_PROFILER Maybe<uint64_t> innerWindowID; if (auto* window = mDocument->GetInnerWindow()) { innerWindowID = Some(window->WindowID()); } AutoProfilerTracing tracingLayoutFlush( - "Paint", "Reflow", geckoprofiler::category::LAYOUT, + "Paint", "Reflow", JS::ProfilingCategoryPair::LAYOUT, std::move(mReflowCause), innerWindowID); mReflowCause = nullptr; #endif FlushPendingScrollAnchorSelections(); if (mReflowContinueTimer) { mReflowContinueTimer->Cancel();
--- a/layout/base/PresShell.h +++ b/layout/base/PresShell.h @@ -2856,18 +2856,18 @@ class PresShell final : public nsStubDoc // Reflow roots that need to be reflowed. DirtyRootsList mDirtyRoots; #ifdef MOZ_GECKO_PROFILER // These two fields capture call stacks of any changes that require a restyle // or a reflow. Only the first change per restyle / reflow is recorded (the // one that caused a call to SetNeedStyleFlush() / SetNeedLayoutFlush()). - UniquePtr<ProfileChunkedBuffer> mStyleCause; - UniquePtr<ProfileChunkedBuffer> mReflowCause; + UniqueProfilerBacktrace mStyleCause; + UniqueProfilerBacktrace mReflowCause; #endif nsTArray<UniquePtr<DelayedEvent>> mDelayedEvents; nsRevocableEventPtr<nsSynthMouseMoveEvent> mSynthMouseMoveEvent; TouchManager mTouchManager;
--- a/layout/base/PresShellInlines.h +++ b/layout/base/PresShellInlines.h @@ -18,34 +18,34 @@ void PresShell::SetNeedLayoutFlush() { if (dom::Document* doc = mDocument->GetDisplayDocument()) { if (PresShell* presShell = doc->GetPresShell()) { presShell->mNeedLayoutFlush = true; } } #ifdef MOZ_GECKO_PROFILER if (!mReflowCause) { - mReflowCause = profiler_capture_backtrace(); + mReflowCause = profiler_get_backtrace(); } #endif mLayoutTelemetry.IncReqsPerFlush(FlushType::Layout); } void PresShell::SetNeedStyleFlush() { mNeedStyleFlush = true; if (dom::Document* doc = mDocument->GetDisplayDocument()) { if (PresShell* presShell = doc->GetPresShell()) { presShell->mNeedStyleFlush = true; } } #ifdef MOZ_GECKO_PROFILER if (!mStyleCause) { - mStyleCause = profiler_capture_backtrace(); + mStyleCause = profiler_get_backtrace(); } #endif mLayoutTelemetry.IncReqsPerFlush(FlushType::Layout); } void PresShell::EnsureStyleFlush() { SetNeedStyleFlush();
--- a/layout/base/nsCSSFrameConstructor.cpp +++ b/layout/base/nsCSSFrameConstructor.cpp @@ -124,20 +124,16 @@ #include "mozilla/dom/SVGTests.h" #include "mozilla/SVGGradientFrame.h" #include "mozilla/SVGUtils.h" #include "nsRefreshDriver.h" #include "nsTextNode.h" #include "ActiveLayerTracker.h" -#ifdef MOZ_GECKO_PROFILER -# include "mozilla/ProfilerMarkerTypes.h" -#endif - using namespace mozilla; using namespace mozilla::dom; // An alias for convenience. static const nsIFrame::ChildListID kPrincipalList = nsIFrame::kPrincipalList; nsIFrame* NS_NewHTMLCanvasFrame(PresShell* aPresShell, ComputedStyle* aStyle); @@ -8193,18 +8189,19 @@ static nsIFrame* FindPreviousNonWhitespa f = f->GetPrevSibling(); } while (f && IsWhitespaceFrame(f)); return f; } bool nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval( nsIFrame* aFrame) { #define TRACE(reason) \ - PROFILER_MARKER("MaybeRecreateContainerForFrameRemoval: " reason, LAYOUT, \ - {}, Tracing, "Layout") + PROFILER_TRACING_MARKER("Layout", \ + "MaybeRecreateContainerForFrameRemoval: " reason, \ + LAYOUT, TRACING_EVENT) MOZ_ASSERT(aFrame, "Must have a frame"); MOZ_ASSERT(aFrame->GetParent(), "Frame shouldn't be root"); MOZ_ASSERT(aFrame == aFrame->FirstContinuation(), "aFrame not the result of GetPrimaryFrame()?"); nsIFrame* inFlowFrame = aFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW) ? aFrame->GetPlaceholderFrame() : aFrame; @@ -10717,18 +10714,19 @@ bool nsCSSFrameConstructor::MaybeRecreat // -moz-column-span-wrapper frame, we need to reframe the multi-column // containing block. // // We can only be here if none of the new inserted nsIContent* nodes (via // ContentAppended or ContentRangeInserted) have column-span:all style, yet // some of them have column-span:all descendants. Sadly, there's no way to // detect this by checking FrameConstructionItems in WipeContainingBlock(). // Otherwise, we would have already wiped the multi-column containing block. - PROFILER_MARKER("Reframe multi-column after constructing frame list", - LAYOUT, {}, Tracing, "Layout"); + PROFILER_TRACING_MARKER( + "Layout", "Reframe multi-column after constructing frame list", LAYOUT, + TRACING_EVENT); // aFrameList can contain placeholder frames. In order to destroy their // associated out-of-flow frames properly, we need to manually flush all the // out-of-flow frames in aState to their container frames. aState.ProcessFrameInsertionsForAllLists(); aFrameList.DestroyFrames(); RecreateFramesForContent( GetMultiColumnContainingBlockFor(aParentFrame)->GetContent(), @@ -11043,19 +11041,19 @@ static bool IsSafeToAppendToIBSplitInlin aNextSibling = aParentFrame->GetNextSibling(); aParentFrame = aParentFrame->GetParent(); } while (IsInlineFrame(aParentFrame)); return true; } bool nsCSSFrameConstructor::WipeInsertionParent(nsContainerFrame* aFrame) { -#define TRACE(reason) \ - PROFILER_MARKER("WipeInsertionParent: " reason, LAYOUT, {}, Tracing, \ - "Layout"); +#define TRACE(reason) \ + PROFILER_TRACING_MARKER("Layout", "WipeInsertionParent: " reason, LAYOUT, \ + TRACING_EVENT) const LayoutFrameType frameType = aFrame->Type(); // FIXME(emilio): This looks terribly inefficient if you insert elements deep // in a MathML subtree. if (aFrame->IsFrameOfType(nsIFrame::eMathML)) { TRACE("MathML"); RecreateFramesForContent(aFrame->GetContent(), InsertionKind::Async); @@ -11105,19 +11103,19 @@ bool nsCSSFrameConstructor::WipeInsertio #undef TRACE } bool nsCSSFrameConstructor::WipeContainingBlock( nsFrameConstructorState& aState, nsIFrame* aContainingBlock, nsIFrame* aFrame, FrameConstructionItemList& aItems, bool aIsAppend, nsIFrame* aPrevSibling) { -#define TRACE(reason) \ - PROFILER_MARKER("WipeContainingBlock: " reason, LAYOUT, {}, Tracing, \ - "Layout"); +#define TRACE(reason) \ + PROFILER_TRACING_MARKER("Layout", "WipeContainingBlock: " reason, LAYOUT, \ + TRACING_EVENT) if (aItems.IsEmpty()) { return false; } // Before we go and append the frames, we must check for several // special situations.
--- a/layout/base/nsRefreshDriver.cpp +++ b/layout/base/nsRefreshDriver.cpp @@ -2061,18 +2061,19 @@ void nsRefreshDriver::Tick(VsyncId aId, // removed, because sometimes observers can be added and removed // often depending on what other things are going on and in that // situation we don't want to thrash our timer. So instead we // wait until we get a Notify() call when we have no observers // before stopping the timer. // On top level content pages keep the timer running initially so that we // paint the page soon enough. if (ShouldKeepTimerRunningWhileWaitingForFirstContentfulPaint()) { - PROFILER_MARKER("RefreshDriver waiting for first contentful paint", - GRAPHICS, {}, Tracing, "Paint"); + PROFILER_TRACING_MARKER( + "Paint", "RefreshDriver waiting for first contentful paint", GRAPHICS, + TRACING_EVENT); } else { StopTimer(); } return; } AUTO_PROFILER_LABEL("nsRefreshDriver::Tick", LAYOUT);
--- a/modules/libpref/Preferences.cpp +++ b/modules/libpref/Preferences.cpp @@ -9,16 +9,19 @@ #include <ctype.h> #include <stdlib.h> #include <string.h> #include "SharedPrefMap.h" #include "base/basictypes.h" #include "GeckoProfiler.h" +#ifdef MOZ_GECKO_PROFILER +# include "ProfilerMarkerPayload.h" +#endif #include "MainThreadUtils.h" #include "mozilla/ArenaAllocatorExtensions.h" #include "mozilla/ArenaAllocator.h" #include "mozilla/ArrayUtils.h" #include "mozilla/Attributes.h" #include "mozilla/Components.h" #include "mozilla/dom/PContent.h" #include "mozilla/HashFunctions.h" @@ -4272,106 +4275,51 @@ static nsCString PrefValueToString(const #endif // These preference getter wrappers allow us to look up the value for static // preferences based on their native types, rather than manually mapping them to // the appropriate Preferences::Get* functions. // We define these methods in a struct which is made friend of Preferences in // order to access private members. struct Internals { -#ifdef MOZ_GECKO_PROFILER - struct PreferenceReadMarker { - static constexpr Span<const char> MarkerTypeName() { - return MakeStringSpan("PreferenceRead"); - } - static void StreamJSONMarkerData( - baseprofiler::SpliceableJSONWriter& aWriter, - const ProfilerString8View& aPrefName, - const Maybe<PrefValueKind>& aPrefKind, PrefType aPrefType, - const ProfilerString8View& aPrefValue) { - aWriter.StringProperty("prefName", aPrefName); - aWriter.StringProperty("prefKind", PrefValueKindToString(aPrefKind)); - aWriter.StringProperty("prefType", PrefTypeToString(aPrefType)); - aWriter.StringProperty("prefValue", aPrefValue); - } - static MarkerSchema MarkerTypeDisplay() { - using MS = MarkerSchema; - MS schema{MS::Location::markerChart, MS::Location::markerTable}; - schema.AddKeyLabelFormat("prefName", "Name", MS::Format::string); - schema.AddKeyLabelFormat("prefKind", "Kind", MS::Format::string); - schema.AddKeyLabelFormat("prefType", "Type", MS::Format::string); - schema.AddKeyLabelFormat("prefValue", "Value", MS::Format::string); - return schema; - } - - private: - static Span<const char> PrefValueKindToString( - const Maybe<PrefValueKind>& aKind) { - if (aKind) { - return *aKind == PrefValueKind::Default ? MakeStringSpan("Default") - : MakeStringSpan("User"); - } - return "Shared"; - } - - static Span<const char> PrefTypeToString(PrefType type) { - switch (type) { - case PrefType::None: - return "None"; - case PrefType::Int: - return "Int"; - case PrefType::Bool: - return "Bool"; - case PrefType::String: - return "String"; - default: - MOZ_ASSERT_UNREACHABLE("Unknown preference type."); - } - } - }; -#endif // MOZ_GECKO_PROFILER - template <typename T> static nsresult GetPrefValue(const char* aPrefName, T&& aResult, PrefValueKind aKind) { nsresult rv = NS_ERROR_UNEXPECTED; NS_ENSURE_TRUE(Preferences::InitStaticMembers(), NS_ERROR_NOT_AVAILABLE); if (Maybe<PrefWrapper> pref = pref_Lookup(aPrefName)) { rv = pref->GetValue(aKind, std::forward<T>(aResult)); #ifdef MOZ_GECKO_PROFILER if (profiler_feature_active(ProfilerFeature::PreferenceReads)) { - profiler_add_marker( - "PreferenceRead", baseprofiler::category::OTHER_PreferenceRead, {}, - PreferenceReadMarker{}, - ProfilerString8View::WrapNullTerminatedString(aPrefName), - Some(aKind), pref->Type(), PrefValueToString(aResult)); + PROFILER_ADD_MARKER_WITH_PAYLOAD( + "PreferenceRead", OTHER_PreferenceRead, PrefMarkerPayload, + (aPrefName, Some(aKind), Some(pref->Type()), + PrefValueToString(aResult), TimeStamp::Now())); } #endif } return rv; } template <typename T> static nsresult GetSharedPrefValue(const char* aName, T* aResult) { nsresult rv = NS_ERROR_UNEXPECTED; if (Maybe<PrefWrapper> pref = pref_SharedLookup(aName)) { rv = pref->GetValue(PrefValueKind::User, aResult); #ifdef MOZ_GECKO_PROFILER if (profiler_feature_active(ProfilerFeature::PreferenceReads)) { - profiler_add_marker( - "PreferenceRead", baseprofiler::category::OTHER_PreferenceRead, {}, - PreferenceReadMarker{}, - ProfilerString8View::WrapNullTerminatedString(aName), - Nothing() /* indicates Shared */, pref->Type(), - PrefValueToString(aResult)); + PROFILER_ADD_MARKER_WITH_PAYLOAD( + "PreferenceRead", OTHER_PreferenceRead, PrefMarkerPayload, + (aName, Nothing() /* indicates Shared */, Some(pref->Type()), + PrefValueToString(aResult), TimeStamp::Now())); } #endif } return rv; } template <typename T>
--- a/mozglue/baseprofiler/core/ProfileBufferEntry.cpp +++ b/mozglue/baseprofiler/core/ProfileBufferEntry.cpp @@ -780,30 +780,23 @@ void ProfileBuffer::StreamMarkersToJSON( double aSinceTime, UniqueStacks& aUniqueStacks) const { mEntries.ReadEach([&](ProfileBufferEntryReader& aER) { auto type = static_cast<ProfileBufferEntry::Kind>( aER.ReadObject<ProfileBufferEntry::KindUnderlyingType>()); MOZ_ASSERT(static_cast<ProfileBufferEntry::KindUnderlyingType>(type) < static_cast<ProfileBufferEntry::KindUnderlyingType>( ProfileBufferEntry::Kind::MODERN_LIMIT)); - bool entryWasFullyRead = false; - - if (type == ProfileBufferEntry::Kind::Marker) { - entryWasFullyRead = ::mozilla::base_profiler_markers_detail:: - DeserializeAfterKindAndStream( - aER, aWriter, aThreadId, - [&](ProfileChunkedBuffer& aChunkedBuffer) { - ProfilerBacktrace backtrace("", &aChunkedBuffer); - backtrace.StreamJSON(aWriter, TimeStamp::ProcessCreation(), - aUniqueStacks); - }); - } - - if (!entryWasFullyRead) { + if (type != ProfileBufferEntry::Kind::Marker || + !::mozilla::base_profiler_markers_detail::DeserializeAfterKindAndStream( + aER, aWriter, aThreadId, [&](ProfileChunkedBuffer& aChunkedBuffer) { + ProfilerBacktrace backtrace("", &aChunkedBuffer); + backtrace.StreamJSON(aWriter, TimeStamp::ProcessCreation(), + aUniqueStacks); + })) { // Not a marker, or marker for another thread. // We probably didn't read the whole entry, so we need to skip to the end. aER.SetRemainingBytes(0); } }); } void ProfileBuffer::StreamProfilerOverheadToJSON(
--- a/mozglue/baseprofiler/public/BaseProfilerMarkerTypes.h +++ b/mozglue/baseprofiler/public/BaseProfilerMarkerTypes.h @@ -24,17 +24,147 @@ // existing PROFILER_ADD_MARKER calls. See meta bug 1661394. #include "mozilla/BaseProfilerMarkers.h" #ifdef MOZ_GECKO_PROFILER namespace mozilla::baseprofiler::markers { -struct MediaSampleMarker { +struct Tracing { + static constexpr Span<const char> MarkerTypeName() { + return MakeStringSpan("tracing"); + } + static void StreamJSONMarkerData(SpliceableJSONWriter& aWriter, + const ProfilerString8View& aCategory) { + if (aCategory.Length() != 0) { + aWriter.StringProperty("category", aCategory); + } + } + static MarkerSchema MarkerTypeDisplay() { + using MS = MarkerSchema; + MS schema{MS::Location::markerChart, MS::Location::markerTable, + MS::Location::timelineOverview}; + schema.AddKeyLabelFormat("category", "Type", MS::Format::string); + return schema; + } +}; + +struct UserTimingMark { + static constexpr Span<const char> MarkerTypeName() { + return MakeStringSpan("UserTimingMark"); + } + static void StreamJSONMarkerData(SpliceableJSONWriter& aWriter, + const ProfilerString8View& aName) { + aWriter.StringProperty("name", aName); + aWriter.StringProperty("entryType", "mark"); + aWriter.NullProperty("startMark"); + aWriter.NullProperty("endMark"); + } + static MarkerSchema MarkerTypeDisplay() { + using MS = MarkerSchema; + MS schema{MS::Location::markerChart, MS::Location::markerTable}; + schema.SetAllLabels("{marker.data.name}"); + schema.AddStaticLabelValue("Marker", "UserTiming"); + schema.AddKeyLabelFormat("entryType", "Entry Type", MS::Format::string); + schema.AddKeyLabelFormat("name", "Name", MS::Format::string); + schema.AddStaticLabelValue( + "Description", + "UserTimingMark is created using the DOM API performance.mark()."); + return schema; + } +}; + +struct UserTimingMeasure { + static constexpr Span<const char> MarkerTypeName() { + return MakeStringSpan("UserTimingMeasure"); + } + static void StreamJSONMarkerData(SpliceableJSONWriter& aWriter, + const ProfilerString8View& aName, + const Maybe<ProfilerString8View>& aStartMark, + const Maybe<ProfilerString8View>& aEndMark) { + aWriter.StringProperty("name", aName); + aWriter.StringProperty("entryType", "measure"); + + if (aStartMark.isSome()) { + aWriter.StringProperty("startMark", *aStartMark); + } else { + aWriter.NullProperty("startMark"); + } + if (aEndMark.isSome()) { + aWriter.StringProperty("endMark", *aEndMark); + } else { + aWriter.NullProperty("endMark"); + } + } + static MarkerSchema MarkerTypeDisplay() { + using MS = MarkerSchema; + MS schema{MS::Location::markerChart, MS::Location::markerTable}; + schema.SetAllLabels("{marker.data.name}"); + schema.AddStaticLabelValue("Marker", "UserTiming"); + schema.AddKeyLabelFormat("entryType", "Entry Type", MS::Format::string); + schema.AddKeyLabelFormat("name", "Name", MS::Format::string); + schema.AddKeyLabelFormat("startMark", "Start Mark", MS::Format::string); + schema.AddKeyLabelFormat("endMark", "End Mark", MS::Format::string); + schema.AddStaticLabelValue("Description", + "UserTimingMeasure is created using the DOM API " + "performance.measure()."); + return schema; + } +}; + +struct Hang { + static constexpr Span<const char> MarkerTypeName() { + return MakeStringSpan("BHR-detected hang"); + } + static void StreamJSONMarkerData(SpliceableJSONWriter& aWriter) {} + static MarkerSchema MarkerTypeDisplay() { + using MS = MarkerSchema; + MS schema{MS::Location::markerChart, MS::Location::markerTable, + MS::Location::timelineOverview}; + return schema; + } +}; + +struct LongTask { + static constexpr Span<const char> MarkerTypeName() { + return MakeStringSpan("MainThreadLongTask"); + } + static void StreamJSONMarkerData(SpliceableJSONWriter& aWriter) { + aWriter.StringProperty("category", "LongTask"); + } + static MarkerSchema MarkerTypeDisplay() { + using MS = MarkerSchema; + MS schema{MS::Location::markerChart, MS::Location::markerTable}; + schema.AddKeyLabelFormat("category", "Type", MS::Format::string); + return schema; + } +}; + +struct Log { + static constexpr Span<const char> MarkerTypeName() { + return MakeStringSpan("Log"); + } + static void StreamJSONMarkerData(SpliceableJSONWriter& aWriter, + const ProfilerString8View& aModule, + const ProfilerString8View& aText) { + aWriter.StringProperty("module", aModule); + aWriter.StringProperty("name", aText); + } + static MarkerSchema MarkerTypeDisplay() { + using MS = MarkerSchema; + MS schema{MS::Location::markerTable}; + schema.SetTableLabel("({marker.data.module}) {marker.data.name}"); + schema.AddKeyLabelFormat("module", "Module", MS::Format::string); + schema.AddKeyLabelFormat("name", "Name", MS::Format::string); + return schema; + } +}; + +struct MediaSample { static constexpr Span<const char> MarkerTypeName() { return MakeStringSpan("MediaSample"); } static void StreamJSONMarkerData(SpliceableJSONWriter& aWriter, int64_t aSampleStartTimeUs, int64_t aSampleEndTimeUs) { aWriter.IntProperty("sampleStartTimeUs", aSampleStartTimeUs); aWriter.IntProperty("sampleEndTimeUs", aSampleEndTimeUs); @@ -45,25 +175,13 @@ struct MediaSampleMarker { schema.AddKeyLabelFormat("sampleStartTimeUs", "Sample start time", MS::Format::microseconds); schema.AddKeyLabelFormat("sampleEndTimeUs", "Sample end time", MS::Format::microseconds); return schema; } }; -struct ContentBuildMarker { - static constexpr Span<const char> MarkerTypeName() { - return MakeStringSpan("CONTENT_FULL_PAINT_TIME"); - } - static void StreamJSONMarkerData(SpliceableJSONWriter& aWriter) {} - static MarkerSchema MarkerTypeDisplay() { - using MS = MarkerSchema; - MS schema{MS::Location::markerChart, MS::Location::markerTable}; - return schema; - } -}; - } // namespace mozilla::baseprofiler::markers #endif // MOZ_GECKO_PROFILER #endif // BaseProfilerMarkerTypes_h
--- a/mozglue/baseprofiler/public/BaseProfilerMarkers.h +++ b/mozglue/baseprofiler/public/BaseProfilerMarkers.h @@ -155,35 +155,16 @@ struct Text { using MS = MarkerSchema; MS schema{MS::Location::markerChart, MS::Location::markerTable}; schema.SetChartLabel("{marker.name} - {marker.data.name}"); schema.SetTableLabel("{marker.name} - {marker.data.name}"); schema.AddKeyLabelFormat("name", "Details", MarkerSchema::Format::string); return schema; } }; - -struct Tracing { - static constexpr Span<const char> MarkerTypeName() { - return MakeStringSpan("tracing"); - } - static void StreamJSONMarkerData(SpliceableJSONWriter& aWriter, - const ProfilerString8View& aCategory) { - if (aCategory.Length() != 0) { - aWriter.StringProperty("category", aCategory); - } - } - static MarkerSchema MarkerTypeDisplay() { - using MS = MarkerSchema; - MS schema{MS::Location::markerChart, MS::Location::markerTable, - MS::Location::timelineOverview}; - schema.AddKeyLabelFormat("category", "Type", MS::Format::string); - return schema; - } -}; } // namespace mozilla::baseprofiler::markers // Add a text marker. This macro is safe to use even if MOZ_GECKO_PROFILER is // not #defined. # define BASE_PROFILER_MARKER_TEXT(markerName, categoryName, options, text) \ do { \ AUTO_PROFILER_STATS(BASE_PROFILER_MARKER_TEXT); \ ::mozilla::baseprofiler::AddMarker( \
--- a/mozglue/baseprofiler/public/BaseProfilerMarkersPrerequisites.h +++ b/mozglue/baseprofiler/public/BaseProfilerMarkersPrerequisites.h @@ -124,30 +124,29 @@ class MOZ_STACK_CLASS ProfilerStringView // Implicit constructor from an expiring string_view. We assume that the // pointed-at string will outlive this ProfilerStringView. constexpr MOZ_IMPLICIT ProfilerStringView( std::basic_string_view<CHAR>&& aStringView) : ProfilerStringView(aStringView.data(), aStringView.length(), Ownership::Reference) {} // Implicit constructor from std::string. - constexpr MOZ_IMPLICIT ProfilerStringView( - const std::basic_string<CHAR>& aString) + constexpr MOZ_IMPLICIT ProfilerStringView(const std::string& aString) : ProfilerStringView(aString.data(), aString.length(), Ownership::Reference) {} // Construction from a raw pointer to a null-terminated string. // This is a named class-static function to make it more obvious where work is // being done (to determine the string length), and encourage users to instead // provide a length, if already known. // TODO: Find callers and convert them to constructor instead if possible. static constexpr ProfilerStringView WrapNullTerminatedString( const CHAR* aString) { return ProfilerStringView( - aString, aString ? std::char_traits<CHAR>::length(aString) : 0, + aString, aString ? std::char_traits<char>::length(aString) : 0, Ownership::Reference); } // Implicit constructor for an object with member functions `Data()` // `Length()`, and `IsLiteral()`, common in xpcom strings. template < typename String, typename DataReturnType = decltype(std::declval<const String>().Data()), @@ -178,18 +177,18 @@ class MOZ_STACK_CLASS ProfilerStringView [[nodiscard]] constexpr bool IsLiteral() const { return mOwnership == Ownership::Literal; } [[nodiscard]] constexpr bool IsReference() const { return mOwnership == Ownership::Reference; } // No `IsOwned...()` because it's a secret, only used internally! - [[nodiscard]] operator Span<const CHAR>() const { - return Span<const CHAR>(Data(), Length()); + [[nodiscard]] operator Span<const char>() const { + return Span<const char>(Data(), Length()); } private: enum class Ownership { Literal, Reference, OwnedThroughStringView }; // Allow deserializer to store anything here. friend ProfileBufferEntryReader::Deserializer<ProfilerStringView>;
--- a/mozglue/baseprofiler/public/ProfileBufferEntryKinds.h +++ b/mozglue/baseprofiler/public/ProfileBufferEntryKinds.h @@ -65,18 +65,21 @@ enum class ProfileBufferEntryKind : Prof #undef KIND // Any value under `LEGACY_LIMIT` represents a `ProfileBufferEntry`. LEGACY_LIMIT, // Any value starting here does *not* represent a `ProfileBufferEntry` and // requires separate decoding and handling. - // Markers and their data. - Marker = LEGACY_LIMIT, + // Marker data, including payload. + MarkerData = LEGACY_LIMIT, + + // Markers from 2.0 specs. + Marker, // Optional between TimeBeforeCompactStack and CompactStack. UnresponsiveDurationMs, // Collection of legacy stack entries, must follow a ThreadId and // TimeBeforeCompactStack (which are not included in the CompactStack; // TimeBeforeCompactStack is equivalent to Time, but indicates that a // CompactStack follows shortly afterwards).
--- a/mozglue/baseprofiler/public/ProfileBufferEntrySerialization.h +++ b/mozglue/baseprofiler/public/ProfileBufferEntrySerialization.h @@ -744,25 +744,24 @@ struct ProfileBufferEntryWriter::Seriali aEW.WriteBytes(&aWrapper.mRawPointer, sizeof(aWrapper.mRawPointer)); } }; // Usage: `aER.ReadObject<Foo*>;` or `Foo* p; aER.ReadIntoObject(p);`, no // wrapper necessary. template <typename T> struct ProfileBufferEntryReader::Deserializer<ProfileBufferRawPointer<T>> { - static void ReadInto(ProfileBufferEntryReader& aER, - ProfileBufferRawPointer<T>& aPtr) { - aER.ReadBytes(&aPtr.mRawPointer, sizeof(aPtr)); + static void ReadInto(ProfileBufferEntryReader& aER, T*& aPtr) { + aER.ReadBytes(&aPtr, sizeof(aPtr)); } - static ProfileBufferRawPointer<T> Read(ProfileBufferEntryReader& aER) { - ProfileBufferRawPointer<T> rawPointer; - ReadInto(aER, rawPointer); - return rawPointer; + static T* Read(ProfileBufferEntryReader& aER) { + T* ptr; + ReadInto(aER, ptr); + return ptr; } }; // ---------------------------------------------------------------------------- // std::string contents // std::string contents are serialized as the number of characters (encoded as // ULEB128) and all the characters in the string. The terminal '\0' is omitted.
--- a/mozglue/tests/TestBaseProfiler.cpp +++ b/mozglue/tests/TestBaseProfiler.cpp @@ -3009,237 +3009,207 @@ void TestLiteralEmptyStringView() { static_assert(mozilla::LiteralEmptyStringView<char16_t>() == std::basic_string_view<char16_t>(u"")); static_assert(!!mozilla::LiteralEmptyStringView<char16_t>().data()); static_assert(mozilla::LiteralEmptyStringView<char16_t>().length() == 0); printf("TestLiteralEmptyStringView done\n"); } -template <typename CHAR> void TestProfilerStringView() { - if constexpr (std::is_same_v<CHAR, char>) { - printf("TestProfilerStringView<char>...\n"); - } else if constexpr (std::is_same_v<CHAR, char16_t>) { - printf("TestProfilerStringView<char16_t>...\n"); - } else { - MOZ_RELEASE_ASSERT(false, - "TestProfilerStringView only handles char and char16_t"); - } + printf("TestProfilerStringView...\n"); // Used to verify implicit constructions, as this will normally be used in // function parameters. - auto BSV = [](mozilla::ProfilerStringView<CHAR>&& aBSV) { - return std::move(aBSV); - }; - - // These look like string literals, as expected by some string constructors. - const CHAR empty[0 + 1] = {CHAR('\0')}; - const CHAR hi[2 + 1] = { - CHAR('h'), - CHAR('i'), - CHAR('\0'), + auto BS8V = [](mozilla::ProfilerString8View&& aBS8V) { + return std::move(aBS8V); }; // Literal empty string. - MOZ_RELEASE_ASSERT(BSV(empty).Data()); - MOZ_RELEASE_ASSERT(BSV(empty).Data()[0] == CHAR('\0')); - MOZ_RELEASE_ASSERT(BSV(empty).Length() == 0); - MOZ_RELEASE_ASSERT(BSV(empty).IsLiteral()); - MOZ_RELEASE_ASSERT(!BSV(empty).IsReference()); + MOZ_RELEASE_ASSERT(BS8V("").Data()); + MOZ_RELEASE_ASSERT(BS8V("").Data()[0] == '\0'); + MOZ_RELEASE_ASSERT(BS8V("").Length() == 0); + MOZ_RELEASE_ASSERT(BS8V("").IsLiteral()); + MOZ_RELEASE_ASSERT(!BS8V("").IsReference()); // Literal non-empty string. - MOZ_RELEASE_ASSERT(BSV(hi).Data()); - MOZ_RELEASE_ASSERT(BSV(hi).Data()[0] == CHAR('h')); - MOZ_RELEASE_ASSERT(BSV(hi).Data()[1] == CHAR('i')); - MOZ_RELEASE_ASSERT(BSV(hi).Data()[2] == CHAR('\0')); - MOZ_RELEASE_ASSERT(BSV(hi).Length() == 2); - MOZ_RELEASE_ASSERT(BSV(hi).IsLiteral()); - MOZ_RELEASE_ASSERT(!BSV(hi).IsReference()); + MOZ_RELEASE_ASSERT(BS8V("hi").Data()); + MOZ_RELEASE_ASSERT(BS8V("hi").Data()[0] == 'h'); + MOZ_RELEASE_ASSERT(BS8V("hi").Data()[1] == 'i'); + MOZ_RELEASE_ASSERT(BS8V("hi").Data()[2] == '\0'); + MOZ_RELEASE_ASSERT(BS8V("hi").Length() == 2); + MOZ_RELEASE_ASSERT(BS8V("hi").IsLiteral()); + MOZ_RELEASE_ASSERT(!BS8V("hi").IsReference()); // std::string_view to a literal empty string. - MOZ_RELEASE_ASSERT(BSV(std::basic_string_view<CHAR>(empty)).Data()); - MOZ_RELEASE_ASSERT(BSV(std::basic_string_view<CHAR>(empty)).Data()[0] == - CHAR('\0')); - MOZ_RELEASE_ASSERT(BSV(std::basic_string_view<CHAR>(empty)).Length() == 0); - MOZ_RELEASE_ASSERT(!BSV(std::basic_string_view<CHAR>(empty)).IsLiteral()); - MOZ_RELEASE_ASSERT(BSV(std::basic_string_view<CHAR>(empty)).IsReference()); + MOZ_RELEASE_ASSERT(BS8V(std::string_view("")).Data()); + MOZ_RELEASE_ASSERT(BS8V(std::string_view("")).Data()[0] == '\0'); + MOZ_RELEASE_ASSERT(BS8V(std::string_view("")).Length() == 0); + MOZ_RELEASE_ASSERT(!BS8V(std::string_view("")).IsLiteral()); + MOZ_RELEASE_ASSERT(BS8V(std::string_view("")).IsReference()); // std::string_view to a literal non-empty string. - MOZ_RELEASE_ASSERT(BSV(std::basic_string_view<CHAR>(hi)).Data()); - MOZ_RELEASE_ASSERT(BSV(std::basic_string_view<CHAR>(hi)).Data()[0] == - CHAR('h')); - MOZ_RELEASE_ASSERT(BSV(std::basic_string_view<CHAR>(hi)).Data()[1] == - CHAR('i')); - MOZ_RELEASE_ASSERT(BSV(std::basic_string_view<CHAR>(hi)).Data()[2] == - CHAR('\0')); - MOZ_RELEASE_ASSERT(BSV(std::basic_string_view<CHAR>(hi)).Length() == 2); - MOZ_RELEASE_ASSERT(!BSV(std::basic_string_view<CHAR>(hi)).IsLiteral()); - MOZ_RELEASE_ASSERT(BSV(std::basic_string_view<CHAR>(hi)).IsReference()); + MOZ_RELEASE_ASSERT(BS8V(std::string_view("hi")).Data()); + MOZ_RELEASE_ASSERT(BS8V(std::string_view("hi")).Data()[0] == 'h'); + MOZ_RELEASE_ASSERT(BS8V(std::string_view("hi")).Data()[1] == 'i'); + MOZ_RELEASE_ASSERT(BS8V(std::string_view("hi")).Data()[2] == '\0'); + MOZ_RELEASE_ASSERT(BS8V(std::string_view("hi")).Length() == 2); + MOZ_RELEASE_ASSERT(!BS8V(std::string_view("hi")).IsLiteral()); + MOZ_RELEASE_ASSERT(BS8V(std::string_view("hi")).IsReference()); // Default std::string_view points at nullptr, ProfilerStringView converts it // to the literal empty string. - MOZ_RELEASE_ASSERT(!std::basic_string_view<CHAR>().data()); - MOZ_RELEASE_ASSERT(BSV(std::basic_string_view<CHAR>()).Data()); - MOZ_RELEASE_ASSERT(BSV(std::basic_string_view<CHAR>()).Data()[0] == - CHAR('\0')); - MOZ_RELEASE_ASSERT(BSV(std::basic_string_view<CHAR>()).Length() == 0); - MOZ_RELEASE_ASSERT(BSV(std::basic_string_view<CHAR>()).IsLiteral()); - MOZ_RELEASE_ASSERT(!BSV(std::basic_string_view<CHAR>()).IsReference()); + MOZ_RELEASE_ASSERT(!std::string_view().data()); + MOZ_RELEASE_ASSERT(BS8V(std::string_view()).Data()); + MOZ_RELEASE_ASSERT(BS8V(std::string_view()).Data()[0] == '\0'); + MOZ_RELEASE_ASSERT(BS8V(std::string_view()).Length() == 0); + MOZ_RELEASE_ASSERT(BS8V(std::string_view()).IsLiteral()); + MOZ_RELEASE_ASSERT(!BS8V(std::string_view()).IsReference()); // std::string to a literal empty string. - MOZ_RELEASE_ASSERT(BSV(std::basic_string<CHAR>(empty)).Data()); - MOZ_RELEASE_ASSERT(BSV(std::basic_string<CHAR>(empty)).Data()[0] == - CHAR('\0')); - MOZ_RELEASE_ASSERT(BSV(std::basic_string<CHAR>(empty)).Length() == 0); - MOZ_RELEASE_ASSERT(!BSV(std::basic_string<CHAR>(empty)).IsLiteral()); - MOZ_RELEASE_ASSERT(BSV(std::basic_string<CHAR>(empty)).IsReference()); + MOZ_RELEASE_ASSERT(BS8V(std::string("")).Data()); + MOZ_RELEASE_ASSERT(BS8V(std::string("")).Data()[0] == '\0'); + MOZ_RELEASE_ASSERT(BS8V(std::string("")).Length() == 0); + MOZ_RELEASE_ASSERT(!BS8V(std::string("")).IsLiteral()); + MOZ_RELEASE_ASSERT(BS8V(std::string("")).IsReference()); // std::string to a literal non-empty string. - MOZ_RELEASE_ASSERT(BSV(std::basic_string<CHAR>(hi)).Data()); - MOZ_RELEASE_ASSERT(BSV(std::basic_string<CHAR>(hi)).Data()[0] == CHAR('h')); - MOZ_RELEASE_ASSERT(BSV(std::basic_string<CHAR>(hi)).Data()[1] == CHAR('i')); - MOZ_RELEASE_ASSERT(BSV(std::basic_string<CHAR>(hi)).Data()[2] == CHAR('\0')); - MOZ_RELEASE_ASSERT(BSV(std::basic_string<CHAR>(hi)).Length() == 2); - MOZ_RELEASE_ASSERT(!BSV(std::basic_string<CHAR>(hi)).IsLiteral()); - MOZ_RELEASE_ASSERT(BSV(std::basic_string<CHAR>(hi)).IsReference()); - - // Default std::string contains an empty null-terminated string. - MOZ_RELEASE_ASSERT(std::basic_string<CHAR>().data()); - MOZ_RELEASE_ASSERT(BSV(std::basic_string<CHAR>()).Data()); - MOZ_RELEASE_ASSERT(BSV(std::basic_string<CHAR>()).Data()[0] == CHAR('\0')); - MOZ_RELEASE_ASSERT(BSV(std::basic_string<CHAR>()).Length() == 0); - MOZ_RELEASE_ASSERT(!BSV(std::basic_string<CHAR>()).IsLiteral()); - MOZ_RELEASE_ASSERT(BSV(std::basic_string<CHAR>()).IsReference()); - - // Class that quacks like nsTString (with Data(), Length(), IsLiteral()), to - // check that ProfilerStringView can read from them. - class FakeNsTString { + MOZ_RELEASE_ASSERT(BS8V(std::string("hi")).Data()); + MOZ_RELEASE_ASSERT(BS8V(std::string("hi")).Data()[0] == 'h'); + MOZ_RELEASE_ASSERT(BS8V(std::string("hi")).Data()[1] == 'i'); + MOZ_RELEASE_ASSERT(BS8V(std::string("hi")).Data()[2] == '\0'); + MOZ_RELEASE_ASSERT(BS8V(std::string("hi")).Length() == 2); + MOZ_RELEASE_ASSERT(!BS8V(std::string("hi")).IsLiteral()); + MOZ_RELEASE_ASSERT(BS8V(std::string("hi")).IsReference()); + + // Default std::string_view contains an empty null-terminated string. + MOZ_RELEASE_ASSERT(std::string().data()); + MOZ_RELEASE_ASSERT(BS8V(std::string()).Data()); + MOZ_RELEASE_ASSERT(BS8V(std::string()).Data()[0] == '\0'); + MOZ_RELEASE_ASSERT(BS8V(std::string()).Length() == 0); + MOZ_RELEASE_ASSERT(!BS8V(std::string()).IsLiteral()); + MOZ_RELEASE_ASSERT(BS8V(std::string()).IsReference()); + + class FakeNsCString { public: - FakeNsTString(const CHAR* aData, size_t aLength, bool aIsLiteral) + FakeNsCString(const char* aData, size_t aLength, bool aIsLiteral) : mData(aData), mLength(aLength), mIsLiteral(aIsLiteral) {} - const CHAR* Data() const { return mData; } + const char* Data() const { return mData; } size_t Length() const { return mLength; } bool IsLiteral() const { return mIsLiteral; } private: - const CHAR* mData; + const char* mData; size_t mLength; bool mIsLiteral; }; - // FakeNsTString to nullptr. - MOZ_RELEASE_ASSERT(BSV(FakeNsTString(nullptr, 0, true)).Data()); - MOZ_RELEASE_ASSERT(BSV(FakeNsTString(nullptr, 0, true)).Data()[0] == - CHAR('\0')); - MOZ_RELEASE_ASSERT(BSV(FakeNsTString(nullptr, 0, true)).Length() == 0); - MOZ_RELEASE_ASSERT(BSV(FakeNsTString(nullptr, 0, true)).IsLiteral()); - MOZ_RELEASE_ASSERT(!BSV(FakeNsTString(nullptr, 0, true)).IsReference()); - - // FakeNsTString to a literal empty string. - MOZ_RELEASE_ASSERT(BSV(FakeNsTString(empty, 0, true)).Data()); - MOZ_RELEASE_ASSERT(BSV(FakeNsTString(empty, 0, true)).Data()[0] == - CHAR('\0')); - MOZ_RELEASE_ASSERT(BSV(FakeNsTString(empty, 0, true)).Length() == 0); - MOZ_RELEASE_ASSERT(BSV(FakeNsTString(empty, 0, true)).IsLiteral()); - MOZ_RELEASE_ASSERT(!BSV(FakeNsTString(empty, 0, true)).IsReference()); - - // FakeNsTString to a literal non-empty string. - MOZ_RELEASE_ASSERT(BSV(FakeNsTString(hi, 2, true)).Data()); - MOZ_RELEASE_ASSERT(BSV(FakeNsTString(hi, 2, true)).Data()[0] == CHAR('h')); - MOZ_RELEASE_ASSERT(BSV(FakeNsTString(hi, 2, true)).Data()[1] == CHAR('i')); - MOZ_RELEASE_ASSERT(BSV(FakeNsTString(hi, 2, true)).Data()[2] == CHAR('\0')); - MOZ_RELEASE_ASSERT(BSV(FakeNsTString(hi, 2, true)).Length() == 2); - MOZ_RELEASE_ASSERT(BSV(FakeNsTString(hi, 2, true)).IsLiteral()); - MOZ_RELEASE_ASSERT(!BSV(FakeNsTString(hi, 2, true)).IsReference()); - - // FakeNsTString to a non-literal non-empty string. - MOZ_RELEASE_ASSERT(BSV(FakeNsTString(hi, 2, false)).Data()); - MOZ_RELEASE_ASSERT(BSV(FakeNsTString(hi, 2, false)).Data()[0] == CHAR('h')); - MOZ_RELEASE_ASSERT(BSV(FakeNsTString(hi, 2, false)).Data()[1] == CHAR('i')); - MOZ_RELEASE_ASSERT(BSV(FakeNsTString(hi, 2, false)).Data()[2] == CHAR('\0')); - MOZ_RELEASE_ASSERT(BSV(FakeNsTString(hi, 2, false)).Length() == 2); - MOZ_RELEASE_ASSERT(!BSV(FakeNsTString(hi, 2, false)).IsLiteral()); - MOZ_RELEASE_ASSERT(BSV(FakeNsTString(hi, 2, false)).IsReference()); + // FakeNsCString to nullptr. + MOZ_RELEASE_ASSERT(BS8V(FakeNsCString(nullptr, 0, true)).Data()); + MOZ_RELEASE_ASSERT(BS8V(FakeNsCString(nullptr, 0, true)).Data()[0] == '\0'); + MOZ_RELEASE_ASSERT(BS8V(FakeNsCString(nullptr, 0, true)).Length() == 0); + MOZ_RELEASE_ASSERT(BS8V(FakeNsCString(nullptr, 0, true)).IsLiteral()); + MOZ_RELEASE_ASSERT(!BS8V(FakeNsCString(nullptr, 0, true)).IsReference()); + + // FakeNsCString to a literal empty string. + MOZ_RELEASE_ASSERT(BS8V(FakeNsCString("", 0, true)).Data()); + MOZ_RELEASE_ASSERT(BS8V(FakeNsCString("", 0, true)).Data()[0] == '\0'); + MOZ_RELEASE_ASSERT(BS8V(FakeNsCString("", 0, true)).Length() == 0); + MOZ_RELEASE_ASSERT(BS8V(FakeNsCString("", 0, true)).IsLiteral()); + MOZ_RELEASE_ASSERT(!BS8V(FakeNsCString("", 0, true)).IsReference()); + + // FakeNsCString to a literal non-empty string. + MOZ_RELEASE_ASSERT(BS8V(FakeNsCString("hi", 2, true)).Data()); + MOZ_RELEASE_ASSERT(BS8V(FakeNsCString("hi", 2, true)).Data()[0] == 'h'); + MOZ_RELEASE_ASSERT(BS8V(FakeNsCString("hi", 2, true)).Data()[1] == 'i'); + MOZ_RELEASE_ASSERT(BS8V(FakeNsCString("hi", 2, true)).Data()[2] == '\0'); + MOZ_RELEASE_ASSERT(BS8V(FakeNsCString("hi", 2, true)).Length() == 2); + MOZ_RELEASE_ASSERT(BS8V(FakeNsCString("hi", 2, true)).IsLiteral()); + MOZ_RELEASE_ASSERT(!BS8V(FakeNsCString("hi", 2, true)).IsReference()); + + // FakeNsCString to a non-literal non-empty string. + MOZ_RELEASE_ASSERT(BS8V(FakeNsCString("hi", 2, false)).Data()); + MOZ_RELEASE_ASSERT(BS8V(FakeNsCString("hi", 2, false)).Data()[0] == 'h'); + MOZ_RELEASE_ASSERT(BS8V(FakeNsCString("hi", 2, false)).Data()[1] == 'i'); + MOZ_RELEASE_ASSERT(BS8V(FakeNsCString("hi", 2, false)).Data()[2] == '\0'); + MOZ_RELEASE_ASSERT(BS8V(FakeNsCString("hi", 2, false)).Length() == 2); + MOZ_RELEASE_ASSERT(!BS8V(FakeNsCString("hi", 2, false)).IsLiteral()); + MOZ_RELEASE_ASSERT(BS8V(FakeNsCString("hi", 2, false)).IsReference()); // Serialization and deserialization (with ownership). constexpr size_t bufferMaxSize = 1024; constexpr ProfileChunkedBuffer::Length chunkMinSize = 128; ProfileBufferChunkManagerWithLocalLimit cm(bufferMaxSize, chunkMinSize); ProfileChunkedBuffer cb(ProfileChunkedBuffer::ThreadSafety::WithMutex, cm); // Literal string, serialized as raw pointer. - MOZ_RELEASE_ASSERT(cb.PutObject(BSV(hi))); + MOZ_RELEASE_ASSERT(cb.PutObject(BS8V("hi"))); { unsigned read = 0; - ProfilerStringView<CHAR> outerBSV; + ProfilerString8View outerBS8V; cb.ReadEach([&](ProfileBufferEntryReader& aER) { ++read; - auto bsv = aER.ReadObject<ProfilerStringView<CHAR>>(); - MOZ_RELEASE_ASSERT(bsv.Data()); - MOZ_RELEASE_ASSERT(bsv.Data()[0] == CHAR('h')); - MOZ_RELEASE_ASSERT(bsv.Data()[1] == CHAR('i')); - MOZ_RELEASE_ASSERT(bsv.Data()[2] == CHAR('\0')); - MOZ_RELEASE_ASSERT(bsv.Length() == 2); - MOZ_RELEASE_ASSERT(bsv.IsLiteral()); - MOZ_RELEASE_ASSERT(!bsv.IsReference()); - outerBSV = std::move(bsv); + auto bs8v = aER.ReadObject<ProfilerString8View>(); + MOZ_RELEASE_ASSERT(bs8v.Data()); + MOZ_RELEASE_ASSERT(bs8v.Data()[0] == 'h'); + MOZ_RELEASE_ASSERT(bs8v.Data()[1] == 'i'); + MOZ_RELEASE_ASSERT(bs8v.Data()[2] == '\0'); + MOZ_RELEASE_ASSERT(bs8v.Length() == 2); + MOZ_RELEASE_ASSERT(bs8v.IsLiteral()); + MOZ_RELEASE_ASSERT(!bs8v.IsReference()); + outerBS8V = std::move(bs8v); }); MOZ_RELEASE_ASSERT(read == 1); - MOZ_RELEASE_ASSERT(outerBSV.Data()); - MOZ_RELEASE_ASSERT(outerBSV.Data()[0] == CHAR('h')); - MOZ_RELEASE_ASSERT(outerBSV.Data()[1] == CHAR('i')); - MOZ_RELEASE_ASSERT(outerBSV.Data()[2] == CHAR('\0')); - MOZ_RELEASE_ASSERT(outerBSV.Length() == 2); - MOZ_RELEASE_ASSERT(outerBSV.IsLiteral()); - MOZ_RELEASE_ASSERT(!outerBSV.IsReference()); + MOZ_RELEASE_ASSERT(outerBS8V.Data()); + MOZ_RELEASE_ASSERT(outerBS8V.Data()[0] == 'h'); + MOZ_RELEASE_ASSERT(outerBS8V.Data()[1] == 'i'); + MOZ_RELEASE_ASSERT(outerBS8V.Data()[2] == '\0'); + MOZ_RELEASE_ASSERT(outerBS8V.Length() == 2); + MOZ_RELEASE_ASSERT(outerBS8V.IsLiteral()); + MOZ_RELEASE_ASSERT(!outerBS8V.IsReference()); } cb.Clear(); // Non-literal string, content is serialized. - std::basic_string<CHAR> hiString(hi); - MOZ_RELEASE_ASSERT(cb.PutObject(BSV(hiString))); + std::string hiString("hi"); + MOZ_RELEASE_ASSERT(cb.PutObject(BS8V(hiString))); { unsigned read = 0; - ProfilerStringView<CHAR> outerBSV; + ProfilerString8View outerBS8V; cb.ReadEach([&](ProfileBufferEntryReader& aER) { ++read; - auto bsv = aER.ReadObject<ProfilerStringView<CHAR>>(); - MOZ_RELEASE_ASSERT(bsv.Data()); - MOZ_RELEASE_ASSERT(bsv.Data() != hiString.data()); - MOZ_RELEASE_ASSERT(bsv.Data()[0] == CHAR('h')); - MOZ_RELEASE_ASSERT(bsv.Data()[1] == CHAR('i')); - MOZ_RELEASE_ASSERT(bsv.Data()[2] == CHAR('\0')); - MOZ_RELEASE_ASSERT(bsv.Length() == 2); + auto bs8v = aER.ReadObject<ProfilerString8View>(); + MOZ_RELEASE_ASSERT(bs8v.Data()); + MOZ_RELEASE_ASSERT(bs8v.Data() != hiString.data()); + MOZ_RELEASE_ASSERT(bs8v.Data()[0] == 'h'); + MOZ_RELEASE_ASSERT(bs8v.Data()[1] == 'i'); + MOZ_RELEASE_ASSERT(bs8v.Data()[2] == '\0'); + MOZ_RELEASE_ASSERT(bs8v.Length() == 2); // Special ownership case, neither a literal nor a reference! - MOZ_RELEASE_ASSERT(!bsv.IsLiteral()); - MOZ_RELEASE_ASSERT(!bsv.IsReference()); + MOZ_RELEASE_ASSERT(!bs8v.IsLiteral()); + MOZ_RELEASE_ASSERT(!bs8v.IsReference()); // Test move of ownership. - outerBSV = std::move(bsv); + outerBS8V = std::move(bs8v); // NOLINTNEXTLINE(bugprone-use-after-move, clang-analyzer-cplusplus.Move) - MOZ_RELEASE_ASSERT(bsv.Length() == 0); + MOZ_RELEASE_ASSERT(bs8v.Length() == 0); }); MOZ_RELEASE_ASSERT(read == 1); - MOZ_RELEASE_ASSERT(outerBSV.Data()); - MOZ_RELEASE_ASSERT(outerBSV.Data() != hiString.data()); - MOZ_RELEASE_ASSERT(outerBSV.Data()[0] == CHAR('h')); - MOZ_RELEASE_ASSERT(outerBSV.Data()[1] == CHAR('i')); - MOZ_RELEASE_ASSERT(outerBSV.Data()[2] == CHAR('\0')); - MOZ_RELEASE_ASSERT(outerBSV.Length() == 2); - MOZ_RELEASE_ASSERT(!outerBSV.IsLiteral()); - MOZ_RELEASE_ASSERT(!outerBSV.IsReference()); + MOZ_RELEASE_ASSERT(outerBS8V.Data()); + MOZ_RELEASE_ASSERT(outerBS8V.Data() != hiString.data()); + MOZ_RELEASE_ASSERT(outerBS8V.Data()[0] == 'h'); + MOZ_RELEASE_ASSERT(outerBS8V.Data()[1] == 'i'); + MOZ_RELEASE_ASSERT(outerBS8V.Data()[2] == '\0'); + MOZ_RELEASE_ASSERT(outerBS8V.Length() == 2); + MOZ_RELEASE_ASSERT(!outerBS8V.IsLiteral()); + MOZ_RELEASE_ASSERT(!outerBS8V.IsReference()); } - if constexpr (std::is_same_v<CHAR, char>) { - printf("TestProfilerStringView<char> done\n"); - } else if constexpr (std::is_same_v<CHAR, char16_t>) { - printf("TestProfilerStringView<char16_t> done\n"); - } + printf("TestProfilerStringView done\n"); } void TestProfilerDependencies() { TestPowerOfTwoMask(); TestPowerOfTwo(); TestLEB128(); TestChunk(); TestChunkManagerSingle(); @@ -3249,18 +3219,17 @@ void TestProfilerDependencies() { TestChunkedBuffer(); TestChunkedBufferSingle(); TestModuloBuffer(); TestBlocksRingBufferAPI(); TestBlocksRingBufferUnderlyingBufferChanges(); TestBlocksRingBufferThreading(); TestBlocksRingBufferSerialization(); TestLiteralEmptyStringView(); - TestProfilerStringView<char>(); - TestProfilerStringView<char16_t>(); + TestProfilerStringView(); } // Increase the depth, to a maximum (to avoid too-deep recursion). static constexpr size_t NextDepth(size_t aDepth) { constexpr size_t MAX_DEPTH = 128; return (aDepth < MAX_DEPTH) ? (aDepth + 1) : aDepth; } @@ -3466,22 +3435,43 @@ void TestProfiler() { mozilla::baseprofiler::category::OTHER, MarkerInnerWindowId(123), ::mozilla::baseprofiler::markers::NoPayload{})); MOZ_RELEASE_ASSERT(baseprofiler::AddMarker( "tracing", mozilla::baseprofiler::category::OTHER, {}, mozilla::baseprofiler::markers::Tracing{}, "category")); MOZ_RELEASE_ASSERT(baseprofiler::AddMarker( + "mark", mozilla::baseprofiler::category::OTHER, {}, + mozilla::baseprofiler::markers::UserTimingMark{}, "mark name")); + + MOZ_RELEASE_ASSERT(baseprofiler::AddMarker( + "measure", mozilla::baseprofiler::category::OTHER, {}, + mozilla::baseprofiler::markers::UserTimingMeasure{}, "measure name", + Some(ProfilerString8View("start")), Some(ProfilerString8View("end")))); + + MOZ_RELEASE_ASSERT( + baseprofiler::AddMarker("hang", mozilla::baseprofiler::category::OTHER, + {}, mozilla::baseprofiler::markers::Hang{})); + + MOZ_RELEASE_ASSERT(baseprofiler::AddMarker( + "longtask", mozilla::baseprofiler::category::OTHER, {}, + mozilla::baseprofiler::markers::LongTask{})); + + MOZ_RELEASE_ASSERT(baseprofiler::AddMarker( "text", mozilla::baseprofiler::category::OTHER, {}, mozilla::baseprofiler::markers::Text{}, "text text")); MOZ_RELEASE_ASSERT(baseprofiler::AddMarker( + "log", mozilla::baseprofiler::category::OTHER, {}, + mozilla::baseprofiler::markers::Log{}, "module", "text")); + + MOZ_RELEASE_ASSERT(baseprofiler::AddMarker( "media sample", mozilla::baseprofiler::category::OTHER, {}, - mozilla::baseprofiler::markers::MediaSampleMarker{}, 123, 456)); + mozilla::baseprofiler::markers::MediaSample{}, 123, 456)); printf("Sleep 1s...\n"); { AUTO_BASE_PROFILER_THREAD_SLEEP; SleepMilli(1000); } printf("baseprofiler_pause()...\n"); @@ -3527,17 +3517,26 @@ void TestProfiler() { std::string_view profileSV = profile.get(); constexpr const auto svnpos = std::string_view::npos; // TODO: Properly parse profile and check fields. // Check for some expected marker schema JSON output. MOZ_RELEASE_ASSERT(profileSV.find("\"markerSchema\": [") != svnpos); MOZ_RELEASE_ASSERT(profileSV.find("\"name\": \"Text\",") != svnpos); MOZ_RELEASE_ASSERT(profileSV.find("\"name\": \"tracing\",") != svnpos); + MOZ_RELEASE_ASSERT(profileSV.find("\"name\": \"UserTimingMark\",") != + svnpos); + MOZ_RELEASE_ASSERT(profileSV.find("\"name\": \"UserTimingMeasure\",") != + svnpos); + MOZ_RELEASE_ASSERT(profileSV.find("\"name\": \"BHR-detected hang\",") != + svnpos); + MOZ_RELEASE_ASSERT(profileSV.find("\"name\": \"Log\",") != svnpos); MOZ_RELEASE_ASSERT(profileSV.find("\"name\": \"MediaSample\",") != svnpos); + MOZ_RELEASE_ASSERT(profileSV.find("\"name\": \"MainThreadLongTask\",") != + svnpos); MOZ_RELEASE_ASSERT(profileSV.find("\"display\": [") != svnpos); MOZ_RELEASE_ASSERT(profileSV.find("\"marker-chart\"") != svnpos); MOZ_RELEASE_ASSERT(profileSV.find("\"marker-table\"") != svnpos); MOZ_RELEASE_ASSERT(profileSV.find("\"format\": \"string\"") != svnpos); // TODO: Add more checks for what's expected in the profile. Some of them // are done in gtest's. printf("baseprofiler_save_profile_to_file()...\n"); @@ -4031,22 +4030,46 @@ void TestPredefinedMarkers() { mozilla::ProfileChunkedBuffer::ThreadSafety::WithoutMutex, chunkManager); MOZ_RELEASE_ASSERT(mozilla::baseprofiler::AddMarkerToBuffer( buffer, std::string_view("tracing"), mozilla::baseprofiler::category::OTHER, {}, mozilla::baseprofiler::markers::Tracing{}, "category")); MOZ_RELEASE_ASSERT(mozilla::baseprofiler::AddMarkerToBuffer( + buffer, std::string_view("mark"), mozilla::baseprofiler::category::OTHER, + {}, mozilla::baseprofiler::markers::UserTimingMark{}, "mark name")); + + MOZ_RELEASE_ASSERT(mozilla::baseprofiler::AddMarkerToBuffer( + buffer, std::string_view("measure"), + mozilla::baseprofiler::category::OTHER, {}, + mozilla::baseprofiler::markers::UserTimingMeasure{}, "measure name ", + mozilla::Some(mozilla::ProfilerString8View(" start ")), + mozilla::Some(mozilla::ProfilerString8View("end")))); + + MOZ_RELEASE_ASSERT(mozilla::baseprofiler::AddMarkerToBuffer( + buffer, std::string_view("hang"), mozilla::baseprofiler::category::OTHER, + {}, mozilla::baseprofiler::markers::Hang{})); + + MOZ_RELEASE_ASSERT(mozilla::baseprofiler::AddMarkerToBuffer( + buffer, std::string_view("long task"), + mozilla::baseprofiler::category::OTHER, {}, + mozilla::baseprofiler::markers::LongTask{})); + + MOZ_RELEASE_ASSERT(mozilla::baseprofiler::AddMarkerToBuffer( buffer, std::string_view("text"), mozilla::baseprofiler::category::OTHER, {}, mozilla::baseprofiler::markers::Text{}, "text text")); MOZ_RELEASE_ASSERT(mozilla::baseprofiler::AddMarkerToBuffer( + buffer, std::string_view("log"), mozilla::baseprofiler::category::OTHER, + {}, mozilla::baseprofiler::markers::Log{}, "module", "text")); + + MOZ_RELEASE_ASSERT(mozilla::baseprofiler::AddMarkerToBuffer( buffer, std::string_view("media"), mozilla::baseprofiler::category::OTHER, - {}, mozilla::baseprofiler::markers::MediaSampleMarker{}, 123, 456)); + {}, mozilla::baseprofiler::markers::MediaSample{}, 123, 456)); # ifdef DEBUG buffer.Dump(); # endif PrintMarkers(buffer); printf("TestPredefinedMarkers done\n");
--- a/netwerk/protocol/http/HttpBaseChannel.h +++ b/netwerk/protocol/http/HttpBaseChannel.h @@ -243,17 +243,17 @@ class HttpBaseChannel : public nsHashPro NS_IMETHOD GetTopLevelOuterContentWindowId(uint64_t* aWindowId) override; NS_IMETHOD SetTopLevelOuterContentWindowId(uint64_t aWindowId) override; NS_IMETHOD GetFlashPluginState( nsIHttpChannel::FlashPluginState* aState) override; using nsIClassifiedChannel::IsThirdPartyTrackingResource; - virtual void SetSource(UniquePtr<ProfileChunkedBuffer> aSource) override { + virtual void SetSource(UniqueProfilerBacktrace aSource) override { mSource = std::move(aSource); } // nsIHttpChannelInternal NS_IMETHOD GetDocumentURI(nsIURI** aDocumentURI) override; NS_IMETHOD SetDocumentURI(nsIURI* aDocumentURI) override; NS_IMETHOD GetRequestVersion(uint32_t* major, uint32_t* minor) override; NS_IMETHOD GetResponseVersion(uint32_t* major, uint32_t* minor) override; @@ -751,17 +751,17 @@ class HttpBaseChannel : public nsHashPro // Use Release-Acquire ordering to ensure the OMT ODA is ignored while channel // is canceled on main thread. Atomic<bool, ReleaseAcquire> mCanceled; Atomic<uint32_t, ReleaseAcquire> mFirstPartyClassificationFlags; Atomic<uint32_t, ReleaseAcquire> mThirdPartyClassificationFlags; Atomic<uint32_t, ReleaseAcquire> mFlashPluginState; - UniquePtr<ProfileChunkedBuffer> mSource; + UniqueProfilerBacktrace mSource; uint32_t mLoadFlags; uint32_t mCaps; uint32_t mClassOfService; uint32_t mUpgradeToSecure : 1; uint32_t mApplyConversion : 1; // Set to true if DoApplyContentConversions has been applied to
--- a/netwerk/protocol/http/HttpChannelChild.cpp +++ b/netwerk/protocol/http/HttpChannelChild.cpp @@ -62,16 +62,20 @@ #include "nsApplicationCache.h" #include "ClassifierDummyChannel.h" #include "nsIOService.h" #ifdef MOZ_TASK_TRACER # include "GeckoTaskTracer.h" #endif +#ifdef MOZ_GECKO_PROFILER +# include "ProfilerMarkerPayload.h" +#endif + #include <functional> using namespace mozilla::dom; using namespace mozilla::ipc; namespace mozilla { namespace net {
--- a/netwerk/protocol/http/nsHttpChannel.cpp +++ b/netwerk/protocol/http/nsHttpChannel.cpp @@ -130,16 +130,20 @@ #include "js/Conversions.h" #include "mozilla/dom/SecFetch.h" #include "mozilla/net/TRRService.h" #ifdef MOZ_TASK_TRACER # include "GeckoTaskTracer.h" #endif +#ifdef MOZ_GECKO_PROFILER +# include "ProfilerMarkerPayload.h" +#endif + namespace mozilla { using namespace dom; namespace net { namespace {
--- a/netwerk/protocol/http/nsIHttpChannel.idl +++ b/netwerk/protocol/http/nsIHttpChannel.idl @@ -496,11 +496,11 @@ interface nsIHttpChannel : nsIIdentChann void logBlockedCORSRequest(in AString aMessage, in ACString aCategory); void logMimeTypeMismatch(in ACString aMessageName, in boolean aWarning, in AString aURL, in AString aContentType); %{ C++ - virtual void SetSource(mozilla::UniquePtr<mozilla::ProfileChunkedBuffer> aSource) {} + virtual void SetSource(UniqueProfilerBacktrace aSource) {} %} };
--- a/toolkit/components/backgroundhangmonitor/BackgroundHangMonitor.cpp +++ b/toolkit/components/backgroundhangmonitor/BackgroundHangMonitor.cpp @@ -26,16 +26,20 @@ #include "nsAppDirectoryServiceDefs.h" #include "nsIObserver.h" #include "nsIObserverService.h" #include "nsThreadUtils.h" #include "nsXULAppAPI.h" #include "prinrval.h" #include "prthread.h" +#ifdef MOZ_GECKO_PROFILER +# include "ProfilerMarkerPayload.h" +#endif + #include <algorithm> // Activate BHR only for one every BHR_BETA_MOD users. // We're doing experimentation with collecting a lot more data from BHR, and // don't want to enable it for beta users at the moment. We can scale this up in // the future. #define BHR_BETA_MOD INT32_MAX; @@ -525,36 +529,22 @@ void BackgroundHangThread::ReportHang(Ti RefPtr<nsHangDetails> hd = new nsHangDetails(std::move(hangDetails), persistedToDisk); hd->Submit(); } // If the profiler is enabled, add a marker. #ifdef MOZ_GECKO_PROFILER if (profiler_can_accept_markers()) { - struct HangMarker { - static constexpr Span<const char> MarkerTypeName() { - return MakeStringSpan("BHR-detected hang"); - } - static void StreamJSONMarkerData( - baseprofiler::SpliceableJSONWriter& aWriter) {} - static MarkerSchema MarkerTypeDisplay() { - using MS = MarkerSchema; - MS schema{MS::Location::markerChart, MS::Location::markerTable, - MS::Location::timelineOverview}; - return schema; - } - }; - - const TimeStamp endTime = TimeStamp::NowUnfuzzed(); - const TimeStamp startTime = endTime - aHangTime; - profiler_add_marker("BHR-detected hang", geckoprofiler::category::OTHER, - {MarkerThreadId(mStackHelper.GetThreadId()), - MarkerTiming::Interval(startTime, endTime)}, - HangMarker{}); + TimeStamp endTime = TimeStamp::Now(); + TimeStamp startTime = endTime - aHangTime; + AUTO_PROFILER_STATS(add_marker_with_HangMarkerPayload); + profiler_add_marker_for_thread( + mStackHelper.GetThreadId(), JS::ProfilingCategoryPair::OTHER, + "BHR-detected hang", HangMarkerPayload(startTime, endTime)); } #endif } void BackgroundHangThread::ReportPermaHang() { // Permanently hanged; called on the monitor thread // mManager->mLock IS locked
--- a/tools/profiler/core/ProfileBufferEntry.cpp +++ b/tools/profiler/core/ProfileBufferEntry.cpp @@ -5,16 +5,17 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "ProfileBufferEntry.h" #include "mozilla/ProfilerMarkers.h" #include "platform.h" #include "ProfileBuffer.h" #include "ProfilerBacktrace.h" +#include "ProfilerMarkerPayload.h" #include "jsapi.h" #include "jsfriendapi.h" #include "mozilla/Logging.h" #include "mozilla/Sprintf.h" #include "mozilla/StackWalk.h" #include "nsThreadUtils.h" #include "nsXULAppAPI.h" @@ -630,16 +631,17 @@ class EntryGetter { // /* internally including: // ( NativeLeafAddr // | Label FrameFlags? DynamicStringFragment* // LineNumber? CategoryPair? // | JitReturnAddr // )+ // */ // ) +// | MarkerData // | Marker // | ( /* Counters */ // CounterId // Time // ( // CounterKey // Count // Number? @@ -1147,33 +1149,88 @@ void ProfileBuffer::StreamMarkersToJSON( double aSinceTime, UniqueStacks& aUniqueStacks) const { mEntries.ReadEach([&](ProfileBufferEntryReader& aER) { auto type = static_cast<ProfileBufferEntry::Kind>( aER.ReadObject<ProfileBufferEntry::KindUnderlyingType>()); MOZ_ASSERT(static_cast<ProfileBufferEntry::KindUnderlyingType>(type) < static_cast<ProfileBufferEntry::KindUnderlyingType>( ProfileBufferEntry::Kind::MODERN_LIMIT)); - bool entryWasFullyRead = false; + // Code should *return* from the switch if the entry was fully read. + // Code should *break* from the switch if the entry was not fully read (we + // then need to adjust the reader position to the end of the entry, as + // expected by the reader code.) + switch (type) { + case ProfileBufferEntry::Kind::MarkerData: + if (aER.ReadObject<int>() != aThreadId) { + break; // Entry not fully read. + } + aWriter.StartArrayElement(); + { + // Extract the information from the buffer: + // Each entry is made up of the following: + // + // [ + // ProfileBufferEntry::Kind::MarkerData, <- already read + // threadId, <- already read + // name, <- next location in entries + // startTime, + // endTime, + // phase, + // categoryPair, + // payload + // ] + auto name = aER.ReadObject<std::string>(); + auto startTime = aER.ReadObject<double>(); + auto endTime = aER.ReadObject<double>(); + auto phase = aER.ReadObject<uint8_t>(); + const JS::ProfilingCategoryPairInfo& info = + GetProfilingCategoryPairInfo( + static_cast<JS::ProfilingCategoryPair>( + aER.ReadObject<uint32_t>())); + auto payload = aER.ReadObject<UniquePtr<ProfilerMarkerPayload>>(); - if (type == ProfileBufferEntry::Kind::Marker) { - entryWasFullyRead = - mozilla::base_profiler_markers_detail::DeserializeAfterKindAndStream( - aER, aWriter, aThreadId, - [&](ProfileChunkedBuffer& aChunkedBuffer) { - ProfilerBacktrace backtrace("", &aChunkedBuffer); - backtrace.StreamJSON(aWriter, aProcessStartTime, aUniqueStacks); - }); + MOZ_ASSERT(aER.RemainingBytes() == 0); + + // Now write this information to JSON with the following schema: + // [name, startTime, endTime, phase, category, data] + aUniqueStacks.mUniqueStrings->WriteElement(aWriter, name); + aWriter.DoubleElement(startTime); + aWriter.DoubleElement(endTime); + aWriter.IntElement(phase); + aWriter.IntElement(unsigned(info.mCategory)); + if (payload) { + aWriter.StartObjectElement(SpliceableJSONWriter::SingleLineStyle); + { + payload->StreamPayload(aWriter, aProcessStartTime, aUniqueStacks); + } + aWriter.EndObject(); + } + } + aWriter.EndArray(); + return; // Entry fully read. + + case ProfileBufferEntry::Kind::Marker: + if (mozilla::base_profiler_markers_detail:: + DeserializeAfterKindAndStream( + aER, aWriter, aThreadId, + [&](ProfileChunkedBuffer& aChunkedBuffer) { + ProfilerBacktrace backtrace("", &aChunkedBuffer); + backtrace.StreamJSON(aWriter, aProcessStartTime, + aUniqueStacks); + })) { + return; // Entry fully read. + } + break; // Entry not fully read. + + default: + break; // Entry not fully read. } - if (!entryWasFullyRead) { - // The entry was not a marker, or it was a marker for another thread. - // We probably didn't read the whole entry, so we need to skip to the end. - aER.SetRemainingBytes(0); - } + aER.SetRemainingBytes(0); }); } void ProfileBuffer::StreamProfilerOverheadToJSON( SpliceableJSONWriter& aWriter, const TimeStamp& aProcessStartTime, double aSinceTime) const { mEntries.Read([&](ProfileChunkedBuffer::Reader* aReader) { MOZ_ASSERT(aReader,
new file mode 100644 --- /dev/null +++ b/tools/profiler/core/ProfilerMarkerPayload.cpp @@ -0,0 +1,1126 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ProfilerMarkerPayload.h" + +#include "GeckoProfiler.h" +#include "ProfileBufferEntry.h" +#include "ProfilerBacktrace.h" + +#include "gfxASurface.h" +#include "Layers.h" +#include "mozilla/Maybe.h" +#include "mozilla/net/HttpBaseChannel.h" +#include "mozilla/Preferences.h" +#include "mozilla/ProfileBufferEntrySerializationGeckoExtensions.h" +#include "mozilla/ProfileJSONWriter.h" +#include "mozilla/Sprintf.h" + +#include <inttypes.h> + +using namespace mozilla; + +static UniquePtr<ProfilerMarkerPayload> DeserializeNothing( + ProfileBufferEntryReader&) { + return nullptr; +} + +// Starting at 1 for the initial `DeserializeNothing`. +// static +Atomic<ProfilerMarkerPayload::DeserializerTagAtomic, ReleaseAcquire> + ProfilerMarkerPayload::sDeserializerCount{1}; + +// Initialize `sDeserializers` with `DeserializeNothing` at index 0, all others +// are nullptrs. +// static +ProfilerMarkerPayload::Deserializer + ProfilerMarkerPayload::sDeserializers[DeserializerMax] = { + DeserializeNothing}; + +// static +ProfilerMarkerPayload::DeserializerTag +ProfilerMarkerPayload::TagForDeserializer( + ProfilerMarkerPayload::Deserializer aDeserializer) { + if (!aDeserializer) { + return 0; + } + // Start first search at index 0. + DeserializerTagAtomic start = 0; + for (;;) { + // Read the current count of deserializers. + const DeserializerTagAtomic tagCount = sDeserializerCount; + if (tagCount == 0) { + // Someone else is currently writing into the array, loop around until we + // get a valid count. + continue; + } + for (DeserializerTagAtomic i = start; i < tagCount; ++i) { + if (sDeserializers[i] == aDeserializer) { + // Deserializer already registered, return its tag. + return static_cast<ProfilerMarkerPayload::DeserializerTag>(i); + } + } + // Not found yet, let's register this new deserializer. + // Make sure we haven't reached the limit yet. + MOZ_RELEASE_ASSERT(tagCount < DeserializerMax); + // Reserve `tagCount` as an index, if not already claimed: + // If `sDeserializerCount` is still at our previously-read `tagCount`, + // replace it with a special 0 value to indicate a write. + if (sDeserializerCount.compareExchange(tagCount, 0)) { + // Here we own the `tagCount` index, write the deserializer there. + sDeserializers[tagCount] = aDeserializer; + // And publish by writing the real new count (1 past our index). + sDeserializerCount = tagCount + 1; + return static_cast<ProfilerMarkerPayload::DeserializerTag>(tagCount); + } + // Someone else beat us to grab an index, and it could be for the same + // deserializer! So let's just try searching starting from our recorded + // `tagCount` (and maybe attempting again to register). It should be rare + // enough and quick enough that it won't impact performances. + start = tagCount; + } +} + +// static +ProfilerMarkerPayload::Deserializer ProfilerMarkerPayload::DeserializerForTag( + ProfilerMarkerPayload::DeserializerTag aTag) { + MOZ_RELEASE_ASSERT(aTag < DeserializerMax); + MOZ_RELEASE_ASSERT(aTag < sDeserializerCount); + return sDeserializers[aTag]; +} + +static void MOZ_ALWAYS_INLINE WriteTime(SpliceableJSONWriter& aWriter, + const TimeStamp& aProcessStartTime, + const TimeStamp& aTime, + const char* aName) { + if (!aTime.IsNull()) { + aWriter.DoubleProperty(MakeStringSpan(aName), + (aTime - aProcessStartTime).ToMilliseconds()); + } +} + +void ProfilerMarkerPayload::StreamType(const char* aMarkerType, + SpliceableJSONWriter& aWriter) const { + MOZ_ASSERT(aMarkerType); + aWriter.StringProperty("type", MakeStringSpan(aMarkerType)); +} + +ProfileBufferEntryWriter::Length +ProfilerMarkerPayload::CommonPropsTagAndSerializationBytes() const { + return sizeof(DeserializerTag) + + ProfileBufferEntryWriter::SumBytes( + mCommonProps.mStartTime, mCommonProps.mEndTime, + mCommonProps.mStack, mCommonProps.mInnerWindowID); +} + +void ProfilerMarkerPayload::SerializeTagAndCommonProps( + DeserializerTag aDeserializerTag, + ProfileBufferEntryWriter& aEntryWriter) const { + aEntryWriter.WriteObject(aDeserializerTag); + aEntryWriter.WriteObject(mCommonProps.mStartTime); + aEntryWriter.WriteObject(mCommonProps.mEndTime); + aEntryWriter.WriteObject(mCommonProps.mStack); + aEntryWriter.WriteObject(mCommonProps.mInnerWindowID); +} + +// static +ProfilerMarkerPayload::CommonProps +ProfilerMarkerPayload::DeserializeCommonProps( + ProfileBufferEntryReader& aEntryReader) { + CommonProps props; + aEntryReader.ReadIntoObject(props.mStartTime); + aEntryReader.ReadIntoObject(props.mEndTime); + aEntryReader.ReadIntoObject(props.mStack); + aEntryReader.ReadIntoObject(props.mInnerWindowID); + return props; +} + +// Deprecated: This function is providing a way for a few payloads to use the +// start time and end time in their payloads, which is currently deprecated. +// The startTime and endTime were removed from most payloads, in favor of +// the MarkerTiming::Phase idea. However, IPC and Network markers still have +// them as it was harder to upgrade the front-end without them. +void ProfilerMarkerPayload::StreamStartEndTime( + SpliceableJSONWriter& aWriter, const TimeStamp& aProcessStartTime) const { + WriteTime(aWriter, aProcessStartTime, mCommonProps.mStartTime, "startTime"); + WriteTime(aWriter, aProcessStartTime, mCommonProps.mEndTime, "endTime"); +} + +void ProfilerMarkerPayload::StreamCommonProps( + const char* aMarkerType, SpliceableJSONWriter& aWriter, + const TimeStamp& aProcessStartTime, UniqueStacks& aUniqueStacks) const { + StreamType(aMarkerType, aWriter); + if (mCommonProps.mInnerWindowID) { + // Here, we are converting uint64_t to double. Both Browsing Context and + // Inner Window IDs are creating using + // `nsContentUtils::GenerateProcessSpecificId`, which is specifically + // designed to only use 53 of the 64 bits to be lossless when passed into + // and out of JS as a double. + aWriter.DoubleProperty("innerWindowID", mCommonProps.mInnerWindowID.ref()); + } + if (mCommonProps.mStack) { + aWriter.StartObjectProperty("stack"); + { + mCommonProps.mStack->StreamJSON(aWriter, aProcessStartTime, + aUniqueStacks); + } + aWriter.EndObject(); + } +} + +ProfileBufferEntryWriter::Length +TracingMarkerPayload::TagAndSerializationBytes() const { + return CommonPropsTagAndSerializationBytes() + + ProfileBufferEntryWriter::SumBytes( + WrapProfileBufferRawPointer(mCategory), mKind); +} + +void TracingMarkerPayload::SerializeTagAndPayload( + ProfileBufferEntryWriter& aEntryWriter) const { + static const DeserializerTag tag = TagForDeserializer(Deserialize); + SerializeTagAndPayload(tag, aEntryWriter); +} + +void TracingMarkerPayload::SerializeTagAndPayload( + DeserializerTag aDeserializerTag, + ProfileBufferEntryWriter& aEntryWriter) const { + SerializeTagAndCommonProps(aDeserializerTag, aEntryWriter); + aEntryWriter.WriteObject(WrapProfileBufferRawPointer(mCategory)); + aEntryWriter.WriteObject(mKind); +} + +// static +UniquePtr<ProfilerMarkerPayload> TracingMarkerPayload::Deserialize( + ProfileBufferEntryReader& aEntryReader) { + ProfilerMarkerPayload::CommonProps props = + DeserializeCommonProps(aEntryReader); + const char* category = aEntryReader.ReadObject<const char*>(); + TracingKind kind = aEntryReader.ReadObject<TracingKind>(); + return UniquePtr<ProfilerMarkerPayload>( + new TracingMarkerPayload(std::move(props), category, kind)); +} + +void TracingMarkerPayload::StreamPayload(SpliceableJSONWriter& aWriter, + const TimeStamp& aProcessStartTime, + UniqueStacks& aUniqueStacks) const { + StreamCommonProps("tracing", aWriter, aProcessStartTime, aUniqueStacks); + + if (mCategory) { + aWriter.StringProperty("category", MakeStringSpan(mCategory)); + } + + if (mKind == TRACING_INTERVAL_START) { + aWriter.StringProperty("interval", "start"); + } else if (mKind == TRACING_INTERVAL_END) { + aWriter.StringProperty("interval", "end"); + } +} + +ProfileBufferEntryWriter::Length +UserTimingMarkerPayload::TagAndSerializationBytes() const { + return CommonPropsTagAndSerializationBytes() + + ProfileBufferEntryWriter::SumBytes( + WrapProfileBufferRawPointer(mEntryType), mName, mStartMark, + mEndMark); +} + +void UserTimingMarkerPayload::SerializeTagAndPayload( + ProfileBufferEntryWriter& aEntryWriter) const { + static const DeserializerTag tag = TagForDeserializer(Deserialize); + SerializeTagAndCommonProps(tag, aEntryWriter); + aEntryWriter.WriteObject(WrapProfileBufferRawPointer(mEntryType)); + aEntryWriter.WriteObject(mName); + aEntryWriter.WriteObject(mStartMark); + aEntryWriter.WriteObject(mEndMark); +} + +// static +UniquePtr<ProfilerMarkerPayload> UserTimingMarkerPayload::Deserialize( + ProfileBufferEntryReader& aEntryReader) { + ProfilerMarkerPayload::CommonProps props = + DeserializeCommonProps(aEntryReader); + auto entryType = aEntryReader.ReadObject<const char*>(); + auto name = aEntryReader.ReadObject<nsString>(); + auto startMark = aEntryReader.ReadObject<Maybe<nsString>>(); + auto endMark = aEntryReader.ReadObject<Maybe<nsString>>(); + return UniquePtr<ProfilerMarkerPayload>( + new UserTimingMarkerPayload(std::move(props), entryType, std::move(name), + std::move(startMark), std::move(endMark))); +} + +void UserTimingMarkerPayload::StreamPayload(SpliceableJSONWriter& aWriter, + const TimeStamp& aProcessStartTime, + UniqueStacks& aUniqueStacks) const { + StreamCommonProps("UserTiming", aWriter, aProcessStartTime, aUniqueStacks); + aWriter.StringProperty("name", NS_ConvertUTF16toUTF8(mName)); + aWriter.StringProperty("entryType", MakeStringSpan(mEntryType)); + + if (mStartMark.isSome()) { + aWriter.StringProperty("startMark", + NS_ConvertUTF16toUTF8(mStartMark.value())); + } else { + aWriter.NullProperty("startMark"); + } + if (mEndMark.isSome()) { + aWriter.StringProperty("endMark", NS_ConvertUTF16toUTF8(mEndMark.value())); + } else { + aWriter.NullProperty("endMark"); + } +} + +ProfileBufferEntryWriter::Length TimingMarkerPayload::TagAndSerializationBytes() + const { + return CommonPropsTagAndSerializationBytes(); +} + +void TimingMarkerPayload::SerializeTagAndPayload( + ProfileBufferEntryWriter& aEntryWriter) const { + static const DeserializerTag tag = TagForDeserializer(Deserialize); + SerializeTagAndCommonProps(tag, aEntryWriter); +} + +// static +UniquePtr<ProfilerMarkerPayload> TimingMarkerPayload::Deserialize( + ProfileBufferEntryReader& aEntryReader) { + ProfilerMarkerPayload::CommonProps props = + DeserializeCommonProps(aEntryReader); + return UniquePtr<ProfilerMarkerPayload>( + new TimingMarkerPayload(std::move(props))); +} + +void TimingMarkerPayload::StreamPayload(SpliceableJSONWriter& aWriter, + const TimeStamp& aProcessStartTime, + UniqueStacks& aUniqueStacks) const { + StreamCommonProps("Timing", aWriter, aProcessStartTime, aUniqueStacks); +} + +ProfileBufferEntryWriter::Length TextMarkerPayload::TagAndSerializationBytes() + const { + return CommonPropsTagAndSerializationBytes() + + ProfileBufferEntryWriter::SumBytes(mText); +} + +void TextMarkerPayload::SerializeTagAndPayload( + ProfileBufferEntryWriter& aEntryWriter) const { + static const DeserializerTag tag = TagForDeserializer(Deserialize); + SerializeTagAndCommonProps(tag, aEntryWriter); + aEntryWriter.WriteObject(mText); +} + +// static +UniquePtr<ProfilerMarkerPayload> TextMarkerPayload::Deserialize( + ProfileBufferEntryReader& aEntryReader) { + ProfilerMarkerPayload::CommonProps props = + DeserializeCommonProps(aEntryReader); + auto text = aEntryReader.ReadObject<nsCString>(); + return UniquePtr<ProfilerMarkerPayload>( + new TextMarkerPayload(std::move(props), std::move(text))); +} + +void TextMarkerPayload::StreamPayload(SpliceableJSONWriter& aWriter, + const TimeStamp& aProcessStartTime, + UniqueStacks& aUniqueStacks) const { + StreamCommonProps("Text", aWriter, aProcessStartTime, aUniqueStacks); + aWriter.StringProperty("name", mText); +} + +ProfileBufferEntryWriter::Length LogMarkerPayload::TagAndSerializationBytes() + const { + return CommonPropsTagAndSerializationBytes() + + ProfileBufferEntryWriter::SumBytes(mModule, mText); +} + +void LogMarkerPayload::SerializeTagAndPayload( + ProfileBufferEntryWriter& aEntryWriter) const { + static const DeserializerTag tag = TagForDeserializer(Deserialize); + SerializeTagAndCommonProps(tag, aEntryWriter); + aEntryWriter.WriteObject(mModule); + aEntryWriter.WriteObject(mText); +} + +// static +UniquePtr<ProfilerMarkerPayload> LogMarkerPayload::Deserialize( + ProfileBufferEntryReader& aEntryReader) { + ProfilerMarkerPayload::CommonProps props = + DeserializeCommonProps(aEntryReader); + auto module = aEntryReader.ReadObject<nsAutoCStringN<32>>(); + auto text = aEntryReader.ReadObject<nsCString>(); + return UniquePtr<ProfilerMarkerPayload>(new LogMarkerPayload( + std::move(props), std::move(module), std::move(text))); +} + +void LogMarkerPayload::StreamPayload(SpliceableJSONWriter& aWriter, + const TimeStamp& aProcessStartTime, + UniqueStacks& aUniqueStacks) const { + StreamCommonProps("Log", aWriter, aProcessStartTime, aUniqueStacks); + aWriter.StringProperty("name", mText); + aWriter.StringProperty("module", mModule); +} + +MediaSampleMarkerPayload::MediaSampleMarkerPayload( + const int64_t aSampleStartTimeUs, const int64_t aSampleEndTimeUs) + : mSampleStartTimeUs(aSampleStartTimeUs), + mSampleEndTimeUs(aSampleEndTimeUs) {} + +MediaSampleMarkerPayload::MediaSampleMarkerPayload( + CommonProps&& aCommonProps, const int64_t aSampleStartTimeUs, + const int64_t aSampleEndTimeUs) + : ProfilerMarkerPayload(std::move(aCommonProps)), + mSampleStartTimeUs(aSampleStartTimeUs), + mSampleEndTimeUs(aSampleEndTimeUs) {} + +ProfileBufferEntryWriter::Length +MediaSampleMarkerPayload::TagAndSerializationBytes() const { + return CommonPropsTagAndSerializationBytes() + + ProfileBufferEntryWriter::SumBytes(mSampleStartTimeUs, + mSampleEndTimeUs); +} + +void MediaSampleMarkerPayload::SerializeTagAndPayload( + ProfileBufferEntryWriter& aEntryWriter) const { + static const DeserializerTag tag = TagForDeserializer(Deserialize); + SerializeTagAndCommonProps(tag, aEntryWriter); + aEntryWriter.WriteObject(mSampleStartTimeUs); + aEntryWriter.WriteObject(mSampleEndTimeUs); +} + +/* static */ +UniquePtr<ProfilerMarkerPayload> MediaSampleMarkerPayload::Deserialize( + ProfileBufferEntryReader& aEntryReader) { + ProfilerMarkerPayload::CommonProps props = + DeserializeCommonProps(aEntryReader); + auto sampleStartTimeUs = aEntryReader.ReadObject<int64_t>(); + auto sampleEndTimeUs = aEntryReader.ReadObject<int64_t>(); + return UniquePtr<ProfilerMarkerPayload>(new MediaSampleMarkerPayload( + std::move(props), sampleStartTimeUs, sampleEndTimeUs)); +} + +void MediaSampleMarkerPayload::StreamPayload( + SpliceableJSONWriter& aWriter, const TimeStamp& aProcessStartTime, + UniqueStacks& aUniqueStacks) const { + StreamCommonProps("MediaSample", aWriter, aProcessStartTime, aUniqueStacks); + aWriter.IntProperty("sampleStartTimeUs", mSampleStartTimeUs); + aWriter.IntProperty("sampleEndTimeUs", mSampleEndTimeUs); +} + +ProfileBufferEntryWriter::Length PrefMarkerPayload::TagAndSerializationBytes() + const { + return CommonPropsTagAndSerializationBytes() + + ProfileBufferEntryWriter::SumBytes(mPrefAccessTime, mPrefName, + mPrefKind, mPrefType, mPrefValue); +} + +void PrefMarkerPayload::SerializeTagAndPayload( + ProfileBufferEntryWriter& aEntryWriter) const { + static const DeserializerTag tag = TagForDeserializer(Deserialize); + SerializeTagAndCommonProps(tag, aEntryWriter); + aEntryWriter.WriteObject(mPrefAccessTime); + aEntryWriter.WriteObject(mPrefName); + aEntryWriter.WriteObject(mPrefKind); + aEntryWriter.WriteObject(mPrefType); + aEntryWriter.WriteObject(mPrefValue); +} + +// static +UniquePtr<ProfilerMarkerPayload> PrefMarkerPayload::Deserialize( + ProfileBufferEntryReader& aEntryReader) { + ProfilerMarkerPayload::CommonProps props = + DeserializeCommonProps(aEntryReader); + auto prefAccessTime = aEntryReader.ReadObject<TimeStamp>(); + auto prefName = aEntryReader.ReadObject<nsCString>(); + auto prefKind = aEntryReader.ReadObject<Maybe<PrefValueKind>>(); + auto prefType = aEntryReader.ReadObject<Maybe<PrefType>>(); + auto prefValue = aEntryReader.ReadObject<nsCString>(); + return UniquePtr<ProfilerMarkerPayload>(new PrefMarkerPayload( + std::move(props), prefAccessTime, std::move(prefName), + std::move(prefKind), std::move(prefType), std::move(prefValue))); +} + +static Span<const char> PrefValueKindToString( + const Maybe<PrefValueKind>& aKind) { + if (aKind) { + return *aKind == PrefValueKind::Default ? MakeStringSpan("Default") + : MakeStringSpan("User"); + } + return MakeStringSpan("Shared"); +} + +static Span<const char> PrefTypeToString(const Maybe<PrefType>& type) { + if (type) { + switch (*type) { + case PrefType::None: + return MakeStringSpan("None"); + case PrefType::Int: + return MakeStringSpan("Int"); + case PrefType::Bool: + return MakeStringSpan("Bool"); + case PrefType::String: + return MakeStringSpan("String"); + default: + MOZ_ASSERT_UNREACHABLE("Unknown preference type."); + } + } + return MakeStringSpan("Preference not found"); +} + +void PrefMarkerPayload::StreamPayload(SpliceableJSONWriter& aWriter, + const TimeStamp& aProcessStartTime, + UniqueStacks& aUniqueStacks) const { + StreamCommonProps("PreferenceRead", aWriter, aProcessStartTime, + aUniqueStacks); + WriteTime(aWriter, aProcessStartTime, mPrefAccessTime, "prefAccessTime"); + aWriter.StringProperty("prefName", mPrefName); + aWriter.StringProperty("prefKind", PrefValueKindToString(mPrefKind)); + aWriter.StringProperty("prefType", PrefTypeToString(mPrefType)); + aWriter.StringProperty("prefValue", mPrefValue); +} + +ProfileBufferEntryWriter::Length +LayerTranslationMarkerPayload::TagAndSerializationBytes() const { + return CommonPropsTagAndSerializationBytes() + + ProfileBufferEntryWriter::SumBytes(WrapProfileBufferRawPointer(mLayer), + mPoint); +} + +void LayerTranslationMarkerPayload::SerializeTagAndPayload( + ProfileBufferEntryWriter& aEntryWriter) const { + static const DeserializerTag tag = TagForDeserializer(Deserialize); + SerializeTagAndCommonProps(tag, aEntryWriter); + aEntryWriter.WriteObject(WrapProfileBufferRawPointer(mLayer)); + aEntryWriter.WriteObject(mPoint); +} + +// static +UniquePtr<ProfilerMarkerPayload> LayerTranslationMarkerPayload::Deserialize( + ProfileBufferEntryReader& aEntryReader) { + ProfilerMarkerPayload::CommonProps props = + DeserializeCommonProps(aEntryReader); + auto layer = aEntryReader.ReadObject<layers::Layer*>(); + auto point = aEntryReader.ReadObject<gfx::Point>(); + return UniquePtr<ProfilerMarkerPayload>( + new LayerTranslationMarkerPayload(std::move(props), layer, point)); +} + +void LayerTranslationMarkerPayload::StreamPayload( + SpliceableJSONWriter& aWriter, const TimeStamp& aProcessStartTime, + UniqueStacks& aUniqueStacks) const { + StreamType("LayerTranslation", aWriter); + const size_t bufferSize = 32; + char buffer[bufferSize]; + const int written = SprintfLiteral(buffer, "%p", mLayer); + MOZ_RELEASE_ASSERT(written > 0); + + aWriter.StringProperty("layer", Span<const char>(buffer, size_t(written))); + aWriter.IntProperty("x", mPoint.x); + aWriter.IntProperty("y", mPoint.y); +} + +ProfileBufferEntryWriter::Length VsyncMarkerPayload::TagAndSerializationBytes() + const { + return CommonPropsTagAndSerializationBytes(); +} + +void VsyncMarkerPayload::SerializeTagAndPayload( + ProfileBufferEntryWriter& aEntryWriter) const { + static const DeserializerTag tag = TagForDeserializer(Deserialize); + SerializeTagAndCommonProps(tag, aEntryWriter); +} + +// static +UniquePtr<ProfilerMarkerPayload> VsyncMarkerPayload::Deserialize( + ProfileBufferEntryReader& aEntryReader) { + ProfilerMarkerPayload::CommonProps props = + DeserializeCommonProps(aEntryReader); + return UniquePtr<ProfilerMarkerPayload>( + new VsyncMarkerPayload(std::move(props))); +} + +void VsyncMarkerPayload::StreamPayload(SpliceableJSONWriter& aWriter, + const TimeStamp& aProcessStartTime, + UniqueStacks& aUniqueStacks) const { + StreamType("VsyncTimestamp", aWriter); +} + +ProfileBufferEntryWriter::Length +NetworkMarkerPayload::TagAndSerializationBytes() const { + return CommonPropsTagAndSerializationBytes() + + ProfileBufferEntryWriter::SumBytes( + mID, mURI, mRedirectURI, mRequestMethod, mType, mPri, mCount, + mTimings, mCacheDisposition, mContentType); +} + +void NetworkMarkerPayload::SerializeTagAndPayload( + ProfileBufferEntryWriter& aEntryWriter) const { + static const DeserializerTag tag = TagForDeserializer(Deserialize); + SerializeTagAndCommonProps(tag, aEntryWriter); + aEntryWriter.WriteObject(mID); + aEntryWriter.WriteObject(mURI); + aEntryWriter.WriteObject(mRedirectURI); + aEntryWriter.WriteObject(mRequestMethod); + aEntryWriter.WriteObject(mType); + aEntryWriter.WriteObject(mPri); + aEntryWriter.WriteObject(mCount); + aEntryWriter.WriteObject(mTimings); + aEntryWriter.WriteObject(mCacheDisposition); + aEntryWriter.WriteObject(mContentType); +} + +// static +UniquePtr<ProfilerMarkerPayload> NetworkMarkerPayload::Deserialize( + ProfileBufferEntryReader& aEntryReader) { + ProfilerMarkerPayload::CommonProps props = + DeserializeCommonProps(aEntryReader); + auto id = aEntryReader.ReadObject<int64_t>(); + auto uri = aEntryReader.ReadObject<UniqueFreePtr<char>>(); + auto redirectURI = aEntryReader.ReadObject<UniqueFreePtr<char>>(); + auto requestMethod = aEntryReader.ReadObject<nsCString>(); + auto type = aEntryReader.ReadObject<NetworkLoadType>(); + auto pri = aEntryReader.ReadObject<int32_t>(); + auto count = aEntryReader.ReadObject<int64_t>(); + auto timings = aEntryReader.ReadObject<net::TimingStruct>(); + auto cacheDisposition = aEntryReader.ReadObject<net::CacheDisposition>(); + auto contentType = aEntryReader.ReadObject<Maybe<nsAutoCString>>(); + return UniquePtr<ProfilerMarkerPayload>(new NetworkMarkerPayload( + std::move(props), id, std::move(uri), std::move(redirectURI), + std::move(requestMethod), type, pri, count, timings, cacheDisposition, + std::move(contentType))); +} + +static Span<const char> GetNetworkState(NetworkLoadType aType) { + switch (aType) { + case NetworkLoadType::LOAD_START: + return MakeStringSpan("STATUS_START"); + case NetworkLoadType::LOAD_STOP: + return MakeStringSpan("STATUS_STOP"); + case NetworkLoadType::LOAD_REDIRECT: + return MakeStringSpan("STATUS_REDIRECT"); + } + return MakeStringSpan(""); +} + +static Span<const char> GetCacheState(net::CacheDisposition aCacheDisposition) { + switch (aCacheDisposition) { + case net::kCacheUnresolved: + return MakeStringSpan("Unresolved"); + case net::kCacheHit: + return MakeStringSpan("Hit"); + case net::kCacheHitViaReval: + return MakeStringSpan("HitViaReval"); + case net::kCacheMissedViaReval: + return MakeStringSpan("MissedViaReval"); + case net::kCacheMissed: + return MakeStringSpan("Missed"); + case net::kCacheUnknown: + default: + return MakeStringSpan(""); + } +} + +void NetworkMarkerPayload::StreamPayload(SpliceableJSONWriter& aWriter, + const TimeStamp& aProcessStartTime, + UniqueStacks& aUniqueStacks) const { + StreamCommonProps("Network", aWriter, aProcessStartTime, aUniqueStacks); + // This payload still streams a startTime and endTime property because it made + // the migration to MarkerTiming on the front-end easier. + StreamStartEndTime(aWriter, aProcessStartTime); + + aWriter.IntProperty("id", mID); + // want to use aUniqueStacks.mUniqueStrings->WriteElement(aWriter, + // typeString); + aWriter.StringProperty("status", GetNetworkState(mType)); + if (Span<const char> cacheString = GetCacheState(mCacheDisposition); + !cacheString.empty()) { + aWriter.StringProperty("cache", cacheString); + } + aWriter.IntProperty("pri", mPri); + if (mCount > 0) { + aWriter.IntProperty("count", mCount); + } + if (mURI) { + aWriter.StringProperty("URI", MakeStringSpan(mURI.get())); + } + if (mRedirectURI) { + aWriter.StringProperty("RedirectURI", MakeStringSpan(mRedirectURI.get())); + } + aWriter.StringProperty("requestMethod", mRequestMethod); + + if (mContentType.isSome()) { + aWriter.StringProperty("contentType", mContentType.value()); + } else { + aWriter.NullProperty("contentType"); + } + + if (mType != NetworkLoadType::LOAD_START) { + WriteTime(aWriter, aProcessStartTime, mTimings.domainLookupStart, + "domainLookupStart"); + WriteTime(aWriter, aProcessStartTime, mTimings.domainLookupEnd, + "domainLookupEnd"); + WriteTime(aWriter, aProcessStartTime, mTimings.connectStart, + "connectStart"); + WriteTime(aWriter, aProcessStartTime, mTimings.tcpConnectEnd, + "tcpConnectEnd"); + WriteTime(aWriter, aProcessStartTime, mTimings.secureConnectionStart, + "secureConnectionStart"); + WriteTime(aWriter, aProcessStartTime, mTimings.connectEnd, "connectEnd"); + WriteTime(aWriter, aProcessStartTime, mTimings.requestStart, + "requestStart"); + WriteTime(aWriter, aProcessStartTime, mTimings.responseStart, + "responseStart"); + WriteTime(aWriter, aProcessStartTime, mTimings.responseEnd, "responseEnd"); + } +} + +ProfileBufferEntryWriter::Length ScreenshotPayload::TagAndSerializationBytes() + const { + return CommonPropsTagAndSerializationBytes() + + ProfileBufferEntryWriter::SumBytes(mScreenshotDataURL, mWindowSize, + mWindowIdentifier); +} + +void ScreenshotPayload::SerializeTagAndPayload( + ProfileBufferEntryWriter& aEntryWriter) const { + static const DeserializerTag tag = TagForDeserializer(Deserialize); + SerializeTagAndCommonProps(tag, aEntryWriter); + aEntryWriter.WriteObject(mScreenshotDataURL); + aEntryWriter.WriteObject(mWindowSize); + aEntryWriter.WriteObject(mWindowIdentifier); +} + +// static +UniquePtr<ProfilerMarkerPayload> ScreenshotPayload::Deserialize( + ProfileBufferEntryReader& aEntryReader) { + ProfilerMarkerPayload::CommonProps props = + DeserializeCommonProps(aEntryReader); + auto screenshotDataURL = aEntryReader.ReadObject<nsCString>(); + auto windowSize = aEntryReader.ReadObject<gfx::IntSize>(); + auto windowIdentifier = aEntryReader.ReadObject<uintptr_t>(); + return UniquePtr<ProfilerMarkerPayload>( + new ScreenshotPayload(std::move(props), std::move(screenshotDataURL), + windowSize, windowIdentifier)); +} + +void ScreenshotPayload::StreamPayload(SpliceableJSONWriter& aWriter, + const TimeStamp& aProcessStartTime, + UniqueStacks& aUniqueStacks) const { + StreamType("CompositorScreenshot", aWriter); + aUniqueStacks.mUniqueStrings->WriteProperty(aWriter, MakeStringSpan("url"), + mScreenshotDataURL); + + char hexWindowID[32]; + const int written = + SprintfLiteral(hexWindowID, "0x%" PRIXPTR, mWindowIdentifier); + MOZ_RELEASE_ASSERT(written > 0); + aWriter.StringProperty("windowID", + Span<const char>(hexWindowID, size_t(written))); + aWriter.DoubleProperty("windowWidth", mWindowSize.width); + aWriter.DoubleProperty("windowHeight", mWindowSize.height); +} + +ProfileBufferEntryWriter::Length +GCSliceMarkerPayload::TagAndSerializationBytes() const { + return CommonPropsTagAndSerializationBytes() + + ProfileBufferEntryWriter::SumBytes(mTimingJSON); +} + +void GCSliceMarkerPayload::SerializeTagAndPayload( + ProfileBufferEntryWriter& aEntryWriter) const { + static const DeserializerTag tag = TagForDeserializer(Deserialize); + SerializeTagAndCommonProps(tag, aEntryWriter); + aEntryWriter.WriteObject(mTimingJSON); +} + +// static +UniquePtr<ProfilerMarkerPayload> GCSliceMarkerPayload::Deserialize( + ProfileBufferEntryReader& aEntryReader) { + ProfilerMarkerPayload::CommonProps props = + DeserializeCommonProps(aEntryReader); + auto timingJSON = aEntryReader.ReadObject<JS::UniqueChars>(); + return UniquePtr<ProfilerMarkerPayload>( + new GCSliceMarkerPayload(std::move(props), std::move(timingJSON))); +} + +void GCSliceMarkerPayload::StreamPayload(SpliceableJSONWriter& aWriter, + const TimeStamp& aProcessStartTime, + UniqueStacks& aUniqueStacks) const { + MOZ_ASSERT(mTimingJSON); + StreamCommonProps("GCSlice", aWriter, aProcessStartTime, aUniqueStacks); + if (mTimingJSON) { + aWriter.SplicedJSONProperty(MakeStringSpan("timings"), + MakeStringSpan(mTimingJSON.get())); + } else { + aWriter.NullProperty("timings"); + } +} + +ProfileBufferEntryWriter::Length BudgetMarkerPayload::TagAndSerializationBytes() + const { + return CommonPropsTagAndSerializationBytes(); +} + +void BudgetMarkerPayload::SerializeTagAndPayload( + ProfileBufferEntryWriter& aEntryWriter) const { + static const DeserializerTag tag = TagForDeserializer(Deserialize); + SerializeTagAndCommonProps(tag, aEntryWriter); +} + +// static +UniquePtr<ProfilerMarkerPayload> BudgetMarkerPayload::Deserialize( + ProfileBufferEntryReader& aEntryReader) { + ProfilerMarkerPayload::CommonProps props = + DeserializeCommonProps(aEntryReader); + return UniquePtr<ProfilerMarkerPayload>( + new BudgetMarkerPayload(std::move(props))); +} + +void BudgetMarkerPayload::StreamPayload(SpliceableJSONWriter& aWriter, + const TimeStamp& aProcessStartTime, + UniqueStacks& aUniqueStacks) const { + StreamCommonProps("Budget", aWriter, aProcessStartTime, aUniqueStacks); +} + +ProfileBufferEntryWriter::Length +GCMajorMarkerPayload::TagAndSerializationBytes() const { + return CommonPropsTagAndSerializationBytes() + + ProfileBufferEntryWriter::SumBytes(mTimingJSON); +} + +void GCMajorMarkerPayload::SerializeTagAndPayload( + ProfileBufferEntryWriter& aEntryWriter) const { + static const DeserializerTag tag = TagForDeserializer(Deserialize); + SerializeTagAndCommonProps(tag, aEntryWriter); + aEntryWriter.WriteObject(mTimingJSON); +} + +// static +UniquePtr<ProfilerMarkerPayload> GCMajorMarkerPayload::Deserialize( + ProfileBufferEntryReader& aEntryReader) { + ProfilerMarkerPayload::CommonProps props = + DeserializeCommonProps(aEntryReader); + auto timingJSON = aEntryReader.ReadObject<JS::UniqueChars>(); + return UniquePtr<ProfilerMarkerPayload>( + new GCMajorMarkerPayload(std::move(props), std::move(timingJSON))); +} + +void GCMajorMarkerPayload::StreamPayload(SpliceableJSONWriter& aWriter, + const TimeStamp& aProcessStartTime, + UniqueStacks& aUniqueStacks) const { + MOZ_ASSERT(mTimingJSON); + StreamCommonProps("GCMajor", aWriter, aProcessStartTime, aUniqueStacks); + if (mTimingJSON) { + aWriter.SplicedJSONProperty(MakeStringSpan("timings"), + MakeStringSpan(mTimingJSON.get())); + } else { + aWriter.NullProperty("timings"); + } +} + +ProfileBufferEntryWriter::Length +GCMinorMarkerPayload::TagAndSerializationBytes() const { + return CommonPropsTagAndSerializationBytes() + + ProfileBufferEntryWriter::SumBytes(mTimingData); +} + +void GCMinorMarkerPayload::SerializeTagAndPayload( + ProfileBufferEntryWriter& aEntryWriter) const { + static const DeserializerTag tag = TagForDeserializer(Deserialize); + SerializeTagAndCommonProps(tag, aEntryWriter); + aEntryWriter.WriteObject(mTimingData); +} + +// static +UniquePtr<ProfilerMarkerPayload> GCMinorMarkerPayload::Deserialize( + ProfileBufferEntryReader& aEntryReader) { + ProfilerMarkerPayload::CommonProps props = + DeserializeCommonProps(aEntryReader); + auto timingData = aEntryReader.ReadObject<JS::UniqueChars>(); + return UniquePtr<ProfilerMarkerPayload>( + new GCMinorMarkerPayload(std::move(props), std::move(timingData))); +} + +void GCMinorMarkerPayload::StreamPayload(SpliceableJSONWriter& aWriter, + const TimeStamp& aProcessStartTime, + UniqueStacks& aUniqueStacks) const { + MOZ_ASSERT(mTimingData); + StreamCommonProps("GCMinor", aWriter, aProcessStartTime, aUniqueStacks); + if (mTimingData) { + aWriter.SplicedJSONProperty(MakeStringSpan("nursery"), + MakeStringSpan(mTimingData.get())); + } else { + aWriter.NullProperty("nursery"); + } +} + +ProfileBufferEntryWriter::Length HangMarkerPayload::TagAndSerializationBytes() + const { + return CommonPropsTagAndSerializationBytes(); +} + +void HangMarkerPayload::SerializeTagAndPayload( + ProfileBufferEntryWriter& aEntryWriter) const { + static const DeserializerTag tag = TagForDeserializer(Deserialize); + SerializeTagAndCommonProps(tag, aEntryWriter); +} + +// static +UniquePtr<ProfilerMarkerPayload> HangMarkerPayload::Deserialize( + ProfileBufferEntryReader& aEntryReader) { + ProfilerMarkerPayload::CommonProps props = + DeserializeCommonProps(aEntryReader); + return UniquePtr<ProfilerMarkerPayload>( + new HangMarkerPayload(std::move(props))); +} + +void HangMarkerPayload::StreamPayload(SpliceableJSONWriter& aWriter, + const TimeStamp& aProcessStartTime, + UniqueStacks& aUniqueStacks) const { + StreamCommonProps("BHR-detected hang", aWriter, aProcessStartTime, + aUniqueStacks); +} + +ProfileBufferEntryWriter::Length StyleMarkerPayload::TagAndSerializationBytes() + const { + return CommonPropsTagAndSerializationBytes() + + ProfileBufferEntryWriter::SumBytes(mStats); +} + +void StyleMarkerPayload::SerializeTagAndPayload( + ProfileBufferEntryWriter& aEntryWriter) const { + static const DeserializerTag tag = TagForDeserializer(Deserialize); + SerializeTagAndCommonProps(tag, aEntryWriter); + aEntryWriter.WriteObject(mStats); +} + +// static +UniquePtr<ProfilerMarkerPayload> StyleMarkerPayload::Deserialize( + ProfileBufferEntryReader& aEntryReader) { + ProfilerMarkerPayload::CommonProps props = + DeserializeCommonProps(aEntryReader); + auto stats = aEntryReader.ReadObject<ServoTraversalStatistics>(); + return UniquePtr<ProfilerMarkerPayload>( + new StyleMarkerPayload(std::move(props), stats)); +} + +void StyleMarkerPayload::StreamPayload(SpliceableJSONWriter& aWriter, + const TimeStamp& aProcessStartTime, + UniqueStacks& aUniqueStacks) const { + StreamCommonProps("Styles", aWriter, aProcessStartTime, aUniqueStacks); + aWriter.StringProperty("category", "Paint"); + aWriter.IntProperty("elementsTraversed", mStats.mElementsTraversed); + aWriter.IntProperty("elementsStyled", mStats.mElementsStyled); + aWriter.IntProperty("elementsMatched", mStats.mElementsMatched); + aWriter.IntProperty("stylesShared", mStats.mStylesShared); + aWriter.IntProperty("stylesReused", mStats.mStylesReused); +} + +ProfileBufferEntryWriter::Length +LongTaskMarkerPayload::TagAndSerializationBytes() const { + return CommonPropsTagAndSerializationBytes(); +} + +void LongTaskMarkerPayload::SerializeTagAndPayload( + ProfileBufferEntryWriter& aEntryWriter) const { + static const DeserializerTag tag = TagForDeserializer(Deserialize); + SerializeTagAndCommonProps(tag, aEntryWriter); +} + +// static +UniquePtr<ProfilerMarkerPayload> LongTaskMarkerPayload::Deserialize( + ProfileBufferEntryReader& aEntryReader) { + ProfilerMarkerPayload::CommonProps props = + DeserializeCommonProps(aEntryReader); + return UniquePtr<ProfilerMarkerPayload>( + new LongTaskMarkerPayload(std::move(props))); +} + +void LongTaskMarkerPayload::StreamPayload(SpliceableJSONWriter& aWriter, + const TimeStamp& aProcessStartTime, + UniqueStacks& aUniqueStacks) const { + StreamCommonProps("MainThreadLongTask", aWriter, aProcessStartTime, + aUniqueStacks); + aWriter.StringProperty("category", "LongTask"); +} + +ProfileBufferEntryWriter::Length +JsAllocationMarkerPayload::TagAndSerializationBytes() const { + return CommonPropsTagAndSerializationBytes() + + ProfileBufferEntryWriter::SumBytes(mTypeName, mClassName, + mDescriptiveTypeName, mCoarseType, + mSize, mInNursery); +} + +void JsAllocationMarkerPayload::SerializeTagAndPayload( + ProfileBufferEntryWriter& aEntryWriter) const { + static const DeserializerTag tag = TagForDeserializer(Deserialize); + SerializeTagAndCommonProps(tag, aEntryWriter); + aEntryWriter.WriteObject(mTypeName); + aEntryWriter.WriteObject(mClassName); + aEntryWriter.WriteObject(mDescriptiveTypeName); + aEntryWriter.WriteObject(WrapProfileBufferRawPointer(mCoarseType)); + aEntryWriter.WriteObject(mSize); + aEntryWriter.WriteObject(mInNursery); +} + +// static +UniquePtr<ProfilerMarkerPayload> JsAllocationMarkerPayload::Deserialize( + ProfileBufferEntryReader& aEntryReader) { + ProfilerMarkerPayload::CommonProps props = + DeserializeCommonProps(aEntryReader); + auto typeName = aEntryReader.ReadObject<UniqueFreePtr<const char16_t>>(); + auto className = aEntryReader.ReadObject<UniqueFreePtr<const char>>(); + auto descriptiveTypeName = + aEntryReader.ReadObject<UniqueFreePtr<const char16_t>>(); + auto coarseType = aEntryReader.ReadObject<const char*>(); + auto size = aEntryReader.ReadObject<uint64_t>(); + auto inNursery = aEntryReader.ReadObject<bool>(); + return UniquePtr<ProfilerMarkerPayload>(new JsAllocationMarkerPayload( + std::move(props), std::move(typeName), std::move(className), + std::move(descriptiveTypeName), coarseType, size, inNursery)); +} + +void JsAllocationMarkerPayload::StreamPayload( + SpliceableJSONWriter& aWriter, const TimeStamp& aProcessStartTime, + UniqueStacks& aUniqueStacks) const { + StreamCommonProps("JS allocation", aWriter, aProcessStartTime, aUniqueStacks); + + if (mClassName) { + aWriter.StringProperty("className", MakeStringSpan(mClassName.get())); + } + if (mTypeName) { + aWriter.StringProperty("typeName", NS_ConvertUTF16toUTF8(mTypeName.get())); + } + if (mDescriptiveTypeName) { + aWriter.StringProperty("descriptiveTypeName", + NS_ConvertUTF16toUTF8(mDescriptiveTypeName.get())); + } + aWriter.StringProperty("coarseType", MakeStringSpan(mCoarseType)); + aWriter.IntProperty("size", mSize); + aWriter.BoolProperty("inNursery", mInNursery); +} + +ProfileBufferEntryWriter::Length +NativeAllocationMarkerPayload::TagAndSerializationBytes() const { + return CommonPropsTagAndSerializationBytes() + + ProfileBufferEntryWriter::SumBytes(mSize, mThreadId, mMemoryAddress); +} + +void NativeAllocationMarkerPayload::SerializeTagAndPayload( + ProfileBufferEntryWriter& aEntryWriter) const { + static const DeserializerTag tag = TagForDeserializer(Deserialize); + SerializeTagAndCommonProps(tag, aEntryWriter); + aEntryWriter.WriteObject(mSize); + aEntryWriter.WriteObject(mMemoryAddress); + aEntryWriter.WriteObject(mThreadId); +} + +// static +UniquePtr<ProfilerMarkerPayload> NativeAllocationMarkerPayload::Deserialize( + ProfileBufferEntryReader& aEntryReader) { + ProfilerMarkerPayload::CommonProps props = + DeserializeCommonProps(aEntryReader); + auto size = aEntryReader.ReadObject<int64_t>(); + auto memoryAddress = aEntryReader.ReadObject<uintptr_t>(); + auto threadId = aEntryReader.ReadObject<int>(); + return UniquePtr<ProfilerMarkerPayload>(new NativeAllocationMarkerPayload( + std::move(props), size, memoryAddress, threadId)); +} + +void NativeAllocationMarkerPayload::StreamPayload( + SpliceableJSONWriter& aWriter, const TimeStamp& aProcessStartTime, + UniqueStacks& aUniqueStacks) const { + StreamCommonProps("Native allocation", aWriter, aProcessStartTime, + aUniqueStacks); + aWriter.IntProperty("size", mSize); + aWriter.IntProperty("memoryAddress", static_cast<int64_t>(mMemoryAddress)); + aWriter.IntProperty("threadId", mThreadId); +} + +ProfileBufferEntryWriter::Length IPCMarkerPayload::TagAndSerializationBytes() + const { + return CommonPropsTagAndSerializationBytes() + + ProfileBufferEntryWriter::SumBytes(mOtherPid, mMessageSeqno, + mMessageType, mSide, mDirection, + mPhase, mSync); +} + +void IPCMarkerPayload::SerializeTagAndPayload( + ProfileBufferEntryWriter& aEntryWriter) const { + static const DeserializerTag tag = TagForDeserializer(Deserialize); + SerializeTagAndCommonProps(tag, aEntryWriter); + aEntryWriter.WriteObject(mOtherPid); + aEntryWriter.WriteObject(mMessageSeqno); + aEntryWriter.WriteObject(mMessageType); + aEntryWriter.WriteObject(mSide); + aEntryWriter.WriteObject(mDirection); + aEntryWriter.WriteObject(mPhase); + aEntryWriter.WriteObject(mSync); +} + +// static +UniquePtr<ProfilerMarkerPayload> IPCMarkerPayload::Deserialize( + ProfileBufferEntryReader& aEntryReader) { + ProfilerMarkerPayload::CommonProps props = + DeserializeCommonProps(aEntryReader); + auto otherPid = aEntryReader.ReadObject<int32_t>(); + auto messageSeqno = aEntryReader.ReadObject<int32_t>(); + auto messageType = aEntryReader.ReadObject<IPC::Message::msgid_t>(); + auto side = aEntryReader.ReadObject<ipc::Side>(); + auto direction = aEntryReader.ReadObject<ipc::MessageDirection>(); + auto phase = aEntryReader.ReadObject<ipc::MessagePhase>(); + auto sync = aEntryReader.ReadObject<bool>(); + return UniquePtr<ProfilerMarkerPayload>( + new IPCMarkerPayload(std::move(props), otherPid, messageSeqno, + messageType, side, direction, phase, sync)); +} + +static Span<const char> IPCSideToString(ipc::Side aSide) { + switch (aSide) { + case ipc::ParentSide: + return MakeStringSpan("parent"); + case ipc::ChildSide: + return MakeStringSpan("child"); + case ipc::UnknownSide: + return MakeStringSpan("unknown"); + default: + MOZ_ASSERT_UNREACHABLE("Invalid IPC side"); + return MakeStringSpan("<invalid IPC side>"); + } +} + +static Span<const char> IPCPhaseToString(ipc::MessagePhase aPhase) { + switch (aPhase) { + case ipc::MessagePhase::Endpoint: + return MakeStringSpan("endpoint"); + case ipc::MessagePhase::TransferStart: + return MakeStringSpan("transferStart"); + case ipc::MessagePhase::TransferEnd: + return MakeStringSpan("transferEnd"); + default: + MOZ_ASSERT_UNREACHABLE("Invalid IPC phase"); + return MakeStringSpan("<invalid IPC phase>"); + } +} + +void IPCMarkerPayload::StreamPayload(SpliceableJSONWriter& aWriter, + const TimeStamp& aProcessStartTime, + UniqueStacks& aUniqueStacks) const { + using namespace mozilla::ipc; + StreamCommonProps("IPC", aWriter, aProcessStartTime, aUniqueStacks); + + // This payload still streams a startTime and endTime property because it made + // the migration to MarkerTiming on the front-end easier. + StreamStartEndTime(aWriter, aProcessStartTime); + + aWriter.IntProperty("otherPid", mOtherPid); + aWriter.IntProperty("messageSeqno", mMessageSeqno); + aWriter.StringProperty( + "messageType", + MakeStringSpan(IPC::StringFromIPCMessageType(mMessageType))); + aWriter.StringProperty("side", IPCSideToString(mSide)); + aWriter.StringProperty("direction", mDirection == MessageDirection::eSending + ? MakeStringSpan("sending") + : MakeStringSpan("receiving")); + aWriter.StringProperty("phase", IPCPhaseToString(mPhase)); + aWriter.BoolProperty("sync", mSync); +}
--- a/tools/profiler/core/RegisteredThread.cpp +++ b/tools/profiler/core/RegisteredThread.cpp @@ -32,16 +32,17 @@ RegisteredThread::~RegisteredThread() { size_t RegisteredThread::SizeOfIncludingThis( mozilla::MallocSizeOf aMallocSizeOf) const { size_t n = aMallocSizeOf(this); // Measurement of the following members may be added later if DMD finds it // is worthwhile: // - mPlatformData + // - mRacyRegisteredThread.mPendingMarkers // // The following members are not measured: // - mThreadInfo: because it is non-owning return n; } void RegisteredThread::GetRunningEventDelay(const mozilla::TimeStamp& aNow,
--- a/tools/profiler/core/memory_hooks.cpp +++ b/tools/profiler/core/memory_hooks.cpp @@ -260,16 +260,20 @@ class ThreadIntercept { // needed because collecting stacks creates new allocations. When blocked, // these allocations are then ignored by the memory hook. static PROFILER_THREAD_LOCAL(bool) tlsIsBlocked; // This is a quick flag to check and see if the allocations feature is enabled // or disabled. static mozilla::Atomic<bool, mozilla::Relaxed> sAllocationsFeatureEnabled; + // The markers will be stored on the main thread. Retain the id to the main + // thread of this process here. + static mozilla::Atomic<int, mozilla::Relaxed> sMainThreadId; + ThreadIntercept() = default; // Only allow consumers to access this information if they run // ThreadIntercept::MaybeGet and ask through the non-static version. static bool IsBlocked_() { // When the native allocations feature is turned on, memory hooks run on // every single allocation. For a subset of these allocations, the stack // gets sampled by running profiler_get_backtrace(), which locks the @@ -306,26 +310,33 @@ class ThreadIntercept { void Unblock() { MOZ_ASSERT(tlsIsBlocked.get()); tlsIsBlocked.set(false); } bool IsBlocked() const { return ThreadIntercept::IsBlocked_(); } - static void EnableAllocationFeature() { sAllocationsFeatureEnabled = true; } + static void EnableAllocationFeature(int aMainThreadId) { + sAllocationsFeatureEnabled = true; + sMainThreadId = aMainThreadId; + } static void DisableAllocationFeature() { sAllocationsFeatureEnabled = false; } + + static int MainThreadId() { return sMainThreadId; } }; PROFILER_THREAD_LOCAL(bool) ThreadIntercept::tlsIsBlocked; mozilla::Atomic<bool, mozilla::Relaxed> ThreadIntercept::sAllocationsFeatureEnabled(false); +mozilla::Atomic<int, mozilla::Relaxed> ThreadIntercept::sMainThreadId(0); + // An object of this class must be created (on the stack) before running any // code that might allocate. class AutoBlockIntercepts { ThreadIntercept& mThreadIntercept; public: // Disallow copy and assign. AutoBlockIntercepts(const AutoBlockIntercepts&) = delete; @@ -373,17 +384,17 @@ static void AllocCallback(void* aPtr, si // larger allocations are weighted heavier than smaller allocations. MOZ_ASSERT(gBernoulli, "gBernoulli must be properly installed for the memory hooks."); if ( // First perform the Bernoulli trial. gBernoulli->trial(actualSize) && // Second, attempt to add a marker if the Bernoulli trial passed. profiler_add_native_allocation_marker( - static_cast<int64_t>(actualSize), + ThreadIntercept::MainThreadId(), static_cast<int64_t>(actualSize), reinterpret_cast<uintptr_t>(aPtr))) { MOZ_ASSERT(gAllocationTracker, "gAllocationTracker must be properly installed for the memory " "hooks."); // Only track the memory if the allocation marker was actually added to the // profiler. gAllocationTracker->AddMemoryAddress(aPtr); } @@ -416,17 +427,18 @@ static void FreeCallback(void* aPtr) { // Perform a bernoulli trial, which will return true or false based on its // configured probability. It takes into account the byte size so that // larger allocations are weighted heavier than smaller allocations. MOZ_ASSERT( gAllocationTracker, "gAllocationTracker must be properly installed for the memory hooks."); if (gAllocationTracker->RemoveMemoryAddressIfFound(aPtr)) { // This size here is negative, indicating a deallocation. - profiler_add_native_allocation_marker(signedSize, + profiler_add_native_allocation_marker(ThreadIntercept::MainThreadId(), + signedSize, reinterpret_cast<uintptr_t>(aPtr)); } } } // namespace mozilla::profiler //--------------------------------------------------------------------------- // malloc/free interception @@ -550,17 +562,17 @@ void install_memory_hooks() { } // Remove the hooks, but leave the sCounter machinery. Deleting the counter // would race with any existing memory hooks that are currently running. Rather // than adding overhead here of mutexes it's cheaper for the performance to just // leak these values. void remove_memory_hooks() { jemalloc_replace_dynamic(nullptr); } -void enable_native_allocations() { +void enable_native_allocations(int aMainThreadId) { // The bloat log tracks allocations and deallocations. This can conflict // with the memory hook machinery, as the bloat log creates its own // allocations. This means we can re-enter inside the bloat log machinery. At // this time, the bloat log does not know about cannot handle the native // allocation feature. // // At the time of this writing, we hit this assertion: // IsIdle(oldState) || IsRead(oldState) in Checker::StartReadOp() @@ -579,17 +591,17 @@ void enable_native_allocations() { // #12: profiler_get_backtrace() // ... MOZ_ASSERT(!PR_GetEnv("XPCOM_MEM_BLOAT_LOG"), "The bloat log feature is not compatible with the native " "allocations instrumentation."); EnsureBernoulliIsInstalled(); EnsureAllocationTrackerIsInstalled(); - ThreadIntercept::EnableAllocationFeature(); + ThreadIntercept::EnableAllocationFeature(aMainThreadId); } // This is safe to call even if native allocations hasn't been enabled. void disable_native_allocations() { ThreadIntercept::DisableAllocationFeature(); if (gAllocationTracker) { gAllocationTracker->Reset(); }
--- a/tools/profiler/core/memory_hooks.h +++ b/tools/profiler/core/memory_hooks.h @@ -8,16 +8,16 @@ #define memory_hooks_h #if defined(MOZ_REPLACE_MALLOC) && defined(MOZ_PROFILER_MEMORY) namespace mozilla { namespace profiler { void install_memory_hooks(); void remove_memory_hooks(); -void enable_native_allocations(); +void enable_native_allocations(int aMainThreadId); void disable_native_allocations(); } // namespace profiler } // namespace mozilla #endif #endif
--- a/tools/profiler/core/platform.cpp +++ b/tools/profiler/core/platform.cpp @@ -32,31 +32,31 @@ #include "GeckoProfilerReporter.h" #include "PageInformation.h" #include "ProfileBuffer.h" #include "ProfiledThreadData.h" #include "ProfilerBacktrace.h" #include "ProfilerChild.h" #include "ProfilerCodeAddressService.h" #include "ProfilerIOInterposeObserver.h" +#include "ProfilerMarkerPayload.h" #include "ProfilerParent.h" #include "RegisteredThread.h" #include "shared-libraries.h" #include "ThreadInfo.h" #include "VTuneProfiler.h" #include "js/TraceLoggerAPI.h" #include "js/ProfilingFrameIterator.h" #include "memory_hooks.h" #include "mozilla/ArrayUtils.h" #include "mozilla/Atomics.h" #include "mozilla/AutoProfilerLabel.h" #include "mozilla/ExtensionPolicyService.h" #include "mozilla/extensions/WebExtensionPolicy.h" -#include "mozilla/net/HttpBaseChannel.h" // for net::TimingStruct #include "mozilla/Printf.h" #include "mozilla/ProfileBufferChunkManagerSingle.h" #include "mozilla/ProfileBufferChunkManagerWithLocalLimit.h" #include "mozilla/ProfileChunkedBuffer.h" #include "mozilla/SchedulerGroup.h" #include "mozilla/Services.h" #include "mozilla/StackWalk.h" #include "mozilla/StaticPtr.h" @@ -1626,16 +1626,54 @@ void ProfilingStackOwner::DumpStackAndCr } MOZ_CRASH("Non-empty stack!"); } // The name of the main thread. static const char* const kMainThreadName = "GeckoMain"; +// TODO - It is better to have the marker timing created by the original callers +// of the profiler_add_marker API, rather than deduce it from the payload. This +// is a bigger code diff for adding MarkerTiming, so do that work in a +// follow-up. +MarkerTiming get_marker_timing_from_payload( + const ProfilerMarkerPayload& aPayload) { + const TimeStamp& start = aPayload.GetStartTime(); + const TimeStamp& end = aPayload.GetEndTime(); + if (start.IsNull()) { + if (end.IsNull()) { + // The payload contains no time information, use the current time. + return MarkerTiming::InstantAt(TimeStamp::NowUnfuzzed()); + } + return MarkerTiming::IntervalEnd(end); + } + if (end.IsNull()) { + return MarkerTiming::IntervalStart(start); + } + if (start == end) { + return MarkerTiming::InstantAt(start); + } + return MarkerTiming::Interval(start, end); +} + +// Add the marker to the given buffer with the given information. +// This is a unified insertion point for all the markers. +static void StoreMarker(ProfileChunkedBuffer& aChunkedBuffer, int aThreadId, + const char* aMarkerName, + const MarkerTiming& aMarkerTiming, + JS::ProfilingCategoryPair aCategoryPair, + const ProfilerMarkerPayload* aPayload) { + aChunkedBuffer.PutObjects( + ProfileBufferEntry::Kind::MarkerData, aThreadId, + WrapProfileBufferUnownedCString(aMarkerName), + aMarkerTiming.GetStartTime(), aMarkerTiming.GetEndTime(), + aMarkerTiming.GetPhase(), static_cast<uint32_t>(aCategoryPair), aPayload); +} + //////////////////////////////////////////////////////////////////////// // BEGIN sampling/unwinding code // The registers used for stack unwinding and a few other sampling purposes. // The ctor does nothing; users are responsible for filling in the fields. class Registers { public: Registers() : mPC{nullptr}, mSP{nullptr}, mFP{nullptr}, mLR{nullptr} {} @@ -2493,17 +2531,17 @@ static PreRecordedMetaInformation PreRec return info; } static void StreamMetaJSCustomObject( PSLockRef aLock, SpliceableJSONWriter& aWriter, bool aIsShuttingDown, const PreRecordedMetaInformation& aPreRecordedMetaInformation) { MOZ_RELEASE_ASSERT(CorePS::Exists() && ActivePS::Exists(aLock)); - aWriter.IntProperty("version", 22); + aWriter.IntProperty("version", 21); // The "startTime" field holds the number of milliseconds since midnight // January 1, 1970 GMT. This grotty code computes (Now - (Now - // ProcessStartTime)) to convert CorePS::ProcessStartTime() into that form. TimeDuration delta = TimeStamp::NowUnfuzzed() - CorePS::ProcessStartTime(); aWriter.DoubleProperty( "startTime", static_cast<double>(PR_Now() / 1000.0 - delta.ToMilliseconds())); @@ -2739,25 +2777,29 @@ static void CollectJavaThreadProfileData : CorePS::ProcessStartTime() + TimeDuration::FromMilliseconds(endTimeMs); MarkerTiming timing = endTimeMs == 0 ? MarkerTiming::InstantAt(startTime) : MarkerTiming::Interval(startTime, endTime); if (!text) { // This marker doesn't have a text. - AddMarkerToBuffer(aProfileBuffer.UnderlyingChunkedBuffer(), markerName, - geckoprofiler::category::JAVA_ANDROID, - {MarkerThreadId(threadId), std::move(timing)}); + StoreMarker(aProfileBuffer.UnderlyingChunkedBuffer(), threadId, + markerName.get(), timing, + JS::ProfilingCategoryPair::JAVA_ANDROID, nullptr); } else { // This marker has a text. - AddMarkerToBuffer(aProfileBuffer.UnderlyingChunkedBuffer(), markerName, - geckoprofiler::category::JAVA_ANDROID, - {MarkerThreadId(threadId), std::move(timing)}, - geckoprofiler::markers::Text{}, text->ToCString()); + nsCString textString = text->ToCString(); + const TextMarkerPayload payload(textString, startTime, endTime, Nothing(), + nullptr); + + // Put the marker inside the buffer. + StoreMarker(aProfileBuffer.UnderlyingChunkedBuffer(), threadId, + markerName.get(), timing, + JS::ProfilingCategoryPair::JAVA_ANDROID, &payload); } } } #endif UniquePtr<ProfilerCodeAddressService> profiler_code_address_service_for_presymbolication() { static const bool preSymbolicate = []() { @@ -4547,17 +4589,17 @@ static void locked_profiler_start(PSLock if (HasMinimumLength(baseprofile.get(), 2)) { // The BaseProfiler startup profile will be stored as a separate "process" // in the Gecko Profiler profile, and shown as a new track under the // corresponding Gecko Profiler thread. ActivePS::AddBaseProfileThreads(aLock, std::move(baseprofile)); } // Set up profiling for each registered thread, if appropriate. - bool isMainThreadBeingProfiled = false; + Maybe<int> mainThreadId; int tid = profiler_current_thread_id(); const Vector<UniquePtr<RegisteredThread>>& registeredThreads = CorePS::RegisteredThreads(aLock); for (auto& registeredThread : registeredThreads) { RefPtr<ThreadInfo> info = registeredThread->Info(); if (ActivePS::ShouldProfileThread(aLock, info)) { registeredThread->RacyRegisteredThread().SetIsBeingProfiled(true); @@ -4574,17 +4616,17 @@ static void locked_profiler_start(PSLock } else if (info->IsMainThread()) { // Dispatch a runnable to the main thread to call PollJSSampling(), // so that we don't have wait for the next JS interrupt callback in // order to start profiling JS. TriggerPollJSSamplingOnMainThread(); } } if (info->IsMainThread()) { - isMainThreadBeingProfiled = true; + mainThreadId = Some(info->ThreadId()); } registeredThread->RacyRegisteredThread().ReinitializeOnResume(); if (registeredThread->GetJSContext()) { profiledThreadData->NotifyReceivedJSContext(0); } } } @@ -4610,18 +4652,18 @@ static void locked_profiler_start(PSLock java::GeckoJavaSampler::Start( javaInterval, std::round((double)(capacity.Value()) * interval / (double)(javaInterval))); } #endif #if defined(MOZ_REPLACE_MALLOC) && defined(MOZ_PROFILER_MEMORY) if (ActivePS::FeatureNativeAllocations(aLock)) { - if (isMainThreadBeingProfiled) { - mozilla::profiler::enable_native_allocations(); + if (mainThreadId.isSome()) { + mozilla::profiler::enable_native_allocations(mainThreadId.value()); } else { NS_WARNING( "The nativeallocations feature is turned on, but the main thread is " "not being profiled. The allocations are only stored on the main " "thread."); } } #endif @@ -5020,16 +5062,21 @@ void profiler_add_sampled_counter(BasePr void profiler_remove_sampled_counter(BaseProfilerCount* aCounter) { DEBUG_LOG("profiler_remove_sampled_counter(%s)", aCounter->mLabel); PSAutoLock lock(gPSMutex); // Note: we don't enforce a final sample, though we could do so if the // profiler was active CorePS::RemoveCounter(lock, aCounter); } +static void maybelocked_profiler_add_marker_for_thread( + int aThreadId, JS::ProfilingCategoryPair aCategoryPair, + const char* aMarkerName, const ProfilerMarkerPayload& aPayload, + const PSAutoLock* aLockOrNull); + ProfilingStack* profiler_register_thread(const char* aName, void* aGuessStackTop) { DEBUG_LOG("profiler_register_thread(%s)", aName); MOZ_RELEASE_ASSERT(CorePS::Exists()); // Make sure we have a nsThread wrapper for the current thread, and that NSPR // knows its name. @@ -5056,18 +5103,20 @@ ProfilingStack* profiler_register_thread // In the meantime, we record a marker that could be used in the frontend. nsCString text("Thread "); text.AppendInt(profiler_current_thread_id()); text.AppendLiteral(" \""); text.AppendASCII(thread->Info()->Name()); text.AppendLiteral("\" attempted to re-register as \""); text.AppendASCII(aName); text.AppendLiteral("\""); - PROFILER_MARKER_TEXT("profiler_register_thread again", OTHER_Profiling, - MarkerThreadId::MainThread(), text); + maybelocked_profiler_add_marker_for_thread( + profiler_main_thread_id(), JS::ProfilingCategoryPair::OTHER_Profiling, + "profiler_register_thread again", + TextMarkerPayload(text, TimeStamp::NowUnfuzzed()), &lock); return &thread->RacyRegisteredThread().ProfilingStack(); } void* stackTop = GetStackTop(aGuessStackTop); return locked_register_thread(lock, aName, stackTop); } @@ -5137,18 +5186,20 @@ void profiler_unregister_thread() { // We cannot record a marker on this thread because it was already // unregistered. Send it to the main thread (unless this *is* already the // main thread, which has been unregistered); this may be useful to catch // mismatched register/unregister pairs in Firefox. if (int tid = profiler_current_thread_id(); tid != profiler_main_thread_id()) { nsCString threadIdString; threadIdString.AppendInt(tid); - PROFILER_MARKER_TEXT("profiler_unregister_thread again", OTHER_Profiling, - MarkerThreadId::MainThread(), threadIdString); + maybelocked_profiler_add_marker_for_thread( + profiler_main_thread_id(), JS::ProfilingCategoryPair::OTHER_Profiling, + "profiler_unregister_thread again", + TextMarkerPayload(threadIdString, TimeStamp::NowUnfuzzed()), &lock); } } } void profiler_register_page(uint64_t aBrowsingContextID, uint64_t aInnerWindowID, const nsCString& aUrl, uint64_t aEmbedderInnerWindowID) { DEBUG_LOG("profiler_register_page(%" PRIu64 ", %" PRIu64 ", %s, %" PRIu64 ")", @@ -5360,270 +5411,254 @@ UniqueProfilerBacktrace profiler_get_bac return UniqueProfilerBacktrace( new ProfilerBacktrace("SyncProfile", std::move(buffer))); } void ProfilerBacktraceDestructor::operator()(ProfilerBacktrace* aBacktrace) { delete aBacktrace; } +static void racy_profiler_add_marker(const char* aMarkerName, + JS::ProfilingCategoryPair aCategoryPair, + const ProfilerMarkerPayload* aPayload) { + MOZ_RELEASE_ASSERT(CorePS::Exists()); + + // This function is hot enough that we use RacyFeatures, not ActivePS. + if (!profiler_can_accept_markers()) { + return; + } + + // Note that it's possible that the above test would change again before we + // actually record the marker. Because of this imprecision it's possible to + // miss a marker or record one we shouldn't. Either way is not a big deal. + + RacyRegisteredThread* racyRegisteredThread = + TLSRegisteredThread::RacyRegisteredThread(); + if (!racyRegisteredThread || !racyRegisteredThread->IsBeingProfiled()) { + return; + } + + const MarkerTiming markerTiming = + aPayload ? get_marker_timing_from_payload(*aPayload) + : MarkerTiming::InstantNow(); + + StoreMarker(CorePS::CoreBuffer(), racyRegisteredThread->ThreadId(), + aMarkerName, markerTiming, aCategoryPair, aPayload); +} + +void profiler_add_marker(const char* aMarkerName, + JS::ProfilingCategoryPair aCategoryPair, + const ProfilerMarkerPayload& aPayload) { + racy_profiler_add_marker(aMarkerName, aCategoryPair, &aPayload); +} + // This is a simplified version of profiler_add_marker that can be easily passed // into the JS engine. void profiler_add_js_marker(const char* aMarkerName, const char* aMarkerText) { PROFILER_MARKER_TEXT( ProfilerString8View::WrapNullTerminatedString(aMarkerName), JS, {}, ProfilerString8View::WrapNullTerminatedString(aMarkerText)); } void profiler_add_js_allocation_marker(JS::RecordAllocationInfo&& info) { if (!profiler_can_accept_markers()) { return; } - - struct JsAllocationMarker { - static constexpr mozilla::Span<const char> MarkerTypeName() { - return mozilla::MakeStringSpan("JS allocation"); - } - static void StreamJSONMarkerData( - mozilla::baseprofiler::SpliceableJSONWriter& aWriter, - const mozilla::ProfilerString16View& aTypeName, - const mozilla::ProfilerString8View& aClassName, - const mozilla::ProfilerString16View& aDescriptiveTypeName, - const mozilla::ProfilerString8View& aCoarseType, uint64_t aSize, - bool aInNursery) { - if (aClassName.Length() != 0) { - aWriter.StringProperty("className", aClassName); - } - if (aTypeName.Length() != 0) { - aWriter.StringProperty( - "typeName", - NS_ConvertUTF16toUTF8(aTypeName.Data(), aTypeName.Length())); - } - if (aDescriptiveTypeName.Length() != 0) { - aWriter.StringProperty( - "descriptiveTypeName", - NS_ConvertUTF16toUTF8(aDescriptiveTypeName.Data(), - aDescriptiveTypeName.Length())); - } - aWriter.StringProperty("coarseType", aCoarseType); - aWriter.IntProperty("size", aSize); - aWriter.BoolProperty("inNursery", aInNursery); - } - static mozilla::MarkerSchema MarkerTypeDisplay() { - return mozilla::MarkerSchema::SpecialFrontendLocation{}; - } - }; - + AUTO_PROFILER_STATS(add_marker_with_JsAllocationMarkerPayload); profiler_add_marker( - "JS allocation", geckoprofiler::category::JS, MarkerStack::Capture(), - JsAllocationMarker{}, - ProfilerString16View::WrapNullTerminatedString(info.typeName), - ProfilerString8View::WrapNullTerminatedString(info.className), - ProfilerString16View::WrapNullTerminatedString(info.descriptiveTypeName), - ProfilerString8View::WrapNullTerminatedString(info.coarseType), info.size, - info.inNursery); + "JS allocation", JS::ProfilingCategoryPair::JS, + JsAllocationMarkerPayload(TimeStamp::Now(), std::move(info), + profiler_get_backtrace())); } bool profiler_is_locked_on_current_thread() { // This function is used to help users avoid calling `profiler_...` functions // when the profiler may already have a lock in place, which would prevent a // 2nd recursive lock (resulting in a crash or a never-ending wait). // So we must return `true` for any of: // - The main profiler mutex, used by most functions, and/or // - The buffer mutex, used directly in some functions without locking the // main mutex, e.g., marker-related functions. return gPSMutex.IsLockedOnCurrentThread() || CorePS::CoreBuffer().IsThreadSafeAndLockedOnCurrentThread(); } -static constexpr net::TimingStruct scEmptyNetTimingStruct; - void profiler_add_network_marker( nsIURI* aURI, const nsACString& aRequestMethod, int32_t aPriority, uint64_t aChannelId, NetworkLoadType aType, mozilla::TimeStamp aStart, mozilla::TimeStamp aEnd, int64_t aCount, mozilla::net::CacheDisposition aCacheDisposition, uint64_t aInnerWindowID, const mozilla::net::TimingStruct* aTimings, nsIURI* aRedirectURI, - UniquePtr<ProfileChunkedBuffer> aSource, + UniqueProfilerBacktrace aSource, const Maybe<nsDependentCString>& aContentType) { if (!profiler_can_accept_markers()) { return; } - - nsAutoCStringN<2048> name; - name.AppendASCII("Load "); - // top 32 bits are process id of the load - name.AppendInt(aChannelId & 0xFFFFFFFFu); - - // These can do allocations/frees/etc; avoid if not active - nsAutoCStringN<2048> spec; + // These do allocations/frees/etc; avoid if not active + nsAutoCString spec; + nsAutoCString redirect_spec; if (aURI) { aURI->GetAsciiSpec(spec); - name.AppendASCII(": "); - name.Append(spec); - } - - nsAutoCString redirect_spec; + } if (aRedirectURI) { aRedirectURI->GetAsciiSpec(redirect_spec); } - - struct NetworkMarker { - static constexpr Span<const char> MarkerTypeName() { - return MakeStringSpan("Network"); + // top 32 bits are process id of the load + uint32_t id = static_cast<uint32_t>(aChannelId & 0xFFFFFFFF); + char name[2048]; + SprintfLiteral(name, "Load %d: %s", id, PromiseFlatCString(spec).get()); + AUTO_PROFILER_STATS(add_marker_with_NetworkMarkerPayload); + profiler_add_marker( + name, JS::ProfilingCategoryPair::NETWORK, + NetworkMarkerPayload(static_cast<int64_t>(aChannelId), + PromiseFlatCString(spec).get(), aRequestMethod, + aType, aStart, aEnd, aPriority, aCount, + aCacheDisposition, aInnerWindowID, aTimings, + PromiseFlatCString(redirect_spec).get(), + std::move(aSource), aContentType)); +} + +static void maybelocked_profiler_add_marker_for_thread( + int aThreadId, JS::ProfilingCategoryPair aCategoryPair, + const char* aMarkerName, const ProfilerMarkerPayload& aPayload, + const PSAutoLock* aLockOrNull) { + MOZ_RELEASE_ASSERT(CorePS::Exists()); + + if (!profiler_can_accept_markers()) { + return; + } + +#ifdef DEBUG + auto checkThreadId = [](int aThreadId, const PSAutoLock& aLock) { + if (!ActivePS::Exists(aLock)) { + return; } - static void StreamJSONMarkerData( - baseprofiler::SpliceableJSONWriter& aWriter, mozilla::TimeStamp aStart, - mozilla::TimeStamp aEnd, int64_t aID, const ProfilerString8View& aURI, - const ProfilerString8View& aRequestMethod, NetworkLoadType aType, - int32_t aPri, int64_t aCount, net::CacheDisposition aCacheDisposition, - const net::TimingStruct& aTimings, - const ProfilerString8View& aRedirectURI, - const ProfilerString8View& aContentType) { - // This payload still streams a startTime and endTime property because it - // made the migration to MarkerTiming on the front-end easier. - baseprofiler::WritePropertyTime(aWriter, "startTime", aStart); - baseprofiler::WritePropertyTime(aWriter, "endTime", aEnd); - - aWriter.IntProperty("id", aID); - aWriter.StringProperty("status", GetNetworkState(aType)); - if (Span<const char> cacheString = GetCacheState(aCacheDisposition); - !cacheString.IsEmpty()) { - aWriter.StringProperty("cache", cacheString); - } - aWriter.IntProperty("pri", aPri); - if (aCount > 0) { - aWriter.IntProperty("count", aCount); - } - if (aURI.Length() != 0) { - aWriter.StringProperty("URI", aURI); - } - if (aRedirectURI.Length() != 0) { - aWriter.StringProperty("RedirectURI", aRedirectURI); - } - aWriter.StringProperty("requestMethod", aRequestMethod); - - if (aContentType.Length() != 0) { - aWriter.StringProperty("contentType", aContentType); - } else { - aWriter.NullProperty("contentType"); - } - - if (aType != NetworkLoadType::LOAD_START) { - baseprofiler::WritePropertyTime(aWriter, "domainLookupStart", - aTimings.domainLookupStart); - baseprofiler::WritePropertyTime(aWriter, "domainLookupEnd", - aTimings.domainLookupEnd); - baseprofiler::WritePropertyTime(aWriter, "connectStart", - aTimings.connectStart); - baseprofiler::WritePropertyTime(aWriter, "tcpConnectEnd", - aTimings.tcpConnectEnd); - baseprofiler::WritePropertyTime(aWriter, "secureConnectionStart", - aTimings.secureConnectionStart); - baseprofiler::WritePropertyTime(aWriter, "connectEnd", - aTimings.connectEnd); - baseprofiler::WritePropertyTime(aWriter, "requestStart", - aTimings.requestStart); - baseprofiler::WritePropertyTime(aWriter, "responseStart", - aTimings.responseStart); - baseprofiler::WritePropertyTime(aWriter, "responseEnd", - aTimings.responseEnd); + + // Assert that our thread ID makes sense + bool realThread = false; + const Vector<UniquePtr<RegisteredThread>>& registeredThreads = + CorePS::RegisteredThreads(aLock); + for (auto& thread : registeredThreads) { + RefPtr<ThreadInfo> info = thread->Info(); + if (info->ThreadId() == aThreadId) { + realThread = true; + break; } } - static MarkerSchema MarkerTypeDisplay() { - return MarkerSchema::SpecialFrontendLocation{}; - } - - private: - static Span<const char> GetNetworkState(NetworkLoadType aType) { - switch (aType) { - case NetworkLoadType::LOAD_START: - return MakeStringSpan("STATUS_START"); - case NetworkLoadType::LOAD_STOP: - return MakeStringSpan("STATUS_STOP"); - case NetworkLoadType::LOAD_REDIRECT: - return MakeStringSpan("STATUS_REDIRECT"); - default: - return MakeStringSpan(""); - } - } - - static Span<const char> GetCacheState( - net::CacheDisposition aCacheDisposition) { - switch (aCacheDisposition) { - case net::kCacheUnresolved: - return MakeStringSpan("Unresolved"); - case net::kCacheHit: - return MakeStringSpan("Hit"); - case net::kCacheHitViaReval: - return MakeStringSpan("HitViaReval"); - case net::kCacheMissedViaReval: - return MakeStringSpan("MissedViaReval"); - case net::kCacheMissed: - return MakeStringSpan("Missed"); - case net::kCacheUnknown: - default: - return MakeStringSpan(""); - } - } + MOZ_ASSERT(realThread, "Invalid thread id"); }; - profiler_add_marker( - name, geckoprofiler::category::NETWORK, - {MarkerTiming::Interval(aStart, aEnd), - MarkerStack::TakeBacktrace(std::move(aSource)), - MarkerInnerWindowId(aInnerWindowID)}, - NetworkMarker{}, aStart, aEnd, static_cast<int64_t>(aChannelId), spec, - aRequestMethod, aType, aPriority, aCount, aCacheDisposition, - aTimings ? *aTimings : scEmptyNetTimingStruct, redirect_spec, - aContentType ? ProfilerString8View(*aContentType) - : ProfilerString8View()); + if (aLockOrNull) { + checkThreadId(aThreadId, *aLockOrNull); + } else { + PSAutoLock lock(gPSMutex); + checkThreadId(aThreadId, lock); + } +#endif + + StoreMarker(CorePS::CoreBuffer(), aThreadId, aMarkerName, + get_marker_timing_from_payload(aPayload), aCategoryPair, + &aPayload); } -bool profiler_add_native_allocation_marker(int64_t aSize, +void profiler_add_marker_for_thread(int aThreadId, + JS::ProfilingCategoryPair aCategoryPair, + const char* aMarkerName, + const ProfilerMarkerPayload& aPayload) { + return maybelocked_profiler_add_marker_for_thread( + aThreadId, aCategoryPair, aMarkerName, aPayload, nullptr); +} + +void profiler_add_marker_for_mainthread(JS::ProfilingCategoryPair aCategoryPair, + const char* aMarkerName, + const ProfilerMarkerPayload& aPayload) { + profiler_add_marker_for_thread(profiler_main_thread_id(), aCategoryPair, + aMarkerName, aPayload); +} + +bool profiler_add_native_allocation_marker(int aMainThreadId, int64_t aSize, uintptr_t aMemoryAddress) { if (!profiler_can_accept_markers()) { return false; } // Because native allocations may be intercepted anywhere, blocking while // locking the profiler mutex here could end up causing a deadlock if another // mutex is taken, which the profiler may indirectly need elsewhere. // See bug 1642726 for such a scenario. // So instead we bail out if the mutex is already locked. Native allocations // are statistically sampled anyway, so missing a few because of this is // acceptable. if (gPSMutex.IsLockedOnCurrentThread()) { return false; } - struct NativeAllocationMarker { - static constexpr mozilla::Span<const char> MarkerTypeName() { - return mozilla::MakeStringSpan("Native allocation"); - } - static void StreamJSONMarkerData( - mozilla::baseprofiler::SpliceableJSONWriter& aWriter, int64_t aSize, - uintptr_t aMemoryAddress, int aThreadId) { - aWriter.IntProperty("size", aSize); - aWriter.IntProperty("memoryAddress", - static_cast<int64_t>(aMemoryAddress)); - aWriter.IntProperty("threadId", aThreadId); - } - static mozilla::MarkerSchema MarkerTypeDisplay() { - return mozilla::MarkerSchema::SpecialFrontendLocation{}; - } - }; - - profiler_add_marker("Native allocation", geckoprofiler::category::OTHER, - {MarkerThreadId::MainThread(), MarkerStack::Capture()}, - NativeAllocationMarker{}, aSize, aMemoryAddress, - profiler_current_thread_id()); + AUTO_PROFILER_STATS(add_marker_with_NativeAllocationMarkerPayload); + maybelocked_profiler_add_marker_for_thread( + aMainThreadId, JS::ProfilingCategoryPair::OTHER, "Native allocation", + NativeAllocationMarkerPayload(TimeStamp::Now(), aSize, aMemoryAddress, + profiler_current_thread_id(), + profiler_get_backtrace()), + nullptr); return true; } +void profiler_tracing_marker(const char* aCategoryString, + const char* aMarkerName, + JS::ProfilingCategoryPair aCategoryPair, + TracingKind aKind, + const Maybe<uint64_t>& aInnerWindowID) { + MOZ_RELEASE_ASSERT(CorePS::Exists()); + + VTUNE_TRACING(aMarkerName, aKind); + + // This function is hot enough that we use RacyFeatures, notActivePS. + if (!profiler_can_accept_markers()) { + return; + } + + AUTO_PROFILER_STATS(add_marker_with_TracingMarkerPayload); + profiler_add_marker( + aMarkerName, aCategoryPair, + TracingMarkerPayload(aCategoryString, aKind, TimeStamp::NowUnfuzzed(), + aInnerWindowID)); +} + +void profiler_tracing_marker(const char* aCategoryString, + const char* aMarkerName, + JS::ProfilingCategoryPair aCategoryPair, + TracingKind aKind, UniqueProfilerBacktrace aCause, + const Maybe<uint64_t>& aInnerWindowID) { + MOZ_RELEASE_ASSERT(CorePS::Exists()); + + VTUNE_TRACING(aMarkerName, aKind); + + // This function is hot enough that we use RacyFeatures, notActivePS. + if (!profiler_can_accept_markers()) { + return; + } + + profiler_add_marker( + aMarkerName, aCategoryPair, + TracingMarkerPayload(aCategoryString, aKind, TimeStamp::NowUnfuzzed(), + aInnerWindowID, std::move(aCause))); +} + +void profiler_add_text_marker(const char* aMarkerName, const nsACString& aText, + JS::ProfilingCategoryPair aCategoryPair, + const mozilla::TimeStamp& aStartTime, + const mozilla::TimeStamp& aEndTime, + const mozilla::Maybe<uint64_t>& aInnerWindowID, + UniqueProfilerBacktrace aCause) { + AUTO_PROFILER_STATS(add_marker_with_TextMarkerPayload); + profiler_add_marker(aMarkerName, aCategoryPair, + TextMarkerPayload(aText, aStartTime, aEndTime, + aInnerWindowID, std::move(aCause))); +} + void profiler_set_js_context(JSContext* aCx) { MOZ_ASSERT(aCx); PSAutoLock lock(gPSMutex); RegisteredThread* registeredThread = TLSRegisteredThread::RegisteredThread(lock); if (!registeredThread) {
--- a/tools/profiler/moz.build +++ b/tools/profiler/moz.build @@ -10,27 +10,29 @@ if CONFIG["MOZ_GECKO_PROFILER"]: XPIDL_SOURCES += [ "gecko/nsIProfiler.idl", ] EXPORTS += [ "public/ChildProfilerController.h", "public/GeckoProfilerReporter.h", "public/ProfilerChild.h", "public/ProfilerCodeAddressService.h", + "public/ProfilerMarkerPayload.h", "public/ProfilerParent.h", "public/shared-libraries.h", ] UNIFIED_SOURCES += [ "core/PageInformation.cpp", "core/platform.cpp", "core/ProfileBuffer.cpp", "core/ProfileBufferEntry.cpp", "core/ProfiledThreadData.cpp", "core/ProfilerBacktrace.cpp", "core/ProfilerCodeAddressService.cpp", + "core/ProfilerMarkerPayload.cpp", "core/RegisteredThread.cpp", "gecko/ChildProfilerController.cpp", "gecko/nsProfilerStartParams.cpp", "gecko/ProfilerChild.cpp", "gecko/ProfilerIOInterposeObserver.cpp", "gecko/ProfilerParent.cpp", ] if CONFIG["MOZ_REPLACE_MALLOC"] and CONFIG["MOZ_PROFILER_MEMORY"]:
--- a/tools/profiler/public/GeckoProfiler.h +++ b/tools/profiler/public/GeckoProfiler.h @@ -57,16 +57,27 @@ # define AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING(label, categoryPair, nsCStr) # define AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING_NONSENSITIVE( \ label, categoryPair, nsCStr) # define AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING(label, categoryPair, nsStr) # define AUTO_PROFILER_LABEL_FAST(label, categoryPair, ctx) # define AUTO_PROFILER_LABEL_DYNAMIC_FAST(label, dynamicString, categoryPair, \ ctx, flags) +# define PROFILER_ADD_MARKER_WITH_PAYLOAD(markerName, categoryPair, \ + PayloadType, payloadArgs) + +# define PROFILER_TRACING_MARKER(categoryString, markerName, categoryPair, \ + kind) +# define PROFILER_TRACING_MARKER_DOCSHELL(categoryString, markerName, \ + categoryPair, kind, docshell) +# define AUTO_PROFILER_TRACING_MARKER(categoryString, markerName, categoryPair) +# define AUTO_PROFILER_TRACING_MARKER_DOCSHELL(categoryString, markerName, \ + categoryPair, docShell) + // Function stubs for when MOZ_GECKO_PROFILER is not defined. // This won't be used, it's just there to allow the empty definition of // `profiler_get_backtrace`. struct ProfilerBacktrace {}; using UniqueProfilerBacktrace = mozilla::UniquePtr<int>; // Get/Capture-backtrace functions can return nullptr or false, the result @@ -104,16 +115,17 @@ profiler_capture_backtrace() { # include "nscore.h" # include "nsString.h" # include <functional> # include <stdint.h> class ProfilerBacktrace; class ProfilerCodeAddressService; +class ProfilerMarkerPayload; namespace mozilla { class ProfileBufferControlledChunkManager; class ProfileChunkedBuffer; namespace baseprofiler { class SpliceableJSONWriter; } // namespace baseprofiler namespace net { struct TimingStruct; @@ -873,39 +885,68 @@ mozilla::Maybe<ProfilerBufferInfo> profi // additional set of flags. The flags parameter should carry values from the // js::ProfilingStackFrame::Flags enum. # define AUTO_PROFILER_LABEL_DYNAMIC_FAST(label, dynamicString, categoryPair, \ ctx, flags) \ mozilla::AutoProfilerLabel PROFILER_RAII( \ ctx, label, dynamicString, JS::ProfilingCategoryPair::categoryPair, \ flags) +// `PayloadType` is a sub-class of MarkerPayload, `parenthesizedPayloadArgs` is +// the argument list used to construct that `PayloadType`. E.g.: +// `PROFILER_ADD_MARKER_WITH_PAYLOAD("Load", DOM, TextMarkerPayload, +// ("text", start, end, ds, dsh))` +# define PROFILER_ADD_MARKER_WITH_PAYLOAD( \ + markerName, categoryPair, PayloadType, parenthesizedPayloadArgs) \ + do { \ + AUTO_PROFILER_STATS(add_marker_with_##PayloadType); \ + ::profiler_add_marker(markerName, \ + ::JS::ProfilingCategoryPair::categoryPair, \ + PayloadType parenthesizedPayloadArgs); \ + } while (false) + +void profiler_add_marker(const char* aMarkerName, + JS::ProfilingCategoryPair aCategoryPair, + const ProfilerMarkerPayload& aPayload); + void profiler_add_js_marker(const char* aMarkerName, const char* aMarkerText); void profiler_add_js_allocation_marker(JS::RecordAllocationInfo&& info); // Returns true or or false depending on whether the marker was actually added // or not. -bool profiler_add_native_allocation_marker(int64_t aSize, - uintptr_t aMemoryAddress); +bool profiler_add_native_allocation_marker(int aMainThreadId, int64_t aSize, + uintptr_t aMemorySize); // Returns true if the profiler lock is currently held *on the current thread*. // This may be used by re-entrant code that may call profiler functions while // the profiler already has the lock (which would deadlock). bool profiler_is_locked_on_current_thread(); +// Insert a marker in the profile timeline for a specified thread. +void profiler_add_marker_for_thread(int aThreadId, + JS::ProfilingCategoryPair aCategoryPair, + const char* aMarkerName, + const ProfilerMarkerPayload& aPayload); + +// Insert a marker in the profile timeline for the main thread. +// This may be used to gather some markers from any thread, that should be +// displayed in the main thread track. +void profiler_add_marker_for_mainthread(JS::ProfilingCategoryPair aCategoryPair, + const char* aMarkerName, + const ProfilerMarkerPayload& aPayload); + enum class NetworkLoadType { LOAD_START, LOAD_STOP, LOAD_REDIRECT }; void profiler_add_network_marker( nsIURI* aURI, const nsACString& aRequestMethod, int32_t aPriority, uint64_t aChannelId, NetworkLoadType aType, mozilla::TimeStamp aStart, mozilla::TimeStamp aEnd, int64_t aCount, mozilla::net::CacheDisposition aCacheDisposition, uint64_t aInnerWindowID, const mozilla::net::TimingStruct* aTimings = nullptr, - nsIURI* aRedirectURI = nullptr, - mozilla::UniquePtr<mozilla::ProfileChunkedBuffer> aSource = nullptr, + nsIURI* aRedirectURI = nullptr, UniqueProfilerBacktrace aSource = nullptr, const mozilla::Maybe<nsDependentCString>& aContentType = mozilla::Nothing()); enum TracingKind { TRACING_EVENT, TRACING_INTERVAL_START, TRACING_INTERVAL_END, }; @@ -923,16 +964,50 @@ inline mozilla::MarkerInnerWindowId Mark mozilla::Maybe<uint64_t> id = profiler_get_inner_window_id_from_docshell(aDocshell); if (!id) { return mozilla::MarkerInnerWindowId::NoId(); } return mozilla::MarkerInnerWindowId(*id); } +// Adds a tracing marker to the profile. A no-op if the profiler is inactive. + +# define PROFILER_TRACING_MARKER(categoryString, markerName, categoryPair, \ + kind) \ + profiler_tracing_marker(categoryString, markerName, \ + JS::ProfilingCategoryPair::categoryPair, kind) +# define PROFILER_TRACING_MARKER_DOCSHELL(categoryString, markerName, \ + categoryPair, kind, docShell) \ + profiler_tracing_marker( \ + categoryString, markerName, JS::ProfilingCategoryPair::categoryPair, \ + kind, profiler_get_inner_window_id_from_docshell(docShell)) + +void profiler_tracing_marker( + const char* aCategoryString, const char* aMarkerName, + JS::ProfilingCategoryPair aCategoryPair, TracingKind aKind, + const mozilla::Maybe<uint64_t>& aInnerWindowID = mozilla::Nothing()); +void profiler_tracing_marker( + const char* aCategoryString, const char* aMarkerName, + JS::ProfilingCategoryPair aCategoryPair, TracingKind aKind, + UniqueProfilerBacktrace aCause, + const mozilla::Maybe<uint64_t>& aInnerWindowID = mozilla::Nothing()); + +// Adds a START/END pair of tracing markers. +# define AUTO_PROFILER_TRACING_MARKER(categoryString, markerName, \ + categoryPair) \ + mozilla::AutoProfilerTracing PROFILER_RAII( \ + categoryString, markerName, JS::ProfilingCategoryPair::categoryPair, \ + mozilla::Nothing()) +# define AUTO_PROFILER_TRACING_MARKER_DOCSHELL(categoryString, markerName, \ + categoryPair, docShell) \ + mozilla::AutoProfilerTracing PROFILER_RAII( \ + categoryString, markerName, JS::ProfilingCategoryPair::categoryPair, \ + profiler_get_inner_window_id_from_docshell(docShell)) + //--------------------------------------------------------------------------- // Output profiles //--------------------------------------------------------------------------- // Set a user-friendly process name, used in JSON stream. Allows an optional // detailed name which may include private info (eTLD+1 in fission) void profiler_set_process_name(const nsACString& aProcessName, const nsACString* aETLDplus1 = nullptr); @@ -1145,16 +1220,54 @@ class MOZ_RAII AutoProfilerLabel { enum class State { Uninitialized = 0, Initialized, Unavailable }; static State sState; static MOZ_THREAD_LOCAL(ProfilingStackOwner*) sProfilingStackOwnerTLS; }; }; +class MOZ_RAII AutoProfilerTracing { + public: + AutoProfilerTracing(const char* aCategoryString, const char* aMarkerName, + JS::ProfilingCategoryPair aCategoryPair, + const mozilla::Maybe<uint64_t>& aInnerWindowID) + : mCategoryString(aCategoryString), + mMarkerName(aMarkerName), + mCategoryPair(aCategoryPair), + mInnerWindowID(aInnerWindowID) { + profiler_tracing_marker(mCategoryString, mMarkerName, aCategoryPair, + TRACING_INTERVAL_START, mInnerWindowID); + } + + AutoProfilerTracing(const char* aCategoryString, const char* aMarkerName, + JS::ProfilingCategoryPair aCategoryPair, + UniqueProfilerBacktrace aBacktrace, + const mozilla::Maybe<uint64_t>& aInnerWindowID) + : mCategoryString(aCategoryString), + mMarkerName(aMarkerName), + mCategoryPair(aCategoryPair), + mInnerWindowID(aInnerWindowID) { + profiler_tracing_marker(mCategoryString, mMarkerName, aCategoryPair, + TRACING_INTERVAL_START, std::move(aBacktrace), + mInnerWindowID); + } + + ~AutoProfilerTracing() { + profiler_tracing_marker(mCategoryString, mMarkerName, mCategoryPair, + TRACING_INTERVAL_END, mInnerWindowID); + } + + protected: + const char* mCategoryString; + const char* mMarkerName; + const JS::ProfilingCategoryPair mCategoryPair; + const mozilla::Maybe<uint64_t> mInnerWindowID; +}; + // Get the MOZ_PROFILER_STARTUP* environment variables that should be // supplied to a child process that is about to be launched, in order // to make that child process start with the same profiler settings as // in the current process. The given function is invoked once for // each variable to be set. void GetProfilerEnvVarsForChildProcess( std::function<void(const char* key, const char* value)>&& aSetEnv);
new file mode 100644 --- /dev/null +++ b/tools/profiler/public/ProfilerMarkerPayload.h @@ -0,0 +1,839 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef ProfilerMarkerPayload_h +#define ProfilerMarkerPayload_h + +#include "mozilla/Atomics.h" +#include "mozilla/Attributes.h" +#include "mozilla/ipc/ProtocolUtils.h" +#include "mozilla/Maybe.h" +#include "mozilla/net/TimingStruct.h" +#include "mozilla/Preferences.h" +#include "mozilla/ProfileBufferEntrySerialization.h" +#include "mozilla/RefPtr.h" +#include "mozilla/TimeStamp.h" +#include "mozilla/UniquePtr.h" +#include "mozilla/UniquePtrExtensions.h" + +#include "nsString.h" +#include "nsCRTGlue.h" +#include "GeckoProfiler.h" + +#include "js/Utility.h" +#include "js/AllocationRecording.h" +#include "js/ProfilingFrameIterator.h" +#include "gfxASurface.h" +#include "mozilla/ServoTraversalStatistics.h" + +namespace mozilla { +namespace baseprofiler { +class SpliceableJSONWriter; +} // namespace baseprofiler +namespace layers { +class Layer; +} // namespace layers +} // namespace mozilla + +class UniqueStacks; + +// This is an abstract class that can be implemented to supply data to be +// attached with a profiler marker. +// +// When subclassing this, note that the destructor can be called on any thread, +// i.e. not necessarily on the thread that created the object. +class ProfilerMarkerPayload { + public: + explicit ProfilerMarkerPayload( + const mozilla::Maybe<uint64_t>& aInnerWindowID = mozilla::Nothing(), + UniqueProfilerBacktrace aStack = nullptr) + : mCommonProps{mozilla::TimeStamp{}, mozilla::TimeStamp{}, + std::move(aStack), aInnerWindowID} {} + + ProfilerMarkerPayload( + const mozilla::TimeStamp& aStartTime, const mozilla::TimeStamp& aEndTime, + const mozilla::Maybe<uint64_t>& aInnerWindowID = mozilla::Nothing(), + UniqueProfilerBacktrace aStack = nullptr) + : mCommonProps{aStartTime, aEndTime, std::move(aStack), aInnerWindowID} {} + + virtual ~ProfilerMarkerPayload() = default; + + // Compute the number of bytes needed to serialize the `DeserializerTag` and + // payload, including in the no-payload (nullptr) case. + static mozilla::ProfileBufferEntryWriter::Length TagAndSerializationBytes( + const ProfilerMarkerPayload* aPayload) { + if (!aPayload) { + return sizeof(DeserializerTag); + } + return aPayload->TagAndSerializationBytes(); + } + + // Serialize the payload into an EntryWriter, including in the no-payload + // (nullptr) case. Must be of the exact size given by + // `TagAndSerializationBytes(aPayload)`. + static void TagAndSerialize(const ProfilerMarkerPayload* aPayload, + mozilla::ProfileBufferEntryWriter& aEntryWriter) { + if (!aPayload) { + aEntryWriter.WriteObject(DeserializerTag(0)); + return; + } + aPayload->SerializeTagAndPayload(aEntryWriter); + } + + // Deserialize a payload from an EntryReader, including in the no-payload + // (nullptr) case. + static mozilla::UniquePtr<ProfilerMarkerPayload> DeserializeTagAndPayload( + mozilla::ProfileBufferEntryReader& aER) { + const auto tag = aER.ReadObject<DeserializerTag>(); + Deserializer deserializer = DeserializerForTag(tag); + return deserializer(aER); + } + + virtual void StreamPayload( + mozilla::baseprofiler::SpliceableJSONWriter& aWriter, + const mozilla::TimeStamp& aProcessStartTime, + UniqueStacks& aUniqueStacks) const = 0; + + const mozilla::TimeStamp& GetStartTime() const { + return mCommonProps.mStartTime; + } + const mozilla::TimeStamp& GetEndTime() const { return mCommonProps.mEndTime; } + + protected: + // A `Deserializer` is a free function that can read a serialized payload from + // an `EntryReader` and return a reconstructed `ProfilerMarkerPayload` + // sub-object (may be null if there was no payload). + typedef mozilla::UniquePtr<ProfilerMarkerPayload> (*Deserializer)( + mozilla::ProfileBufferEntryReader&); + + // A `DeserializerTag` will be added before the payload, to help select the + // correct deserializer when reading back the payload. + using DeserializerTag = unsigned char; + + // This needs to be big enough to handle all possible sub-types of + // ProfilerMarkerPayload. + static constexpr DeserializerTag DeserializerMax = 32; + + // We need an atomic type that can hold a `DeserializerTag`. (Atomic doesn't + // work with too-small types.) + using DeserializerTagAtomic = int; + + // Number of currently-registered deserializers. + static mozilla::Atomic<DeserializerTagAtomic, mozilla::ReleaseAcquire> + sDeserializerCount; + + // List of currently-registered deserializers. + // sDeserializers[0] is a no-payload deserializer. + static Deserializer sDeserializers[DeserializerMax]; + + // Get the `DeserializerTag` for a `Deserializer` (which gets registered on + // the first call.) Tag 0 means no payload; a null `aDeserializer` gives that + // 0 tag. + static DeserializerTag TagForDeserializer(Deserializer aDeserializer); + + // Get the `Deserializer` for a given `DeserializerTag`. + // Tag 0 is reserved as no-payload deserializer (which returns nullptr). + static Deserializer DeserializerForTag(DeserializerTag aTag); + + struct CommonProps { + mozilla::TimeStamp mStartTime; + mozilla::TimeStamp mEndTime; + UniqueProfilerBacktrace mStack; + mozilla::Maybe<uint64_t> mInnerWindowID; + }; + + // Deserializers can use this base constructor. + explicit ProfilerMarkerPayload(CommonProps&& aCommonProps) + : mCommonProps(std::move(aCommonProps)) {} + + // Serialization/deserialization of common props in ProfilerMarkerPayload. + mozilla::ProfileBufferEntryWriter::Length + CommonPropsTagAndSerializationBytes() const; + void SerializeTagAndCommonProps( + DeserializerTag aDeserializerTag, + mozilla::ProfileBufferEntryWriter& aEntryWriter) const; + static CommonProps DeserializeCommonProps( + mozilla::ProfileBufferEntryReader& aEntryReader); + + void StreamType(const char* aMarkerType, + mozilla::baseprofiler::SpliceableJSONWriter& aWriter) const; + + void StreamCommonProps(const char* aMarkerType, + mozilla::baseprofiler::SpliceableJSONWriter& aWriter, + const mozilla::TimeStamp& aProcessStartTime, + UniqueStacks& aUniqueStacks) const; + void StreamStartEndTime(mozilla::baseprofiler::SpliceableJSONWriter& aWriter, + const mozilla::TimeStamp& aProcessStartTime) const; + + private: + // Compute the number of bytes needed to serialize payload in + // `SerializeTagAndPayload` below. + virtual mozilla::ProfileBufferEntryWriter::Length TagAndSerializationBytes() + const = 0; + + // Serialize the payload into an EntryWriter. + // Must be of the exact size given by `TagAndSerializationBytes()`. + virtual void SerializeTagAndPayload( + mozilla::ProfileBufferEntryWriter& aEntryWriter) const = 0; + + CommonProps mCommonProps; +}; + +#define DECL_STREAM_PAYLOAD \ + void StreamPayload(mozilla::baseprofiler::SpliceableJSONWriter& aWriter, \ + const mozilla::TimeStamp& aProcessStartTime, \ + UniqueStacks& aUniqueStacks) const override; \ + static mozilla::UniquePtr<ProfilerMarkerPayload> Deserialize( \ + mozilla::ProfileBufferEntryReader& aEntryReader); \ + mozilla::ProfileBufferEntryWriter::Length TagAndSerializationBytes() \ + const override; \ + void SerializeTagAndPayload(mozilla::ProfileBufferEntryWriter& aEntryWriter) \ + const override; + +// TODO: Increase the coverage of tracing markers that include InnerWindowID +// information +class TracingMarkerPayload : public ProfilerMarkerPayload { + public: + TracingMarkerPayload( + const char* aCategory, TracingKind aKind, const mozilla::TimeStamp& aTime, + const mozilla::Maybe<uint64_t>& aInnerWindowID = mozilla::Nothing(), + UniqueProfilerBacktrace aCause = nullptr) + : ProfilerMarkerPayload((aKind != TracingKind::TRACING_INTERVAL_END) + ? aTime + : mozilla::TimeStamp{}, + (aKind != TracingKind::TRACING_INTERVAL_START) + ? aTime + : mozilla::TimeStamp{}, + aInnerWindowID, std::move(aCause)), + mCategory(aCategory), + mKind(aKind) {} + + TracingMarkerPayload(const char* aCategory, const mozilla::TimeStamp& aStart, + const mozilla::TimeStamp& aEnd) + : ProfilerMarkerPayload(aStart, aEnd, mozilla::Nothing(), nullptr), + mCategory(aCategory), + mKind(TRACING_EVENT) {} + + DECL_STREAM_PAYLOAD + + protected: + TracingMarkerPayload(CommonProps&& aCommonProps, const char* aCategory, + TracingKind aKind) + : ProfilerMarkerPayload(std::move(aCommonProps)), + mCategory(aCategory), + mKind(aKind) {} + + // May be used by derived classes. + void SerializeTagAndPayload( + DeserializerTag aDeserializerTag, + mozilla::ProfileBufferEntryWriter& aEntryWriter) const; + + private: + const char* mCategory; + TracingKind mKind; +}; + +class BudgetMarkerPayload : public ProfilerMarkerPayload { + public: + BudgetMarkerPayload(const mozilla::TimeStamp& aStartTime, + const mozilla::TimeStamp& aEndTime) + : ProfilerMarkerPayload(aStartTime, aEndTime) {} + + DECL_STREAM_PAYLOAD + + private: + explicit BudgetMarkerPayload(CommonProps&& aCommonProps) + : ProfilerMarkerPayload(std::move(aCommonProps)) {} +}; + +class PrefMarkerPayload : public ProfilerMarkerPayload { + public: + PrefMarkerPayload(const char* aPrefName, + const mozilla::Maybe<mozilla::PrefValueKind>& aPrefKind, + const mozilla::Maybe<mozilla::PrefType>& aPrefType, + const nsCString& aPrefValue, + const mozilla::TimeStamp& aPrefAccessTime) + : ProfilerMarkerPayload(aPrefAccessTime, aPrefAccessTime), + mPrefAccessTime(aPrefAccessTime), + mPrefName(aPrefName), + mPrefKind(aPrefKind), + mPrefType(aPrefType), + mPrefValue(aPrefValue) {} + + DECL_STREAM_PAYLOAD + + private: + PrefMarkerPayload(CommonProps&& aCommonProps, + mozilla::TimeStamp aPrefAccessTime, nsCString&& aPrefName, + mozilla::Maybe<mozilla::PrefValueKind>&& aPrefKind, + mozilla::Maybe<mozilla::PrefType>&& aPrefType, + nsCString&& aPrefValue) + : ProfilerMarkerPayload(std::move(aCommonProps)), + mPrefAccessTime(aPrefAccessTime), + mPrefName(aPrefName), + mPrefKind(aPrefKind), + mPrefType(aPrefType), + mPrefValue(aPrefValue) {} + + mozilla::TimeStamp mPrefAccessTime; + nsCString mPrefName; + // Nothing means this is a shared preference. Something, on the other hand, + // holds an actual PrefValueKind indicating either a Default or User + // preference. + mozilla::Maybe<mozilla::PrefValueKind> mPrefKind; + // Nothing means that the mPrefName preference was not found. Something + // contains the type of the preference. + mozilla::Maybe<mozilla::PrefType> mPrefType; + nsCString mPrefValue; +}; + +class UserTimingMarkerPayload : public ProfilerMarkerPayload { + public: + UserTimingMarkerPayload(const nsAString& aName, + const mozilla::TimeStamp& aStartTime, + const mozilla::Maybe<uint64_t>& aInnerWindowID) + : ProfilerMarkerPayload(aStartTime, aStartTime, aInnerWindowID), + mEntryType("mark"), + mName(aName) {} + + UserTimingMarkerPayload(const nsAString& aName, + const mozilla::Maybe<nsString>& aStartMark, + const mozilla::Maybe<nsString>& aEndMark, + const mozilla::TimeStamp& aStartTime, + const mozilla::TimeStamp& aEndTime, + const mozilla::Maybe<uint64_t>& aInnerWindowID) + : ProfilerMarkerPayload(aStartTime, aEndTime, aInnerWindowID), + mEntryType("measure"), + mName(aName), + mStartMark(aStartMark), + mEndMark(aEndMark) {} + + DECL_STREAM_PAYLOAD + + private: + UserTimingMarkerPayload(CommonProps&& aCommonProps, const char* aEntryType, + nsString&& aName, + mozilla::Maybe<nsString>&& aStartMark, + mozilla::Maybe<nsString>&& aEndMark) + : ProfilerMarkerPayload(std::move(aCommonProps)), + mEntryType(aEntryType), + mName(std::move(aName)), + mStartMark(std::move(aStartMark)), + mEndMark(std::move(aEndMark)) {} + + // Either "mark" or "measure". + const char* mEntryType; + nsString mName; + mozilla::Maybe<nsString> mStartMark; + mozilla::Maybe<nsString> mEndMark; +}; + +// Contains the translation applied to a 2d layer so we can track the layer +// position at each frame. +class LayerTranslationMarkerPayload : public ProfilerMarkerPayload { + public: + LayerTranslationMarkerPayload(mozilla::layers::Layer* aLayer, + mozilla::gfx::Point aPoint, + mozilla::TimeStamp aStartTime) + : ProfilerMarkerPayload(aStartTime, aStartTime), + mLayer(aLayer), + mPoint(aPoint) {} + + DECL_STREAM_PAYLOAD + + private: + LayerTranslationMarkerPayload(CommonProps&& aCommonProps, + mozilla::layers::Layer* aLayer, + mozilla::gfx::Point aPoint) + : ProfilerMarkerPayload(std::move(aCommonProps)), + mLayer(aLayer), + mPoint(aPoint) {} + + mozilla::layers::Layer* mLayer; + mozilla::gfx::Point mPoint; +}; + +#include "Units.h" // For ScreenIntPoint + +// Tracks when a vsync occurs according to the HardwareComposer. +class VsyncMarkerPayload : public ProfilerMarkerPayload { + public: + explicit VsyncMarkerPayload(mozilla::TimeStamp aVsyncTimestamp) + : ProfilerMarkerPayload(aVsyncTimestamp, aVsyncTimestamp) {} + + DECL_STREAM_PAYLOAD + + private: + explicit VsyncMarkerPayload(CommonProps&& aCommonProps) + : ProfilerMarkerPayload(std::move(aCommonProps)) {} +}; + +class NetworkMarkerPayload : public ProfilerMarkerPayload { + public: + NetworkMarkerPayload(int64_t aID, const char* aURI, + const nsACString& aRequestMethod, NetworkLoadType aType, + const mozilla::TimeStamp& aStartTime, + const mozilla::TimeStamp& aEndTime, int32_t aPri, + int64_t aCount, + mozilla::net::CacheDisposition aCacheDisposition, + uint64_t aInnerWindowID, + const mozilla::net::TimingStruct* aTimings = nullptr, + const char* aRedirectURI = nullptr, + UniqueProfilerBacktrace aSource = nullptr, + const mozilla::Maybe<nsDependentCString>& aContentType = + mozilla::Nothing()) + : ProfilerMarkerPayload(aStartTime, aEndTime, + mozilla::Some(aInnerWindowID), + std::move(aSource)), + mID(aID), + mURI(aURI ? strdup(aURI) : nullptr), + mRedirectURI(aRedirectURI && (strlen(aRedirectURI) > 0) + ? strdup(aRedirectURI) + : nullptr), + mRequestMethod(aRequestMethod), + mType(aType), + mPri(aPri), + mCount(aCount), + mCacheDisposition(aCacheDisposition), + mContentType(aContentType) { + if (aTimings) { + mTimings = *aTimings; + } + } + + DECL_STREAM_PAYLOAD + + private: + NetworkMarkerPayload(CommonProps&& aCommonProps, int64_t aID, + mozilla::UniqueFreePtr<char>&& aURI, + mozilla::UniqueFreePtr<char>&& aRedirectURI, + nsCString&& aRequestMethod, NetworkLoadType aType, + int32_t aPri, int64_t aCount, + mozilla::net::TimingStruct aTimings, + mozilla::net::CacheDisposition aCacheDisposition, + mozilla::Maybe<nsAutoCString>&& aContentType) + : ProfilerMarkerPayload(std::move(aCommonProps)), + mID(aID), + mURI(std::move(aURI)), + mRedirectURI(std::move(aRedirectURI)), + mRequestMethod(std::move(aRequestMethod)), + mType(aType), + mPri(aPri), + mCount(aCount), + mTimings(aTimings), + mCacheDisposition(aCacheDisposition), + mContentType(std::move(aContentType)) {} + + int64_t mID; + mozilla::UniqueFreePtr<char> mURI; + mozilla::UniqueFreePtr<char> mRedirectURI; + // Request method and content type further down are usually short, + // e.g., "GET" and "text/html", so we use nsAutoCString to reduce + // heap usage; the bigger object size is acceptable here because + // markers are short-lived on-stack objects. + nsAutoCString mRequestMethod; + NetworkLoadType mType; + int32_t mPri; + int64_t mCount; + mozilla::net::TimingStruct mTimings; + mozilla::net::CacheDisposition mCacheDisposition; + mozilla::Maybe<nsAutoCString> mContentType; +}; + +class ScreenshotPayload : public ProfilerMarkerPayload { + public: + explicit ScreenshotPayload(mozilla::TimeStamp aTimeStamp, + nsCString&& aScreenshotDataURL, + const mozilla::gfx::IntSize& aWindowSize, + uintptr_t aWindowIdentifier) + : ProfilerMarkerPayload(aTimeStamp, mozilla::TimeStamp()), + mScreenshotDataURL(std::move(aScreenshotDataURL)), + mWindowSize(aWindowSize), + mWindowIdentifier(aWindowIdentifier) {} + + DECL_STREAM_PAYLOAD + + private: + ScreenshotPayload(CommonProps&& aCommonProps, nsCString&& aScreenshotDataURL, + mozilla::gfx::IntSize aWindowSize, + uintptr_t aWindowIdentifier) + : ProfilerMarkerPayload(std::move(aCommonProps)), + mScreenshotDataURL(std::move(aScreenshotDataURL)), + mWindowSize(aWindowSize), + mWindowIdentifier(aWindowIdentifier) {} + + nsCString mScreenshotDataURL; + mozilla::gfx::IntSize mWindowSize; + uintptr_t mWindowIdentifier; +}; + +class GCSliceMarkerPayload : public ProfilerMarkerPayload { + public: + GCSliceMarkerPayload(const mozilla::TimeStamp& aStartTime, + const mozilla::TimeStamp& aEndTime, + JS::UniqueChars&& aTimingJSON) + : ProfilerMarkerPayload(aStartTime, aEndTime), + mTimingJSON(std::move(aTimingJSON)) {} + + DECL_STREAM_PAYLOAD + + private: + GCSliceMarkerPayload(CommonProps&& aCommonProps, + JS::UniqueChars&& aTimingJSON) + : ProfilerMarkerPayload(std::move(aCommonProps)), + mTimingJSON(std::move(aTimingJSON)) {} + + JS::UniqueChars mTimingJSON; +}; + +class GCMajorMarkerPayload : public ProfilerMarkerPayload { + public: + GCMajorMarkerPayload(const mozilla::TimeStamp& aStartTime, + const mozilla::TimeStamp& aEndTime, + JS::UniqueChars&& aTimingJSON) + : ProfilerMarkerPayload(aStartTime, aEndTime), + mTimingJSON(std::move(aTimingJSON)) {} + + DECL_STREAM_PAYLOAD + + private: + GCMajorMarkerPayload(CommonProps&& aCommonProps, + JS::UniqueChars&& aTimingJSON) + : ProfilerMarkerPayload(std::move(aCommonProps)), + mTimingJSON(std::move(aTimingJSON)) {} + + JS::UniqueChars mTimingJSON; +}; + +class GCMinorMarkerPayload : public ProfilerMarkerPayload { + public: + GCMinorMarkerPayload(const mozilla::TimeStamp& aStartTime, + const mozilla::TimeStamp& aEndTime, + JS::UniqueChars&& aTimingData) + : ProfilerMarkerPayload(aStartTime, aEndTime), + mTimingData(std::move(aTimingData)) {} + + DECL_STREAM_PAYLOAD + + private: + GCMinorMarkerPayload(CommonProps&& aCommonProps, + JS::UniqueChars&& aTimingData) + : ProfilerMarkerPayload(std::move(aCommonProps)), + mTimingData(std::move(aTimingData)) {} + + JS::UniqueChars mTimingData; +}; + +class HangMarkerPayload : public ProfilerMarkerPayload { + public: + HangMarkerPayload(const mozilla::TimeStamp& aStartTime, + const mozilla::TimeStamp& aEndTime) + : ProfilerMarkerPayload(aStartTime, aEndTime) {} + + DECL_STREAM_PAYLOAD + private: + explicit HangMarkerPayload(CommonProps&& aCommonProps) + : ProfilerMarkerPayload(std::move(aCommonProps)) {} +}; + +class StyleMarkerPayload : public ProfilerMarkerPayload { + public: + StyleMarkerPayload(const mozilla::TimeStamp& aStartTime, + const mozilla::TimeStamp& aEndTime, + UniqueProfilerBacktrace aCause, + const mozilla::ServoTraversalStatistics& aStats, + const mozilla::Maybe<uint64_t>& aInnerWindowID) + : ProfilerMarkerPayload(aStartTime, aEndTime, aInnerWindowID, + std::move(aCause)), + mStats(aStats) {} + + DECL_STREAM_PAYLOAD + + private: + StyleMarkerPayload(CommonProps&& aCommonProps, + mozilla::ServoTraversalStatistics aStats) + : ProfilerMarkerPayload(std::move(aCommonProps)), mStats(aStats) {} + + mozilla::ServoTraversalStatistics mStats; +}; + +class LongTaskMarkerPayload : public ProfilerMarkerPayload { + public: + LongTaskMarkerPayload(const mozilla::TimeStamp& aStartTime, + const mozilla::TimeStamp& aEndTime) + : ProfilerMarkerPayload(aStartTime, aEndTime) {} + + DECL_STREAM_PAYLOAD + + private: + explicit LongTaskMarkerPayload(CommonProps&& aCommonProps) + : ProfilerMarkerPayload(std::move(aCommonProps)) {} +}; + +class TimingMarkerPayload : public ProfilerMarkerPayload { + public: + TimingMarkerPayload(const mozilla::TimeStamp& aStartTime, + const mozilla::TimeStamp& aEndTime) + : ProfilerMarkerPayload(aStartTime, aEndTime) {} + + DECL_STREAM_PAYLOAD + + private: + explicit TimingMarkerPayload(CommonProps&& aCommonProps) + : ProfilerMarkerPayload(std::move(aCommonProps)) {} +}; + +class TextMarkerPayload : public ProfilerMarkerPayload { + public: + TextMarkerPayload(const nsACString& aText, + const mozilla::TimeStamp& aStartTime) + : ProfilerMarkerPayload(aStartTime, aStartTime), mText(aText) {} + + TextMarkerPayload(const nsACString& aText, + const mozilla::TimeStamp& aStartTime, + const mozilla::TimeStamp& aEndTime) + : ProfilerMarkerPayload(aStartTime, aEndTime), mText(aText) {} + + TextMarkerPayload(const nsACString& aText, + const mozilla::TimeStamp& aStartTime, + const mozilla::Maybe<uint64_t>& aInnerWindowID) + : ProfilerMarkerPayload(aStartTime, aStartTime, aInnerWindowID), + mText(aText) {} + + TextMarkerPayload(const nsACString& aText, + const mozilla::TimeStamp& aStartTime, + const mozilla::TimeStamp& aEndTime, + const mozilla::Maybe<uint64_t>& aInnerWindowID, + UniqueProfilerBacktrace aCause = nullptr) + : ProfilerMarkerPayload(aStartTime, aEndTime, aInnerWindowID, + std::move(aCause)), + mText(aText) {} + + DECL_STREAM_PAYLOAD + + private: + TextMarkerPayload(CommonProps&& aCommonProps, nsCString&& aText) + : ProfilerMarkerPayload(std::move(aCommonProps)), + mText(std::move(aText)) {} + + nsCString mText; +}; + +class LogMarkerPayload : public ProfilerMarkerPayload { + public: + LogMarkerPayload(const char* aModule, const char* aText, + const mozilla::TimeStamp& aStartTime) + : ProfilerMarkerPayload(aStartTime, aStartTime), + mModule(aModule), + mText(aText) {} + + LogMarkerPayload(const char* aModule, const char* aText, + const mozilla::TimeStamp& aStartTime, + const mozilla::TimeStamp& aEndTime) + : ProfilerMarkerPayload(aStartTime, aEndTime), + mModule(aModule), + mText(aText) {} + + DECL_STREAM_PAYLOAD + + private: + LogMarkerPayload(CommonProps&& aCommonProps, nsAutoCStringN<32>&& aModule, + nsCString&& aText) + : ProfilerMarkerPayload(std::move(aCommonProps)), + mModule(std::move(aModule)), + mText(std::move(aText)) {} + + nsAutoCStringN<32> mModule; // longest known LazyLogModule name is ~24 + nsCString mText; +}; + +class MediaSampleMarkerPayload : public ProfilerMarkerPayload { + public: + MediaSampleMarkerPayload(const int64_t aSampleStartTimeUs, + const int64_t aSampleEndTimeUs); + DECL_STREAM_PAYLOAD + + private: + MediaSampleMarkerPayload(CommonProps&& aCommonProps, + const int64_t aSampleStartTimeUs, + const int64_t aSampleEndTimeUs); + + int64_t mSampleStartTimeUs; + int64_t mSampleEndTimeUs; +}; + +class JsAllocationMarkerPayload : public ProfilerMarkerPayload { + public: + JsAllocationMarkerPayload(const mozilla::TimeStamp& aStartTime, + JS::RecordAllocationInfo&& aInfo, + UniqueProfilerBacktrace aStack) + : ProfilerMarkerPayload(aStartTime, aStartTime, mozilla::Nothing(), + std::move(aStack)), + // Copy the strings, and take ownership of them. + mTypeName(aInfo.typeName ? NS_xstrdup(aInfo.typeName) : nullptr), + mClassName(aInfo.className ? strdup(aInfo.className) : nullptr), + mDescriptiveTypeName(aInfo.descriptiveTypeName + ? NS_xstrdup(aInfo.descriptiveTypeName) + : nullptr), + // The coarseType points to a string literal, so does not need to be + // duplicated. + mCoarseType(aInfo.coarseType), + mSize(aInfo.size), + mInNursery(aInfo.inNursery) {} + + DECL_STREAM_PAYLOAD + + private: + JsAllocationMarkerPayload( + CommonProps&& aCommonProps, + mozilla::UniqueFreePtr<const char16_t>&& aTypeName, + mozilla::UniqueFreePtr<const char>&& aClassName, + mozilla::UniqueFreePtr<const char16_t>&& aDescriptiveTypeName, + const char* aCoarseType, uint64_t aSize, bool aInNursery) + : ProfilerMarkerPayload(std::move(aCommonProps)), + mTypeName(std::move(aTypeName)), + mClassName(std::move(aClassName)), + mDescriptiveTypeName(std::move(aDescriptiveTypeName)), + mCoarseType(aCoarseType), + mSize(aSize), + mInNursery(aInNursery) {} + + mozilla::UniqueFreePtr<const char16_t> mTypeName; + mozilla::UniqueFreePtr<const char> mClassName; + mozilla::UniqueFreePtr<const char16_t> mDescriptiveTypeName; + // Points to a string literal, so does not need to be freed. + const char* mCoarseType; + + // The size in bytes of the allocation. + uint64_t mSize; + + // Whether or not the allocation is in the nursery or not. + bool mInNursery; +}; + +// This payload is for collecting information about native allocations. There is +// a memory hook into malloc and other memory functions that can sample a subset +// of the allocations. This information is then stored in this payload. +class NativeAllocationMarkerPayload : public ProfilerMarkerPayload { + public: + NativeAllocationMarkerPayload(const mozilla::TimeStamp& aStartTime, + int64_t aSize, uintptr_t aMemoryAddress, + int aThreadId, UniqueProfilerBacktrace aStack) + : ProfilerMarkerPayload(aStartTime, aStartTime, mozilla::Nothing(), + std::move(aStack)), + mSize(aSize), + mMemoryAddress(aMemoryAddress), + mThreadId(aThreadId) {} + + DECL_STREAM_PAYLOAD + + private: + NativeAllocationMarkerPayload(CommonProps&& aCommonProps, int64_t aSize, + uintptr_t aMemoryAddress, int aThreadId) + : ProfilerMarkerPayload(std::move(aCommonProps)), + mSize(aSize), + mMemoryAddress(aMemoryAddress), + mThreadId(aThreadId) {} + + // The size in bytes of the allocation. If the number is negative then it + // represents a de-allocation. + int64_t mSize; + // The memory address of the allocation or de-allocation. + uintptr_t mMemoryAddress; + + int mThreadId; +}; + +class IPCMarkerPayload : public ProfilerMarkerPayload { + public: + IPCMarkerPayload(int32_t aOtherPid, int32_t aMessageSeqno, + IPC::Message::msgid_t aMessageType, mozilla::ipc::Side aSide, + mozilla::ipc::MessageDirection aDirection, + mozilla::ipc::MessagePhase aPhase, bool aSync, + const mozilla::TimeStamp& aStartTime) + : ProfilerMarkerPayload(aStartTime, aStartTime), + mOtherPid(aOtherPid), + mMessageSeqno(aMessageSeqno), + mMessageType(aMessageType), + mSide(aSide), + mDirection(aDirection), + mPhase(aPhase), + mSync(aSync) {} + + DECL_STREAM_PAYLOAD + + private: + IPCMarkerPayload(CommonProps&& aCommonProps, int32_t aOtherPid, + int32_t aMessageSeqno, IPC::Message::msgid_t aMessageType, + mozilla::ipc::Side aSide, + mozilla::ipc::MessageDirection aDirection, + mozilla::ipc::MessagePhase aPhase, bool aSync) + : ProfilerMarkerPayload(std::move(aCommonProps)), + mOtherPid(aOtherPid), + mMessageSeqno(aMessageSeqno), + mMessageType(aMessageType), + mSide(aSide), + mDirection(aDirection), + mPhase(aPhase), + mSync(aSync) {} + + int32_t mOtherPid; + int32_t mMessageSeqno; + IPC::Message::msgid_t mMessageType; + mozilla::ipc::Side mSide; + mozilla::ipc::MessageDirection mDirection; + mozilla::ipc::MessagePhase mPhase; + bool mSync; +}; + +namespace mozilla { + +// Serialize a pointed-at ProfilerMarkerPayload, may be null when there are no +// payloads. +template <> +struct ProfileBufferEntryWriter::Serializer<const ProfilerMarkerPayload*> { + static Length Bytes(const ProfilerMarkerPayload* aPayload) { + return ProfilerMarkerPayload::TagAndSerializationBytes(aPayload); + } + + static void Write(ProfileBufferEntryWriter& aEW, + const ProfilerMarkerPayload* aPayload) { + ProfilerMarkerPayload::TagAndSerialize(aPayload, aEW); + } +}; + +// Serialize a pointed-at ProfilerMarkerPayload, may be null when there are no +// payloads. +template <> +struct ProfileBufferEntryWriter::Serializer<UniquePtr<ProfilerMarkerPayload>> { + static Length Bytes(const UniquePtr<ProfilerMarkerPayload>& aPayload) { + return ProfilerMarkerPayload::TagAndSerializationBytes(aPayload.get()); + } + + static void Write(ProfileBufferEntryWriter& aEW, + const UniquePtr<ProfilerMarkerPayload>& aPayload) { + ProfilerMarkerPayload::TagAndSerialize(aPayload.get(), aEW); + } +}; + +// Deserialize a ProfilerMarkerPayload into a UniquePtr, may be null if there +// are no payloads. +template <> +struct ProfileBufferEntryReader::Deserializer< + UniquePtr<ProfilerMarkerPayload>> { + static void ReadInto(ProfileBufferEntryReader& aER, + UniquePtr<ProfilerMarkerPayload>& aPayload) { + aPayload = Read(aER); + } + + static UniquePtr<ProfilerMarkerPayload> Read(ProfileBufferEntryReader& aER) { + return ProfilerMarkerPayload::DeserializeTagAndPayload(aER); + } +}; + +} // namespace mozilla + +#endif // ProfilerMarkerPayload_h
--- a/tools/profiler/public/ProfilerMarkerTypes.h +++ b/tools/profiler/public/ProfilerMarkerTypes.h @@ -18,24 +18,496 @@ // Further work is needed to complete these definitions, and use them to convert // existing PROFILER_ADD_MARKER calls. See meta bug 1661394. #include "mozilla/BaseProfilerMarkerTypes.h" #include "mozilla/ProfilerMarkers.h" #ifdef MOZ_GECKO_PROFILER +# include "gfxASurface.h" +# include "js/AllocationRecording.h" # include "js/ProfilingFrameIterator.h" # include "js/Utility.h" +# include "Layers.h" +# include "mozilla/ipc/ProtocolUtils.h" +# include "mozilla/net/HttpBaseChannel.h" # include "mozilla/Preferences.h" # include "mozilla/ServoTraversalStatistics.h" namespace geckoprofiler::markers { // Import some common markers from mozilla::baseprofiler::markers. -using MediaSampleMarker = mozilla::baseprofiler::markers::MediaSampleMarker; -using ContentBuildMarker = mozilla::baseprofiler::markers::ContentBuildMarker; +using Tracing = mozilla::baseprofiler::markers::Tracing; +using UserTimingMark = mozilla::baseprofiler::markers::UserTimingMark; +using UserTimingMeasure = mozilla::baseprofiler::markers::UserTimingMeasure; +using Hang = mozilla::baseprofiler::markers::Hang; +using LongTask = mozilla::baseprofiler::markers::LongTask; +using Log = mozilla::baseprofiler::markers::Log; +using MediaSample = mozilla::baseprofiler::markers::MediaSample; + +struct Budget { + static constexpr mozilla::Span<const char> MarkerTypeName() { + return mozilla::MakeStringSpan("Budget"); + } + static void StreamJSONMarkerData( + mozilla::baseprofiler::SpliceableJSONWriter& aWriter) {} + static mozilla::MarkerSchema MarkerTypeDisplay() { + using MS = mozilla::MarkerSchema; + MS schema{MS::Location::markerChart, MS::Location::markerTable}; + // Nothing outside the defaults. + return schema; + } +}; + +struct Pref { + static constexpr mozilla::Span<const char> MarkerTypeName() { + return mozilla::MakeStringSpan("PreferenceRead"); + } + static void StreamJSONMarkerData( + mozilla::baseprofiler::SpliceableJSONWriter& aWriter, + const mozilla::ProfilerString8View& aPrefName, + const mozilla::Maybe<mozilla::PrefValueKind>& aPrefKind, + const mozilla::Maybe<mozilla::PrefType>& aPrefType, + const mozilla::ProfilerString8View& aPrefValue, + const mozilla::TimeStamp& aPrefAccessTime) { + // TODO: This looks like it's always `Now()`, so it could probably be + // removed; but the frontend may need updating first. + mozilla::baseprofiler::WritePropertyTime(aWriter, "prefAccessTime", + aPrefAccessTime); + aWriter.StringProperty("prefName", aPrefName); + aWriter.StringProperty("prefKind", PrefValueKindToString(aPrefKind)); + aWriter.StringProperty("prefType", PrefTypeToString(aPrefType)); + aWriter.StringProperty("prefValue", aPrefValue); + } + static mozilla::MarkerSchema MarkerTypeDisplay() { + using MS = mozilla::MarkerSchema; + MS schema{MS::Location::markerChart, MS::Location::markerTable}; + schema.AddKeyLabelFormat("prefName", "Name", MS::Format::string); + schema.AddKeyLabelFormat("prefKind", "Kind", MS::Format::string); + schema.AddKeyLabelFormat("prefType", "Type", MS::Format::string); + schema.AddKeyLabelFormat("prefValue", "Value", MS::Format::string); + return schema; + } + + private: + static mozilla::Span<const char> PrefValueKindToString( + const mozilla::Maybe<mozilla::PrefValueKind>& aKind) { + if (aKind) { + return *aKind == mozilla::PrefValueKind::Default + ? mozilla::MakeStringSpan("Default") + : mozilla::MakeStringSpan("User"); + } + return "Shared"; + } + + static mozilla::Span<const char> PrefTypeToString( + const mozilla::Maybe<mozilla::PrefType>& type) { + if (type) { + switch (*type) { + case mozilla::PrefType::None: + return "None"; + case mozilla::PrefType::Int: + return "Int"; + case mozilla::PrefType::Bool: + return "Bool"; + case mozilla::PrefType::String: + return "String"; + default: + MOZ_ASSERT_UNREACHABLE("Unknown preference type."); + } + } + return "Preference not found"; + } +}; + +// Contains the translation applied to a 2d layer so we can track the layer +// position at each frame. +struct LayerTranslation { + static constexpr mozilla::Span<const char> MarkerTypeName() { + return mozilla::MakeStringSpan("LayerTranslation"); + } + static void StreamJSONMarkerData( + mozilla::baseprofiler::SpliceableJSONWriter& aWriter, + mozilla::layers::Layer* aLayer, mozilla::gfx::Point aPoint) { + const size_t bufferSize = 32; + char buffer[bufferSize]; + SprintfLiteral(buffer, "%p", aLayer); + + aWriter.StringProperty("layer", buffer); + aWriter.IntProperty("x", aPoint.x); + aWriter.IntProperty("y", aPoint.y); + } + static mozilla::MarkerSchema MarkerTypeDisplay() { + using MS = mozilla::MarkerSchema; + MS schema{MS::Location::markerChart, MS::Location::markerTable}; + schema.AddKeyLabelFormat("layer", "Layer", MS::Format::string); + schema.AddKeyLabelFormat("x", "X", MS::Format::integer); + schema.AddKeyLabelFormat("y", "Y", MS::Format::integer); + return schema; + } +}; + +// Tracks when a vsync occurs according to the HardwareComposer. +struct Vsync { + static constexpr mozilla::Span<const char> MarkerTypeName() { + return mozilla::MakeStringSpan("VsyncTimestamp"); + } + static void StreamJSONMarkerData( + mozilla::baseprofiler::SpliceableJSONWriter& aWriter) {} + static mozilla::MarkerSchema MarkerTypeDisplay() { + using MS = mozilla::MarkerSchema; + MS schema{MS::Location::markerChart, MS::Location::markerTable}; + // Nothing outside the defaults. + return schema; + } +}; + +struct Network { + static constexpr mozilla::Span<const char> MarkerTypeName() { + return mozilla::MakeStringSpan("Network"); + } + static void StreamJSONMarkerData( + mozilla::baseprofiler::SpliceableJSONWriter& aWriter, int64_t aID, + const mozilla::ProfilerString8View& aURI, NetworkLoadType aType, + const mozilla::TimeStamp& aStartTime, const mozilla::TimeStamp& aEndTime, + int32_t aPri, int64_t aCount, + mozilla::net::CacheDisposition aCacheDisposition, uint64_t aInnerWindowID, + const mozilla::net::TimingStruct& aTimings, + const mozilla::ProfilerString8View& aRedirectURI, + UniqueProfilerBacktrace aSource, + const mozilla::ProfilerString8View& aContentType) { + // TODO: Remove these Legacy start&end times when frontend is updated. + mozilla::baseprofiler::WritePropertyTime(aWriter, "startTime", aStartTime); + mozilla::baseprofiler::WritePropertyTime(aWriter, "endTime", aEndTime); + + aWriter.IntProperty("id", aID); + mozilla::Span<const char> typeString = GetNetworkState(aType); + mozilla::Span<const char> cacheString = GetCacheState(aCacheDisposition); + // want to use aUniqueStacks.mUniqueStrings->WriteElement(aWriter, + // typeString); + aWriter.StringProperty("status", typeString); + if (!cacheString.IsEmpty()) { + aWriter.StringProperty("cache", cacheString); + } + aWriter.IntProperty("pri", aPri); + if (aCount > 0) { + aWriter.IntProperty("count", aCount); + } + if (aURI.Length() != 0) { + aWriter.StringProperty("URI", aURI); + } + if (aRedirectURI.Length() != 0) { + aWriter.StringProperty("RedirectURI", aRedirectURI); + } + + if (aContentType.Length() != 0) { + aWriter.StringProperty("contentType", aContentType); + } else { + aWriter.NullProperty("contentType"); + } + + if (aType != NetworkLoadType::LOAD_START) { + mozilla::baseprofiler::WritePropertyTime(aWriter, "domainLookupStart", + aTimings.domainLookupStart); + mozilla::baseprofiler::WritePropertyTime(aWriter, "domainLookupEnd", + aTimings.domainLookupEnd); + mozilla::baseprofiler::WritePropertyTime(aWriter, "connectStart", + aTimings.connectStart); + mozilla::baseprofiler::WritePropertyTime(aWriter, "tcpConnectEnd", + aTimings.tcpConnectEnd); + mozilla::baseprofiler::WritePropertyTime(aWriter, "secureConnectionStart", + aTimings.secureConnectionStart); + mozilla::baseprofiler::WritePropertyTime(aWriter, "connectEnd", + aTimings.connectEnd); + mozilla::baseprofiler::WritePropertyTime(aWriter, "requestStart", + aTimings.requestStart); + mozilla::baseprofiler::WritePropertyTime(aWriter, "responseStart", + aTimings.responseStart); + mozilla::baseprofiler::WritePropertyTime(aWriter, "responseEnd", + aTimings.responseEnd); + } + } + static mozilla::MarkerSchema MarkerTypeDisplay() { + return mozilla::MarkerSchema::SpecialFrontendLocation{}; + } + + private: + static mozilla::Span<const char> GetNetworkState(NetworkLoadType aType) { + switch (aType) { + case NetworkLoadType::LOAD_START: + return mozilla::MakeStringSpan("STATUS_START"); + case NetworkLoadType::LOAD_STOP: + return mozilla::MakeStringSpan("STATUS_STOP"); + case NetworkLoadType::LOAD_REDIRECT: + return mozilla::MakeStringSpan("STATUS_REDIRECT"); + } + return mozilla::MakeStringSpan(""); + } + + static mozilla::Span<const char> GetCacheState( + mozilla::net::CacheDisposition aCacheDisposition) { + switch (aCacheDisposition) { + case mozilla::net::kCacheUnresolved: + return mozilla::MakeStringSpan("Unresolved"); + case mozilla::net::kCacheHit: + return mozilla::MakeStringSpan("Hit"); + case mozilla::net::kCacheHitViaReval: + return mozilla::MakeStringSpan("HitViaReval"); + case mozilla::net::kCacheMissedViaReval: + return mozilla::MakeStringSpan("MissedViaReval"); + case mozilla::net::kCacheMissed: + return mozilla::MakeStringSpan("Missed"); + case mozilla::net::kCacheUnknown: + default: + return mozilla::MakeStringSpan(""); + } + } +}; + +struct ScreenshotPayload { + static constexpr mozilla::Span<const char> MarkerTypeName() { + return mozilla::MakeStringSpan("CompositorScreenshot"); + } + static void StreamJSONMarkerData( + mozilla::baseprofiler::SpliceableJSONWriter& aWriter, + const mozilla::ProfilerString8View& aScreenshotDataURL, + const mozilla::gfx::IntSize& aWindowSize, uintptr_t aWindowIdentifier) { + // TODO: Use UniqueStacks&Strings + // aUniqueStacks.mUniqueStrings->WriteProperty(aWriter, "url", + // mScreenshotDataURL.get()); + aWriter.StringProperty("url", aScreenshotDataURL); + + char hexWindowID[32]; + SprintfLiteral(hexWindowID, "0x%" PRIXPTR, aWindowIdentifier); + aWriter.StringProperty("windowID", hexWindowID); + aWriter.DoubleProperty("windowWidth", aWindowSize.width); + aWriter.DoubleProperty("windowHeight", aWindowSize.height); + } + static mozilla::MarkerSchema MarkerTypeDisplay() { + return mozilla::MarkerSchema::SpecialFrontendLocation{}; + } +}; + +struct GCSlice { + static constexpr mozilla::Span<const char> MarkerTypeName() { + return mozilla::MakeStringSpan("GCSlice"); + } + static void StreamJSONMarkerData( + mozilla::baseprofiler::SpliceableJSONWriter& aWriter, + const mozilla::ProfilerString8View& aTimingJSON) { + if (aTimingJSON.Length() != 0) { + // TODO: Is SplicedJSONProperty necessary here? (Guessing yes!) + // aWriter.SplicedJSONProperty("timings", aTimingJSON); + aWriter.StringProperty("timings", aTimingJSON); + } else { + aWriter.NullProperty("timings"); + } + } + static mozilla::MarkerSchema MarkerTypeDisplay() { + using MS = mozilla::MarkerSchema; + MS schema{MS::Location::markerChart, MS::Location::markerTable, + MS::Location::timelineMemory}; + // No display instructions here, there is special handling in the front-end. + return schema; + } +}; + +struct GCMajor { + static constexpr mozilla::Span<const char> MarkerTypeName() { + return mozilla::MakeStringSpan("GCMajor"); + } + static void StreamJSONMarkerData( + mozilla::baseprofiler::SpliceableJSONWriter& aWriter, + const mozilla::ProfilerString8View& aTimingJSON) { + if (aTimingJSON.Length() != 0) { + // TODO: Is SplicedJSONProperty necessary here? (Guessing yes!) + // aWriter.SplicedJSONProperty("timings", aTimingJSON); + aWriter.StringProperty("timings", aTimingJSON); + } else { + aWriter.NullProperty("timings"); + } + } + static mozilla::MarkerSchema MarkerTypeDisplay() { + using MS = mozilla::MarkerSchema; + MS schema{MS::Location::markerChart, MS::Location::markerTable, + MS::Location::timelineMemory}; + // No display instructions here, there is special handling in the front-end. + return schema; + } +}; + +struct GCMinor { + static constexpr mozilla::Span<const char> MarkerTypeName() { + return mozilla::MakeStringSpan("GCMinor"); + } + static void StreamJSONMarkerData( + mozilla::baseprofiler::SpliceableJSONWriter& aWriter, + const mozilla::ProfilerString8View& aTimingJSON) { + if (aTimingJSON.Length() != 0) { + // TODO: Is SplicedJSONProperty necessary here? (Guessing yes!) + // aWriter.SplicedJSONProperty("nursery", aTimingJSON); + aWriter.StringProperty("nursery", aTimingJSON); + } else { + aWriter.NullProperty("nursery"); + } + } + static mozilla::MarkerSchema MarkerTypeDisplay() { + using MS = mozilla::MarkerSchema; + MS schema{MS::Location::markerChart, MS::Location::markerTable, + MS::Location::timelineMemory}; + // No display instructions here, there is special handling in the front-end. + return schema; + } +}; + +struct StyleMarkerPayload { + static constexpr mozilla::Span<const char> MarkerTypeName() { + return mozilla::MakeStringSpan("Styles"); + } + static void StreamJSONMarkerData( + mozilla::baseprofiler::SpliceableJSONWriter& aWriter, + const mozilla::ServoTraversalStatistics& aStats) { + aWriter.IntProperty("elementsTraversed", aStats.mElementsTraversed); + aWriter.IntProperty("elementsStyled", aStats.mElementsStyled); + aWriter.IntProperty("elementsMatched", aStats.mElementsMatched); + aWriter.IntProperty("stylesShared", aStats.mStylesShared); + aWriter.IntProperty("stylesReused", aStats.mStylesReused); + } + static mozilla::MarkerSchema MarkerTypeDisplay() { + using MS = mozilla::MarkerSchema; + MS schema{MS::Location::markerChart, MS::Location::markerTable, + MS::Location::timelineOverview}; + schema.AddKeyLabelFormat("elementsTraversed", "Elements traversed", + MS::Format::integer); + schema.AddKeyLabelFormat("elementsStyled", "Elements styled", + MS::Format::integer); + schema.AddKeyLabelFormat("elementsMatched", "Elements matched", + MS::Format::integer); + schema.AddKeyLabelFormat("stylesShared", "Styles shared", + MS::Format::integer); + schema.AddKeyLabelFormat("stylesReused", "Styles reused", + MS::Format::integer); + return schema; + } +}; + +class JsAllocationMarkerPayload { + static constexpr mozilla::Span<const char> MarkerTypeName() { + return mozilla::MakeStringSpan("JS allocation"); + } + static void StreamJSONMarkerData( + mozilla::baseprofiler::SpliceableJSONWriter& aWriter, + const mozilla::ProfilerString16View& aTypeName, + const mozilla::ProfilerString8View& aClassName, + const mozilla::ProfilerString16View& aDescriptiveTypeName, + const mozilla::ProfilerString8View& aCoarseType, uint64_t aSize, + bool aInNursery) { + if (aClassName.Length() != 0) { + aWriter.StringProperty("className", aClassName); + } + if (aTypeName.Length() != 0) { + aWriter.StringProperty( + "typeName", + NS_ConvertUTF16toUTF8(aTypeName.Data(), aTypeName.Length())); + } + if (aDescriptiveTypeName.Length() != 0) { + aWriter.StringProperty( + "descriptiveTypeName", + NS_ConvertUTF16toUTF8(aDescriptiveTypeName.Data(), + aDescriptiveTypeName.Length())); + } + aWriter.StringProperty("coarseType", aCoarseType); + aWriter.IntProperty("size", aSize); + aWriter.BoolProperty("inNursery", aInNursery); + } + static mozilla::MarkerSchema MarkerTypeDisplay() { + return mozilla::MarkerSchema::SpecialFrontendLocation{}; + } +}; + +// This payload is for collecting information about native allocations. There is +// a memory hook into malloc and other memory functions that can sample a subset +// of the allocations. This information is then stored in this payload. +struct NativeAllocationMarkerPayload { + static constexpr mozilla::Span<const char> MarkerTypeName() { + return mozilla::MakeStringSpan("Native allocation"); + } + static void StreamJSONMarkerData( + mozilla::baseprofiler::SpliceableJSONWriter& aWriter, int64_t aSize, + uintptr_t aMemoryAddress, int aThreadId) { + aWriter.IntProperty("size", aSize); + aWriter.IntProperty("memoryAddress", static_cast<int64_t>(aMemoryAddress)); + aWriter.IntProperty("threadId", aThreadId); + } + static mozilla::MarkerSchema MarkerTypeDisplay() { + return mozilla::MarkerSchema::SpecialFrontendLocation{}; + } +}; + +struct IPCMarkerPayload { + static constexpr mozilla::Span<const char> MarkerTypeName() { + return mozilla::MakeStringSpan("IPC"); + } + static void StreamJSONMarkerData( + mozilla::baseprofiler::SpliceableJSONWriter& aWriter, int32_t aOtherPid, + int32_t aMessageSeqno, IPC::Message::msgid_t aMessageType, + mozilla::ipc::Side aSide, mozilla::ipc::MessageDirection aDirection, + mozilla::ipc::MessagePhase aPhase, bool aSync, + const mozilla::TimeStamp& aTime) { + // TODO: Remove these Legacy times when frontend is updated. + mozilla::baseprofiler::WritePropertyTime(aWriter, "startTime", aTime); + mozilla::baseprofiler::WritePropertyTime(aWriter, "endTime", aTime); + + using namespace mozilla::ipc; + aWriter.IntProperty("otherPid", aOtherPid); + aWriter.IntProperty("messageSeqno", aMessageSeqno); + aWriter.StringProperty( + "messageType", + mozilla::MakeStringSpan(IPC::StringFromIPCMessageType(aMessageType))); + aWriter.StringProperty("side", IPCSideToString(aSide)); + aWriter.StringProperty("direction", + aDirection == MessageDirection::eSending + ? mozilla::MakeStringSpan("sending") + : mozilla::MakeStringSpan("receiving")); + aWriter.StringProperty("phase", IPCPhaseToString(aPhase)); + aWriter.BoolProperty("sync", aSync); + } + static mozilla::MarkerSchema MarkerTypeDisplay() { + return mozilla::MarkerSchema::SpecialFrontendLocation{}; + } + + private: + static mozilla::Span<const char> IPCSideToString(mozilla::ipc::Side aSide) { + switch (aSide) { + case mozilla::ipc::ParentSide: + return mozilla::MakeStringSpan("parent"); + case mozilla::ipc::ChildSide: + return mozilla::MakeStringSpan("child"); + case mozilla::ipc::UnknownSide: + return mozilla::MakeStringSpan("unknown"); + default: + MOZ_ASSERT_UNREACHABLE("Invalid IPC side"); + return mozilla::MakeStringSpan("<invalid IPC side>"); + } + } + + static mozilla::Span<const char> IPCPhaseToString( + mozilla::ipc::MessagePhase aPhase) { + switch (aPhase) { + case mozilla::ipc::MessagePhase::Endpoint: + return mozilla::MakeStringSpan("endpoint"); + case mozilla::ipc::MessagePhase::TransferStart: + return mozilla::MakeStringSpan("transferStart"); + case mozilla::ipc::MessagePhase::TransferEnd: + return mozilla::MakeStringSpan("transferEnd"); + default: + MOZ_ASSERT_UNREACHABLE("Invalid IPC phase"); + return mozilla::MakeStringSpan("<invalid IPC phase>"); + } + } +}; } // namespace geckoprofiler::markers #endif // MOZ_GECKO_PROFILER #endif // ProfilerMarkerTypes_h
--- a/tools/profiler/public/ProfilerMarkers.h +++ b/tools/profiler/public/ProfilerMarkers.h @@ -39,19 +39,16 @@ #include "GeckoProfiler.h" #ifndef MOZ_GECKO_PROFILER # define PROFILER_MARKER_UNTYPED(markerName, categoryName, ...) # define PROFILER_MARKER(markerName, categoryName, options, MarkerType, ...) # define PROFILER_MARKER_TEXT(markerName, categoryName, options, text) # define AUTO_PROFILER_MARKER_TEXT(markerName, categoryName, options, text) -# define AUTO_PROFILER_TRACING_MARKER(categoryString, markerName, categoryPair) -# define AUTO_PROFILER_TRACING_MARKER_DOCSHELL(categoryString, markerName, \ - categoryPair, docShell) #else // ndef MOZ_GECKO_PROFILER // Bring category names from Base Profiler into the geckoprofiler::category // namespace, for consistency with other Gecko Profiler identifiers. namespace geckoprofiler::category { using namespace ::mozilla::baseprofiler::category; } @@ -127,19 +124,18 @@ inline mozilla::ProfileBufferBlockIndex do { \ AUTO_PROFILER_STATS(PROFILER_MARKER_with_##MarkerType); \ ::profiler_add_marker( \ markerName, ::geckoprofiler::category::categoryName, options, \ ::geckoprofiler::markers::MarkerType{}, ##__VA_ARGS__); \ } while (false) namespace geckoprofiler::markers { -// Most common marker types. Others are in ProfilerMarkerTypes.h. +// Most common marker type. Others are in ProfilerMarkerTypes.h. using Text = ::mozilla::baseprofiler::markers::Text; -using Tracing = mozilla::baseprofiler::markers::Tracing; } // namespace geckoprofiler::markers // Add a text marker. This macro is safe to use even if MOZ_GECKO_PROFILER is // not #defined. # define PROFILER_MARKER_TEXT(markerName, categoryName, options, text) \ do { \ AUTO_PROFILER_STATS(PROFILER_MARKER_TEXT); \ ::profiler_add_marker(markerName, \ @@ -184,80 +180,11 @@ class MOZ_RAII AutoProfilerTextMarker { // Creates an AutoProfilerTextMarker RAII object. This macro is safe to use // even if MOZ_GECKO_PROFILER is not #defined. # define AUTO_PROFILER_MARKER_TEXT(markerName, categoryName, options, text) \ AutoProfilerTextMarker PROFILER_RAII( \ markerName, ::mozilla::baseprofiler::category::categoryName, options, \ text) -class MOZ_RAII AutoProfilerTracing { - public: - AutoProfilerTracing(const char* aCategoryString, const char* aMarkerName, - mozilla::MarkerCategory aCategoryPair, - const mozilla::Maybe<uint64_t>& aInnerWindowID) - : mCategoryString(aCategoryString), - mMarkerName(aMarkerName), - mCategoryPair(aCategoryPair), - mInnerWindowID(aInnerWindowID) { - profiler_add_marker( - mozilla::ProfilerString8View::WrapNullTerminatedString(mMarkerName), - mCategoryPair, - {mozilla::MarkerTiming::IntervalStart(), - mozilla::MarkerInnerWindowId(mInnerWindowID)}, - geckoprofiler::markers::Tracing{}, - mozilla::ProfilerString8View::WrapNullTerminatedString( - mCategoryString)); - } - - AutoProfilerTracing( - const char* aCategoryString, const char* aMarkerName, - mozilla::MarkerCategory aCategoryPair, - mozilla::UniquePtr<mozilla::ProfileChunkedBuffer> aBacktrace, - const mozilla::Maybe<uint64_t>& aInnerWindowID) - : mCategoryString(aCategoryString), - mMarkerName(aMarkerName), - mCategoryPair(aCategoryPair), - mInnerWindowID(aInnerWindowID) { - profiler_add_marker( - mozilla::ProfilerString8View::WrapNullTerminatedString(mMarkerName), - mCategoryPair, - {mozilla::MarkerTiming::IntervalStart(), - mozilla::MarkerInnerWindowId(mInnerWindowID), - mozilla::MarkerStack::TakeBacktrace(std::move(aBacktrace))}, - geckoprofiler::markers::Tracing{}, - mozilla::ProfilerString8View::WrapNullTerminatedString( - mCategoryString)); - } - - ~AutoProfilerTracing() { - profiler_add_marker( - mozilla::ProfilerString8View::WrapNullTerminatedString(mMarkerName), - mCategoryPair, - {mozilla::MarkerTiming::IntervalEnd(), - mozilla::MarkerInnerWindowId(mInnerWindowID)}, - geckoprofiler::markers::Tracing{}, - mozilla::ProfilerString8View::WrapNullTerminatedString( - mCategoryString)); - } - - protected: - const char* mCategoryString; - const char* mMarkerName; - const mozilla::MarkerCategory mCategoryPair; - const mozilla::Maybe<uint64_t> mInnerWindowID; -}; - -// Adds a START/END pair of tracing markers. -# define AUTO_PROFILER_TRACING_MARKER(categoryString, markerName, \ - categoryPair) \ - AutoProfilerTracing PROFILER_RAII(categoryString, markerName, \ - geckoprofiler::category::categoryPair, \ - mozilla::Nothing()) -# define AUTO_PROFILER_TRACING_MARKER_DOCSHELL(categoryString, markerName, \ - categoryPair, docShell) \ - AutoProfilerTracing PROFILER_RAII( \ - categoryString, markerName, geckoprofiler::category::categoryPair, \ - profiler_get_inner_window_id_from_docshell(docShell)) - #endif // nfed MOZ_GECKO_PROFILER else #endif // ProfilerMarkers_h
--- a/tools/profiler/tests/gtest/GeckoProfiler.cpp +++ b/tools/profiler/tests/gtest/GeckoProfiler.cpp @@ -9,16 +9,17 @@ // happens when calling these functions. They don't do much inspection of // profiler internals. #include "GeckoProfiler.h" #include "mozilla/ProfilerMarkerTypes.h" #include "mozilla/ProfilerMarkers.h" #include "platform.h" #include "ProfileBuffer.h" +#include "ProfilerMarkerPayload.h" #include "js/Initialization.h" #include "js/Printf.h" #include "jsapi.h" #include "json/json.h" #include "mozilla/Atomics.h" #include "mozilla/BlocksRingBuffer.h" #include "mozilla/ProfileBufferEntrySerializationGeckoExtensions.h" @@ -573,38 +574,126 @@ TEST(GeckoProfiler, Pause) ASSERT_TRUE(profiler_can_accept_markers()); profiler_stop(); ASSERT_TRUE(!profiler_is_paused()); ASSERT_TRUE(!profiler_can_accept_markers()); } +// A class that keeps track of how many instances have been created, streamed, +// and destroyed. +class GTestMarkerPayload : public ProfilerMarkerPayload { + public: + explicit GTestMarkerPayload(int aN) : mN(aN) { ++sNumCreated; } + + virtual ~GTestMarkerPayload() { ++sNumDestroyed; } + + DECL_STREAM_PAYLOAD + + private: + GTestMarkerPayload(CommonProps&& aCommonProps, int aN) + : ProfilerMarkerPayload(std::move(aCommonProps)), mN(aN) { + ++sNumDeserialized; + } + + int mN; + + public: + // The number of GTestMarkerPayload instances that have been created, + // streamed, and destroyed. + static int sNumCreated; + static int sNumSerialized; + static int sNumDeserialized; + static int sNumStreamed; + static int sNumDestroyed; +}; + +int GTestMarkerPayload::sNumCreated = 0; +int GTestMarkerPayload::sNumSerialized = 0; +int GTestMarkerPayload::sNumDeserialized = 0; +int GTestMarkerPayload::sNumStreamed = 0; +int GTestMarkerPayload::sNumDestroyed = 0; + +ProfileBufferEntryWriter::Length GTestMarkerPayload::TagAndSerializationBytes() + const { + return CommonPropsTagAndSerializationBytes() + + ProfileBufferEntryWriter::SumBytes(mN); +} + +void GTestMarkerPayload::SerializeTagAndPayload( + ProfileBufferEntryWriter& aEntryWriter) const { + static const DeserializerTag tag = TagForDeserializer(Deserialize); + SerializeTagAndCommonProps(tag, aEntryWriter); + aEntryWriter.WriteObject(mN); + ++sNumSerialized; +} + +// static +UniquePtr<ProfilerMarkerPayload> GTestMarkerPayload::Deserialize( + ProfileBufferEntryReader& aEntryReader) { + ProfilerMarkerPayload::CommonProps props = + DeserializeCommonProps(aEntryReader); + auto n = aEntryReader.ReadObject<int>(); + return UniquePtr<ProfilerMarkerPayload>( + new GTestMarkerPayload(std::move(props), n)); +} + +void GTestMarkerPayload::StreamPayload(SpliceableJSONWriter& aWriter, + const mozilla::TimeStamp& aStartTime, + UniqueStacks& aUniqueStacks) const { + StreamCommonProps("gtest", aWriter, aStartTime, aUniqueStacks); + char buf[64]; + int written = SprintfLiteral(buf, "gtest-%d", mN); + ASSERT_GT(written, 0); + aWriter.IntProperty(mozilla::Span<const char>(buf, size_t(written)), mN); + ++sNumStreamed; +} + TEST(GeckoProfiler, Markers) { uint32_t features = ProfilerFeature::StackWalk; const char* filters[] = {"GeckoMain"}; profiler_start(PROFILER_DEFAULT_ENTRIES, PROFILER_DEFAULT_INTERVAL, features, filters, MOZ_ARRAY_LENGTH(filters), 0); - PROFILER_MARKER("tracing event", OTHER, {}, Tracing, "A"); - PROFILER_MARKER("tracing start", OTHER, MarkerTiming::IntervalStart(), - Tracing, "A"); - PROFILER_MARKER("tracing end", OTHER, MarkerTiming::IntervalEnd(), Tracing, - "A"); + // Used in markers below. + TimeStamp ts0 = TimeStamp::NowUnfuzzed(); - auto bt = profiler_capture_backtrace(); - PROFILER_MARKER("tracing event with stack", OTHER, - MarkerStack::TakeBacktrace(std::move(bt)), Tracing, "B"); + profiler_tracing_marker("A", "tracing event", + JS::ProfilingCategoryPair::OTHER, TRACING_EVENT); + PROFILER_TRACING_MARKER("A", "tracing start", OTHER, TRACING_INTERVAL_START); + PROFILER_TRACING_MARKER("A", "tracing end", OTHER, TRACING_INTERVAL_END); + + UniqueProfilerBacktrace bt = profiler_get_backtrace(); + profiler_tracing_marker("B", "tracing event with stack", + JS::ProfilingCategoryPair::OTHER, TRACING_EVENT, + std::move(bt)); { AUTO_PROFILER_TRACING_MARKER("C", "auto tracing", OTHER); } PROFILER_MARKER_UNTYPED("M1", OTHER, {}); + PROFILER_ADD_MARKER_WITH_PAYLOAD("M2", OTHER, TracingMarkerPayload, + ("C", TRACING_EVENT, ts0)); PROFILER_MARKER_UNTYPED("M3", OTHER, {}); + PROFILER_ADD_MARKER_WITH_PAYLOAD( + "M4", OTHER, TracingMarkerPayload, + ("C", TRACING_EVENT, ts0, mozilla::Nothing(), profiler_get_backtrace())); + + for (int i = 0; i < 10; i++) { + PROFILER_ADD_MARKER_WITH_PAYLOAD("M5", OTHER, GTestMarkerPayload, (i)); + } + // The GTestMarkerPayloads should have been created, serialized, and + // destroyed. + EXPECT_EQ(GTestMarkerPayload::sNumCreated, 10); + EXPECT_EQ(GTestMarkerPayload::sNumSerialized, 10); + EXPECT_EQ(GTestMarkerPayload::sNumDeserialized, 0); + EXPECT_EQ(GTestMarkerPayload::sNumStreamed, 0); + EXPECT_EQ(GTestMarkerPayload::sNumDestroyed, 10); // Create three strings: two that are the maximum allowed length, and one that // is one char longer. static const size_t kMax = ProfileBuffer::kMaxFrameKeyLength; UniquePtr<char[]> okstr1 = MakeUnique<char[]>(kMax); UniquePtr<char[]> okstr2 = MakeUnique<char[]>(kMax); UniquePtr<char[]> longstr = MakeUnique<char[]>(kMax + 1); UniquePtr<char[]> longstrCut = MakeUnique<char[]>(kMax + 1); @@ -669,86 +758,121 @@ TEST(GeckoProfiler, Markers) // to serialized numbers in other markers.) MOZ_RELEASE_ASSERT( profiler_add_marker("FirstMarker", geckoprofiler::category::OTHER, MarkerTiming::Interval(ts1, ts2), geckoprofiler::markers::Text{}, "FirstMarker")); // Other markers in alphabetical order of payload class names. - nsCOMPtr<nsIURI> uri; - ASSERT_TRUE( - NS_SUCCEEDED(NS_NewURI(getter_AddRefs(uri), "http://mozilla.org/"_ns))); - // The marker name will be "Load <aChannelId>: <aURI>". - profiler_add_network_marker( - /* nsIURI* aURI */ uri, - /* const nsACString& aRequestMethod */ "GET"_ns, - /* int32_t aPriority */ 34, - /* uint64_t aChannelId */ 1, - /* NetworkLoadType aType */ NetworkLoadType::LOAD_START, - /* mozilla::TimeStamp aStart */ ts1, - /* mozilla::TimeStamp aEnd */ ts2, - /* int64_t aCount */ 56, - /* mozilla::net::CacheDisposition aCacheDisposition */ - net::kCacheHit, - /* uint64_t aInnerWindowID */ 78 - /* const mozilla::net::TimingStruct* aTimings = nullptr */ - /* nsIURI* aRedirectURI = nullptr */ - /* mozilla::UniquePtr<mozilla::ProfileChunkedBuffer> aSource = - nullptr */ - /* const mozilla::Maybe<nsDependentCString>& aContentType = - mozilla::Nothing() */); + { + const char gcMajorJSON[] = "42"; + const auto len = strlen(gcMajorJSON); + char* buffer = + static_cast<char*>(js::SystemAllocPolicy{}.pod_malloc<char>(len + 1)); + strncpy(buffer, gcMajorJSON, len); + buffer[len] = '\0'; + PROFILER_ADD_MARKER_WITH_PAYLOAD("GCMajorMarkerPayload marker", OTHER, + GCMajorMarkerPayload, + (ts1, ts2, JS::UniqueChars(buffer))); + } + + { + const char gcMinorJSON[] = "43"; + const auto len = strlen(gcMinorJSON); + char* buffer = + static_cast<char*>(js::SystemAllocPolicy{}.pod_malloc<char>(len + 1)); + strncpy(buffer, gcMinorJSON, len); + buffer[len] = '\0'; + PROFILER_ADD_MARKER_WITH_PAYLOAD("GCMinorMarkerPayload marker", OTHER, + GCMinorMarkerPayload, + (ts1, ts2, JS::UniqueChars(buffer))); + } + + { + const char gcSliceJSON[] = "44"; + const auto len = strlen(gcSliceJSON); + char* buffer = + static_cast<char*>(js::SystemAllocPolicy{}.pod_malloc<char>(len + 1)); + strncpy(buffer, gcSliceJSON, len); + buffer[len] = '\0'; + PROFILER_ADD_MARKER_WITH_PAYLOAD("GCSliceMarkerPayload marker", OTHER, + GCSliceMarkerPayload, + (ts1, ts2, JS::UniqueChars(buffer))); + } + + PROFILER_ADD_MARKER_WITH_PAYLOAD("HangMarkerPayload marker", OTHER, + HangMarkerPayload, (ts1, ts2)); + + PROFILER_ADD_MARKER_WITH_PAYLOAD("LogMarkerPayload marker", OTHER, + LogMarkerPayload, ("module", "text", ts1)); + + PROFILER_ADD_MARKER_WITH_PAYLOAD("LongTaskMarkerPayload marker", OTHER, + LongTaskMarkerPayload, (ts1, ts2)); + + PROFILER_ADD_MARKER_WITH_PAYLOAD("NativeAllocationMarkerPayload marker", + OTHER, NativeAllocationMarkerPayload, + (ts1, 9876543210, 1234, 5678, nullptr)); - profiler_add_network_marker( - /* nsIURI* aURI */ uri, - /* const nsACString& aRequestMethod */ "GET"_ns, - /* int32_t aPriority */ 34, - /* uint64_t aChannelId */ 12, - /* NetworkLoadType aType */ NetworkLoadType::LOAD_STOP, - /* mozilla::TimeStamp aStart */ ts1, - /* mozilla::TimeStamp aEnd */ ts2, - /* int64_t aCount */ 56, - /* mozilla::net::CacheDisposition aCacheDisposition */ - net::kCacheUnresolved, - /* uint64_t aInnerWindowID */ 78, - /* const mozilla::net::TimingStruct* aTimings = nullptr */ nullptr, - /* nsIURI* aRedirectURI = nullptr */ nullptr, - /* mozilla::UniquePtr<mozilla::ProfileChunkedBuffer> aSource = - nullptr */ - nullptr, - /* const mozilla::Maybe<nsDependentCString>& aContentType = - mozilla::Nothing() */ - Some(nsDependentCString("text/html"))); + nsCString requestMethod = "GET"_ns; + PROFILER_ADD_MARKER_WITH_PAYLOAD( + "NetworkMarkerPayload start marker", OTHER, NetworkMarkerPayload, + (1, "http://mozilla.org/", requestMethod, NetworkLoadType::LOAD_START, + ts1, ts2, 34, 56, net::kCacheHit, 78)); + + PROFILER_ADD_MARKER_WITH_PAYLOAD( + "NetworkMarkerPayload stop marker", OTHER, NetworkMarkerPayload, + (12, "http://mozilla.org/", requestMethod, NetworkLoadType::LOAD_STOP, + ts1, ts2, 34, 56, net::kCacheUnresolved, 78, nullptr, nullptr, nullptr, + Some(nsDependentCString("text/html")))); + + PROFILER_ADD_MARKER_WITH_PAYLOAD( + "NetworkMarkerPayload redirect marker", OTHER, NetworkMarkerPayload, + (123, "http://mozilla.org/", requestMethod, + NetworkLoadType::LOAD_REDIRECT, ts1, ts2, 34, 56, net::kCacheUnresolved, + 78, nullptr, "http://example.com/")); + + PROFILER_ADD_MARKER_WITH_PAYLOAD( + "PrefMarkerPayload marker", OTHER, PrefMarkerPayload, + ("preference name", mozilla::Nothing(), mozilla::Nothing(), + "preference value"_ns, ts1)); - nsCOMPtr<nsIURI> redirectURI; - ASSERT_TRUE(NS_SUCCEEDED( - NS_NewURI(getter_AddRefs(redirectURI), "http://example.com/"_ns))); - profiler_add_network_marker( - /* nsIURI* aURI */ uri, - /* const nsACString& aRequestMethod */ "GET"_ns, - /* int32_t aPriority */ 34, - /* uint64_t aChannelId */ 123, - /* NetworkLoadType aType */ NetworkLoadType::LOAD_REDIRECT, - /* mozilla::TimeStamp aStart */ ts1, - /* mozilla::TimeStamp aEnd */ ts2, - /* int64_t aCount */ 56, - /* mozilla::net::CacheDisposition aCacheDisposition */ - net::kCacheUnresolved, - /* uint64_t aInnerWindowID */ 78, - /* const mozilla::net::TimingStruct* aTimings = nullptr */ nullptr, - /* nsIURI* aRedirectURI = nullptr */ redirectURI - /* mozilla::UniquePtr<mozilla::ProfileChunkedBuffer> aSource = - nullptr */ - /* const mozilla::Maybe<nsDependentCString>& aContentType = - mozilla::Nothing() */); + nsCString screenshotURL = "url"_ns; + PROFILER_ADD_MARKER_WITH_PAYLOAD( + "ScreenshotPayload marker", OTHER, ScreenshotPayload, + (ts1, std::move(screenshotURL), mozilla::gfx::IntSize(12, 34), + uintptr_t(0x45678u))); + + PROFILER_ADD_MARKER_WITH_PAYLOAD("TextMarkerPayload marker 1", OTHER, + TextMarkerPayload, ("text"_ns, ts1)); + + PROFILER_ADD_MARKER_WITH_PAYLOAD("TextMarkerPayload marker 2", OTHER, + TextMarkerPayload, ("text"_ns, ts1, ts2)); + + PROFILER_ADD_MARKER_WITH_PAYLOAD("UserTimingMarkerPayload marker mark", OTHER, + UserTimingMarkerPayload, + (u"mark name"_ns, ts1, mozilla::Nothing())); + + PROFILER_ADD_MARKER_WITH_PAYLOAD( + "UserTimingMarkerPayload marker measure", OTHER, UserTimingMarkerPayload, + (u"measure name"_ns, Some(u"start mark"_ns), Some(u"end mark"_ns), ts1, + ts2, mozilla::Nothing())); + + PROFILER_ADD_MARKER_WITH_PAYLOAD("VsyncMarkerPayload marker", OTHER, + VsyncMarkerPayload, (ts1)); + + PROFILER_ADD_MARKER_WITH_PAYLOAD( + "IPCMarkerPayload marker", IPC, IPCMarkerPayload, + (1111, 1, 3 /* PAPZ::Msg_LayerTransforms */, mozilla::ipc::ParentSide, + mozilla::ipc::MessageDirection::eSending, + mozilla::ipc::MessagePhase::Endpoint, false, ts1)); MOZ_RELEASE_ASSERT(profiler_add_marker( "Text in main thread with stack", geckoprofiler::category::OTHER, - {MarkerStack::Capture(), MarkerTiming::Interval(ts1, ts2)}, - geckoprofiler::markers::Text{}, "")); + MarkerStack::Capture(), geckoprofiler::markers::Text{}, "")); MOZ_RELEASE_ASSERT(profiler_add_marker( "Text from main thread with stack", geckoprofiler::category::OTHER, MarkerOptions(MarkerThreadId::MainThread(), MarkerStack::Capture()), geckoprofiler::markers::Text{}, "")); std::thread registeredThread([]() { AUTO_PROFILER_REGISTER_THREAD("Marker test sub-thread"); // Marker in non-profiled thread won't be stored. @@ -779,50 +903,110 @@ TEST(GeckoProfiler, Markers) geckoprofiler::markers::Text{}, "")); }); unregisteredThread.join(); MOZ_RELEASE_ASSERT( profiler_add_marker("Tracing", geckoprofiler::category::OTHER, {}, geckoprofiler::markers::Tracing{}, "category")); + MOZ_RELEASE_ASSERT(profiler_add_marker( + "UserTimingMark", geckoprofiler::category::OTHER, {}, + geckoprofiler::markers::UserTimingMark{}, "mark name")); + + MOZ_RELEASE_ASSERT(profiler_add_marker( + "UserTimingMeasure", geckoprofiler::category::OTHER, {}, + geckoprofiler::markers::UserTimingMeasure{}, "measure name", + Some(mozilla::ProfilerString8View("start")), + Some(mozilla::ProfilerString8View("end")))); + + MOZ_RELEASE_ASSERT(profiler_add_marker("Hang", geckoprofiler::category::OTHER, + {}, geckoprofiler::markers::Hang{})); + + MOZ_RELEASE_ASSERT(profiler_add_marker("LongTask", + geckoprofiler::category::OTHER, {}, + geckoprofiler::markers::LongTask{})); + MOZ_RELEASE_ASSERT(profiler_add_marker("Text", geckoprofiler::category::OTHER, {}, geckoprofiler::markers::Text{}, "Text text")); - MOZ_RELEASE_ASSERT(profiler_add_marker( - "MediaSample", geckoprofiler::category::OTHER, {}, - geckoprofiler::markers::MediaSampleMarker{}, 123, 456)); + MOZ_RELEASE_ASSERT(profiler_add_marker("Log", geckoprofiler::category::OTHER, + {}, geckoprofiler::markers::Log{}, + "module", "log text")); + + MOZ_RELEASE_ASSERT( + profiler_add_marker("MediaSample", geckoprofiler::category::OTHER, {}, + geckoprofiler::markers::MediaSample{}, 123, 456)); + + MOZ_RELEASE_ASSERT(profiler_add_marker("Budget", + geckoprofiler::category::OTHER, {}, + geckoprofiler::markers::Budget{})); SpliceableChunkedJSONWriter w; w.Start(); EXPECT_TRUE(::profiler_stream_json_for_this_process(w)); w.End(); + // The GTestMarkerPayloads should have been deserialized, streamed, and + // destroyed. + EXPECT_EQ(GTestMarkerPayload::sNumCreated, 10 + 0); + EXPECT_EQ(GTestMarkerPayload::sNumSerialized, 10 + 0); + EXPECT_EQ(GTestMarkerPayload::sNumDeserialized, 0 + 10); + EXPECT_EQ(GTestMarkerPayload::sNumStreamed, 0 + 10); + EXPECT_EQ(GTestMarkerPayload::sNumDestroyed, 10 + 10); + UniquePtr<char[]> profile = w.ChunkedWriteFunc().CopyData(); ASSERT_TRUE(!!profile.get()); // Expected markers, in order. enum State { S_tracing_event, S_tracing_start, S_tracing_end, S_tracing_event_with_stack, S_tracing_auto_tracing_start, S_tracing_auto_tracing_end, S_M1, + S_tracing_M2_C, S_M3, + S_tracing_M4_C_stack, + S_M5_gtest0, + S_M5_gtest1, + S_M5_gtest2, + S_M5_gtest3, + S_M5_gtest4, + S_M5_gtest5, + S_M5_gtest6, + S_M5_gtest7, + S_M5_gtest8, + S_M5_gtest9, S_Markers2DefaultEmptyOptions, S_Markers2DefaultWithOptions, S_Markers2ExplicitDefaultEmptyOptions, S_Markers2ExplicitDefaultWithOptions, S_FirstMarker, + S_GCMajorMarkerPayload, + S_GCMinorMarkerPayload, + S_GCSliceMarkerPayload, + S_HangMarkerPayload, + S_LogMarkerPayload, + S_LongTaskMarkerPayload, + S_NativeAllocationMarkerPayload, S_NetworkMarkerPayload_start, S_NetworkMarkerPayload_stop, S_NetworkMarkerPayload_redirect, + S_PrefMarkerPayload, + S_ScreenshotPayload, + S_TextMarkerPayload1, + S_TextMarkerPayload2, + S_UserTimingMarkerPayload_mark, + S_UserTimingMarkerPayload_measure, + S_VsyncMarkerPayload, + S_IPCMarkerPayload, S_TextWithStack, S_TextToMTWithStack, S_RegThread_TextToMTWithStack, S_UnregThread_TextToMTWithStack, S_LAST, } state = State(0); @@ -1045,132 +1229,314 @@ TEST(GeckoProfiler, Markers) std::string typeString = type.asString(); if (nameString == "tracing event") { EXPECT_EQ(state, S_tracing_event); state = State(S_tracing_event + 1); EXPECT_EQ(typeString, "tracing"); EXPECT_TIMING_INSTANT; EXPECT_EQ_JSON(payload["category"], String, "A"); + EXPECT_TRUE(payload["interval"].isNull()); EXPECT_TRUE(payload["stack"].isNull()); } else if (nameString == "tracing start") { EXPECT_EQ(state, S_tracing_start); state = State(S_tracing_start + 1); EXPECT_EQ(typeString, "tracing"); EXPECT_TIMING_START; EXPECT_EQ_JSON(payload["category"], String, "A"); + EXPECT_EQ_JSON(payload["interval"], String, "start"); EXPECT_TRUE(payload["stack"].isNull()); } else if (nameString == "tracing end") { EXPECT_EQ(state, S_tracing_end); state = State(S_tracing_end + 1); EXPECT_EQ(typeString, "tracing"); EXPECT_TIMING_END; EXPECT_EQ_JSON(payload["category"], String, "A"); + EXPECT_EQ_JSON(payload["interval"], String, "end"); EXPECT_TRUE(payload["stack"].isNull()); } else if (nameString == "tracing event with stack") { EXPECT_EQ(state, S_tracing_event_with_stack); state = State(S_tracing_event_with_stack + 1); EXPECT_EQ(typeString, "tracing"); EXPECT_TIMING_INSTANT; EXPECT_EQ_JSON(payload["category"], String, "B"); + EXPECT_TRUE(payload["interval"].isNull()); EXPECT_TRUE(payload["stack"].isObject()); } else if (nameString == "auto tracing") { switch (state) { case S_tracing_auto_tracing_start: state = State(S_tracing_auto_tracing_start + 1); EXPECT_EQ(typeString, "tracing"); EXPECT_TIMING_START; EXPECT_EQ_JSON(payload["category"], String, "C"); + EXPECT_EQ_JSON(payload["interval"], String, "start"); EXPECT_TRUE(payload["stack"].isNull()); break; case S_tracing_auto_tracing_end: state = State(S_tracing_auto_tracing_end + 1); EXPECT_EQ(typeString, "tracing"); EXPECT_TIMING_END; EXPECT_EQ_JSON(payload["category"], String, "C"); + EXPECT_EQ_JSON(payload["interval"], String, "end"); ASSERT_TRUE(payload["stack"].isNull()); break; default: EXPECT_TRUE(state == S_tracing_auto_tracing_start || state == S_tracing_auto_tracing_end); break; } + } else if (nameString == "M2") { + EXPECT_EQ(state, S_tracing_M2_C); + state = State(S_tracing_M2_C + 1); + EXPECT_EQ(typeString, "tracing"); + EXPECT_TIMING_INSTANT; + EXPECT_EQ_JSON(payload["category"], String, "C"); + EXPECT_TRUE(payload["interval"].isNull()); + EXPECT_TRUE(payload["stack"].isNull()); + + } else if (nameString == "M4") { + EXPECT_EQ(state, S_tracing_M4_C_stack); + state = State(S_tracing_M4_C_stack + 1); + EXPECT_EQ(typeString, "tracing"); + EXPECT_TIMING_INSTANT; + EXPECT_EQ_JSON(payload["category"], String, "C"); + EXPECT_TRUE(payload["interval"].isNull()); + EXPECT_TRUE(payload["stack"].isObject()); + + } else if (nameString == "M5") { + EXPECT_EQ(typeString, "gtest"); + // It should only have one more element (apart from "type"). + ASSERT_EQ(payload.size(), 2u); + const auto itEnd = payload.end(); + for (auto it = payload.begin(); it != itEnd; ++it) { + std::string key = it.name(); + if (key != "type") { + const Json::Value& value = *it; + ASSERT_TRUE(value.isInt()); + int valueInt = value.asInt(); + // We expect `"gtest-<i>" : <i>`. + EXPECT_EQ(state, State(S_M5_gtest0 + valueInt)); + state = State(state + 1); + EXPECT_EQ(key, + std::string("gtest-") + std::to_string(valueInt)); + } + } + } else if (nameString == "default-templated markers 2.0 with option") { // TODO: Remove this when bug 1646714 lands. EXPECT_EQ(state, S_Markers2DefaultWithOptions); state = State(S_Markers2DefaultWithOptions + 1); EXPECT_EQ(typeString, "NoPayloadUserData"); EXPECT_FALSE(payload["stack"].isNull()); } else if (nameString == "FirstMarker") { // Record start and end times, to compare with timestamps in // following markers. EXPECT_EQ(state, S_FirstMarker); ts1Double = marker[START_TIME].asDouble(); ts2Double = marker[END_TIME].asDouble(); state = State(S_FirstMarker + 1); + } else if (nameString == "GCMajorMarkerPayload marker") { + EXPECT_EQ(state, S_GCMajorMarkerPayload); + state = State(S_GCMajorMarkerPayload + 1); + EXPECT_EQ(typeString, "GCMajor"); + EXPECT_TIMING_INTERVAL_AT(ts1Double, ts2Double); + EXPECT_TRUE(payload["stack"].isNull()); + EXPECT_EQ_JSON(payload["timings"], Int, 42); - } else if (nameString == "Load 1: http://mozilla.org/") { + } else if (nameString == "GCMinorMarkerPayload marker") { + EXPECT_EQ(state, S_GCMinorMarkerPayload); + state = State(S_GCMinorMarkerPayload + 1); + EXPECT_EQ(typeString, "GCMinor"); + EXPECT_TIMING_INTERVAL_AT(ts1Double, ts2Double); + EXPECT_TRUE(payload["stack"].isNull()); + EXPECT_EQ_JSON(payload["nursery"], Int, 43); + + } else if (nameString == "GCSliceMarkerPayload marker") { + EXPECT_EQ(state, S_GCSliceMarkerPayload); + state = State(S_GCSliceMarkerPayload + 1); + EXPECT_EQ(typeString, "GCSlice"); + EXPECT_TIMING_INTERVAL_AT(ts1Double, ts2Double); + EXPECT_TRUE(payload["stack"].isNull()); + EXPECT_EQ_JSON(payload["timings"], Int, 44); + + } else if (nameString == "HangMarkerPayload marker") { + EXPECT_EQ(state, S_HangMarkerPayload); + state = State(S_HangMarkerPayload + 1); + EXPECT_EQ(typeString, "BHR-detected hang"); + EXPECT_TIMING_INTERVAL_AT(ts1Double, ts2Double); + EXPECT_TRUE(payload["stack"].isNull()); + + } else if (nameString == "LogMarkerPayload marker") { + EXPECT_EQ(state, S_LogMarkerPayload); + state = State(S_LogMarkerPayload + 1); + EXPECT_EQ(typeString, "Log"); + EXPECT_TIMING_INSTANT_AT(ts1Double); + EXPECT_TRUE(payload["stack"].isNull()); + EXPECT_EQ_JSON(payload["name"], String, "text"); + EXPECT_EQ_JSON(payload["module"], String, "module"); + + } else if (nameString == "LongTaskMarkerPayload marker") { + EXPECT_EQ(state, S_LongTaskMarkerPayload); + state = State(S_LongTaskMarkerPayload + 1); + EXPECT_EQ(typeString, "MainThreadLongTask"); + EXPECT_TIMING_INTERVAL_AT(ts1Double, ts2Double); + EXPECT_TRUE(payload["stack"].isNull()); + EXPECT_EQ_JSON(payload["category"], String, "LongTask"); + + } else if (nameString == "NativeAllocationMarkerPayload marker") { + EXPECT_EQ(state, S_NativeAllocationMarkerPayload); + state = State(S_NativeAllocationMarkerPayload + 1); + EXPECT_EQ(typeString, "Native allocation"); + EXPECT_TIMING_INSTANT_AT(ts1Double); + EXPECT_TRUE(payload["stack"].isNull()); + EXPECT_EQ_JSON(payload["size"], Int64, 9876543210); + EXPECT_EQ_JSON(payload["memoryAddress"], Int64, 1234); + EXPECT_EQ_JSON(payload["threadId"], Int64, 5678); + + } else if (nameString == "NetworkMarkerPayload start marker") { EXPECT_EQ(state, S_NetworkMarkerPayload_start); state = State(S_NetworkMarkerPayload_start + 1); EXPECT_EQ(typeString, "Network"); - EXPECT_EQ_JSON(payload["startTime"], Double, ts1Double); - EXPECT_EQ_JSON(payload["endTime"], Double, ts2Double); EXPECT_EQ_JSON(payload["id"], Int64, 1); EXPECT_EQ_JSON(payload["URI"], String, "http://mozilla.org/"); EXPECT_EQ_JSON(payload["requestMethod"], String, "GET"); EXPECT_EQ_JSON(payload["pri"], Int64, 34); EXPECT_EQ_JSON(payload["count"], Int64, 56); EXPECT_EQ_JSON(payload["cache"], String, "Hit"); - EXPECT_TRUE(payload["RedirectURI"].isNull()); + EXPECT_EQ_JSON(payload["RedirectURI"], String, ""); EXPECT_TRUE(payload["contentType"].isNull()); - } else if (nameString == "Load 12: http://mozilla.org/") { + } else if (nameString == "NetworkMarkerPayload stop marker") { EXPECT_EQ(state, S_NetworkMarkerPayload_stop); state = State(S_NetworkMarkerPayload_stop + 1); EXPECT_EQ(typeString, "Network"); - EXPECT_EQ_JSON(payload["startTime"], Double, ts1Double); - EXPECT_EQ_JSON(payload["endTime"], Double, ts2Double); EXPECT_EQ_JSON(payload["id"], Int64, 12); EXPECT_EQ_JSON(payload["URI"], String, "http://mozilla.org/"); EXPECT_EQ_JSON(payload["requestMethod"], String, "GET"); EXPECT_EQ_JSON(payload["pri"], Int64, 34); EXPECT_EQ_JSON(payload["count"], Int64, 56); EXPECT_EQ_JSON(payload["cache"], String, "Unresolved"); - EXPECT_TRUE(payload["RedirectURI"].isNull()); + EXPECT_EQ_JSON(payload["RedirectURI"], String, ""); EXPECT_EQ_JSON(payload["contentType"], String, "text/html"); - } else if (nameString == "Load 123: http://mozilla.org/") { + } else if (nameString == "NetworkMarkerPayload redirect marker") { EXPECT_EQ(state, S_NetworkMarkerPayload_redirect); state = State(S_NetworkMarkerPayload_redirect + 1); EXPECT_EQ(typeString, "Network"); - EXPECT_EQ_JSON(payload["startTime"], Double, ts1Double); - EXPECT_EQ_JSON(payload["endTime"], Double, ts2Double); EXPECT_EQ_JSON(payload["id"], Int64, 123); EXPECT_EQ_JSON(payload["URI"], String, "http://mozilla.org/"); EXPECT_EQ_JSON(payload["requestMethod"], String, "GET"); EXPECT_EQ_JSON(payload["pri"], Int64, 34); EXPECT_EQ_JSON(payload["count"], Int64, 56); EXPECT_EQ_JSON(payload["cache"], String, "Unresolved"); EXPECT_EQ_JSON(payload["RedirectURI"], String, "http://example.com/"); EXPECT_TRUE(payload["contentType"].isNull()); + } else if (nameString == "PrefMarkerPayload marker") { + EXPECT_EQ(state, S_PrefMarkerPayload); + state = State(S_PrefMarkerPayload + 1); + EXPECT_EQ(typeString, "PreferenceRead"); + EXPECT_TIMING_INSTANT_AT(ts1Double); + EXPECT_TRUE(payload["stack"].isNull()); + EXPECT_EQ_JSON(payload["prefAccessTime"], Double, ts1Double); + EXPECT_EQ_JSON(payload["prefName"], String, "preference name"); + EXPECT_EQ_JSON(payload["prefKind"], String, "Shared"); + EXPECT_EQ_JSON(payload["prefType"], String, + "Preference not found"); + EXPECT_EQ_JSON(payload["prefValue"], String, + "preference value"); + + } else if (nameString == "ScreenshotPayload marker") { + EXPECT_EQ(state, S_ScreenshotPayload); + state = State(S_ScreenshotPayload + 1); + EXPECT_EQ(typeString, "CompositorScreenshot"); + EXPECT_EQ_STRINGTABLE(payload["url"], "url"); + EXPECT_EQ_JSON(payload["windowID"], String, "0x45678"); + EXPECT_EQ_JSON(payload["windowWidth"], Int, 12); + EXPECT_EQ_JSON(payload["windowHeight"], Int, 34); + + } else if (nameString == "TextMarkerPayload marker 1") { + EXPECT_EQ(state, S_TextMarkerPayload1); + state = State(S_TextMarkerPayload1 + 1); + EXPECT_EQ(typeString, "Text"); + EXPECT_TIMING_INSTANT_AT(ts1Double); + EXPECT_TRUE(payload["stack"].isNull()); + EXPECT_EQ_JSON(payload["name"], String, "text"); + + } else if (nameString == "TextMarkerPayload marker 2") { + EXPECT_EQ(state, S_TextMarkerPayload2); + state = State(S_TextMarkerPayload2 + 1); + EXPECT_EQ(typeString, "Text"); + EXPECT_TIMING_INTERVAL_AT(ts1Double, ts2Double); + EXPECT_TRUE(payload["stack"].isNull()); + EXPECT_EQ_JSON(payload["name"], String, "text"); + + } else if (nameString == "UserTimingMarkerPayload marker mark") { + EXPECT_EQ(state, S_UserTimingMarkerPayload_mark); + state = State(S_UserTimingMarkerPayload_mark + 1); + EXPECT_EQ(typeString, "UserTiming"); + EXPECT_TIMING_INSTANT_AT(ts1Double); + EXPECT_TRUE(payload["stack"].isNull()); + EXPECT_EQ_JSON(payload["name"], String, "mark name"); + EXPECT_EQ_JSON(payload["entryType"], String, "mark"); + + } else if (nameString == + "UserTimingMarkerPayload marker measure") { + EXPECT_EQ(state, S_UserTimingMarkerPayload_measure); + state = State(S_UserTimingMarkerPayload_measure + 1); + EXPECT_EQ(typeString, "UserTiming"); + EXPECT_TIMING_INTERVAL_AT(ts1Double, ts2Double); + EXPECT_TRUE(payload["stack"].isNull()); + EXPECT_EQ_JSON(payload["name"], String, "measure name"); + EXPECT_EQ_JSON(payload["entryType"], String, "measure"); + EXPECT_EQ_JSON(payload["startMark"], String, "start mark"); + EXPECT_EQ_JSON(payload["endMark"], String, "end mark"); + + } else if (nameString == "VsyncMarkerPayload marker") { + EXPECT_EQ(state, S_VsyncMarkerPayload); + state = State(S_VsyncMarkerPayload + 1); + EXPECT_EQ(typeString, "VsyncTimestamp"); + // Timestamp is stored in marker outside of payload. + EXPECT_TIMING_INSTANT_AT(ts1Double); + EXPECT_TRUE(payload["stack"].isNull()); + + } else if (nameString == "IPCMarkerPayload marker") { + EXPECT_EQ(state, S_IPCMarkerPayload); + state = State(S_IPCMarkerPayload + 1); + EXPECT_EQ(typeString, "IPC"); + EXPECT_TIMING_INSTANT_AT(ts1Double); + + // The startTime and endTime are currently duplicated in the + // payload. + EXPECT_EQ_JSON(payload["startTime"], Double, ts1Double); + EXPECT_EQ_JSON(payload["endTime"], Double, ts1Double); + + EXPECT_TRUE(payload["stack"].isNull()); + EXPECT_EQ_JSON(payload["otherPid"], Int, 1111); + EXPECT_EQ_JSON(payload["messageSeqno"], Int, 1); + EXPECT_EQ_JSON(payload["messageType"], String, + "PAPZ::Msg_LayerTransforms"); + EXPECT_EQ_JSON(payload["side"], String, "parent"); + EXPECT_EQ_JSON(payload["direction"], String, "sending"); + EXPECT_EQ_JSON(payload["phase"], String, "endpoint"); + EXPECT_EQ_JSON(payload["sync"], Bool, false); + } else if (nameString == "Text in main thread with stack") { EXPECT_EQ(state, S_TextWithStack); state = State(S_TextWithStack + 1); EXPECT_EQ(typeString, "Text"); EXPECT_FALSE(payload["stack"].isNull()); - EXPECT_TIMING_INTERVAL_AT(ts1Double, ts2Double); EXPECT_EQ_JSON(payload["name"], String, ""); } else if (nameString == "Text from main thread with stack") { EXPECT_EQ(state, S_TextToMTWithStack); state = State(S_TextToMTWithStack + 1); EXPECT_EQ(typeString, "Text"); EXPECT_FALSE(payload["stack"].isNull()); EXPECT_EQ_JSON(payload["name"], String, ""); @@ -1273,16 +1639,80 @@ TEST(GeckoProfiler, Markers) ASSERT_EQ(data.size(), 1u); ASSERT_TRUE(data[0u].isObject()); EXPECT_EQ_JSON(data[0u]["key"], String, "category"); EXPECT_EQ_JSON(data[0u]["label"], String, "Type"); EXPECT_EQ_JSON(data[0u]["format"], String, "string"); + } else if (nameString == "UserTimingMark") { + EXPECT_EQ(display.size(), 2u); + EXPECT_EQ(display[0u].asString(), "marker-chart"); + EXPECT_EQ(display[1u].asString(), "marker-table"); + + ASSERT_EQ(data.size(), 4u); + + ASSERT_TRUE(data[0u].isObject()); + EXPECT_EQ_JSON(data[0u]["label"], String, "Marker"); + EXPECT_EQ_JSON(data[0u]["value"], String, "UserTiming"); + + ASSERT_TRUE(data[1u].isObject()); + EXPECT_EQ_JSON(data[1u]["key"], String, "entryType"); + EXPECT_EQ_JSON(data[1u]["label"], String, "Entry Type"); + EXPECT_EQ_JSON(data[1u]["format"], String, "string"); + + ASSERT_TRUE(data[2u].isObject()); + EXPECT_EQ_JSON(data[2u]["key"], String, "name"); + EXPECT_EQ_JSON(data[2u]["label"], String, "Name"); + EXPECT_EQ_JSON(data[2u]["format"], String, "string"); + + ASSERT_TRUE(data[3u].isObject()); + EXPECT_EQ_JSON(data[3u]["label"], String, "Description"); + EXPECT_EQ_JSON(data[3u]["value"], String, + "UserTimingMark is created using the DOM API " + "performance.mark()."); + + } else if (nameString == "UserTimingMeasure") { + EXPECT_EQ(display.size(), 2u); + EXPECT_EQ(display[0u].asString(), "marker-chart"); + EXPECT_EQ(display[1u].asString(), "marker-table"); + + ASSERT_EQ(data.size(), 6u); + + ASSERT_TRUE(data[0u].isObject()); + EXPECT_EQ_JSON(data[0u]["label"], String, "Marker"); + EXPECT_EQ_JSON(data[0u]["value"], String, "UserTiming"); + + ASSERT_TRUE(data[1u].isObject()); + EXPECT_EQ_JSON(data[1u]["key"], String, "entryType"); + EXPECT_EQ_JSON(data[1u]["label"], String, "Entry Type"); + EXPECT_EQ_JSON(data[1u]["format"], String, "string"); + + ASSERT_TRUE(data[2u].isObject()); + EXPECT_EQ_JSON(data[2u]["key"], String, "name"); + EXPECT_EQ_JSON(data[2u]["label"], String, "Name"); + EXPECT_EQ_JSON(data[2u]["format"], String, "string"); + + ASSERT_TRUE(data[3u].isObject()); + EXPECT_EQ_JSON(data[3u]["key"], String, "startMark"); + EXPECT_EQ_JSON(data[3u]["label"], String, "Start Mark"); + EXPECT_EQ_JSON(data[3u]["format"], String, "string"); + + ASSERT_TRUE(data[4u].isObject()); + EXPECT_EQ_JSON(data[4u]["key"], String, "endMark"); + EXPECT_EQ_JSON(data[4u]["label"], String, "End Mark"); + EXPECT_EQ_JSON(data[4u]["format"], String, "string"); + + ASSERT_TRUE(data[5u].isObject()); + EXPECT_EQ_JSON(data[5u]["label"], String, "Description"); + EXPECT_EQ_JSON(data[5u]["value"], String, + "UserTimingMeasure is created using the DOM API " + "performance.measure()."); + } else if (nameString == "BHR-detected hang") { EXPECT_EQ(display.size(), 3u); EXPECT_EQ(display[0u].asString(), "marker-chart"); EXPECT_EQ(display[1u].asString(), "marker-table"); EXPECT_EQ(display[2u].asString(), "timeline-overview"); ASSERT_EQ(data.size(), 0u); @@ -1342,18 +1772,28 @@ TEST(GeckoProfiler, Markers) ADD_FAILURE() << "Unknown marker schema '" << nameString.c_str() << "'"; } } // Check that we've got all expected schema. EXPECT_TRUE(testedSchemaNames.find("Text") != testedSchemaNames.end()); EXPECT_TRUE(testedSchemaNames.find("tracing") != testedSchemaNames.end()); + EXPECT_TRUE(testedSchemaNames.find("UserTimingMark") != + testedSchemaNames.end()); + EXPECT_TRUE(testedSchemaNames.find("UserTimingMeasure") != + testedSchemaNames.end()); + EXPECT_TRUE(testedSchemaNames.find("BHR-detected hang") != + testedSchemaNames.end()); + EXPECT_TRUE(testedSchemaNames.find("MainThreadLongTask") != + testedSchemaNames.end()); + EXPECT_TRUE(testedSchemaNames.find("Log") != testedSchemaNames.end()); EXPECT_TRUE(testedSchemaNames.find("MediaSample") != testedSchemaNames.end()); + EXPECT_TRUE(testedSchemaNames.find("Budget") != testedSchemaNames.end()); } // markerSchema } // meta Maybe<ProfilerBufferInfo> info = profiler_get_buffer_info(); MOZ_RELEASE_ASSERT(info.isSome()); printf("Profiler buffer range: %llu .. %llu (%llu bytes)\n", static_cast<unsigned long long>(info->mRangeStart), static_cast<unsigned long long>(info->mRangeEnd), @@ -1378,37 +1818,80 @@ TEST(GeckoProfiler, Markers) info->mCountersNs.min, info->mCountersNs.sum / info->mCountersNs.n, info->mCountersNs.max, info->mCountersNs.n); printf(" - Threads: %7.1f .. %7.1f .. %7.1f [%u]\n", info->mThreadsNs.min, info->mThreadsNs.sum / info->mThreadsNs.n, info->mThreadsNs.max, info->mThreadsNs.n); profiler_stop(); + // Nothing more should have happened to the GTestMarkerPayloads. + EXPECT_EQ(GTestMarkerPayload::sNumCreated, 10 + 0 + 0); + EXPECT_EQ(GTestMarkerPayload::sNumSerialized, 10 + 0 + 0); + EXPECT_EQ(GTestMarkerPayload::sNumDeserialized, 0 + 10 + 0); + EXPECT_EQ(GTestMarkerPayload::sNumStreamed, 0 + 10 + 0); + EXPECT_EQ(GTestMarkerPayload::sNumDestroyed, 10 + 10 + 0); + // Try to add markers while the profiler is stopped. - PROFILER_MARKER_UNTYPED("marker after profiler_stop", OTHER); + for (int i = 0; i < 10; i++) { + PROFILER_ADD_MARKER_WITH_PAYLOAD("M5", OTHER, GTestMarkerPayload, (i)); + } // Warning: this could be racy profiler_start(PROFILER_DEFAULT_ENTRIES, PROFILER_DEFAULT_INTERVAL, features, filters, MOZ_ARRAY_LENGTH(filters), 0); - // This last marker shouldn't get streamed. - SpliceableChunkedJSONWriter w2; - w2.Start(); - EXPECT_TRUE(::profiler_stream_json_for_this_process(w2)); - w2.End(); - UniquePtr<char[]> profile2 = w.ChunkedWriteFunc().CopyData(); - ASSERT_TRUE(!!profile2.get()); - EXPECT_TRUE( - std::string_view(profile2.get()).find("marker after profiler_stop") == - std::string_view::npos); + EXPECT_TRUE(::profiler_stream_json_for_this_process(w)); profiler_stop(); + + // The second set of GTestMarkerPayloads should not have been serialized or + // streamed. + EXPECT_EQ(GTestMarkerPayload::sNumCreated, 10 + 0 + 0 + 10); + EXPECT_EQ(GTestMarkerPayload::sNumSerialized, 10 + 0 + 0 + 0); + EXPECT_EQ(GTestMarkerPayload::sNumDeserialized, 0 + 10 + 0 + 0); + EXPECT_EQ(GTestMarkerPayload::sNumStreamed, 0 + 10 + 0 + 0); + EXPECT_EQ(GTestMarkerPayload::sNumDestroyed, 10 + 10 + 0 + 10); } +// The duration limit will be removed from Firefox, see bug 1632365. +#if 0 +TEST(GeckoProfiler, DurationLimit) +{ + uint32_t features = ProfilerFeature::StackWalk; + const char* filters[] = {"GeckoMain"}; + + profiler_start(PROFILER_DEFAULT_ENTRIES, PROFILER_DEFAULT_INTERVAL, features, + filters, MOZ_ARRAY_LENGTH(filters), 0, Some(1.5)); + + // Clear up the counters after the last test. + GTestMarkerPayload::sNumCreated = 0; + GTestMarkerPayload::sNumSerialized = 0; + GTestMarkerPayload::sNumDeserialized = 0; + GTestMarkerPayload::sNumStreamed = 0; + GTestMarkerPayload::sNumDestroyed = 0; + + PROFILER_ADD_MARKER_WITH_PAYLOAD("M1", OTHER, GTestMarkerPayload, (1)); + PR_Sleep(PR_MillisecondsToInterval(1100)); + PROFILER_ADD_MARKER_WITH_PAYLOAD("M2", OTHER, GTestMarkerPayload, (2)); + PR_Sleep(PR_MillisecondsToInterval(500)); + + SpliceableChunkedJSONWriter w; + ASSERT_TRUE(profiler_stream_json_for_this_process(w)); + + // Both markers created, serialized, destroyed; Only the first marker should + // have been deserialized, streamed, and destroyed again. + EXPECT_EQ(GTestMarkerPayload::sNumCreated, 2); + EXPECT_EQ(GTestMarkerPayload::sNumSerialized, 2); + EXPECT_EQ(GTestMarkerPayload::sNumDeserialized, 1); + EXPECT_EQ(GTestMarkerPayload::sNumStreamed, 1); + EXPECT_EQ(GTestMarkerPayload::sNumDestroyed, 3); +} +#endif + #define COUNTER_NAME "TestCounter" #define COUNTER_DESCRIPTION "Test of counters in profiles" #define COUNTER_NAME2 "Counter2" #define COUNTER_DESCRIPTION2 "Second Test of counters in profiles" PROFILER_DEFINE_COUNT_TOTAL(TestCounter, COUNTER_NAME, COUNTER_DESCRIPTION); PROFILER_DEFINE_COUNT_TOTAL(TestCounter2, COUNTER_NAME2, COUNTER_DESCRIPTION2);
--- a/xpcom/base/CycleCollectedJSRuntime.cpp +++ b/xpcom/base/CycleCollectedJSRuntime.cpp @@ -88,16 +88,20 @@ #include "nsCycleCollectionParticipant.h" #include "nsCycleCollector.h" #include "nsDOMJSUtils.h" #include "nsExceptionHandler.h" #include "nsJSUtils.h" #include "nsStringBuffer.h" #include "nsWrapperCache.h" +#ifdef MOZ_GECKO_PROFILER +# include "ProfilerMarkerPayload.h" +#endif + #if defined(XP_MACOSX) # include "nsMacUtilsImpl.h" #endif #include "nsThread.h" #include "nsThreadUtils.h" #include "xpcpublic.h" @@ -983,75 +987,25 @@ void CycleCollectedJSRuntime::GCSliceCal JS::GCProgress aProgress, const JS::GCDescription& aDesc) { CycleCollectedJSRuntime* self = CycleCollectedJSRuntime::Get(); MOZ_ASSERT(CycleCollectedJSContext::Get()->Context() == aContext); #ifdef MOZ_GECKO_PROFILER if (profiler_thread_is_being_profiled()) { if (aProgress == JS::GC_CYCLE_END) { - struct GCMajorMarker { - static constexpr mozilla::Span<const char> MarkerTypeName() { - return mozilla::MakeStringSpan("GCMajor"); - } - static void StreamJSONMarkerData( - mozilla::baseprofiler::SpliceableJSONWriter& aWriter, - const mozilla::ProfilerString8View& aTimingJSON) { - if (aTimingJSON.Length() != 0) { - aWriter.SplicedJSONProperty("timings", aTimingJSON); - } else { - aWriter.NullProperty("timings"); - } - } - static mozilla::MarkerSchema MarkerTypeDisplay() { - using MS = mozilla::MarkerSchema; - MS schema{MS::Location::markerChart, MS::Location::markerTable, - MS::Location::timelineMemory}; - // No display instructions here, there is special handling in the - // front-end. - return schema; - } - }; - - profiler_add_marker("GCMajor", baseprofiler::category::GCCC, - MarkerTiming::Interval(aDesc.startTime(aContext), - aDesc.endTime(aContext)), - GCMajorMarker{}, - ProfilerString8View::WrapNullTerminatedString( - aDesc.formatJSONProfiler(aContext).get())); + PROFILER_ADD_MARKER_WITH_PAYLOAD( + "GCMajor", GCCC, GCMajorMarkerPayload, + (aDesc.startTime(aContext), aDesc.endTime(aContext), + aDesc.formatJSONProfiler(aContext))); } else if (aProgress == JS::GC_SLICE_END) { - struct GCSliceMarker { - static constexpr mozilla::Span<const char> MarkerTypeName() { - return mozilla::MakeStringSpan("GCSlice"); - } - static void StreamJSONMarkerData( - mozilla::baseprofiler::SpliceableJSONWriter& aWriter, - const mozilla::ProfilerString8View& aTimingJSON) { - if (aTimingJSON.Length() != 0) { - aWriter.SplicedJSONProperty("timings", aTimingJSON); - } else { - aWriter.NullProperty("timings"); - } - } - static mozilla::MarkerSchema MarkerTypeDisplay() { - using MS = mozilla::MarkerSchema; - MS schema{MS::Location::markerChart, MS::Location::markerTable, - MS::Location::timelineMemory}; - // No display instructions here, there is special handling in the - // front-end. - return schema; - } - }; - - profiler_add_marker("GCSlice", baseprofiler::category::GCCC, - MarkerTiming::Interval(aDesc.lastSliceStart(aContext), - aDesc.lastSliceEnd(aContext)), - GCSliceMarker{}, - ProfilerString8View::WrapNullTerminatedString( - aDesc.sliceToJSONProfiler(aContext).get())); + PROFILER_ADD_MARKER_WITH_PAYLOAD( + "GCSlice", GCCC, GCSliceMarkerPayload, + (aDesc.lastSliceStart(aContext), aDesc.lastSliceEnd(aContext), + aDesc.sliceToJSONProfiler(aContext))); } } #endif if (aProgress == JS::GC_CYCLE_END && JS::dbg::FireOnGarbageCollectionHookRequired(aContext)) { JS::GCReason reason = aDesc.reason_; Unused << NS_WARN_IF( @@ -1120,45 +1074,20 @@ void CycleCollectedJSRuntime::GCNurseryC } if (aProgress == JS::GCNurseryProgress::GC_NURSERY_COLLECTION_START) { self->mLatestNurseryCollectionStart = TimeStamp::Now(); } #ifdef MOZ_GECKO_PROFILER else if (aProgress == JS::GCNurseryProgress::GC_NURSERY_COLLECTION_END && profiler_thread_is_being_profiled()) { - struct GCMinorMarker { - static constexpr mozilla::Span<const char> MarkerTypeName() { - return mozilla::MakeStringSpan("GCMinor"); - } - static void StreamJSONMarkerData( - mozilla::baseprofiler::SpliceableJSONWriter& aWriter, - const mozilla::ProfilerString8View& aTimingJSON) { - if (aTimingJSON.Length() != 0) { - aWriter.SplicedJSONProperty("nursery", aTimingJSON); - } else { - aWriter.NullProperty("nursery"); - } - } - static mozilla::MarkerSchema MarkerTypeDisplay() { - using MS = mozilla::MarkerSchema; - MS schema{MS::Location::markerChart, MS::Location::markerTable, - MS::Location::timelineMemory}; - // No display instructions here, there is special handling in the - // front-end. - return schema; - } - }; - - profiler_add_marker( - "GCMinor", baseprofiler::category::GCCC, - MarkerTiming::IntervalUntilNowFrom(self->mLatestNurseryCollectionStart), - GCMinorMarker{}, - ProfilerString8View::WrapNullTerminatedString( - JS::MinorGcToJSON(aContext).get())); + PROFILER_ADD_MARKER_WITH_PAYLOAD( + "GCMinor", GCCC, GCMinorMarkerPayload, + (self->mLatestNurseryCollectionStart, TimeStamp::Now(), + JS::MinorGcToJSON(aContext))); } #endif if (self->mPrevGCNurseryCollectionCallback) { self->mPrevGCNurseryCollectionCallback(aContext, aProgress, aReason); } }
--- a/xpcom/base/Logging.cpp +++ b/xpcom/base/Logging.cpp @@ -18,16 +18,19 @@ #include "mozilla/Sprintf.h" #include "mozilla/UniquePtrExtensions.h" #include "MainThreadUtils.h" #include "nsClassHashtable.h" #include "nsDebug.h" #include "nsDebugImpl.h" #include "NSPRLogModulesParser.h" #include "LogCommandLineHandler.h" +#ifdef MOZ_GECKO_PROFILER +# include "ProfilerMarkerPayload.h" +#endif #include "prenv.h" #ifdef XP_WIN # include <process.h> #else # include <sys/types.h> # include <unistd.h> #endif @@ -411,43 +414,25 @@ class LogModuleManager { // We may have maxed out, allocate a buffer instead. allocatedBuff = mozilla::Vsmprintf(aFmt, aArgs); buffToWrite = allocatedBuff.get(); charsWritten = strlen(buffToWrite); } #ifdef MOZ_GECKO_PROFILER if (mAddProfilerMarker && profiler_can_accept_markers()) { - struct LogMarker { - static constexpr Span<const char> MarkerTypeName() { - return MakeStringSpan("Log"); - } - static void StreamJSONMarkerData( - baseprofiler::SpliceableJSONWriter& aWriter, - const ProfilerString8View& aModule, - const ProfilerString8View& aText) { - aWriter.StringProperty("module", aModule); - aWriter.StringProperty("name", aText); - } - static MarkerSchema MarkerTypeDisplay() { - using MS = MarkerSchema; - MS schema{MS::Location::markerTable}; - schema.SetTableLabel("({marker.data.module}) {marker.data.name}"); - schema.AddKeyLabelFormat("module", "Module", MS::Format::string); - schema.AddKeyLabelFormat("name", "Name", MS::Format::string); - return schema; - } - }; - - profiler_add_marker( - "LogMessages", geckoprofiler::category::OTHER, - aStart ? MarkerTiming::IntervalUntilNowFrom(*aStart) - : MarkerTiming::InstantNow(), - LogMarker{}, ProfilerString8View::WrapNullTerminatedString(aName), - ProfilerString8View::WrapNullTerminatedString(buffToWrite)); + if (aStart) { + PROFILER_ADD_MARKER_WITH_PAYLOAD( + "LogMessages", OTHER, LogMarkerPayload, + (aName, buffToWrite, *aStart, TimeStamp::Now())); + } else { + PROFILER_ADD_MARKER_WITH_PAYLOAD( + "LogMessages", OTHER, LogMarkerPayload, + (aName, buffToWrite, TimeStamp::Now())); + } } #endif // Determine if a newline needs to be appended to the message. const char* newline = ""; if (charsWritten == 0 || buffToWrite[charsWritten - 1] != '\n') { newline = "\n"; }
--- a/xpcom/threads/nsThread.cpp +++ b/xpcom/threads/nsThread.cpp @@ -37,16 +37,19 @@ #include "mozilla/ChaosMode.h" #include "mozilla/Telemetry.h" #include "mozilla/TimeStamp.h" #include "mozilla/Unused.h" #include "mozilla/dom/ScriptSettings.h" #include "nsThreadSyncDispatch.h" #include "nsServiceManagerUtils.h" #include "GeckoProfiler.h" +#ifdef MOZ_GECKO_PROFILER +# include "ProfilerMarkerPayload.h" +#endif #include "InputEventStatistics.h" #include "ThreadEventQueue.h" #include "ThreadEventTarget.h" #include "ThreadDelay.h" #include <limits> #ifdef XP_LINUX @@ -1516,36 +1519,17 @@ void PerformanceCounterState::MaybeRepor // Idle events (gc...) don't *really* count here if (!mCurrentRunnableIsIdleRunnable) { mLastLongNonIdleTaskEnd = aNow; } mLastLongTaskEnd = aNow; #ifdef MOZ_GECKO_PROFILER if (profiler_thread_is_being_profiled()) { - struct LongTaskMarker { - static constexpr Span<const char> MarkerTypeName() { - return MakeStringSpan("MainThreadLongTask"); - } - static void StreamJSONMarkerData( - baseprofiler::SpliceableJSONWriter& aWriter) { - aWriter.StringProperty("category", "LongTask"); - } - static MarkerSchema MarkerTypeDisplay() { - using MS = MarkerSchema; - MS schema{MS::Location::markerChart, MS::Location::markerTable}; - schema.AddKeyLabelFormat("category", "Type", MS::Format::string); - return schema; - } - }; - - profiler_add_marker(mCurrentRunnableIsIdleRunnable - ? ProfilerString8View("LongIdleTask") - : ProfilerString8View("LongTask"), - geckoprofiler::category::OTHER, - MarkerTiming::Interval(mCurrentTimeSliceStart, aNow), - LongTaskMarker{}); + PROFILER_ADD_MARKER_WITH_PAYLOAD( + mCurrentRunnableIsIdleRunnable ? "LongIdleTask" : "LongTask", OTHER, + LongTaskMarkerPayload, (mCurrentTimeSliceStart, aNow)); } #endif } } } // namespace mozilla