1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set expandtab ts=4 sw=2 sts=2 cin: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "NetworkMarker.h"
9 #include "HttpBaseChannel.h"
10 #include "nsIChannelEventSink.h"
11 #include "mozilla/Perfetto.h"
13 namespace mozilla::net
{
14 struct NetworkMarker
{
15 static constexpr Span
<const char> MarkerTypeName() {
16 return MakeStringSpan("Network");
18 static void StreamJSONMarkerData(
19 baseprofiler::SpliceableJSONWriter
& aWriter
, mozilla::TimeStamp aStart
,
20 mozilla::TimeStamp aEnd
, int64_t aID
, const ProfilerString8View
& aURI
,
21 const ProfilerString8View
& aRequestMethod
, NetworkLoadType aType
,
22 int32_t aPri
, int64_t aCount
, net::CacheDisposition aCacheDisposition
,
23 bool aIsPrivateBrowsing
, const net::TimingStruct
& aTimings
,
24 const ProfilerString8View
& aRedirectURI
,
25 const ProfilerString8View
& aContentType
, uint32_t aRedirectFlags
,
26 int64_t aRedirectChannelId
) {
27 // This payload still streams a startTime and endTime property because it
28 // made the migration to MarkerTiming on the front-end easier.
29 aWriter
.TimeProperty("startTime", aStart
);
30 aWriter
.TimeProperty("endTime", aEnd
);
32 aWriter
.IntProperty("id", aID
);
33 aWriter
.StringProperty("status", GetNetworkState(aType
));
34 if (Span
<const char> cacheString
= GetCacheState(aCacheDisposition
);
35 !cacheString
.IsEmpty()) {
36 aWriter
.StringProperty("cache", cacheString
);
38 aWriter
.IntProperty("pri", aPri
);
40 aWriter
.IntProperty("count", aCount
);
42 if (aURI
.Length() != 0) {
43 aWriter
.StringProperty("URI", aURI
);
45 if (aRedirectURI
.Length() != 0) {
46 aWriter
.StringProperty("RedirectURI", aRedirectURI
);
47 aWriter
.StringProperty("redirectType", getRedirectType(aRedirectFlags
));
49 "isHttpToHttpsRedirect",
50 aRedirectFlags
& nsIChannelEventSink::REDIRECT_STS_UPGRADE
);
52 if (aRedirectChannelId
!= 0) {
53 aWriter
.IntProperty("redirectId", aRedirectChannelId
);
57 aWriter
.StringProperty("requestMethod", aRequestMethod
);
59 if (aContentType
.Length() != 0) {
60 aWriter
.StringProperty("contentType", aContentType
);
62 aWriter
.NullProperty("contentType");
65 if (aIsPrivateBrowsing
) {
66 aWriter
.BoolProperty("isPrivateBrowsing", aIsPrivateBrowsing
);
69 if (aType
!= NetworkLoadType::LOAD_START
) {
70 aWriter
.TimeProperty("domainLookupStart", aTimings
.domainLookupStart
);
71 aWriter
.TimeProperty("domainLookupEnd", aTimings
.domainLookupEnd
);
72 aWriter
.TimeProperty("connectStart", aTimings
.connectStart
);
73 aWriter
.TimeProperty("tcpConnectEnd", aTimings
.tcpConnectEnd
);
74 aWriter
.TimeProperty("secureConnectionStart",
75 aTimings
.secureConnectionStart
);
76 aWriter
.TimeProperty("connectEnd", aTimings
.connectEnd
);
77 aWriter
.TimeProperty("requestStart", aTimings
.requestStart
);
78 aWriter
.TimeProperty("responseStart", aTimings
.responseStart
);
79 aWriter
.TimeProperty("responseEnd", aTimings
.responseEnd
);
82 static MarkerSchema
MarkerTypeDisplay() {
83 return MarkerSchema::SpecialFrontendLocation
{};
86 static Span
<const char> GetNetworkState(NetworkLoadType aType
) {
88 case NetworkLoadType::LOAD_START
:
89 return MakeStringSpan("STATUS_START");
90 case NetworkLoadType::LOAD_STOP
:
91 return MakeStringSpan("STATUS_STOP");
92 case NetworkLoadType::LOAD_REDIRECT
:
93 return MakeStringSpan("STATUS_REDIRECT");
94 case NetworkLoadType::LOAD_CANCEL
:
95 return MakeStringSpan("STATUS_CANCEL");
97 MOZ_ASSERT(false, "Unexpected NetworkLoadType enum value.");
98 return MakeStringSpan("");
102 static Span
<const char> GetCacheState(
103 net::CacheDisposition aCacheDisposition
) {
104 switch (aCacheDisposition
) {
105 case net::kCacheUnresolved
:
106 return MakeStringSpan("Unresolved");
108 return MakeStringSpan("Hit");
109 case net::kCacheHitViaReval
:
110 return MakeStringSpan("HitViaReval");
111 case net::kCacheMissedViaReval
:
112 return MakeStringSpan("MissedViaReval");
113 case net::kCacheMissed
:
114 return MakeStringSpan("Missed");
115 case net::kCacheUnknown
:
116 return MakeStringSpan("");
118 MOZ_ASSERT(false, "Unexpected CacheDisposition enum value.");
119 return MakeStringSpan("");
123 static Span
<const char> getRedirectType(uint32_t aRedirectFlags
) {
124 MOZ_ASSERT(aRedirectFlags
!= 0, "aRedirectFlags should be non-zero");
125 if (aRedirectFlags
& nsIChannelEventSink::REDIRECT_TEMPORARY
) {
126 return MakeStringSpan("Temporary");
128 if (aRedirectFlags
& nsIChannelEventSink::REDIRECT_PERMANENT
) {
129 return MakeStringSpan("Permanent");
131 if (aRedirectFlags
& nsIChannelEventSink::REDIRECT_INTERNAL
) {
132 return MakeStringSpan("Internal");
134 MOZ_ASSERT(false, "Couldn't find a redirect type from aRedirectFlags");
135 return MakeStringSpan("");
138 } // namespace mozilla::net
141 // Define a specialization for NetworkMarker since the payloads are
142 // not trivial to translate directly.
144 void EmitPerfettoTrackEvent
<mozilla::net::NetworkMarker
, mozilla::TimeStamp
,
145 mozilla::TimeStamp
, int64_t, nsAutoCStringN
<2048>,
146 nsACString
, mozilla::net::NetworkLoadType
, int32_t,
147 int64_t, mozilla::net::CacheDisposition
, bool,
148 mozilla::net::TimingStruct
, nsAutoCString
,
149 mozilla::ProfilerString8View
, uint32_t, uint64_t>(
150 const mozilla::ProfilerString8View
& aName
,
151 const mozilla::MarkerCategory
& aCategory
,
152 const mozilla::MarkerOptions
& aOptions
,
153 mozilla::net::NetworkMarker aMarkerType
, const mozilla::TimeStamp
& aStart
,
154 const mozilla::TimeStamp
& aEnd
, const int64_t& aID
,
155 const nsAutoCStringN
<2048>& aURI
, const nsACString
& aRequestMethod
,
156 const mozilla::net::NetworkLoadType
& aType
, const int32_t& aPri
,
157 const int64_t& aCount
,
158 const mozilla::net::CacheDisposition
& aCacheDisposition
,
159 const bool& aIsPrivateBrowsing
, const mozilla::net::TimingStruct
& aTimings
,
160 const nsAutoCString
& aRedirectURI
,
161 const mozilla::ProfilerString8View
& aContentType
,
162 const uint32_t& aRedirectFlags
, const uint64_t& aRedirectChannelId
) {
163 MOZ_ASSERT(!aOptions
.IsTimingUnspecified(),
164 "Timing should be properly defined.");
165 const char* nameStr
= aName
.StringView().data();
170 mozilla::TimeStamp startTime
, endTime
;
171 startTime
= aOptions
.Timing().StartTime();
172 endTime
= aOptions
.Timing().EndTime();
174 perfetto::DynamicString name
{nameStr
};
175 perfetto::DynamicCategory category
{"LOAD"};
178 aOptions
.Timing().MarkerPhase() == mozilla::MarkerTiming::Phase::Interval
,
179 "Expecting an interval phase only.");
181 // Create a unique id for each marker.
182 mozilla::HashNumber hash
=
183 mozilla::HashStringKnownLength(nameStr
, aName
.StringView().length());
184 hash
= mozilla::AddToHash(hash
,
185 startTime
.RawClockMonotonicNanosecondsSinceBoot());
187 mozilla::AddToHash(hash
, endTime
.RawClockMonotonicNanosecondsSinceBoot());
188 perfetto::Track
track(hash
);
190 auto desc
= track
.Serialize();
191 desc
.set_name(nameStr
);
192 perfetto::TrackEvent::SetTrackDescriptor(track
, desc
);
194 PERFETTO_TRACE_EVENT_BEGIN(category
, name
, track
, startTime
);
195 PERFETTO_TRACE_EVENT_END(
196 category
, track
, endTime
, [&](perfetto::EventContext ctx
) {
197 auto* urlArg
= ctx
.event()->add_debug_annotations();
198 urlArg
->set_name("url");
199 urlArg
->set_string_value(aURI
.get());
201 auto* reqMethodArg
= ctx
.event()->add_debug_annotations();
202 reqMethodArg
->set_name("requestMethod");
203 reqMethodArg
->set_string_value(nsAutoCString(aRequestMethod
).get());
205 auto* statusArg
= ctx
.event()->add_debug_annotations();
206 statusArg
->set_name("status");
207 statusArg
->set_string_value(
208 mozilla::net::NetworkMarker::GetNetworkState(aType
).data());
210 auto* cacheArg
= ctx
.event()->add_debug_annotations();
211 cacheArg
->set_name("cache");
212 cacheArg
->set_string_value(
213 mozilla::net::NetworkMarker::GetCacheState(aCacheDisposition
)
216 if (aContentType
.Length() != 0) {
217 auto* contentTypeArg
= ctx
.event()->add_debug_annotations();
218 contentTypeArg
->set_name("contentType");
219 contentTypeArg
->set_string_value(aContentType
.StringView().data());
222 auto* priorityArg
= ctx
.event()->add_debug_annotations();
223 priorityArg
->set_name("priority");
224 priorityArg
->set_int_value(aPri
);
227 auto* countArg
= ctx
.event()->add_debug_annotations();
228 countArg
->set_name("count");
229 countArg
->set_int_value(aCount
);
232 if (aRedirectURI
.Length() != 0) {
233 auto* redirectURIArg
= ctx
.event()->add_debug_annotations();
234 redirectURIArg
->set_name("RedirectURI");
235 redirectURIArg
->set_string_value(aRedirectURI
.get());
237 auto* redirectTypeArg
= ctx
.event()->add_debug_annotations();
238 redirectTypeArg
->set_name("redirectType");
239 redirectTypeArg
->set_string_value(
240 mozilla::net::NetworkMarker::getRedirectType(aRedirectFlags
)
243 auto* httpToHttpsArg
= ctx
.event()->add_debug_annotations();
244 httpToHttpsArg
->set_name("isHttpToHttpsRedirect");
245 httpToHttpsArg
->set_bool_value(
246 aRedirectFlags
& nsIChannelEventSink::REDIRECT_STS_UPGRADE
);
248 if (aRedirectChannelId
!= 0) {
249 auto* redirectIdArg
= ctx
.event()->add_debug_annotations();
250 redirectIdArg
->set_name("redirectId");
251 redirectIdArg
->set_int_value(aRedirectChannelId
);
255 if (aIsPrivateBrowsing
) {
256 auto* privateBrowsingArg
= ctx
.event()->add_debug_annotations();
257 privateBrowsingArg
->set_name("isPrivateBrowsing");
258 privateBrowsingArg
->set_bool_value(aIsPrivateBrowsing
);
261 if (aType
!= mozilla::net::NetworkLoadType::LOAD_START
) {
262 mozilla::TimeStamp startTime
;
263 auto addNetworkTimingAnnotation
=
264 [&startTime
, &ctx
, &aStart
](const mozilla::TimeStamp
& endTime
,
267 // If startTime is not defined, redefine the name of this to
268 // "Waiting for Socket Thread".
270 name
= "Waiting for Socket Thread (us)";
273 mozilla::TimeDuration duration
= endTime
- startTime
;
274 auto* arg
= ctx
.event()->add_debug_annotations();
276 arg
->set_int_value(duration
.ToMilliseconds());
281 addNetworkTimingAnnotation(aTimings
.domainLookupStart
,
282 "Waiting for Socket Thread");
283 addNetworkTimingAnnotation(aTimings
.domainLookupEnd
, "DNS Request");
284 addNetworkTimingAnnotation(aTimings
.connectStart
,
285 "After DNS Request");
286 addNetworkTimingAnnotation(aTimings
.tcpConnectEnd
, "TCP connection");
287 addNetworkTimingAnnotation(aTimings
.secureConnectionStart
,
288 "After TCP connection");
289 addNetworkTimingAnnotation(aTimings
.connectEnd
,
290 "Establishing TLS session");
291 addNetworkTimingAnnotation(aTimings
.requestStart
,
292 "Waiting for HTTP request");
293 addNetworkTimingAnnotation(aTimings
.responseStart
,
294 "HTTP request and waiting for response");
295 addNetworkTimingAnnotation(aTimings
.responseEnd
, "HTTP response");
296 addNetworkTimingAnnotation(aEnd
, "Waiting to transmit the response");
300 #endif // MOZ_PERFETTO
302 namespace mozilla::net
{
303 static constexpr net::TimingStruct scEmptyNetTimingStruct
;
305 void profiler_add_network_marker(
306 nsIURI
* aURI
, const nsACString
& aRequestMethod
, int32_t aPriority
,
307 uint64_t aChannelId
, NetworkLoadType aType
, mozilla::TimeStamp aStart
,
308 mozilla::TimeStamp aEnd
, int64_t aCount
,
309 mozilla::net::CacheDisposition aCacheDisposition
, uint64_t aInnerWindowID
,
310 bool aIsPrivateBrowsing
, const mozilla::net::TimingStruct
* aTimings
,
311 UniquePtr
<ProfileChunkedBuffer
> aSource
,
312 const Maybe
<nsDependentCString
>& aContentType
, nsIURI
* aRedirectURI
,
313 uint32_t aRedirectFlags
, uint64_t aRedirectChannelId
) {
314 if (!profiler_thread_is_being_profiled_for_markers()) {
318 nsAutoCStringN
<2048> name
;
319 name
.AppendASCII("Load ");
320 // top 32 bits are process id of the load
321 name
.AppendInt(aChannelId
& 0xFFFFFFFFu
);
323 // These can do allocations/frees/etc; avoid if not active
324 nsAutoCStringN
<2048> spec
;
326 aURI
->GetAsciiSpec(spec
);
327 name
.AppendASCII(": ");
331 nsAutoCString redirect_spec
;
333 aRedirectURI
->GetAsciiSpec(redirect_spec
);
337 name
, geckoprofiler::category::NETWORK
,
338 {MarkerTiming::Interval(aStart
, aEnd
),
339 MarkerStack::TakeBacktrace(std::move(aSource
)),
340 MarkerInnerWindowId(aInnerWindowID
)},
341 NetworkMarker
{}, aStart
, aEnd
, static_cast<int64_t>(aChannelId
), spec
,
342 aRequestMethod
, aType
, aPriority
, aCount
, aCacheDisposition
,
343 aIsPrivateBrowsing
, aTimings
? *aTimings
: scEmptyNetTimingStruct
,
345 aContentType
? ProfilerString8View(*aContentType
) : ProfilerString8View(),
346 aRedirectFlags
, aRedirectChannelId
);
348 } // namespace mozilla::net