Bug 1909986 - For sidebar revamp, only show chatbot entrypoints (context menu, shortc...
[gecko.git] / netwerk / protocol / http / NetworkMarker.cpp
blobe65ca734451bed750e6791381c0327a7840bd2c6
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);
39 if (aCount > 0) {
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));
48 aWriter.BoolProperty(
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);
61 } else {
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) {
87 switch (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");
96 default:
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");
107 case net::kCacheHit:
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("");
117 default:
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
140 #ifdef MOZ_PERFETTO
141 // Define a specialization for NetworkMarker since the payloads are
142 // not trivial to translate directly.
143 template <>
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();
166 if (!nameStr) {
167 return;
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"};
177 MOZ_ASSERT(
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());
186 hash =
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)
214 .data());
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);
226 if (aCount > 0) {
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)
241 .data());
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,
265 const char* name) {
266 if (endTime) {
267 // If startTime is not defined, redefine the name of this to
268 // "Waiting for Socket Thread".
269 if (!startTime) {
270 name = "Waiting for Socket Thread (us)";
271 startTime = aStart;
273 mozilla::TimeDuration duration = endTime - startTime;
274 auto* arg = ctx.event()->add_debug_annotations();
275 arg->set_name(name);
276 arg->set_int_value(duration.ToMilliseconds());
277 startTime = endTime;
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()) {
315 return;
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;
325 if (aURI) {
326 aURI->GetAsciiSpec(spec);
327 name.AppendASCII(": ");
328 name.Append(spec);
331 nsAutoCString redirect_spec;
332 if (aRedirectURI) {
333 aRedirectURI->GetAsciiSpec(redirect_spec);
336 profiler_add_marker(
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,
344 redirect_spec,
345 aContentType ? ProfilerString8View(*aContentType) : ProfilerString8View(),
346 aRedirectFlags, aRedirectChannelId);
348 } // namespace mozilla::net