diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index 250a7caced67..c924653a10ef 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -244,6 +244,10 @@ # include "nsIWebBrowserPrint.h" #endif +#ifdef MOZ_GECKO_PROFILER +# include "ProfilerMarkerPayload.h" +#endif + using namespace mozilla; using namespace mozilla::dom; using namespace mozilla::net; diff --git a/dom/base/ChromeUtils.cpp b/dom/base/ChromeUtils.cpp index a120d01e8327..4c3e372fdbf6 100644 --- a/dom/base/ChromeUtils.cpp +++ b/dom/base/ChromeUtils.cpp @@ -51,6 +51,9 @@ #include "nsThreadUtils.h" #include "mozJSComponentLoader.h" #include "GeckoProfiler.h" +#ifdef MOZ_GECKO_PROFILER +# include "ProfilerMarkerPayload.h" +#endif #include "nsIException.h" namespace mozilla::dom { diff --git a/dom/base/TimeoutManager.cpp b/dom/base/TimeoutManager.cpp index 32808d6404da..305605119360 100644 --- a/dom/base/TimeoutManager.cpp +++ b/dom/base/TimeoutManager.cpp @@ -22,6 +22,9 @@ #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; @@ -151,13 +154,10 @@ void TimeoutManager::MoveIdleToActive() { 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++; @@ -906,13 +906,10 @@ void TimeoutManager::RunTimeout(const TimeStamp& aNow, 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 diff --git a/dom/base/nsDOMNavigationTiming.cpp b/dom/base/nsDOMNavigationTiming.cpp index b6ec37e8d6eb..c5032bee35f1 100644 --- a/dom/base/nsDOMNavigationTiming.cpp +++ b/dom/base/nsDOMNavigationTiming.cpp @@ -25,7 +25,7 @@ #include "nsPrintfCString.h" #include "prtime.h" #ifdef MOZ_GECKO_PROFILER -# include "mozilla/ProfilerMarkerTypes.h" +# include "ProfilerMarkerPayload.h" #endif using namespace mozilla; @@ -116,18 +116,14 @@ void nsDOMNavigationTiming::NotifyUnloadAccepted(nsIURI* 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() { @@ -136,10 +132,8 @@ void nsDOMNavigationTiming::NotifyLoadEventStart() { } 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(); @@ -169,10 +163,8 @@ void nsDOMNavigationTiming::NotifyLoadEventEnd() { } 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 @@ -187,13 +179,10 @@ void nsDOMNavigationTiming::NotifyLoadEventEnd() { "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(); @@ -252,10 +241,8 @@ void nsDOMNavigationTiming::NotifyDOMContentLoadedStart(nsIURI* aURI) { 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(); @@ -286,10 +273,8 @@ void nsDOMNavigationTiming::NotifyDOMContentLoadedEnd(nsIURI* aURI) { 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, @@ -392,13 +377,10 @@ void nsDOMNavigationTiming::TTITimeout(nsITimer* aTimer) { 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 } @@ -428,13 +410,10 @@ void nsDOMNavigationTiming::NotifyNonBlankPaintForRootContentDocument() { : "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 @@ -480,13 +459,10 @@ void nsDOMNavigationTiming::NotifyContentfulPaintForRootContentDocument( : "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 @@ -532,13 +508,10 @@ void nsDOMNavigationTiming::NotifyDOMContentFlushedForRootContentDocument() { : "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 } diff --git a/dom/events/EventDispatcher.cpp b/dom/events/EventDispatcher.cpp index 28fbfc40cc0c..d61f94ec1fc8 100644 --- a/dom/events/EventDispatcher.cpp +++ b/dom/events/EventDispatcher.cpp @@ -1039,6 +1039,10 @@ nsresult EventDispatcher::Dispatch(nsISupports* aTarget, struct DOMEventMarker { static constexpr Span 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( diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index bf376086af65..b798b4e044ed 100644 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -21,6 +21,9 @@ # 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" @@ -915,7 +918,9 @@ already_AddRefed ContentParent::GetUsedBrowserProcess( 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, @@ -954,7 +959,9 @@ already_AddRefed ContentParent::GetUsedBrowserProcess( 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, @@ -977,7 +984,9 @@ already_AddRefed ContentParent::GetUsedBrowserProcess( 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, @@ -2414,10 +2423,9 @@ bool ContentParent::LaunchSubprocessResolve(bool aIsSync, 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 diff --git a/dom/media/AsyncLogger.h b/dom/media/AsyncLogger.h index b0aee6c6394c..dbebac0b0ce2 100644 --- a/dom/media/AsyncLogger.h +++ b/dom/media/AsyncLogger.h @@ -16,6 +16,9 @@ #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) @@ -196,41 +199,24 @@ class AsyncLogger { } #ifdef MOZ_GECKO_PROFILER { - struct Budget { - static constexpr Span 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); } } } diff --git a/dom/media/MediaDecoderStateMachine.cpp b/dom/media/MediaDecoderStateMachine.cpp index 6105bac69160..1565188f73d5 100644 --- a/dom/media/MediaDecoderStateMachine.cpp +++ b/dom/media/MediaDecoderStateMachine.cpp @@ -27,7 +27,6 @@ #include "AudioSegment.h" #include "DOMMediaStream.h" #include "ImageContainer.h" -#include "GeckoProfiler.h" #include "MediaDecoder.h" #include "MediaDecoderStateMachine.h" #include "MediaShutdownManager.h" @@ -39,8 +38,17 @@ #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 { @@ -2851,9 +2859,8 @@ 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) { @@ -2861,9 +2868,8 @@ void MediaDecoderStateMachine::PushVideo(VideoData* aSample) { 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& aSample) { @@ -3463,8 +3469,8 @@ bool MediaDecoderStateMachine::HasLowBufferedData(const TimeUnit& aThreshold) { 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); } diff --git a/dom/media/MediaFormatReader.cpp b/dom/media/MediaFormatReader.cpp index 2da0157ee57f..8706e0d3e415 100644 --- a/dom/media/MediaFormatReader.cpp +++ b/dom/media/MediaFormatReader.cpp @@ -42,6 +42,15 @@ mozilla::LazyLogModule gMediaDemuxerLog("MediaDemuxer"); 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 { @@ -1922,7 +1931,8 @@ void MediaFormatReader::HandleDemuxedSamples( 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) { @@ -3140,5 +3150,6 @@ void MediaFormatReader::OnFirstDemuxFailed(TrackInfo::TrackType aType, } // namespace mozilla #undef NS_DispatchToMainThread +#undef MEDIA_FORMAT_READER_STATUS_MARKER #undef LOGV #undef LOG diff --git a/dom/media/mediasink/AudioSink.cpp b/dom/media/mediasink/AudioSink.cpp index 926a75dc4861..bec3acd75cac 100644 --- a/dom/media/mediasink/AudioSink.cpp +++ b/dom/media/mediasink/AudioSink.cpp @@ -12,11 +12,28 @@ #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; @@ -254,16 +271,7 @@ UniquePtr AudioSink::PopFrames(uint32_t aFrames) { 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 chunk = MakeUnique(mCurrentData, framesToPop, mCursor->Ptr()); diff --git a/dom/media/mediasink/VideoSink.cpp b/dom/media/mediasink/VideoSink.cpp index 4b53dcac2c35..95dba9aaab65 100644 --- a/dom/media/mediasink/VideoSink.cpp +++ b/dom/media/mediasink/VideoSink.cpp @@ -16,7 +16,6 @@ #include "VideoUtils.h" #include "mozilla/IntegerPrintfMacros.h" -#include "mozilla/ProfilerMarkerTypes.h" #include "mozilla/StaticPrefs_browser.h" #include "mozilla/StaticPrefs_media.h" @@ -30,6 +29,15 @@ extern mozilla::LazyLogModule gMediaDecoderLog; #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; @@ -455,9 +463,8 @@ void VideoSink::RenderVideoFrames(int32_t aMaxFrames, int64_t aClockTime, 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()); } } @@ -496,9 +503,8 @@ void VideoSink::UpdateRenderedVideoFrames() { 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()); } } diff --git a/dom/media/platforms/wmf/WMFDecoderModule.cpp b/dom/media/platforms/wmf/WMFDecoderModule.cpp index 9d53030ff75f..a12e193e4ade 100644 --- a/dom/media/platforms/wmf/WMFDecoderModule.cpp +++ b/dom/media/platforms/wmf/WMFDecoderModule.cpp @@ -27,7 +27,6 @@ #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 @@ -37,19 +36,28 @@ #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()); } diff --git a/dom/media/platforms/wmf/WMFMediaDataDecoder.cpp b/dom/media/platforms/wmf/WMFMediaDataDecoder.cpp index 47f2b1726ef6..7b84f2a71d63 100644 --- a/dom/media/platforms/wmf/WMFMediaDataDecoder.cpp +++ b/dom/media/platforms/wmf/WMFMediaDataDecoder.cpp @@ -9,7 +9,6 @@ #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" @@ -17,6 +16,15 @@ #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) @@ -94,7 +102,8 @@ RefPtr WMFMediaDataDecoder::ProcessError( "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 diff --git a/dom/media/platforms/wrappers/MediaChangeMonitor.cpp b/dom/media/platforms/wrappers/MediaChangeMonitor.cpp index 0edd0d8c4863..351cbcb7bca5 100644 --- a/dom/media/platforms/wrappers/MediaChangeMonitor.cpp +++ b/dom/media/platforms/wrappers/MediaChangeMonitor.cpp @@ -13,10 +13,18 @@ #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 @@ -90,9 +98,11 @@ class H264ChangeMonitor : public MediaChangeMonitor::CodecChangeMonitor { 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; } @@ -194,10 +204,11 @@ class VPXChangeMonitor : public MediaChangeMonitor::CodecChangeMonitor { 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); @@ -744,3 +755,5 @@ void MediaChangeMonitor::FlushThenShutdownDecoder( } } // namespace mozilla + +#undef MEDIA_CHANGE_MONITOR_STATUS_MARKER diff --git a/dom/performance/Performance.cpp b/dom/performance/Performance.cpp index 8d06a0c6ef9a..47764f8e4723 100644 --- a/dom/performance/Performance.cpp +++ b/dom/performance/Performance.cpp @@ -28,6 +28,10 @@ #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 { @@ -212,53 +216,6 @@ void Performance::ClearUserEntries(const Optional& aEntryName, void Performance::ClearResourceTimings() { mResourceEntries.Clear(); } -#ifdef MOZ_GECKO_PROFILER -struct UserTimingMarker { - static constexpr Span MarkerTypeName() { - return MakeStringSpan("UserTiming"); - } - static void StreamJSONMarkerData( - baseprofiler::SpliceableJSONWriter& aWriter, - const ProfilerString16View& aName, bool aIsMeasure, - const Maybe& aStartMark, - const Maybe& 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()) { @@ -280,9 +237,8 @@ void Performance::Mark(const nsAString& aName, ErrorResult& aRv) { 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 } @@ -376,11 +332,9 @@ void Performance::Measure(const nsAString& aName, 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 } diff --git a/dom/script/ScriptLoader.cpp b/dom/script/ScriptLoader.cpp index 319a026eb622..032d762911ce 100644 --- a/dom/script/ScriptLoader.cpp +++ b/dom/script/ScriptLoader.cpp @@ -24,7 +24,6 @@ #include "js/SourceText.h" #include "js/Utility.h" #include "xpcpublic.h" -#include "GeckoProfiler.h" #include "nsCycleCollectionParticipant.h" #include "nsIContent.h" #include "nsJSUtils.h" @@ -84,6 +83,10 @@ #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; @@ -2177,7 +2180,7 @@ NotifyOffThreadScriptLoadCompletedRunnable::Run() { #ifdef MOZ_GECKO_PROFILER if (profiler_is_active()) { - ProfilerString8View scriptSourceString; + const char* scriptSourceString; if (request->IsTextSource()) { scriptSourceString = "ScriptCompileOffThread"; } else if (request->IsBinASTSource()) { @@ -2189,11 +2192,10 @@ NotifyOffThreadScriptLoadCompletedRunnable::Run() { 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 diff --git a/dom/xhr/XMLHttpRequestMainThread.cpp b/dom/xhr/XMLHttpRequestMainThread.cpp index 88f67787e2cc..e74f3e7c52cc 100644 --- a/dom/xhr/XMLHttpRequestMainThread.cpp +++ b/dom/xhr/XMLHttpRequestMainThread.cpp @@ -2458,7 +2458,7 @@ nsresult XMLHttpRequestMainThread::CreateChannel() { rv = httpChannel->SetRequestMethod(mRequestMethod); NS_ENSURE_SUCCESS(rv, rv); - httpChannel->SetSource(profiler_capture_backtrace()); + httpChannel->SetSource(profiler_get_backtrace()); // Set the initiator type nsCOMPtr timedChannel(do_QueryInterface(httpChannel)); @@ -3240,8 +3240,7 @@ void XMLHttpRequestMainThread::SetOriginStack( mOriginStack = std::move(aOriginStack); } -void XMLHttpRequestMainThread::SetSource( - UniquePtr aSource) { +void XMLHttpRequestMainThread::SetSource(UniqueProfilerBacktrace aSource) { if (!mChannel) { return; } diff --git a/dom/xhr/XMLHttpRequestMainThread.h b/dom/xhr/XMLHttpRequestMainThread.h index 6644527ff967..56fa37007cb7 100644 --- a/dom/xhr/XMLHttpRequestMainThread.h +++ b/dom/xhr/XMLHttpRequestMainThread.h @@ -396,7 +396,7 @@ class XMLHttpRequestMainThread final : public XMLHttpRequest, void SetOriginStack(UniquePtr aOriginStack); - void SetSource(UniquePtr aSource); + void SetSource(UniqueProfilerBacktrace aSource); virtual uint16_t ErrorCode() const override { return static_cast(mErrorLoad); diff --git a/dom/xhr/XMLHttpRequestWorker.cpp b/dom/xhr/XMLHttpRequestWorker.cpp index 9184ce1e30db..70cd5261ff68 100644 --- a/dom/xhr/XMLHttpRequestWorker.cpp +++ b/dom/xhr/XMLHttpRequestWorker.cpp @@ -653,7 +653,7 @@ class OpenRunnable final : public WorkerThreadProxySyncRunnable { // Remember the worker thread's stack when the XHR was opened for profiling // purposes. - UniquePtr mSource; + UniqueProfilerBacktrace mSource; public: OpenRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy, @@ -664,7 +664,7 @@ class OpenRunnable final : public WorkerThreadProxySyncRunnable { XMLHttpRequestResponseType aResponseType, const nsString& aMimeTypeOverride, UniquePtr aOriginStack, - UniquePtr aSource = nullptr) + UniqueProfilerBacktrace aSource = nullptr) : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy), mMethod(aMethod), mURL(aURL), @@ -1741,7 +1741,7 @@ void XMLHttpRequestWorker::Open(const nsACString& aMethod, 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); diff --git a/gfx/layers/ProfilerScreenshots.cpp b/gfx/layers/ProfilerScreenshots.cpp index 648326150dd5..e4c4953c8987 100644 --- a/gfx/layers/ProfilerScreenshots.cpp +++ b/gfx/layers/ProfilerScreenshots.cpp @@ -11,6 +11,9 @@ #include "GeckoProfiler.h" #include "gfxUtils.h" #include "nsThreadUtils.h" +#ifdef MOZ_GECKO_PROFILER +# include "ProfilerMarkerPayload.h" +#endif using namespace mozilla; using namespace mozilla::gfx; @@ -81,32 +84,12 @@ void ProfilerScreenshots::SubmitScreenshot( nullptr, &dataURL); if (NS_SUCCEEDED(rv)) { // Add a marker with the data URL. - struct ScreenshotMarker { - static constexpr mozilla::Span 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)); } } diff --git a/gfx/layers/composite/ContainerLayerComposite.cpp b/gfx/layers/composite/ContainerLayerComposite.cpp index 950b6bf0089b..290dd1627767 100644 --- a/gfx/layers/composite/ContainerLayerComposite.cpp +++ b/gfx/layers/composite/ContainerLayerComposite.cpp @@ -40,6 +40,10 @@ #include #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__)) @@ -99,37 +103,9 @@ static void PrintUniformityInfo(Layer* aLayer) { } 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 MarkerTypeName() { - return MakeStringSpan("LayerTranslation"); - } - static void StreamJSONMarkerData( - baseprofiler::SpliceableJSONWriter& aWriter, - ProfileBufferRawPointer 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 } diff --git a/gfx/layers/ipc/CompositorBridgeParent.cpp b/gfx/layers/ipc/CompositorBridgeParent.cpp index b18d97487abd..d680f5ca5767 100644 --- a/gfx/layers/ipc/CompositorBridgeParent.cpp +++ b/gfx/layers/ipc/CompositorBridgeParent.cpp @@ -90,6 +90,9 @@ #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" @@ -2173,23 +2176,8 @@ already_AddRefed CompositorBridgeParent::GetAPZCTreeManager( 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 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 @@ -2754,23 +2742,42 @@ int32_t RecordContentFrameTime( #ifdef MOZ_GECKO_PROFILER if (profiler_can_accept_markers()) { - struct ContentFrameMarker { - static constexpr Span 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(); } - 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 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_FRAME_TIME", aWriter, aProcessStartTime, + aUniqueStacks); + } + + private: + explicit ContentFramePayload(CommonProps&& aCommonProps) + : ProfilerMarkerPayload(std::move(aCommonProps)) {} + static mozilla::UniquePtr Deserialize( + mozilla::ProfileBufferEntryReader& aEntryReader) { + ProfilerMarkerPayload::CommonProps props = + DeserializeCommonProps(aEntryReader); + return UniquePtr( + 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 diff --git a/gfx/layers/ipc/ContentCompositorBridgeParent.cpp b/gfx/layers/ipc/ContentCompositorBridgeParent.cpp index ec6e4e2760eb..b6e1b03f7770 100644 --- a/gfx/layers/ipc/ContentCompositorBridgeParent.cpp +++ b/gfx/layers/ipc/ContentCompositorBridgeParent.cpp @@ -39,7 +39,7 @@ #include "mozilla/StaticPtr.h" #include "mozilla/Telemetry.h" #ifdef MOZ_GECKO_PROFILER -# include "mozilla/BaseProfilerMarkerTypes.h" +# include "ProfilerMarkerPayload.h" #endif namespace mozilla { @@ -372,10 +372,43 @@ void ContentCompositorBridgeParent::ShadowLayersUpdated( 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 Deserialize( + mozilla::ProfileBufferEntryReader& aEntryReader) { + ProfilerMarkerPayload::CommonProps props = + DeserializeCommonProps(aEntryReader); + return UniquePtr( + 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( diff --git a/gfx/layers/wr/WebRenderBridgeParent.cpp b/gfx/layers/wr/WebRenderBridgeParent.cpp index 6ae5741afb68..e8e00357ad76 100644 --- a/gfx/layers/wr/WebRenderBridgeParent.cpp +++ b/gfx/layers/wr/WebRenderBridgeParent.cpp @@ -45,7 +45,7 @@ #endif #ifdef MOZ_GECKO_PROFILER -# include "mozilla/ProfilerMarkerTypes.h" +# include "ProfilerMarkerPayload.h" #endif bool is_in_main_thread() { return NS_IsMainThread(); } @@ -59,20 +59,26 @@ bool is_in_render_thread() { } 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, @@ -220,10 +226,46 @@ class SceneBuiltNotification : public wr::NotificationHandler { 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 Deserialize( + mozilla::ProfileBufferEntryReader& aEntryReader) { + ProfilerMarkerPayload::CommonProps props = + DeserializeCommonProps(aEntryReader); + return UniquePtr( + 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( diff --git a/gfx/webrender_bindings/RenderCompositorNative.cpp b/gfx/webrender_bindings/RenderCompositorNative.cpp index d4861c327a9a..e606b306e8de 100644 --- a/gfx/webrender_bindings/RenderCompositorNative.cpp +++ b/gfx/webrender_bindings/RenderCompositorNative.cpp @@ -17,6 +17,10 @@ #include "mozilla/webrender/RenderThread.h" #include "mozilla/widget/CompositorWidget.h" +#ifdef MOZ_GECKO_PROFILER +# include "ProfilerMarkerPayload.h" +#endif + namespace mozilla { namespace wr { diff --git a/ipc/chromium/src/chrome/common/ipc_channel_utils.cc b/ipc/chromium/src/chrome/common/ipc_channel_utils.cc index 745e8da10c0c..2a05dbdad934 100644 --- a/ipc/chromium/src/chrome/common/ipc_channel_utils.cc +++ b/ipc/chromium/src/chrome/common/ipc_channel_utils.cc @@ -8,6 +8,10 @@ #include "chrome/common/ipc_message.h" +#ifdef MOZ_GECKO_PROFILER +# include "ProfilerMarkerPayload.h" +#endif + namespace IPC { void AddIPCProfilerMarker(const Message& aMessage, int32_t aOtherPid, @@ -21,12 +25,11 @@ void AddIPCProfilerMarker(const Message& aMessage, int32_t aOtherPid, 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 } diff --git a/ipc/glue/MessageChannel.cpp b/ipc/glue/MessageChannel.cpp index 86fb36433b8f..c4401f20a025 100644 --- a/ipc/glue/MessageChannel.cpp +++ b/ipc/glue/MessageChannel.cpp @@ -37,6 +37,10 @@ using namespace mozilla::tasktracer; #endif +#ifdef MOZ_GECKO_PROFILER +# include "ProfilerMarkerPayload.h" +#endif + // Undo the damage done by mozzconf.h #undef compress @@ -2785,11 +2789,11 @@ void MessageChannel::AddProfilerMarker(const IPC::Message& aMessage, 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 diff --git a/ipc/glue/MessageChannel.h b/ipc/glue/MessageChannel.h index cf08ead6d046..fb369449b84a 100644 --- a/ipc/glue/MessageChannel.h +++ b/ipc/glue/MessageChannel.h @@ -29,10 +29,6 @@ #include "MessageLink.h" #include "mozilla/ipc/Transport.h" -#ifdef MOZ_GECKO_PROFILER -# include "mozilla/ProfilerMarkersPrerequisites.h" -#endif - class nsIEventTarget; namespace mozilla { @@ -883,74 +879,4 @@ struct ParamTraits mozilla::ipc::ResponseRejectReason::EndGuard_> {}; } // namespace IPC -#ifdef MOZ_GECKO_PROFILER -namespace geckoprofiler::markers { - -struct IPCMarker { - static constexpr mozilla::Span 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 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(""); - } - } - - static mozilla::Span 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(""); - } - } -}; - -} // namespace geckoprofiler::markers -#endif - #endif // ifndef ipc_glue_MessageChannel_h diff --git a/ipc/mscom/ProfilerMarkers.cpp b/ipc/mscom/ProfilerMarkers.cpp index 591e4347b43f..b7c6cff542a5 100644 --- a/ipc/mscom/ProfilerMarkers.cpp +++ b/ipc/mscom/ProfilerMarkers.cpp @@ -23,10 +23,6 @@ #include #include -#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}}; @@ -137,8 +133,8 @@ void ProfilerMarkerChannelHook::ClientGetSize(REFGUID aExtensionId, REFIID aIid, 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) { @@ -154,8 +150,8 @@ void ProfilerMarkerChannelHook::ClientNotify(REFGUID aExtensionId, REFIID aIid, 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); } } diff --git a/js/xpconnect/src/XPCJSContext.cpp b/js/xpconnect/src/XPCJSContext.cpp index 28f2882acbd8..e6cc40ccc394 100644 --- a/js/xpconnect/src/XPCJSContext.cpp +++ b/js/xpconnect/src/XPCJSContext.cpp @@ -63,6 +63,9 @@ #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. @@ -588,7 +591,9 @@ bool XPCJSContext::InterruptCallback(JSContext* cx) { 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 diff --git a/layout/base/AutoProfilerStyleMarker.h b/layout/base/AutoProfilerStyleMarker.h index a68cfe843f8c..dce95c630da2 100644 --- a/layout/base/AutoProfilerStyleMarker.h +++ b/layout/base/AutoProfilerStyleMarker.h @@ -8,15 +8,16 @@ #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 aCause, + explicit AutoProfilerStyleMarker(UniqueProfilerBacktrace aCause, const Maybe& aInnerWindowID) : mActive(profiler_can_accept_markers()), mStartTime(TimeStamp::Now()), @@ -35,57 +36,17 @@ class MOZ_RAII AutoProfilerStyleMarker { if (!mActive) { return; } - - struct StyleMarker { - static constexpr mozilla::Span 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 mCause; + UniqueProfilerBacktrace mCause; Maybe mInnerWindowID; }; diff --git a/layout/base/PresShell.cpp b/layout/base/PresShell.cpp index 61698bd0d1b0..46ec58bfe0c7 100644 --- a/layout/base/PresShell.cpp +++ b/layout/base/PresShell.cpp @@ -9565,7 +9565,7 @@ bool PresShell::DoReflow(nsIFrame* target, bool aInterruptible, innerWindowID = Some(window->WindowID()); } AutoProfilerTracing tracingLayoutFlush( - "Paint", "Reflow", geckoprofiler::category::LAYOUT, + "Paint", "Reflow", JS::ProfilingCategoryPair::LAYOUT, std::move(mReflowCause), innerWindowID); mReflowCause = nullptr; #endif diff --git a/layout/base/PresShell.h b/layout/base/PresShell.h index 762ec4aea56d..9e7a028039bc 100644 --- a/layout/base/PresShell.h +++ b/layout/base/PresShell.h @@ -2861,8 +2861,8 @@ class PresShell final : public nsStubDocumentObserver, // 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 mStyleCause; - UniquePtr mReflowCause; + UniqueProfilerBacktrace mStyleCause; + UniqueProfilerBacktrace mReflowCause; #endif nsTArray> mDelayedEvents; diff --git a/layout/base/PresShellInlines.h b/layout/base/PresShellInlines.h index cb79a245b2e2..d3ab4ff26fce 100644 --- a/layout/base/PresShellInlines.h +++ b/layout/base/PresShellInlines.h @@ -23,7 +23,7 @@ void PresShell::SetNeedLayoutFlush() { #ifdef MOZ_GECKO_PROFILER if (!mReflowCause) { - mReflowCause = profiler_capture_backtrace(); + mReflowCause = profiler_get_backtrace(); } #endif @@ -40,7 +40,7 @@ void PresShell::SetNeedStyleFlush() { #ifdef MOZ_GECKO_PROFILER if (!mStyleCause) { - mStyleCause = profiler_capture_backtrace(); + mStyleCause = profiler_get_backtrace(); } #endif diff --git a/layout/base/nsCSSFrameConstructor.cpp b/layout/base/nsCSSFrameConstructor.cpp index 486f3a078565..c97c53947cbf 100644 --- a/layout/base/nsCSSFrameConstructor.cpp +++ b/layout/base/nsCSSFrameConstructor.cpp @@ -129,10 +129,6 @@ #include "nsTextNode.h" #include "ActiveLayerTracker.h" -#ifdef MOZ_GECKO_PROFILER -# include "mozilla/ProfilerMarkerTypes.h" -#endif - using namespace mozilla; using namespace mozilla::dom; @@ -8198,8 +8194,9 @@ static nsIFrame* FindPreviousNonWhitespaceSibling(nsIFrame* aFrame) { 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(), @@ -10722,8 +10719,9 @@ bool nsCSSFrameConstructor::MaybeRecreateForColumnSpan( // 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 @@ -11048,9 +11046,9 @@ static bool IsSafeToAppendToIBSplitInline(nsIFrame* aParentFrame, } 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(); @@ -11110,9 +11108,9 @@ 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; diff --git a/layout/base/nsRefreshDriver.cpp b/layout/base/nsRefreshDriver.cpp index 1115649db2bd..29be1eab6e4a 100644 --- a/layout/base/nsRefreshDriver.cpp +++ b/layout/base/nsRefreshDriver.cpp @@ -2064,8 +2064,9 @@ void nsRefreshDriver::Tick(VsyncId aId, TimeStamp aNowTime) { // 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(); } diff --git a/modules/libpref/Preferences.cpp b/modules/libpref/Preferences.cpp index ab86c3af877a..fb07e52abd27 100644 --- a/modules/libpref/Preferences.cpp +++ b/modules/libpref/Preferences.cpp @@ -14,6 +14,9 @@ #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" @@ -4277,58 +4280,6 @@ static nsCString PrefValueToString(const nsACString& s) { return nsCString(s); } // 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 MarkerTypeName() { - return MakeStringSpan("PreferenceRead"); - } - static void StreamJSONMarkerData( - baseprofiler::SpliceableJSONWriter& aWriter, - const ProfilerString8View& aPrefName, - const Maybe& 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 PrefValueKindToString( - const Maybe& aKind) { - if (aKind) { - return *aKind == PrefValueKind::Default ? MakeStringSpan("Default") - : MakeStringSpan("User"); - } - return "Shared"; - } - - static Span 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 static nsresult GetPrefValue(const char* aPrefName, T&& aResult, PrefValueKind aKind) { @@ -4340,11 +4291,10 @@ struct Internals { #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 } @@ -4361,12 +4311,10 @@ struct Internals { #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 } diff --git a/mozglue/baseprofiler/core/ProfileBufferEntry.cpp b/mozglue/baseprofiler/core/ProfileBufferEntry.cpp index 48b4883d4c25..07f944654b9b 100644 --- a/mozglue/baseprofiler/core/ProfileBufferEntry.cpp +++ b/mozglue/baseprofiler/core/ProfileBufferEntry.cpp @@ -785,20 +785,13 @@ void ProfileBuffer::StreamMarkersToJSON(SpliceableJSONWriter& aWriter, MOZ_ASSERT(static_cast(type) < static_cast( 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); diff --git a/mozglue/baseprofiler/public/BaseProfilerMarkerTypes.h b/mozglue/baseprofiler/public/BaseProfilerMarkerTypes.h index 1556b7a2724b..207b2365ff14 100644 --- a/mozglue/baseprofiler/public/BaseProfilerMarkerTypes.h +++ b/mozglue/baseprofiler/public/BaseProfilerMarkerTypes.h @@ -29,7 +29,137 @@ namespace mozilla::baseprofiler::markers { -struct MediaSampleMarker { +struct Tracing { + static constexpr Span 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 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 MarkerTypeName() { + return MakeStringSpan("UserTimingMeasure"); + } + static void StreamJSONMarkerData(SpliceableJSONWriter& aWriter, + const ProfilerString8View& aName, + const Maybe& aStartMark, + const Maybe& 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 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 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 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 MarkerTypeName() { return MakeStringSpan("MediaSample"); } @@ -50,18 +180,6 @@ struct MediaSampleMarker { } }; -struct ContentBuildMarker { - static constexpr Span 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 diff --git a/mozglue/baseprofiler/public/BaseProfilerMarkers.h b/mozglue/baseprofiler/public/BaseProfilerMarkers.h index 9d6ba91d9b35..ed7451213bd9 100644 --- a/mozglue/baseprofiler/public/BaseProfilerMarkers.h +++ b/mozglue/baseprofiler/public/BaseProfilerMarkers.h @@ -160,25 +160,6 @@ struct Text { return schema; } }; - -struct Tracing { - static constexpr Span 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 diff --git a/mozglue/baseprofiler/public/BaseProfilerMarkersPrerequisites.h b/mozglue/baseprofiler/public/BaseProfilerMarkersPrerequisites.h index ee30063647ed..5af3bb3435d4 100644 --- a/mozglue/baseprofiler/public/BaseProfilerMarkersPrerequisites.h +++ b/mozglue/baseprofiler/public/BaseProfilerMarkersPrerequisites.h @@ -129,8 +129,7 @@ class MOZ_STACK_CLASS ProfilerStringView { Ownership::Reference) {} // Implicit constructor from std::string. - constexpr MOZ_IMPLICIT ProfilerStringView( - const std::basic_string& aString) + constexpr MOZ_IMPLICIT ProfilerStringView(const std::string& aString) : ProfilerStringView(aString.data(), aString.length(), Ownership::Reference) {} @@ -142,7 +141,7 @@ class MOZ_STACK_CLASS ProfilerStringView { static constexpr ProfilerStringView WrapNullTerminatedString( const CHAR* aString) { return ProfilerStringView( - aString, aString ? std::char_traits::length(aString) : 0, + aString, aString ? std::char_traits::length(aString) : 0, Ownership::Reference); } @@ -183,8 +182,8 @@ class MOZ_STACK_CLASS ProfilerStringView { } // No `IsOwned...()` because it's a secret, only used internally! - [[nodiscard]] operator Span() const { - return Span(Data(), Length()); + [[nodiscard]] operator Span() const { + return Span(Data(), Length()); } private: diff --git a/mozglue/baseprofiler/public/ProfileBufferEntryKinds.h b/mozglue/baseprofiler/public/ProfileBufferEntryKinds.h index 5e91d9f1637e..86c8b160bfd0 100644 --- a/mozglue/baseprofiler/public/ProfileBufferEntryKinds.h +++ b/mozglue/baseprofiler/public/ProfileBufferEntryKinds.h @@ -70,8 +70,11 @@ enum class ProfileBufferEntryKind : ProfileBufferEntryKindUnderlyingType { // 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, diff --git a/mozglue/baseprofiler/public/ProfileBufferEntrySerialization.h b/mozglue/baseprofiler/public/ProfileBufferEntrySerialization.h index 267b99f10d39..fc20fdb0908a 100644 --- a/mozglue/baseprofiler/public/ProfileBufferEntrySerialization.h +++ b/mozglue/baseprofiler/public/ProfileBufferEntrySerialization.h @@ -749,15 +749,14 @@ struct ProfileBufferEntryWriter::Serializer> { // wrapper necessary. template struct ProfileBufferEntryReader::Deserializer> { - static void ReadInto(ProfileBufferEntryReader& aER, - ProfileBufferRawPointer& aPtr) { - aER.ReadBytes(&aPtr.mRawPointer, sizeof(aPtr)); + static void ReadInto(ProfileBufferEntryReader& aER, T*& aPtr) { + aER.ReadBytes(&aPtr, sizeof(aPtr)); } - static ProfileBufferRawPointer Read(ProfileBufferEntryReader& aER) { - ProfileBufferRawPointer rawPointer; - ReadInto(aER, rawPointer); - return rawPointer; + static T* Read(ProfileBufferEntryReader& aER) { + T* ptr; + ReadInto(aER, ptr); + return ptr; } }; diff --git a/mozglue/tests/TestBaseProfiler.cpp b/mozglue/tests/TestBaseProfiler.cpp index 495b4d68cc7e..54101d5492fd 100644 --- a/mozglue/tests/TestBaseProfiler.cpp +++ b/mozglue/tests/TestBaseProfiler.cpp @@ -3014,152 +3014,126 @@ void TestLiteralEmptyStringView() { printf("TestLiteralEmptyStringView done\n"); } -template void TestProfilerStringView() { - if constexpr (std::is_same_v) { - printf("TestProfilerStringView...\n"); - } else if constexpr (std::is_same_v) { - printf("TestProfilerStringView...\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&& 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(empty)).Data()); - MOZ_RELEASE_ASSERT(BSV(std::basic_string_view(empty)).Data()[0] == - CHAR('\0')); - MOZ_RELEASE_ASSERT(BSV(std::basic_string_view(empty)).Length() == 0); - MOZ_RELEASE_ASSERT(!BSV(std::basic_string_view(empty)).IsLiteral()); - MOZ_RELEASE_ASSERT(BSV(std::basic_string_view(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(hi)).Data()); - MOZ_RELEASE_ASSERT(BSV(std::basic_string_view(hi)).Data()[0] == - CHAR('h')); - MOZ_RELEASE_ASSERT(BSV(std::basic_string_view(hi)).Data()[1] == - CHAR('i')); - MOZ_RELEASE_ASSERT(BSV(std::basic_string_view(hi)).Data()[2] == - CHAR('\0')); - MOZ_RELEASE_ASSERT(BSV(std::basic_string_view(hi)).Length() == 2); - MOZ_RELEASE_ASSERT(!BSV(std::basic_string_view(hi)).IsLiteral()); - MOZ_RELEASE_ASSERT(BSV(std::basic_string_view(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().data()); - MOZ_RELEASE_ASSERT(BSV(std::basic_string_view()).Data()); - MOZ_RELEASE_ASSERT(BSV(std::basic_string_view()).Data()[0] == - CHAR('\0')); - MOZ_RELEASE_ASSERT(BSV(std::basic_string_view()).Length() == 0); - MOZ_RELEASE_ASSERT(BSV(std::basic_string_view()).IsLiteral()); - MOZ_RELEASE_ASSERT(!BSV(std::basic_string_view()).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(empty)).Data()); - MOZ_RELEASE_ASSERT(BSV(std::basic_string(empty)).Data()[0] == - CHAR('\0')); - MOZ_RELEASE_ASSERT(BSV(std::basic_string(empty)).Length() == 0); - MOZ_RELEASE_ASSERT(!BSV(std::basic_string(empty)).IsLiteral()); - MOZ_RELEASE_ASSERT(BSV(std::basic_string(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(hi)).Data()); - MOZ_RELEASE_ASSERT(BSV(std::basic_string(hi)).Data()[0] == CHAR('h')); - MOZ_RELEASE_ASSERT(BSV(std::basic_string(hi)).Data()[1] == CHAR('i')); - MOZ_RELEASE_ASSERT(BSV(std::basic_string(hi)).Data()[2] == CHAR('\0')); - MOZ_RELEASE_ASSERT(BSV(std::basic_string(hi)).Length() == 2); - MOZ_RELEASE_ASSERT(!BSV(std::basic_string(hi)).IsLiteral()); - MOZ_RELEASE_ASSERT(BSV(std::basic_string(hi)).IsReference()); + 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 contains an empty null-terminated string. - MOZ_RELEASE_ASSERT(std::basic_string().data()); - MOZ_RELEASE_ASSERT(BSV(std::basic_string()).Data()); - MOZ_RELEASE_ASSERT(BSV(std::basic_string()).Data()[0] == CHAR('\0')); - MOZ_RELEASE_ASSERT(BSV(std::basic_string()).Length() == 0); - MOZ_RELEASE_ASSERT(!BSV(std::basic_string()).IsLiteral()); - MOZ_RELEASE_ASSERT(BSV(std::basic_string()).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 that quacks like nsTString (with Data(), Length(), IsLiteral()), to - // check that ProfilerStringView can read from them. - class FakeNsTString { + 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()); + // 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()); - // 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()); + // 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()); - // 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()); + // 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()); - // 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 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; @@ -3168,73 +3142,69 @@ void TestProfilerStringView() { 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 outerBSV; + ProfilerString8View outerBS8V; cb.ReadEach([&](ProfileBufferEntryReader& aER) { ++read; - auto bsv = aER.ReadObject>(); - 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(); + 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 hiString(hi); - MOZ_RELEASE_ASSERT(cb.PutObject(BSV(hiString))); + std::string hiString("hi"); + MOZ_RELEASE_ASSERT(cb.PutObject(BS8V(hiString))); { unsigned read = 0; - ProfilerStringView outerBSV; + ProfilerString8View outerBS8V; cb.ReadEach([&](ProfileBufferEntryReader& aER) { ++read; - auto bsv = aER.ReadObject>(); - 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(); + 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) { - printf("TestProfilerStringView done\n"); - } else if constexpr (std::is_same_v) { - printf("TestProfilerStringView done\n"); - } + printf("TestProfilerStringView done\n"); } void TestProfilerDependencies() { @@ -3254,8 +3224,7 @@ void TestProfilerDependencies() { TestBlocksRingBufferThreading(); TestBlocksRingBufferSerialization(); TestLiteralEmptyStringView(); - TestProfilerStringView(); - TestProfilerStringView(); + TestProfilerStringView(); } // Increase the depth, to a maximum (to avoid too-deep recursion). @@ -3470,13 +3439,34 @@ void TestProfiler() { "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"); { @@ -3532,7 +3522,16 @@ void TestProfiler() { 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); @@ -4035,13 +4034,37 @@ void TestPredefinedMarkers() { 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(); diff --git a/netwerk/protocol/http/HttpBaseChannel.h b/netwerk/protocol/http/HttpBaseChannel.h index d327316b02ed..a16f2d7a4fdf 100644 --- a/netwerk/protocol/http/HttpBaseChannel.h +++ b/netwerk/protocol/http/HttpBaseChannel.h @@ -248,7 +248,7 @@ class HttpBaseChannel : public nsHashPropertyBag, using nsIClassifiedChannel::IsThirdPartyTrackingResource; - virtual void SetSource(UniquePtr aSource) override { + virtual void SetSource(UniqueProfilerBacktrace aSource) override { mSource = std::move(aSource); } @@ -756,7 +756,7 @@ class HttpBaseChannel : public nsHashPropertyBag, Atomic mThirdPartyClassificationFlags; Atomic mFlashPluginState; - UniquePtr mSource; + UniqueProfilerBacktrace mSource; uint32_t mLoadFlags; uint32_t mCaps; diff --git a/netwerk/protocol/http/HttpChannelChild.cpp b/netwerk/protocol/http/HttpChannelChild.cpp index 430b8741d0db..0311ba7cb058 100644 --- a/netwerk/protocol/http/HttpChannelChild.cpp +++ b/netwerk/protocol/http/HttpChannelChild.cpp @@ -67,6 +67,10 @@ # include "GeckoTaskTracer.h" #endif +#ifdef MOZ_GECKO_PROFILER +# include "ProfilerMarkerPayload.h" +#endif + #include using namespace mozilla::dom; diff --git a/netwerk/protocol/http/nsHttpChannel.cpp b/netwerk/protocol/http/nsHttpChannel.cpp index 70f91c057dc8..33582d2be6f6 100644 --- a/netwerk/protocol/http/nsHttpChannel.cpp +++ b/netwerk/protocol/http/nsHttpChannel.cpp @@ -135,6 +135,10 @@ # include "GeckoTaskTracer.h" #endif +#ifdef MOZ_GECKO_PROFILER +# include "ProfilerMarkerPayload.h" +#endif + namespace mozilla { using namespace dom; diff --git a/netwerk/protocol/http/nsIHttpChannel.idl b/netwerk/protocol/http/nsIHttpChannel.idl index 35e9d6985a88..be37f78f38a0 100644 --- a/netwerk/protocol/http/nsIHttpChannel.idl +++ b/netwerk/protocol/http/nsIHttpChannel.idl @@ -501,6 +501,6 @@ interface nsIHttpChannel : nsIIdentChannel in AString aContentType); %{ C++ - virtual void SetSource(mozilla::UniquePtr aSource) {} + virtual void SetSource(UniqueProfilerBacktrace aSource) {} %} }; diff --git a/toolkit/components/backgroundhangmonitor/BackgroundHangMonitor.cpp b/toolkit/components/backgroundhangmonitor/BackgroundHangMonitor.cpp index 349bbf337ea6..1bc5ad357da4 100644 --- a/toolkit/components/backgroundhangmonitor/BackgroundHangMonitor.cpp +++ b/toolkit/components/backgroundhangmonitor/BackgroundHangMonitor.cpp @@ -31,6 +31,10 @@ #include "prinrval.h" #include "prthread.h" +#ifdef MOZ_GECKO_PROFILER +# include "ProfilerMarkerPayload.h" +#endif + #include // Activate BHR only for one every BHR_BETA_MOD users. @@ -530,26 +534,12 @@ void BackgroundHangThread::ReportHang(TimeDuration aHangTime, // If the profiler is enabled, add a marker. #ifdef MOZ_GECKO_PROFILER if (profiler_can_accept_markers()) { - struct HangMarker { - static constexpr Span 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 } diff --git a/tools/profiler/core/ProfileBufferEntry.cpp b/tools/profiler/core/ProfileBufferEntry.cpp index d1f77f27c739..fe8f3f8c44d8 100644 --- a/tools/profiler/core/ProfileBufferEntry.cpp +++ b/tools/profiler/core/ProfileBufferEntry.cpp @@ -10,6 +10,7 @@ #include "platform.h" #include "ProfileBuffer.h" #include "ProfilerBacktrace.h" +#include "ProfilerMarkerPayload.h" #include "jsapi.h" #include "jsfriendapi.h" @@ -635,6 +636,7 @@ class EntryGetter { // )+ // */ // ) +// | MarkerData // | Marker // | ( /* Counters */ // CounterId @@ -1152,23 +1154,78 @@ void ProfileBuffer::StreamMarkersToJSON(SpliceableJSONWriter& aWriter, MOZ_ASSERT(static_cast(type) < static_cast( 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() != 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(); + auto startTime = aER.ReadObject(); + auto endTime = aER.ReadObject(); + auto phase = aER.ReadObject(); + const JS::ProfilingCategoryPairInfo& info = + GetProfilingCategoryPairInfo( + static_cast( + aER.ReadObject())); + auto payload = aER.ReadObject>(); - 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); }); } diff --git a/tools/profiler/core/ProfilerMarkerPayload.cpp b/tools/profiler/core/ProfilerMarkerPayload.cpp new file mode 100644 index 000000000000..5c4842d48d7c --- /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 + +using namespace mozilla; + +static UniquePtr DeserializeNothing( + ProfileBufferEntryReader&) { + return nullptr; +} + +// Starting at 1 for the initial `DeserializeNothing`. +// static +Atomic + 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(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(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 TracingMarkerPayload::Deserialize( + ProfileBufferEntryReader& aEntryReader) { + ProfilerMarkerPayload::CommonProps props = + DeserializeCommonProps(aEntryReader); + const char* category = aEntryReader.ReadObject(); + TracingKind kind = aEntryReader.ReadObject(); + return UniquePtr( + 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 UserTimingMarkerPayload::Deserialize( + ProfileBufferEntryReader& aEntryReader) { + ProfilerMarkerPayload::CommonProps props = + DeserializeCommonProps(aEntryReader); + auto entryType = aEntryReader.ReadObject(); + auto name = aEntryReader.ReadObject(); + auto startMark = aEntryReader.ReadObject>(); + auto endMark = aEntryReader.ReadObject>(); + return UniquePtr( + 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 TimingMarkerPayload::Deserialize( + ProfileBufferEntryReader& aEntryReader) { + ProfilerMarkerPayload::CommonProps props = + DeserializeCommonProps(aEntryReader); + return UniquePtr( + 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 TextMarkerPayload::Deserialize( + ProfileBufferEntryReader& aEntryReader) { + ProfilerMarkerPayload::CommonProps props = + DeserializeCommonProps(aEntryReader); + auto text = aEntryReader.ReadObject(); + return UniquePtr( + 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 LogMarkerPayload::Deserialize( + ProfileBufferEntryReader& aEntryReader) { + ProfilerMarkerPayload::CommonProps props = + DeserializeCommonProps(aEntryReader); + auto module = aEntryReader.ReadObject>(); + auto text = aEntryReader.ReadObject(); + return UniquePtr(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 MediaSampleMarkerPayload::Deserialize( + ProfileBufferEntryReader& aEntryReader) { + ProfilerMarkerPayload::CommonProps props = + DeserializeCommonProps(aEntryReader); + auto sampleStartTimeUs = aEntryReader.ReadObject(); + auto sampleEndTimeUs = aEntryReader.ReadObject(); + return UniquePtr(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 PrefMarkerPayload::Deserialize( + ProfileBufferEntryReader& aEntryReader) { + ProfilerMarkerPayload::CommonProps props = + DeserializeCommonProps(aEntryReader); + auto prefAccessTime = aEntryReader.ReadObject(); + auto prefName = aEntryReader.ReadObject(); + auto prefKind = aEntryReader.ReadObject>(); + auto prefType = aEntryReader.ReadObject>(); + auto prefValue = aEntryReader.ReadObject(); + return UniquePtr(new PrefMarkerPayload( + std::move(props), prefAccessTime, std::move(prefName), + std::move(prefKind), std::move(prefType), std::move(prefValue))); +} + +static Span PrefValueKindToString( + const Maybe& aKind) { + if (aKind) { + return *aKind == PrefValueKind::Default ? MakeStringSpan("Default") + : MakeStringSpan("User"); + } + return MakeStringSpan("Shared"); +} + +static Span PrefTypeToString(const Maybe& 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 LayerTranslationMarkerPayload::Deserialize( + ProfileBufferEntryReader& aEntryReader) { + ProfilerMarkerPayload::CommonProps props = + DeserializeCommonProps(aEntryReader); + auto layer = aEntryReader.ReadObject(); + auto point = aEntryReader.ReadObject(); + return UniquePtr( + 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(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 VsyncMarkerPayload::Deserialize( + ProfileBufferEntryReader& aEntryReader) { + ProfilerMarkerPayload::CommonProps props = + DeserializeCommonProps(aEntryReader); + return UniquePtr( + 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 NetworkMarkerPayload::Deserialize( + ProfileBufferEntryReader& aEntryReader) { + ProfilerMarkerPayload::CommonProps props = + DeserializeCommonProps(aEntryReader); + auto id = aEntryReader.ReadObject(); + auto uri = aEntryReader.ReadObject>(); + auto redirectURI = aEntryReader.ReadObject>(); + auto requestMethod = aEntryReader.ReadObject(); + auto type = aEntryReader.ReadObject(); + auto pri = aEntryReader.ReadObject(); + auto count = aEntryReader.ReadObject(); + auto timings = aEntryReader.ReadObject(); + auto cacheDisposition = aEntryReader.ReadObject(); + auto contentType = aEntryReader.ReadObject>(); + return UniquePtr(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 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 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 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 ScreenshotPayload::Deserialize( + ProfileBufferEntryReader& aEntryReader) { + ProfilerMarkerPayload::CommonProps props = + DeserializeCommonProps(aEntryReader); + auto screenshotDataURL = aEntryReader.ReadObject(); + auto windowSize = aEntryReader.ReadObject(); + auto windowIdentifier = aEntryReader.ReadObject(); + return UniquePtr( + 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(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 GCSliceMarkerPayload::Deserialize( + ProfileBufferEntryReader& aEntryReader) { + ProfilerMarkerPayload::CommonProps props = + DeserializeCommonProps(aEntryReader); + auto timingJSON = aEntryReader.ReadObject(); + return UniquePtr( + 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 BudgetMarkerPayload::Deserialize( + ProfileBufferEntryReader& aEntryReader) { + ProfilerMarkerPayload::CommonProps props = + DeserializeCommonProps(aEntryReader); + return UniquePtr( + 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 GCMajorMarkerPayload::Deserialize( + ProfileBufferEntryReader& aEntryReader) { + ProfilerMarkerPayload::CommonProps props = + DeserializeCommonProps(aEntryReader); + auto timingJSON = aEntryReader.ReadObject(); + return UniquePtr( + 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 GCMinorMarkerPayload::Deserialize( + ProfileBufferEntryReader& aEntryReader) { + ProfilerMarkerPayload::CommonProps props = + DeserializeCommonProps(aEntryReader); + auto timingData = aEntryReader.ReadObject(); + return UniquePtr( + 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 HangMarkerPayload::Deserialize( + ProfileBufferEntryReader& aEntryReader) { + ProfilerMarkerPayload::CommonProps props = + DeserializeCommonProps(aEntryReader); + return UniquePtr( + 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 StyleMarkerPayload::Deserialize( + ProfileBufferEntryReader& aEntryReader) { + ProfilerMarkerPayload::CommonProps props = + DeserializeCommonProps(aEntryReader); + auto stats = aEntryReader.ReadObject(); + return UniquePtr( + 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 LongTaskMarkerPayload::Deserialize( + ProfileBufferEntryReader& aEntryReader) { + ProfilerMarkerPayload::CommonProps props = + DeserializeCommonProps(aEntryReader); + return UniquePtr( + 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 JsAllocationMarkerPayload::Deserialize( + ProfileBufferEntryReader& aEntryReader) { + ProfilerMarkerPayload::CommonProps props = + DeserializeCommonProps(aEntryReader); + auto typeName = aEntryReader.ReadObject>(); + auto className = aEntryReader.ReadObject>(); + auto descriptiveTypeName = + aEntryReader.ReadObject>(); + auto coarseType = aEntryReader.ReadObject(); + auto size = aEntryReader.ReadObject(); + auto inNursery = aEntryReader.ReadObject(); + return UniquePtr(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 NativeAllocationMarkerPayload::Deserialize( + ProfileBufferEntryReader& aEntryReader) { + ProfilerMarkerPayload::CommonProps props = + DeserializeCommonProps(aEntryReader); + auto size = aEntryReader.ReadObject(); + auto memoryAddress = aEntryReader.ReadObject(); + auto threadId = aEntryReader.ReadObject(); + return UniquePtr(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(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 IPCMarkerPayload::Deserialize( + ProfileBufferEntryReader& aEntryReader) { + ProfilerMarkerPayload::CommonProps props = + DeserializeCommonProps(aEntryReader); + auto otherPid = aEntryReader.ReadObject(); + auto messageSeqno = aEntryReader.ReadObject(); + auto messageType = aEntryReader.ReadObject(); + auto side = aEntryReader.ReadObject(); + auto direction = aEntryReader.ReadObject(); + auto phase = aEntryReader.ReadObject(); + auto sync = aEntryReader.ReadObject(); + return UniquePtr( + new IPCMarkerPayload(std::move(props), otherPid, messageSeqno, + messageType, side, direction, phase, sync)); +} + +static Span 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(""); + } +} + +static Span 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(""); + } +} + +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); +} diff --git a/tools/profiler/core/RegisteredThread.cpp b/tools/profiler/core/RegisteredThread.cpp index 42501aec629f..a0cb802c91b1 100644 --- a/tools/profiler/core/RegisteredThread.cpp +++ b/tools/profiler/core/RegisteredThread.cpp @@ -37,6 +37,7 @@ size_t RegisteredThread::SizeOfIncludingThis( // 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 diff --git a/tools/profiler/core/memory_hooks.cpp b/tools/profiler/core/memory_hooks.cpp index 51f6425ba8d9..bdc1008ed460 100644 --- a/tools/profiler/core/memory_hooks.cpp +++ b/tools/profiler/core/memory_hooks.cpp @@ -265,6 +265,10 @@ class ThreadIntercept { // or disabled. static mozilla::Atomic sAllocationsFeatureEnabled; + // The markers will be stored on the main thread. Retain the id to the main + // thread of this process here. + static mozilla::Atomic sMainThreadId; + ThreadIntercept() = default; // Only allow consumers to access this information if they run @@ -311,9 +315,14 @@ class ThreadIntercept { 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; @@ -321,6 +330,8 @@ PROFILER_THREAD_LOCAL(bool) ThreadIntercept::tlsIsBlocked; mozilla::Atomic ThreadIntercept::sAllocationsFeatureEnabled(false); +mozilla::Atomic ThreadIntercept::sMainThreadId(0); + // An object of this class must be created (on the stack) before running any // code that might allocate. class AutoBlockIntercepts { @@ -378,7 +389,7 @@ static void AllocCallback(void* aPtr, size_t aReqSize) { gBernoulli->trial(actualSize) && // Second, attempt to add a marker if the Bernoulli trial passed. profiler_add_native_allocation_marker( - static_cast(actualSize), + ThreadIntercept::MainThreadId(), static_cast(actualSize), reinterpret_cast(aPtr))) { MOZ_ASSERT(gAllocationTracker, "gAllocationTracker must be properly installed for the memory " @@ -421,7 +432,8 @@ static void FreeCallback(void* aPtr) { "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(aPtr)); } } @@ -555,7 +567,7 @@ void install_memory_hooks() { // 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 @@ -584,7 +596,7 @@ void enable_native_allocations() { EnsureBernoulliIsInstalled(); EnsureAllocationTrackerIsInstalled(); - ThreadIntercept::EnableAllocationFeature(); + ThreadIntercept::EnableAllocationFeature(aMainThreadId); } // This is safe to call even if native allocations hasn't been enabled. diff --git a/tools/profiler/core/memory_hooks.h b/tools/profiler/core/memory_hooks.h index 7a117ec15a8c..70e97e196c13 100644 --- a/tools/profiler/core/memory_hooks.h +++ b/tools/profiler/core/memory_hooks.h @@ -13,7 +13,7 @@ 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 diff --git a/tools/profiler/core/platform.cpp b/tools/profiler/core/platform.cpp index 9fbe281ccaec..e2279e614c3a 100644 --- a/tools/profiler/core/platform.cpp +++ b/tools/profiler/core/platform.cpp @@ -37,6 +37,7 @@ #include "ProfilerChild.h" #include "ProfilerCodeAddressService.h" #include "ProfilerIOInterposeObserver.h" +#include "ProfilerMarkerPayload.h" #include "ProfilerParent.h" #include "RegisteredThread.h" #include "shared-libraries.h" @@ -51,7 +52,6 @@ #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" @@ -1631,6 +1631,44 @@ void ProfilingStackOwner::DumpStackAndCrash() const { // 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(aCategoryPair), aPayload); +} + //////////////////////////////////////////////////////////////////////// // BEGIN sampling/unwinding code @@ -2744,15 +2782,19 @@ static void CollectJavaThreadProfileData(ProfileBuffer& aProfileBuffer) { 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); } } } @@ -4552,9 +4594,7 @@ static void locked_profiler_start(PSLockRef aLock, PowerOfTwo32 aCapacity, } // Set up profiling for each registered thread, if appropriate. -#if defined(MOZ_REPLACE_MALLOC) && defined(MOZ_PROFILER_MEMORY) - bool isMainThreadBeingProfiled = false; -#endif + Maybe mainThreadId; int tid = profiler_current_thread_id(); const Vector>& registeredThreads = CorePS::RegisteredThreads(aLock); @@ -4580,11 +4620,9 @@ static void locked_profiler_start(PSLockRef aLock, PowerOfTwo32 aCapacity, TriggerPollJSSamplingOnMainThread(); } } -#if defined(MOZ_REPLACE_MALLOC) && defined(MOZ_PROFILER_MEMORY) if (info->IsMainThread()) { - isMainThreadBeingProfiled = true; + mainThreadId = Some(info->ThreadId()); } -#endif registeredThread->RacyRegisteredThread().ReinitializeOnResume(); if (registeredThread->GetJSContext()) { profiledThreadData->NotifyReceivedJSContext(0); @@ -4619,8 +4657,8 @@ static void locked_profiler_start(PSLockRef aLock, PowerOfTwo32 aCapacity, #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 " @@ -5029,6 +5067,11 @@ void profiler_remove_sampled_counter(BaseProfilerCount* aCounter) { 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); @@ -5065,8 +5108,10 @@ ProfilingStack* profiler_register_thread(const char* aName, 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(); } @@ -5146,8 +5191,10 @@ void profiler_unregister_thread() { 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); } } } @@ -5369,6 +5416,40 @@ 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) { @@ -5381,49 +5462,11 @@ void profiler_add_js_allocation_marker(JS::RecordAllocationInfo&& info) { if (!profiler_can_accept_markers()) { return; } - - struct JsAllocationMarker { - static constexpr mozilla::Span 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() { @@ -5438,154 +5481,100 @@ bool profiler_is_locked_on_current_thread() { 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 aSource, + UniqueProfilerBacktrace aSource, const Maybe& 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 MarkerTypeName() { - return MakeStringSpan("Network"); - } - 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 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); - } - } - static MarkerSchema MarkerTypeDisplay() { - return MarkerSchema::SpecialFrontendLocation{}; - } - - private: - static Span 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: - MOZ_ASSERT(false, "Unexpected NetworkLoadType enum value."); - return MakeStringSpan(""); - } - } - - static Span 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: - return MakeStringSpan(""); - default: - MOZ_ASSERT(false, "Unexpected CacheDisposition enum value."); - return MakeStringSpan(""); - } - } - }; - + // top 32 bits are process id of the load + uint32_t id = static_cast(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, geckoprofiler::category::NETWORK, - {MarkerTiming::Interval(aStart, aEnd), - MarkerStack::TakeBacktrace(std::move(aSource)), - MarkerInnerWindowId(aInnerWindowID)}, - NetworkMarker{}, aStart, aEnd, static_cast(aChannelId), spec, - aRequestMethod, aType, aPriority, aCount, aCacheDisposition, - aTimings ? *aTimings : scEmptyNetTimingStruct, redirect_spec, - aContentType ? ProfilerString8View(*aContentType) - : ProfilerString8View()); + name, JS::ProfilingCategoryPair::NETWORK, + NetworkMarkerPayload(static_cast(aChannelId), + PromiseFlatCString(spec).get(), aRequestMethod, + aType, aStart, aEnd, aPriority, aCount, + aCacheDisposition, aInnerWindowID, aTimings, + PromiseFlatCString(redirect_spec).get(), + std::move(aSource), aContentType)); } -bool profiler_add_native_allocation_marker(int64_t aSize, +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; + } + + // Assert that our thread ID makes sense + bool realThread = false; + const Vector>& registeredThreads = + CorePS::RegisteredThreads(aLock); + for (auto& thread : registeredThreads) { + RefPtr info = thread->Info(); + if (info->ThreadId() == aThreadId) { + realThread = true; + break; + } + } + MOZ_ASSERT(realThread, "Invalid thread id"); + }; + + 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); +} + +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; @@ -5602,30 +5591,69 @@ bool profiler_add_native_allocation_marker(int64_t aSize, return false; } - struct NativeAllocationMarker { - static constexpr mozilla::Span 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(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& 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& 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& 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); diff --git a/tools/profiler/moz.build b/tools/profiler/moz.build index 72f85845ae5d..a2626921133f 100644 --- a/tools/profiler/moz.build +++ b/tools/profiler/moz.build @@ -15,6 +15,7 @@ if CONFIG["MOZ_GECKO_PROFILER"]: "public/GeckoProfilerReporter.h", "public/ProfilerChild.h", "public/ProfilerCodeAddressService.h", + "public/ProfilerMarkerPayload.h", "public/ProfilerParent.h", "public/shared-libraries.h", ] @@ -26,6 +27,7 @@ if CONFIG["MOZ_GECKO_PROFILER"]: "core/ProfiledThreadData.cpp", "core/ProfilerBacktrace.cpp", "core/ProfilerCodeAddressService.cpp", + "core/ProfilerMarkerPayload.cpp", "core/RegisteredThread.cpp", "gecko/ChildProfilerController.cpp", "gecko/nsProfilerStartParams.cpp", diff --git a/tools/profiler/public/GeckoProfiler.h b/tools/profiler/public/GeckoProfiler.h index 291210136084..135291898efc 100644 --- a/tools/profiler/public/GeckoProfiler.h +++ b/tools/profiler/public/GeckoProfiler.h @@ -62,6 +62,17 @@ # 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 @@ -109,6 +120,7 @@ profiler_capture_backtrace() { class ProfilerBacktrace; class ProfilerCodeAddressService; +class ProfilerMarkerPayload; namespace mozilla { class ProfileBufferControlledChunkManager; class ProfileChunkedBuffer; @@ -878,19 +890,49 @@ mozilla::Maybe profiler_get_buffer_info(); 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( @@ -899,8 +941,7 @@ void profiler_add_network_marker( mozilla::TimeStamp aEnd, int64_t aCount, mozilla::net::CacheDisposition aCacheDisposition, uint64_t aInnerWindowID, const mozilla::net::TimingStruct* aTimings = nullptr, - nsIURI* aRedirectURI = nullptr, - mozilla::UniquePtr aSource = nullptr, + nsIURI* aRedirectURI = nullptr, UniqueProfilerBacktrace aSource = nullptr, const mozilla::Maybe& aContentType = mozilla::Nothing()); @@ -928,6 +969,40 @@ inline mozilla::MarkerInnerWindowId MarkerInnerWindowIdFromDocShell( 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& aInnerWindowID = mozilla::Nothing()); +void profiler_tracing_marker( + const char* aCategoryString, const char* aMarkerName, + JS::ProfilingCategoryPair aCategoryPair, TracingKind aKind, + UniqueProfilerBacktrace aCause, + const mozilla::Maybe& 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 //--------------------------------------------------------------------------- @@ -1150,6 +1225,44 @@ class MOZ_RAII AutoProfilerLabel { }; }; +class MOZ_RAII AutoProfilerTracing { + public: + AutoProfilerTracing(const char* aCategoryString, const char* aMarkerName, + JS::ProfilingCategoryPair aCategoryPair, + const mozilla::Maybe& 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& 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 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 diff --git a/tools/profiler/public/ProfilerMarkerPayload.h b/tools/profiler/public/ProfilerMarkerPayload.h new file mode 100644 index 000000000000..d09c41bc326c --- /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& 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& 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 DeserializeTagAndPayload( + mozilla::ProfileBufferEntryReader& aER) { + const auto tag = aER.ReadObject(); + 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 (*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 + 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 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 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& 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& aPrefKind, + const mozilla::Maybe& 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&& aPrefKind, + mozilla::Maybe&& 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 mPrefKind; + // Nothing means that the mPrefName preference was not found. Something + // contains the type of the preference. + mozilla::Maybe mPrefType; + nsCString mPrefValue; +}; + +class UserTimingMarkerPayload : public ProfilerMarkerPayload { + public: + UserTimingMarkerPayload(const nsAString& aName, + const mozilla::TimeStamp& aStartTime, + const mozilla::Maybe& aInnerWindowID) + : ProfilerMarkerPayload(aStartTime, aStartTime, aInnerWindowID), + mEntryType("mark"), + mName(aName) {} + + UserTimingMarkerPayload(const nsAString& aName, + const mozilla::Maybe& aStartMark, + const mozilla::Maybe& aEndMark, + const mozilla::TimeStamp& aStartTime, + const mozilla::TimeStamp& aEndTime, + const mozilla::Maybe& 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&& aStartMark, + mozilla::Maybe&& 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 mStartMark; + mozilla::Maybe 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& 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&& aURI, + mozilla::UniqueFreePtr&& aRedirectURI, + nsCString&& aRequestMethod, NetworkLoadType aType, + int32_t aPri, int64_t aCount, + mozilla::net::TimingStruct aTimings, + mozilla::net::CacheDisposition aCacheDisposition, + mozilla::Maybe&& 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 mURI; + mozilla::UniqueFreePtr 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 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& 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& aInnerWindowID) + : ProfilerMarkerPayload(aStartTime, aStartTime, aInnerWindowID), + mText(aText) {} + + TextMarkerPayload(const nsACString& aText, + const mozilla::TimeStamp& aStartTime, + const mozilla::TimeStamp& aEndTime, + const mozilla::Maybe& 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&& aTypeName, + mozilla::UniqueFreePtr&& aClassName, + mozilla::UniqueFreePtr&& 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 mTypeName; + mozilla::UniqueFreePtr mClassName; + mozilla::UniqueFreePtr 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 { + 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> { + static Length Bytes(const UniquePtr& aPayload) { + return ProfilerMarkerPayload::TagAndSerializationBytes(aPayload.get()); + } + + static void Write(ProfileBufferEntryWriter& aEW, + const UniquePtr& 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> { + static void ReadInto(ProfileBufferEntryReader& aER, + UniquePtr& aPayload) { + aPayload = Read(aER); + } + + static UniquePtr Read(ProfileBufferEntryReader& aER) { + return ProfilerMarkerPayload::DeserializeTagAndPayload(aER); + } +}; + +} // namespace mozilla + +#endif // ProfilerMarkerPayload_h diff --git a/tools/profiler/public/ProfilerMarkerTypes.h b/tools/profiler/public/ProfilerMarkerTypes.h index 347f58839ce2..5269b9b4a283 100644 --- a/tools/profiler/public/ProfilerMarkerTypes.h +++ b/tools/profiler/public/ProfilerMarkerTypes.h @@ -23,16 +23,488 @@ #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 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 MarkerTypeName() { + return mozilla::MakeStringSpan("PreferenceRead"); + } + static void StreamJSONMarkerData( + mozilla::baseprofiler::SpliceableJSONWriter& aWriter, + const mozilla::ProfilerString8View& aPrefName, + const mozilla::Maybe& aPrefKind, + const mozilla::Maybe& 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 PrefValueKindToString( + const mozilla::Maybe& aKind) { + if (aKind) { + return *aKind == mozilla::PrefValueKind::Default + ? mozilla::MakeStringSpan("Default") + : mozilla::MakeStringSpan("User"); + } + return "Shared"; + } + + static mozilla::Span PrefTypeToString( + const mozilla::Maybe& 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 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 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 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 typeString = GetNetworkState(aType); + mozilla::Span 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 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 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 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 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 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 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 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 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 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(aMemoryAddress)); + aWriter.IntProperty("threadId", aThreadId); + } + static mozilla::MarkerSchema MarkerTypeDisplay() { + return mozilla::MarkerSchema::SpecialFrontendLocation{}; + } +}; + +struct IPCMarkerPayload { + static constexpr mozilla::Span 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 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(""); + } + } + + static mozilla::Span 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(""); + } + } +}; } // namespace geckoprofiler::markers diff --git a/tools/profiler/public/ProfilerMarkers.h b/tools/profiler/public/ProfilerMarkers.h index 0c077f1fb07c..353ed8f20f41 100644 --- a/tools/profiler/public/ProfilerMarkers.h +++ b/tools/profiler/public/ProfilerMarkers.h @@ -44,9 +44,6 @@ # 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 @@ -132,9 +129,8 @@ inline mozilla::ProfileBufferBlockIndex profiler_add_marker( } 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 @@ -189,75 +185,6 @@ class MOZ_RAII AutoProfilerTextMarker { 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& 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 aBacktrace, - const mozilla::Maybe& 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 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 diff --git a/tools/profiler/tests/gtest/GeckoProfiler.cpp b/tools/profiler/tests/gtest/GeckoProfiler.cpp index ff302f9880d2..035ec110a99b 100644 --- a/tools/profiler/tests/gtest/GeckoProfiler.cpp +++ b/tools/profiler/tests/gtest/GeckoProfiler.cpp @@ -14,6 +14,7 @@ #include "mozilla/ProfilerMarkers.h" #include "platform.h" #include "ProfileBuffer.h" +#include "ProfilerMarkerPayload.h" #include "js/Initialization.h" #include "js/Printf.h" @@ -578,6 +579,75 @@ TEST(GeckoProfiler, Pause) 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 GTestMarkerPayload::Deserialize( + ProfileBufferEntryReader& aEntryReader) { + ProfilerMarkerPayload::CommonProps props = + DeserializeCommonProps(aEntryReader); + auto n = aEntryReader.ReadObject(); + return UniquePtr( + 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(buf, size_t(written)), mN); + ++sNumStreamed; +} + TEST(GeckoProfiler, Markers) { uint32_t features = ProfilerFeature::StackWalk; @@ -586,20 +656,39 @@ TEST(GeckoProfiler, Markers) 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. @@ -674,76 +763,111 @@ TEST(GeckoProfiler, Markers) // Other markers in alphabetical order of payload class names. - nsCOMPtr uri; - ASSERT_TRUE( - NS_SUCCEEDED(NS_NewURI(getter_AddRefs(uri), "http://mozilla.org/"_ns))); - // The marker name will be "Load : ". - 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 aSource = - nullptr */ - /* const mozilla::Maybe& aContentType = - mozilla::Nothing() */); + { + const char gcMajorJSON[] = "42"; + const auto len = strlen(gcMajorJSON); + char* buffer = + static_cast(js::SystemAllocPolicy{}.pod_malloc(len + 1)); + strncpy(buffer, gcMajorJSON, len); + buffer[len] = '\0'; + PROFILER_ADD_MARKER_WITH_PAYLOAD("GCMajorMarkerPayload marker", OTHER, + GCMajorMarkerPayload, + (ts1, ts2, JS::UniqueChars(buffer))); + } - 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 aSource = - nullptr */ - nullptr, - /* const mozilla::Maybe& aContentType = - mozilla::Nothing() */ - Some(nsDependentCString("text/html"))); + { + const char gcMinorJSON[] = "43"; + const auto len = strlen(gcMinorJSON); + char* buffer = + static_cast(js::SystemAllocPolicy{}.pod_malloc(len + 1)); + strncpy(buffer, gcMinorJSON, len); + buffer[len] = '\0'; + PROFILER_ADD_MARKER_WITH_PAYLOAD("GCMinorMarkerPayload marker", OTHER, + GCMinorMarkerPayload, + (ts1, ts2, JS::UniqueChars(buffer))); + } - nsCOMPtr 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 aSource = - nullptr */ - /* const mozilla::Maybe& aContentType = - mozilla::Nothing() */); + { + const char gcSliceJSON[] = "44"; + const auto len = strlen(gcSliceJSON); + char* buffer = + static_cast(js::SystemAllocPolicy{}.pod_malloc(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)); + + 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)); + + 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()), @@ -784,19 +908,52 @@ TEST(GeckoProfiler, Markers) 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 profile = w.ChunkedWriteFunc().CopyData(); ASSERT_TRUE(!!profile.get()); @@ -809,15 +966,42 @@ TEST(GeckoProfiler, Markers) 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, @@ -1050,6 +1234,7 @@ TEST(GeckoProfiler, Markers) 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") { @@ -1058,6 +1243,7 @@ TEST(GeckoProfiler, Markers) 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") { @@ -1066,6 +1252,7 @@ TEST(GeckoProfiler, Markers) 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") { @@ -1074,6 +1261,7 @@ TEST(GeckoProfiler, Markers) 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") { @@ -1083,6 +1271,7 @@ TEST(GeckoProfiler, Markers) 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: @@ -1090,6 +1279,7 @@ TEST(GeckoProfiler, Markers) 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: @@ -1098,6 +1288,43 @@ TEST(GeckoProfiler, Markers) 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-" : `. + 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. @@ -1113,43 +1340,94 @@ TEST(GeckoProfiler, Markers) 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"); @@ -1160,12 +1438,100 @@ TEST(GeckoProfiler, Markers) "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") { @@ -1278,6 +1644,70 @@ TEST(GeckoProfiler, Markers) 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"); @@ -1347,8 +1777,18 @@ TEST(GeckoProfiler, Markers) // 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 @@ -1383,27 +1823,70 @@ TEST(GeckoProfiler, Markers) 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 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" diff --git a/xpcom/base/CycleCollectedJSRuntime.cpp b/xpcom/base/CycleCollectedJSRuntime.cpp index e4df129787d3..05afbdb5717d 100644 --- a/xpcom/base/CycleCollectedJSRuntime.cpp +++ b/xpcom/base/CycleCollectedJSRuntime.cpp @@ -93,6 +93,10 @@ #include "nsStringBuffer.h" #include "nsWrapperCache.h" +#ifdef MOZ_GECKO_PROFILER +# include "ProfilerMarkerPayload.h" +#endif + #if defined(XP_MACOSX) # include "nsMacUtilsImpl.h" #endif @@ -988,65 +992,15 @@ void CycleCollectedJSRuntime::GCSliceCallback(JSContext* aContext, #ifdef MOZ_GECKO_PROFILER if (profiler_thread_is_being_profiled()) { if (aProgress == JS::GC_CYCLE_END) { - struct GCMajorMarker { - static constexpr mozilla::Span 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 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 @@ -1125,35 +1079,10 @@ void CycleCollectedJSRuntime::GCNurseryCollectionCallback( #ifdef MOZ_GECKO_PROFILER else if (aProgress == JS::GCNurseryProgress::GC_NURSERY_COLLECTION_END && profiler_thread_is_being_profiled()) { - struct GCMinorMarker { - static constexpr mozilla::Span 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 diff --git a/xpcom/base/Logging.cpp b/xpcom/base/Logging.cpp index 1ba3f530e546..5d82717f063e 100644 --- a/xpcom/base/Logging.cpp +++ b/xpcom/base/Logging.cpp @@ -23,6 +23,9 @@ #include "nsDebugImpl.h" #include "NSPRLogModulesParser.h" #include "LogCommandLineHandler.h" +#ifdef MOZ_GECKO_PROFILER +# include "ProfilerMarkerPayload.h" +#endif #include "prenv.h" #ifdef XP_WIN @@ -416,33 +419,15 @@ class LogModuleManager { #ifdef MOZ_GECKO_PROFILER if (mAddProfilerMarker && profiler_can_accept_markers()) { - struct LogMarker { - static constexpr Span 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 diff --git a/xpcom/threads/nsThread.cpp b/xpcom/threads/nsThread.cpp index 409e23ecfbc0..9c5868af0ce7 100644 --- a/xpcom/threads/nsThread.cpp +++ b/xpcom/threads/nsThread.cpp @@ -42,6 +42,9 @@ #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" @@ -1521,28 +1524,9 @@ void PerformanceCounterState::MaybeReportAccumulatedTime(TimeStamp aNow) { #ifdef MOZ_GECKO_PROFILER if (profiler_thread_is_being_profiled()) { - struct LongTaskMarker { - static constexpr Span 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 }