tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

NetworkMarker.cpp (18737B)


      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/. */
      6 
      7 #include "NetworkMarker.h"
      8 
      9 #include "HttpBaseChannel.h"
     10 #include "nsIChannelEventSink.h"
     11 #include "mozilla/Perfetto.h"
     12 #include "mozilla/ErrorNames.h"
     13 #include "nsHttpHandler.h"
     14 #include "nsIClassOfService.h"
     15 
     16 namespace mozilla::net {
     17 struct NetworkMarker {
     18  static constexpr Span<const char> MarkerTypeName() {
     19    return MakeStringSpan("Network");
     20  }
     21  static void StreamJSONMarkerData(
     22      baseprofiler::SpliceableJSONWriter& aWriter, mozilla::TimeStamp aStart,
     23      mozilla::TimeStamp aEnd, int64_t aID, const ProfilerString8View& aURI,
     24      const ProfilerString8View& aRequestMethod, NetworkLoadType aType,
     25      int32_t aPri, int64_t aCount,
     26      nsICacheInfoChannel::CacheDisposition aCacheDisposition,
     27      bool aIsPrivateBrowsing, const net::TimingStruct& aTimings,
     28      const ProfilerString8View& aRedirectURI,
     29      const ProfilerString8View& aContentType, uint32_t aRedirectFlags,
     30      int64_t aRedirectChannelId, uint32_t aClassOfServiceFlags,
     31      bool aClassOfServiceIncremental, nsresult aRequestStatus,
     32      const mozilla::Maybe<mozilla::net::HttpVersion> aHttpVersion,
     33      mozilla::Maybe<uint32_t> aResponseStatus) {
     34    // This payload still streams a startTime and endTime property because it
     35    // made the migration to MarkerTiming on the front-end easier.
     36    aWriter.TimeProperty("startTime", aStart);
     37    aWriter.TimeProperty("endTime", aEnd);
     38 
     39    aWriter.IntProperty("id", aID);
     40    aWriter.StringProperty("status", GetNetworkState(aType));
     41 
     42    // Bug 1919148 - Moved aClassOfServiceStr here to ensure that we call
     43    // aWriter.StringProperty before the lifetime of nsAutoCString ends
     44    nsAutoCString aClassOfServiceStr;
     45    GetClassOfService(aClassOfServiceStr, aClassOfServiceFlags);
     46    MOZ_ASSERT(aClassOfServiceStr.Length() > 0,
     47               "aClassOfServiceStr should be set after GetClassOfService");
     48    aWriter.StringProperty("classOfService",
     49                           MakeStringSpan(aClassOfServiceStr.get()));
     50 
     51    uint8_t urgency =
     52        nsHttpHandler::UrgencyFromCoSFlags(aClassOfServiceFlags, aPri);
     53    nsAutoCString priorityHeader;
     54    priorityHeader.AppendPrintf("u=%d", urgency);
     55    if (aClassOfServiceIncremental) {
     56      priorityHeader.Append(", i");
     57    }
     58    aWriter.StringProperty("priorityHeader",
     59                           MakeStringSpan(priorityHeader.get()));
     60 
     61    nsAutoCString aRequestStatusStr;
     62    GetErrorName(aRequestStatus, aRequestStatusStr);
     63    aWriter.StringProperty("requestStatus",
     64                           MakeStringSpan(aRequestStatusStr.get()));
     65 
     66    if (Span<const char> cacheString = GetCacheState(aCacheDisposition);
     67        !cacheString.IsEmpty()) {
     68      aWriter.StringProperty("cache", cacheString);
     69    }
     70    aWriter.IntProperty("pri", aPri);
     71    if (aCount > 0) {
     72      aWriter.IntProperty("count", aCount);
     73    }
     74    if (aURI.Length() != 0) {
     75      aWriter.StringProperty("URI", aURI);
     76    }
     77    if (aRedirectURI.Length() != 0) {
     78      aWriter.StringProperty("RedirectURI", aRedirectURI);
     79      aWriter.StringProperty("redirectType", getRedirectType(aRedirectFlags));
     80      aWriter.BoolProperty(
     81          "isHttpToHttpsRedirect",
     82          aRedirectFlags & nsIChannelEventSink::REDIRECT_STS_UPGRADE);
     83 
     84      if (aRedirectChannelId != 0) {
     85        aWriter.IntProperty("redirectId", aRedirectChannelId);
     86      }
     87    }
     88 
     89    aWriter.StringProperty("requestMethod", aRequestMethod);
     90    if (aHttpVersion) {
     91      aWriter.StringProperty("httpVersion",
     92                             ProfilerString8View::WrapNullTerminatedString(
     93                                 nsHttp::GetProtocolVersion(*aHttpVersion)));
     94    }
     95    if (aResponseStatus) {
     96      aWriter.IntProperty("responseStatus", *aResponseStatus);
     97    }
     98 
     99    if (aContentType.Length() != 0) {
    100      aWriter.StringProperty("contentType", aContentType);
    101    } else {
    102      aWriter.NullProperty("contentType");
    103    }
    104 
    105    if (aIsPrivateBrowsing) {
    106      aWriter.BoolProperty("isPrivateBrowsing", aIsPrivateBrowsing);
    107    }
    108 
    109    if (aType != NetworkLoadType::LOAD_START) {
    110      aWriter.TimeProperty("domainLookupStart", aTimings.domainLookupStart);
    111      aWriter.TimeProperty("domainLookupEnd", aTimings.domainLookupEnd);
    112      aWriter.TimeProperty("connectStart", aTimings.connectStart);
    113      aWriter.TimeProperty("tcpConnectEnd", aTimings.tcpConnectEnd);
    114      aWriter.TimeProperty("secureConnectionStart",
    115                           aTimings.secureConnectionStart);
    116      aWriter.TimeProperty("connectEnd", aTimings.connectEnd);
    117      aWriter.TimeProperty("requestStart", aTimings.requestStart);
    118      aWriter.TimeProperty("responseStart", aTimings.responseStart);
    119      aWriter.TimeProperty("responseEnd", aTimings.responseEnd);
    120    }
    121  }
    122  static MarkerSchema MarkerTypeDisplay() {
    123    return MarkerSchema::SpecialFrontendLocation{};
    124  }
    125 
    126  static Span<const char> GetNetworkState(NetworkLoadType aType) {
    127    switch (aType) {
    128      case NetworkLoadType::LOAD_START:
    129        return MakeStringSpan("STATUS_START");
    130      case NetworkLoadType::LOAD_STOP:
    131        return MakeStringSpan("STATUS_STOP");
    132      case NetworkLoadType::LOAD_REDIRECT:
    133        return MakeStringSpan("STATUS_REDIRECT");
    134      case NetworkLoadType::LOAD_CANCEL:
    135        return MakeStringSpan("STATUS_CANCEL");
    136      default:
    137        MOZ_ASSERT(false, "Unexpected NetworkLoadType enum value.");
    138        return MakeStringSpan("");
    139    }
    140  }
    141 
    142  static Span<const char> GetCacheState(
    143      nsICacheInfoChannel::CacheDisposition aCacheDisposition) {
    144    switch (aCacheDisposition) {
    145      case nsICacheInfoChannel::kCacheUnresolved:
    146        return MakeStringSpan("Unresolved");
    147      case nsICacheInfoChannel::kCacheHit:
    148        return MakeStringSpan("Hit");
    149      case nsICacheInfoChannel::kCacheHitViaReval:
    150        return MakeStringSpan("HitViaReval");
    151      case nsICacheInfoChannel::kCacheMissedViaReval:
    152        return MakeStringSpan("MissedViaReval");
    153      case nsICacheInfoChannel::kCacheMissed:
    154        return MakeStringSpan("Missed");
    155      case nsICacheInfoChannel::kCacheUnknown:
    156        return MakeStringSpan("");
    157      default:
    158        MOZ_ASSERT(false, "Unexpected CacheDisposition enum value.");
    159        return MakeStringSpan("");
    160    }
    161  }
    162 
    163  static Span<const char> getRedirectType(uint32_t aRedirectFlags) {
    164    MOZ_ASSERT(aRedirectFlags != 0, "aRedirectFlags should be non-zero");
    165    if (aRedirectFlags & nsIChannelEventSink::REDIRECT_TEMPORARY) {
    166      return MakeStringSpan("Temporary");
    167    }
    168    if (aRedirectFlags & nsIChannelEventSink::REDIRECT_PERMANENT) {
    169      return MakeStringSpan("Permanent");
    170    }
    171    if (aRedirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL) {
    172      return MakeStringSpan("Internal");
    173    }
    174    MOZ_ASSERT(false, "Couldn't find a redirect type from aRedirectFlags");
    175    return MakeStringSpan("");
    176  }
    177 
    178  // Update an empty string aClassOfServiceStr based on aClassOfServiceFlag
    179  static void GetClassOfService(nsAutoCString& aClassOfServiceStr,
    180                                uint32_t aClassOfServiceFlag) {
    181    MOZ_ASSERT(aClassOfServiceStr.IsEmpty(),
    182               "Flags should not be appended to aClassOfServiceStr before "
    183               "calling GetClassOfService");
    184 
    185    if (aClassOfServiceFlag & nsIClassOfService::Leader) {
    186      aClassOfServiceStr.Append("Leader | ");
    187    }
    188    if (aClassOfServiceFlag & nsIClassOfService::Follower) {
    189      aClassOfServiceStr.Append("Follower | ");
    190    }
    191    if (aClassOfServiceFlag & nsIClassOfService::Speculative) {
    192      aClassOfServiceStr.Append("Speculative | ");
    193    }
    194    if (aClassOfServiceFlag & nsIClassOfService::Background) {
    195      aClassOfServiceStr.Append("Background | ");
    196    }
    197    if (aClassOfServiceFlag & nsIClassOfService::Unblocked) {
    198      aClassOfServiceStr.Append("Unblocked | ");
    199    }
    200    if (aClassOfServiceFlag & nsIClassOfService::Throttleable) {
    201      aClassOfServiceStr.Append("Throttleable | ");
    202    }
    203    if (aClassOfServiceFlag & nsIClassOfService::UrgentStart) {
    204      aClassOfServiceStr.Append("UrgentStart | ");
    205    }
    206    if (aClassOfServiceFlag & nsIClassOfService::DontThrottle) {
    207      aClassOfServiceStr.Append("DontThrottle | ");
    208    }
    209    if (aClassOfServiceFlag & nsIClassOfService::Tail) {
    210      aClassOfServiceStr.Append("Tail | ");
    211    }
    212    if (aClassOfServiceFlag & nsIClassOfService::TailAllowed) {
    213      aClassOfServiceStr.Append("TailAllowed | ");
    214    }
    215    if (aClassOfServiceFlag & nsIClassOfService::TailForbidden) {
    216      aClassOfServiceStr.Append("TailForbidden | ");
    217    }
    218 
    219    if (aClassOfServiceStr.IsEmpty()) {
    220      aClassOfServiceStr.Append("Unset");
    221      return;
    222    }
    223 
    224    MOZ_ASSERT(aClassOfServiceStr.Length() > 3,
    225               "aClassOfServiceStr must be at least 4 characters long to "
    226               "include two blank spaces and a '|' character.");
    227    // Remove the trailing '|'
    228    aClassOfServiceStr.Truncate(aClassOfServiceStr.Length() - 3);
    229  }
    230 };
    231 }  // namespace mozilla::net
    232 
    233 #ifdef MOZ_PERFETTO
    234 // Define a specialization for NetworkMarker since the payloads are
    235 // not trivial to translate directly.
    236 template <>
    237 void EmitPerfettoTrackEvent<mozilla::net::NetworkMarker, mozilla::TimeStamp,
    238                            mozilla::TimeStamp, int64_t, nsAutoCStringN<2048>,
    239                            nsACString, mozilla::net::NetworkLoadType, int32_t,
    240                            int64_t, nsICacheInfoChannel::CacheDisposition,
    241                            bool, mozilla::net::TimingStruct, nsAutoCString,
    242                            mozilla::ProfilerString8View, uint32_t, uint64_t>(
    243    const mozilla::ProfilerString8View& aName,
    244    const mozilla::MarkerCategory& aCategory,
    245    const mozilla::MarkerOptions& aOptions,
    246    mozilla::net::NetworkMarker aMarkerType, const mozilla::TimeStamp& aStart,
    247    const mozilla::TimeStamp& aEnd, const int64_t& aID,
    248    const nsAutoCStringN<2048>& aURI, const nsACString& aRequestMethod,
    249    const mozilla::net::NetworkLoadType& aType, const int32_t& aPri,
    250    const int64_t& aCount,
    251    const nsICacheInfoChannel::CacheDisposition& aCacheDisposition,
    252    const bool& aIsPrivateBrowsing, const mozilla::net::TimingStruct& aTimings,
    253    const nsAutoCString& aRedirectURI,
    254    const mozilla::ProfilerString8View& aContentType,
    255    const uint32_t& aRedirectFlags, const uint64_t& aRedirectChannelId) {
    256  MOZ_ASSERT(!aOptions.IsTimingUnspecified(),
    257             "Timing should be properly defined.");
    258  const char* nameStr = aName.StringView().data();
    259  if (!nameStr) {
    260    return;
    261  }
    262 
    263  mozilla::TimeStamp startTime, endTime;
    264  startTime = aOptions.Timing().StartTime();
    265  endTime = aOptions.Timing().EndTime();
    266 
    267  perfetto::DynamicString name{nameStr};
    268  perfetto::DynamicCategory category{"LOAD"};
    269 
    270  MOZ_ASSERT(
    271      aOptions.Timing().MarkerPhase() == mozilla::MarkerTiming::Phase::Interval,
    272      "Expecting an interval phase only.");
    273 
    274  // Create a unique id for each marker.
    275  mozilla::HashNumber hash =
    276      mozilla::HashStringKnownLength(nameStr, aName.StringView().length());
    277  hash = mozilla::AddToHash(hash,
    278                            startTime.RawClockMonotonicNanosecondsSinceBoot());
    279  hash =
    280      mozilla::AddToHash(hash, endTime.RawClockMonotonicNanosecondsSinceBoot());
    281  perfetto::Track track(hash);
    282 
    283  auto desc = track.Serialize();
    284  desc.set_name(nameStr);
    285  perfetto::TrackEvent::SetTrackDescriptor(track, desc);
    286 
    287  PERFETTO_TRACE_EVENT_BEGIN(category, name, track, startTime);
    288  PERFETTO_TRACE_EVENT_END(
    289      category, track, endTime, [&](perfetto::EventContext ctx) {
    290        auto* urlArg = ctx.event()->add_debug_annotations();
    291        urlArg->set_name("url");
    292        urlArg->set_string_value(aURI.get());
    293 
    294        auto* reqMethodArg = ctx.event()->add_debug_annotations();
    295        reqMethodArg->set_name("requestMethod");
    296        reqMethodArg->set_string_value(nsAutoCString(aRequestMethod).get());
    297 
    298        auto* statusArg = ctx.event()->add_debug_annotations();
    299        statusArg->set_name("status");
    300        statusArg->set_string_value(
    301            mozilla::net::NetworkMarker::GetNetworkState(aType).data());
    302 
    303        auto* cacheArg = ctx.event()->add_debug_annotations();
    304        cacheArg->set_name("cache");
    305        cacheArg->set_string_value(
    306            mozilla::net::NetworkMarker::GetCacheState(aCacheDisposition)
    307                .data());
    308 
    309        if (aContentType.Length() != 0) {
    310          auto* contentTypeArg = ctx.event()->add_debug_annotations();
    311          contentTypeArg->set_name("contentType");
    312          contentTypeArg->set_string_value(aContentType.StringView().data());
    313        }
    314 
    315        auto* priorityArg = ctx.event()->add_debug_annotations();
    316        priorityArg->set_name("priority");
    317        priorityArg->set_int_value(aPri);
    318 
    319        if (aCount > 0) {
    320          auto* countArg = ctx.event()->add_debug_annotations();
    321          countArg->set_name("count");
    322          countArg->set_int_value(aCount);
    323        }
    324 
    325        if (aRedirectURI.Length() != 0) {
    326          auto* redirectURIArg = ctx.event()->add_debug_annotations();
    327          redirectURIArg->set_name("RedirectURI");
    328          redirectURIArg->set_string_value(aRedirectURI.get());
    329 
    330          auto* redirectTypeArg = ctx.event()->add_debug_annotations();
    331          redirectTypeArg->set_name("redirectType");
    332          redirectTypeArg->set_string_value(
    333              mozilla::net::NetworkMarker::getRedirectType(aRedirectFlags)
    334                  .data());
    335 
    336          auto* httpToHttpsArg = ctx.event()->add_debug_annotations();
    337          httpToHttpsArg->set_name("isHttpToHttpsRedirect");
    338          httpToHttpsArg->set_bool_value(
    339              aRedirectFlags & nsIChannelEventSink::REDIRECT_STS_UPGRADE);
    340 
    341          if (aRedirectChannelId != 0) {
    342            auto* redirectIdArg = ctx.event()->add_debug_annotations();
    343            redirectIdArg->set_name("redirectId");
    344            redirectIdArg->set_int_value(aRedirectChannelId);
    345          }
    346        }
    347 
    348        if (aIsPrivateBrowsing) {
    349          auto* privateBrowsingArg = ctx.event()->add_debug_annotations();
    350          privateBrowsingArg->set_name("isPrivateBrowsing");
    351          privateBrowsingArg->set_bool_value(aIsPrivateBrowsing);
    352        }
    353 
    354        if (aType != mozilla::net::NetworkLoadType::LOAD_START) {
    355          mozilla::TimeStamp startTime;
    356          auto addNetworkTimingAnnotation =
    357              [&startTime, &ctx, &aStart](const mozilla::TimeStamp& endTime,
    358                                          const char* name) {
    359                if (endTime) {
    360                  // If startTime is not defined, redefine the name of this to
    361                  // "Waiting for Socket Thread".
    362                  if (!startTime) {
    363                    name = "Waiting for Socket Thread (us)";
    364                    startTime = aStart;
    365                  }
    366                  mozilla::TimeDuration duration = endTime - startTime;
    367                  auto* arg = ctx.event()->add_debug_annotations();
    368                  arg->set_name(name);
    369                  arg->set_int_value(duration.ToMilliseconds());
    370                  startTime = endTime;
    371                }
    372              };
    373 
    374          addNetworkTimingAnnotation(aTimings.domainLookupStart,
    375                                     "Waiting for Socket Thread");
    376          addNetworkTimingAnnotation(aTimings.domainLookupEnd, "DNS Request");
    377          addNetworkTimingAnnotation(aTimings.connectStart,
    378                                     "After DNS Request");
    379          addNetworkTimingAnnotation(aTimings.tcpConnectEnd, "TCP connection");
    380          addNetworkTimingAnnotation(aTimings.secureConnectionStart,
    381                                     "After TCP connection");
    382          addNetworkTimingAnnotation(aTimings.connectEnd,
    383                                     "Establishing TLS session");
    384          addNetworkTimingAnnotation(aTimings.requestStart,
    385                                     "Waiting for HTTP request");
    386          addNetworkTimingAnnotation(aTimings.responseStart,
    387                                     "HTTP request and waiting for response");
    388          addNetworkTimingAnnotation(aTimings.responseEnd, "HTTP response");
    389          addNetworkTimingAnnotation(aEnd, "Waiting to transmit the response");
    390        }
    391      });
    392 }
    393 #endif  // MOZ_PERFETTO
    394 
    395 namespace mozilla::net {
    396 static constexpr net::TimingStruct scEmptyNetTimingStruct;
    397 
    398 void profiler_add_network_marker(
    399    nsIURI* aURI, const nsACString& aRequestMethod, int32_t aPriority,
    400    uint64_t aChannelId, NetworkLoadType aType, mozilla::TimeStamp aStart,
    401    mozilla::TimeStamp aEnd, int64_t aCount,
    402    nsICacheInfoChannel::CacheDisposition aCacheDisposition,
    403    uint64_t aInnerWindowID, bool aIsPrivateBrowsing,
    404    nsIClassOfService* aClassOfService, nsresult aRequestStatus,
    405    const mozilla::net::TimingStruct* aTimings,
    406    UniquePtr<ProfileChunkedBuffer> aSource,
    407    const Maybe<mozilla::net::HttpVersion> aHttpVersion,
    408    const Maybe<uint32_t> aResponseStatus,
    409    const Maybe<nsDependentCString>& aContentType, nsIURI* aRedirectURI,
    410    uint32_t aRedirectFlags, uint64_t aRedirectChannelId) {
    411  if (!profiler_thread_is_being_profiled_for_markers()) {
    412    return;
    413  }
    414 
    415  nsAutoCStringN<2048> name;
    416  name.AppendASCII("Load ");
    417  // top 32 bits are process id of the load
    418  name.AppendInt(aChannelId & 0xFFFFFFFFu);
    419 
    420  // These can do allocations/frees/etc; avoid if not active
    421  nsAutoCStringN<2048> spec;
    422  if (aURI) {
    423    aURI->GetAsciiSpec(spec);
    424    name.AppendASCII(": ");
    425    name.Append(spec);
    426  }
    427 
    428  nsAutoCString redirect_spec;
    429  if (aRedirectURI) {
    430    aRedirectURI->GetAsciiSpec(redirect_spec);
    431  }
    432 
    433  uint32_t classOfServiceFlags = 0;
    434  bool classOfServiceIncremental = false;
    435  if (aClassOfService) {
    436    aClassOfService->GetClassFlags(&classOfServiceFlags);
    437    aClassOfService->GetIncremental(&classOfServiceIncremental);
    438  }
    439 
    440  profiler_add_marker(
    441      name, geckoprofiler::category::NETWORK,
    442      {MarkerTiming::Interval(aStart, aEnd),
    443       MarkerStack::TakeBacktrace(std::move(aSource)),
    444       MarkerInnerWindowId(aInnerWindowID)},
    445      NetworkMarker{}, aStart, aEnd, static_cast<int64_t>(aChannelId), spec,
    446      aRequestMethod, aType, aPriority, aCount, aCacheDisposition,
    447      aIsPrivateBrowsing, aTimings ? *aTimings : scEmptyNetTimingStruct,
    448      redirect_spec,
    449      aContentType ? ProfilerString8View(*aContentType) : ProfilerString8View(),
    450      aRedirectFlags, aRedirectChannelId, classOfServiceFlags,
    451      classOfServiceIncremental, aRequestStatus, aHttpVersion, aResponseStatus);
    452 }
    453 }  // namespace mozilla::net