From cdb26160be38df8abfcf4849086a9230ab645ebb Mon Sep 17 00:00:00 2001 From: Razvan Maries Date: Wed, 18 Nov 2020 20:06:28 +0200 Subject: [PATCH] Backed out 23 changesets (bug 1675409) for build bustages on Preferences.cpp. CLOSED TREE Backed out changeset c1a131a55767 (bug 1675409) Backed out changeset 47d210802a5d (bug 1675409) Backed out changeset e8ebb1c58d30 (bug 1675409) Backed out changeset 69a1e9aeff2a (bug 1675409) Backed out changeset 68f330b387a8 (bug 1675409) Backed out changeset e4750d9ef5a1 (bug 1675409) Backed out changeset bb6bb71e5ab3 (bug 1675409) Backed out changeset 988d7f4716df (bug 1675409) Backed out changeset ca41382e891c (bug 1675409) Backed out changeset 90f3fbbbbeda (bug 1675409) Backed out changeset 9b109d61a6f6 (bug 1675409) Backed out changeset 3dd66abfdaa2 (bug 1675409) Backed out changeset 44181df5f0db (bug 1675409) Backed out changeset bb2603d947fc (bug 1675409) Backed out changeset 97055cf20a56 (bug 1675409) Backed out changeset f88fcf09de0d (bug 1675409) Backed out changeset 7963e1c49786 (bug 1675409) Backed out changeset 4c379c1061c3 (bug 1675409) Backed out changeset b8be8ae7da63 (bug 1675409) Backed out changeset 0b90aa89421e (bug 1675409) Backed out changeset c10fb46467c9 (bug 1675409) Backed out changeset 894ac233b290 (bug 1675409) Backed out changeset 075d1d8e34c2 (bug 1675409) --- docshell/base/nsDocShell.cpp | 4 + dom/base/ChromeUtils.cpp | 3 + dom/base/TimeoutManager.cpp | 25 +- dom/base/nsDOMNavigationTiming.cpp | 93 +- dom/events/EventDispatcher.cpp | 4 + dom/ipc/ContentParent.cpp | 22 +- dom/media/AsyncLogger.h | 48 +- dom/media/MediaDecoderStateMachine.cpp | 28 +- dom/media/MediaFormatReader.cpp | 13 +- dom/media/mediasink/AudioSink.cpp | 30 +- dom/media/mediasink/VideoSink.cpp | 20 +- dom/media/platforms/wmf/WMFDecoderModule.cpp | 16 +- .../platforms/wmf/WMFMediaDataDecoder.cpp | 13 +- .../platforms/wrappers/MediaChangeMonitor.cpp | 27 +- dom/performance/Performance.cpp | 64 +- dom/script/ScriptLoader.cpp | 16 +- dom/xhr/XMLHttpRequestMainThread.cpp | 5 +- dom/xhr/XMLHttpRequestMainThread.h | 2 +- dom/xhr/XMLHttpRequestWorker.cpp | 6 +- gfx/layers/ProfilerScreenshots.cpp | 35 +- .../composite/ContainerLayerComposite.cpp | 38 +- gfx/layers/ipc/CompositorBridgeParent.cpp | 69 +- .../ipc/ContentCompositorBridgeParent.cpp | 43 +- gfx/layers/wr/WebRenderBridgeParent.cpp | 68 +- .../RenderCompositorNative.cpp | 4 + .../src/chrome/common/ipc_channel_utils.cc | 15 +- ipc/glue/MessageChannel.cpp | 14 +- ipc/glue/MessageChannel.h | 74 -- ipc/mscom/ProfilerMarkers.cpp | 12 +- js/xpconnect/src/XPCJSContext.cpp | 7 +- layout/base/AutoProfilerStyleMarker.h | 55 +- layout/base/PresShell.cpp | 2 +- layout/base/PresShell.h | 4 +- layout/base/PresShellInlines.h | 4 +- layout/base/nsCSSFrameConstructor.cpp | 26 +- layout/base/nsRefreshDriver.cpp | 5 +- modules/libpref/Preferences.cpp | 74 +- .../baseprofiler/core/ProfileBufferEntry.cpp | 21 +- .../public/BaseProfilerMarkerTypes.h | 142 ++- .../baseprofiler/public/BaseProfilerMarkers.h | 19 - .../public/BaseProfilerMarkersPrerequisites.h | 9 +- .../public/ProfileBufferEntryKinds.h | 7 +- .../public/ProfileBufferEntrySerialization.h | 13 +- mozglue/tests/TestBaseProfiler.cpp | 351 ++--- netwerk/protocol/http/HttpBaseChannel.h | 4 +- netwerk/protocol/http/HttpChannelChild.cpp | 4 + netwerk/protocol/http/nsHttpChannel.cpp | 4 + netwerk/protocol/http/nsIHttpChannel.idl | 2 +- .../BackgroundHangMonitor.cpp | 30 +- tools/profiler/core/ProfileBufferEntry.cpp | 87 +- tools/profiler/core/ProfilerMarkerPayload.cpp | 1126 +++++++++++++++++ tools/profiler/core/RegisteredThread.cpp | 1 + tools/profiler/core/memory_hooks.cpp | 22 +- tools/profiler/core/memory_hooks.h | 2 +- tools/profiler/core/platform.cpp | 436 ++++--- tools/profiler/moz.build | 2 + tools/profiler/public/GeckoProfiler.h | 121 +- tools/profiler/public/ProfilerMarkerPayload.h | 839 ++++++++++++ tools/profiler/public/ProfilerMarkerTypes.h | 476 ++++++- tools/profiler/public/ProfilerMarkers.h | 75 +- tools/profiler/tests/gtest/GeckoProfiler.cpp | 685 ++++++++-- xpcom/base/CycleCollectedJSRuntime.cpp | 103 +- xpcom/base/Logging.cpp | 39 +- xpcom/threads/nsThread.cpp | 28 +- 64 files changed, 4293 insertions(+), 1343 deletions(-) create mode 100644 tools/profiler/core/ProfilerMarkerPayload.cpp create mode 100644 tools/profiler/public/ProfilerMarkerPayload.h diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index 250a7caced671..c924653a10efa 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 a120d01e83278..4c3e372fdbf66 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 32808d6404da3..305605119360a 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 b6ec37e8d6ebe..c5032bee35f17 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 28fbfc40cc0cb..d61f94ec1fc80 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 bf376086af657..b798b4e044ed9 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 b0aee6c6394c2..dbebac0b0ce23 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 6105bac691604..1565188f73d5c 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 2da0157ee57fa..8706e0d3e4159 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 926a75dc48615..bec3acd75cacd 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 4b53dcac2c358..95dba9aaab65a 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 9d53030ff75f5..a12e193e4ade8 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 47f2b1726ef60..7b84f2a71d636 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 0edd0d8c48635..351cbcb7bca58 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 8d06a0c6ef9ac..47764f8e47231 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 319a026eb6222..032d762911ce5 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 88f67787e2cc6..e74f3e7c52ccd 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 6644527ff967f..56fa37007cb74 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 9184ce1e30db5..70cd5261ff68f 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 648326150dd50..e4c4953c89879 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 950b6bf0089b7..290dd1627767e 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 b18d97487abdf..d680f5ca57672 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); } - }; - profiler_add_marker("CONTENT_FRAME_TIME", geckoprofiler::category::GRAPHICS, - MarkerTiming::Interval(aTxnStart, aCompositeEnd), - ContentFrameMarker{}); + 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))); + } + }; + 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 ec6e4e2760eba..b6e1b03f77704 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 6ae5741afb683..e8e00357ad762 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 d4861c327a9a0..e606b306e8ded 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 745e8da10c0c2..2a05dbdad9346 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 86fb36433b8f7..c4401f20a025d 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 cf08ead6d046c..fb369449b84ae 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 591e4347b43fe..b7c6cff542a52 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 28f2882acbd8a..e6cc40ccc3943 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 a68cfe843f8cf..dce95c630da21 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 61698bd0d1b03..46ec58bfe0c78 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 762ec4aea56db..9e7a028039bc6 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 cb79a245b2e2c..d3ab4ff26fcec 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 486f3a078565e..c97c53947cbf7 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 1115649db2bd7..29be1eab6e4a2 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 ab86c3af877af..fb07e52abd277 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 48b4883d4c25b..07f944654b9b4 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 1556b7a2724bc..207b2365ff14a 100644 --- a/mozglue/baseprofiler/public/BaseProfilerMarkerTypes.h +++ b/mozglue/baseprofiler/public/BaseProfilerMarkerTypes.h @@ -29,35 +29,153 @@ namespace mozilla::baseprofiler::markers { -struct MediaSampleMarker { +struct Tracing { static constexpr Span MarkerTypeName() { - return MakeStringSpan("MediaSample"); + return MakeStringSpan("tracing"); } static void StreamJSONMarkerData(SpliceableJSONWriter& aWriter, - int64_t aSampleStartTimeUs, - int64_t aSampleEndTimeUs) { - aWriter.IntProperty("sampleStartTimeUs", aSampleStartTimeUs); - aWriter.IntProperty("sampleEndTimeUs", aSampleEndTimeUs); + 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.AddKeyLabelFormat("sampleStartTimeUs", "Sample start time", - MS::Format::microseconds); - schema.AddKeyLabelFormat("sampleEndTimeUs", "Sample end time", - MS::Format::microseconds); + 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 ContentBuildMarker { +struct UserTimingMeasure { static constexpr Span MarkerTypeName() { - return MakeStringSpan("CONTENT_FULL_PAINT_TIME"); + 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"); + } + static void StreamJSONMarkerData(SpliceableJSONWriter& aWriter, + int64_t aSampleStartTimeUs, + int64_t aSampleEndTimeUs) { + aWriter.IntProperty("sampleStartTimeUs", aSampleStartTimeUs); + aWriter.IntProperty("sampleEndTimeUs", aSampleEndTimeUs); + } + static MarkerSchema MarkerTypeDisplay() { + using MS = MarkerSchema; + MS schema{MS::Location::markerChart, MS::Location::markerTable}; + schema.AddKeyLabelFormat("sampleStartTimeUs", "Sample start time", + MS::Format::microseconds); + schema.AddKeyLabelFormat("sampleEndTimeUs", "Sample end time", + MS::Format::microseconds); return schema; } }; diff --git a/mozglue/baseprofiler/public/BaseProfilerMarkers.h b/mozglue/baseprofiler/public/BaseProfilerMarkers.h index 9d6ba91d9b355..ed7451213bd9b 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 ee30063647ede..5af3bb3435d42 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 5e91d9f1637e1..86c8b160bfd0b 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 267b99f10d39e..fc20fdb0908a9 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 495b4d68cc7e3..54101d5492fdc 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()); - - // 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()); - - // Class that quacks like nsTString (with Data(), Length(), IsLiteral()), to - // check that ProfilerStringView can read from them. - class FakeNsTString { + MOZ_RELEASE_ASSERT(BS8V(std::string("hi")).Data()); + MOZ_RELEASE_ASSERT(BS8V(std::string("hi")).Data()[0] == 'h'); + MOZ_RELEASE_ASSERT(BS8V(std::string("hi")).Data()[1] == 'i'); + MOZ_RELEASE_ASSERT(BS8V(std::string("hi")).Data()[2] == '\0'); + MOZ_RELEASE_ASSERT(BS8V(std::string("hi")).Length() == 2); + MOZ_RELEASE_ASSERT(!BS8V(std::string("hi")).IsLiteral()); + MOZ_RELEASE_ASSERT(BS8V(std::string("hi")).IsReference()); + + // Default std::string_view contains an empty null-terminated string. + MOZ_RELEASE_ASSERT(std::string().data()); + MOZ_RELEASE_ASSERT(BS8V(std::string()).Data()); + MOZ_RELEASE_ASSERT(BS8V(std::string()).Data()[0] == '\0'); + MOZ_RELEASE_ASSERT(BS8V(std::string()).Length() == 0); + MOZ_RELEASE_ASSERT(!BS8V(std::string()).IsLiteral()); + MOZ_RELEASE_ASSERT(BS8V(std::string()).IsReference()); + + class FakeNsCString { public: - FakeNsTString(const CHAR* aData, size_t aLength, bool aIsLiteral) + FakeNsCString(const char* aData, size_t aLength, bool aIsLiteral) : mData(aData), mLength(aLength), mIsLiteral(aIsLiteral) {} - const CHAR* Data() const { return mData; } + const char* Data() const { return mData; } size_t Length() const { return mLength; } bool IsLiteral() const { return mIsLiteral; } private: - const CHAR* mData; + const char* mData; size_t mLength; bool mIsLiteral; }; - // FakeNsTString to nullptr. - MOZ_RELEASE_ASSERT(BSV(FakeNsTString(nullptr, 0, true)).Data()); - MOZ_RELEASE_ASSERT(BSV(FakeNsTString(nullptr, 0, true)).Data()[0] == - CHAR('\0')); - MOZ_RELEASE_ASSERT(BSV(FakeNsTString(nullptr, 0, true)).Length() == 0); - MOZ_RELEASE_ASSERT(BSV(FakeNsTString(nullptr, 0, true)).IsLiteral()); - MOZ_RELEASE_ASSERT(!BSV(FakeNsTString(nullptr, 0, true)).IsReference()); - - // FakeNsTString to a literal empty string. - MOZ_RELEASE_ASSERT(BSV(FakeNsTString(empty, 0, true)).Data()); - MOZ_RELEASE_ASSERT(BSV(FakeNsTString(empty, 0, true)).Data()[0] == - CHAR('\0')); - MOZ_RELEASE_ASSERT(BSV(FakeNsTString(empty, 0, true)).Length() == 0); - MOZ_RELEASE_ASSERT(BSV(FakeNsTString(empty, 0, true)).IsLiteral()); - MOZ_RELEASE_ASSERT(!BSV(FakeNsTString(empty, 0, true)).IsReference()); - - // FakeNsTString to a literal non-empty string. - MOZ_RELEASE_ASSERT(BSV(FakeNsTString(hi, 2, true)).Data()); - MOZ_RELEASE_ASSERT(BSV(FakeNsTString(hi, 2, true)).Data()[0] == CHAR('h')); - MOZ_RELEASE_ASSERT(BSV(FakeNsTString(hi, 2, true)).Data()[1] == CHAR('i')); - MOZ_RELEASE_ASSERT(BSV(FakeNsTString(hi, 2, true)).Data()[2] == CHAR('\0')); - MOZ_RELEASE_ASSERT(BSV(FakeNsTString(hi, 2, true)).Length() == 2); - MOZ_RELEASE_ASSERT(BSV(FakeNsTString(hi, 2, true)).IsLiteral()); - MOZ_RELEASE_ASSERT(!BSV(FakeNsTString(hi, 2, true)).IsReference()); - - // FakeNsTString to a non-literal non-empty string. - MOZ_RELEASE_ASSERT(BSV(FakeNsTString(hi, 2, false)).Data()); - MOZ_RELEASE_ASSERT(BSV(FakeNsTString(hi, 2, false)).Data()[0] == CHAR('h')); - MOZ_RELEASE_ASSERT(BSV(FakeNsTString(hi, 2, false)).Data()[1] == CHAR('i')); - MOZ_RELEASE_ASSERT(BSV(FakeNsTString(hi, 2, false)).Data()[2] == CHAR('\0')); - MOZ_RELEASE_ASSERT(BSV(FakeNsTString(hi, 2, false)).Length() == 2); - MOZ_RELEASE_ASSERT(!BSV(FakeNsTString(hi, 2, false)).IsLiteral()); - MOZ_RELEASE_ASSERT(BSV(FakeNsTString(hi, 2, false)).IsReference()); + // FakeNsCString to nullptr. + MOZ_RELEASE_ASSERT(BS8V(FakeNsCString(nullptr, 0, true)).Data()); + MOZ_RELEASE_ASSERT(BS8V(FakeNsCString(nullptr, 0, true)).Data()[0] == '\0'); + MOZ_RELEASE_ASSERT(BS8V(FakeNsCString(nullptr, 0, true)).Length() == 0); + MOZ_RELEASE_ASSERT(BS8V(FakeNsCString(nullptr, 0, true)).IsLiteral()); + MOZ_RELEASE_ASSERT(!BS8V(FakeNsCString(nullptr, 0, true)).IsReference()); + + // FakeNsCString to a literal empty string. + MOZ_RELEASE_ASSERT(BS8V(FakeNsCString("", 0, true)).Data()); + MOZ_RELEASE_ASSERT(BS8V(FakeNsCString("", 0, true)).Data()[0] == '\0'); + MOZ_RELEASE_ASSERT(BS8V(FakeNsCString("", 0, true)).Length() == 0); + MOZ_RELEASE_ASSERT(BS8V(FakeNsCString("", 0, true)).IsLiteral()); + MOZ_RELEASE_ASSERT(!BS8V(FakeNsCString("", 0, true)).IsReference()); + + // FakeNsCString to a literal non-empty string. + MOZ_RELEASE_ASSERT(BS8V(FakeNsCString("hi", 2, true)).Data()); + MOZ_RELEASE_ASSERT(BS8V(FakeNsCString("hi", 2, true)).Data()[0] == 'h'); + MOZ_RELEASE_ASSERT(BS8V(FakeNsCString("hi", 2, true)).Data()[1] == 'i'); + MOZ_RELEASE_ASSERT(BS8V(FakeNsCString("hi", 2, true)).Data()[2] == '\0'); + MOZ_RELEASE_ASSERT(BS8V(FakeNsCString("hi", 2, true)).Length() == 2); + MOZ_RELEASE_ASSERT(BS8V(FakeNsCString("hi", 2, true)).IsLiteral()); + MOZ_RELEASE_ASSERT(!BS8V(FakeNsCString("hi", 2, true)).IsReference()); + + // FakeNsCString to a non-literal non-empty string. + MOZ_RELEASE_ASSERT(BS8V(FakeNsCString("hi", 2, false)).Data()); + MOZ_RELEASE_ASSERT(BS8V(FakeNsCString("hi", 2, false)).Data()[0] == 'h'); + MOZ_RELEASE_ASSERT(BS8V(FakeNsCString("hi", 2, false)).Data()[1] == 'i'); + MOZ_RELEASE_ASSERT(BS8V(FakeNsCString("hi", 2, false)).Data()[2] == '\0'); + MOZ_RELEASE_ASSERT(BS8V(FakeNsCString("hi", 2, false)).Length() == 2); + MOZ_RELEASE_ASSERT(!BS8V(FakeNsCString("hi", 2, false)).IsLiteral()); + MOZ_RELEASE_ASSERT(BS8V(FakeNsCString("hi", 2, false)).IsReference()); // Serialization and deserialization (with ownership). constexpr size_t bufferMaxSize = 1024; @@ -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 d327316b02ed7..a16f2d7a4fdfe 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 430b8741d0db5..0311ba7cb058e 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 70f91c057dc87..33582d2be6f64 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 35e9d6985a889..be37f78f38a0c 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 349bbf337ea62..1bc5ad357da4b 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 d1f77f27c739e..fe8f3f8c44d8d 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; - - 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); - }); - } + // 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>(); + + 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. - 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); + default: + break; // Entry not fully read. } + + aER.SetRemainingBytes(0); }); } diff --git a/tools/profiler/core/ProfilerMarkerPayload.cpp b/tools/profiler/core/ProfilerMarkerPayload.cpp new file mode 100644 index 0000000000000..5c4842d48d7c2 --- /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 42501aec629f1..a0cb802c91b1b 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 51f6425ba8d91..bdc1008ed4607 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 7a117ec15a8c9..70e97e196c134 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 9fbe281ccaecf..e2279e614c3a7 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); } + // 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, 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)); +} - 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"); - } +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 (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{}; - } + if (!profiler_can_accept_markers()) { + return; + } - 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(""); - } +#ifdef DEBUG + auto checkThreadId = [](int aThreadId, const PSAutoLock& aLock) { + if (!ActivePS::Exists(aLock)) { + return; } - 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(""); + // 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"); }; - 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()); + 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); } -bool profiler_add_native_allocation_marker(int64_t aSize, +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 72f85845ae5d3..a2626921133f4 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 2912101360848..135291898efc5 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 0000000000000..d09c41bc326c3 --- /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 347f58839ce27..5269b9b4a2837 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 0c077f1fb07ce..353ed8f20f412 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 ff302f9880d26..035ec110a99b7 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() */); - - 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"))); - - 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 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))); + } + + { + 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))); + } + + { + 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 == "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 == "Load 1: http://mozilla.org/") { + } 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 e4df129787d30..05afbdb5717d4 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 1ba3f530e5466..5d82717f063ee 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 409e23ecfbc0f..9c5868af0ce7c 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 }