Bug 1675409 - Migrated NetworkMarkerPayload to Markers 2.0 API - r=gregtatum,necko-reviewers,julienw

While migrating, profiler_add_network_marker was optimized to avoid some string allocations.

Differential Revision: https://phabricator.services.mozilla.com/D96040
This commit is contained in:
Gerald Squelart 2020-11-18 21:48:17 +00:00
parent 4d9cfb4113
commit 6e65d94961
9 changed files with 215 additions and 149 deletions

View File

@ -2458,7 +2458,7 @@ nsresult XMLHttpRequestMainThread::CreateChannel() {
rv = httpChannel->SetRequestMethod(mRequestMethod);
NS_ENSURE_SUCCESS(rv, rv);
httpChannel->SetSource(profiler_get_backtrace());
httpChannel->SetSource(profiler_capture_backtrace());
// Set the initiator type
nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(httpChannel));
@ -3240,7 +3240,8 @@ void XMLHttpRequestMainThread::SetOriginStack(
mOriginStack = std::move(aOriginStack);
}
void XMLHttpRequestMainThread::SetSource(UniqueProfilerBacktrace aSource) {
void XMLHttpRequestMainThread::SetSource(
UniquePtr<ProfileChunkedBuffer> aSource) {
if (!mChannel) {
return;
}

View File

@ -396,7 +396,7 @@ class XMLHttpRequestMainThread final : public XMLHttpRequest,
void SetOriginStack(UniquePtr<SerializedStackHolder> aOriginStack);
void SetSource(UniqueProfilerBacktrace aSource);
void SetSource(UniquePtr<ProfileChunkedBuffer> aSource);
virtual uint16_t ErrorCode() const override {
return static_cast<uint16_t>(mErrorLoad);

View File

@ -653,7 +653,7 @@ class OpenRunnable final : public WorkerThreadProxySyncRunnable {
// Remember the worker thread's stack when the XHR was opened for profiling
// purposes.
UniqueProfilerBacktrace mSource;
UniquePtr<ProfileChunkedBuffer> mSource;
public:
OpenRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
@ -664,7 +664,7 @@ class OpenRunnable final : public WorkerThreadProxySyncRunnable {
XMLHttpRequestResponseType aResponseType,
const nsString& aMimeTypeOverride,
UniquePtr<SerializedStackHolder> aOriginStack,
UniqueProfilerBacktrace aSource = nullptr)
UniquePtr<ProfileChunkedBuffer> 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_get_backtrace());
profiler_capture_backtrace());
++mProxy->mOpenCount;
runnable->Dispatch(Canceling, aRv);

View File

@ -248,7 +248,7 @@ class HttpBaseChannel : public nsHashPropertyBag,
using nsIClassifiedChannel::IsThirdPartyTrackingResource;
virtual void SetSource(UniqueProfilerBacktrace aSource) override {
virtual void SetSource(UniquePtr<ProfileChunkedBuffer> aSource) override {
mSource = std::move(aSource);
}
@ -756,7 +756,7 @@ class HttpBaseChannel : public nsHashPropertyBag,
Atomic<uint32_t, ReleaseAcquire> mThirdPartyClassificationFlags;
Atomic<uint32_t, ReleaseAcquire> mFlashPluginState;
UniqueProfilerBacktrace mSource;
UniquePtr<ProfileChunkedBuffer> mSource;
uint32_t mLoadFlags;
uint32_t mCaps;

View File

@ -501,6 +501,6 @@ interface nsIHttpChannel : nsIIdentChannel
in AString aContentType);
%{ C++
virtual void SetSource(UniqueProfilerBacktrace aSource) {}
virtual void SetSource(mozilla::UniquePtr<mozilla::ProfileChunkedBuffer> aSource) {}
%}
};

View File

@ -52,6 +52,7 @@
#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"
@ -5481,39 +5482,151 @@ 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,
UniqueProfilerBacktrace aSource,
UniquePtr<ProfileChunkedBuffer> aSource,
const Maybe<nsDependentCString>& aContentType) {
if (!profiler_can_accept_markers()) {
return;
}
// These do allocations/frees/etc; avoid if not active
nsAutoCString spec;
nsAutoCString redirect_spec;
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;
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<uint32_t>(aChannelId & 0xFFFFFFFF);
char name[2048];
SprintfLiteral(name, "Load %d: %s", id, PromiseFlatCString(spec).get());
AUTO_PROFILER_STATS(add_marker_with_NetworkMarkerPayload);
struct NetworkMarker {
static constexpr Span<const char> 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<const char> cacheString = GetCacheState(aCacheDisposition);
!cacheString.IsEmpty()) {
aWriter.StringProperty("cache", cacheString);
}
aWriter.IntProperty("pri", aPri);
if (aCount > 0) {
aWriter.IntProperty("count", aCount);
}
if (aURI.Length() != 0) {
aWriter.StringProperty("URI", aURI);
}
if (aRedirectURI.Length() != 0) {
aWriter.StringProperty("RedirectURI", aRedirectURI);
}
aWriter.StringProperty("requestMethod", aRequestMethod);
if (aContentType.Length() != 0) {
aWriter.StringProperty("contentType", aContentType);
} else {
aWriter.NullProperty("contentType");
}
if (aType != NetworkLoadType::LOAD_START) {
baseprofiler::WritePropertyTime(aWriter, "domainLookupStart",
aTimings.domainLookupStart);
baseprofiler::WritePropertyTime(aWriter, "domainLookupEnd",
aTimings.domainLookupEnd);
baseprofiler::WritePropertyTime(aWriter, "connectStart",
aTimings.connectStart);
baseprofiler::WritePropertyTime(aWriter, "tcpConnectEnd",
aTimings.tcpConnectEnd);
baseprofiler::WritePropertyTime(aWriter, "secureConnectionStart",
aTimings.secureConnectionStart);
baseprofiler::WritePropertyTime(aWriter, "connectEnd",
aTimings.connectEnd);
baseprofiler::WritePropertyTime(aWriter, "requestStart",
aTimings.requestStart);
baseprofiler::WritePropertyTime(aWriter, "responseStart",
aTimings.responseStart);
baseprofiler::WritePropertyTime(aWriter, "responseEnd",
aTimings.responseEnd);
}
}
static MarkerSchema MarkerTypeDisplay() {
return MarkerSchema::SpecialFrontendLocation{};
}
private:
static Span<const char> GetNetworkState(NetworkLoadType aType) {
switch (aType) {
case NetworkLoadType::LOAD_START:
return MakeStringSpan("STATUS_START");
case NetworkLoadType::LOAD_STOP:
return MakeStringSpan("STATUS_STOP");
case NetworkLoadType::LOAD_REDIRECT:
return MakeStringSpan("STATUS_REDIRECT");
default:
MOZ_ASSERT(false, "Unexpected NetworkLoadType enum value.");
return MakeStringSpan("");
}
}
static Span<const char> 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("");
}
}
};
profiler_add_marker(
name, JS::ProfilingCategoryPair::NETWORK,
NetworkMarkerPayload(static_cast<int64_t>(aChannelId),
PromiseFlatCString(spec).get(), aRequestMethod,
aType, aStart, aEnd, aPriority, aCount,
aCacheDisposition, aInnerWindowID, aTimings,
PromiseFlatCString(redirect_spec).get(),
std::move(aSource), aContentType));
name, geckoprofiler::category::NETWORK,
{MarkerTiming::Interval(aStart, aEnd),
MarkerStack::TakeBacktrace(std::move(aSource)),
MarkerInnerWindowId(aInnerWindowID)},
NetworkMarker{}, aStart, aEnd, static_cast<int64_t>(aChannelId), spec,
aRequestMethod, aType, aPriority, aCount, aCacheDisposition,
aTimings ? *aTimings : scEmptyNetTimingStruct, redirect_spec,
aContentType ? ProfilerString8View(*aContentType)
: ProfilerString8View());
}
static void maybelocked_profiler_add_marker_for_thread(

View File

@ -941,7 +941,8 @@ 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, UniqueProfilerBacktrace aSource = nullptr,
nsIURI* aRedirectURI = nullptr,
mozilla::UniquePtr<mozilla::ProfileChunkedBuffer> aSource = nullptr,
const mozilla::Maybe<nsDependentCString>& aContentType =
mozilla::Nothing());

View File

@ -28,7 +28,6 @@
# include "js/ProfilingFrameIterator.h"
# include "js/Utility.h"
# include "mozilla/ipc/ProtocolUtils.h"
# include "mozilla/net/HttpBaseChannel.h"
# include "mozilla/Preferences.h"
# include "mozilla/ServoTraversalStatistics.h"
@ -40,108 +39,6 @@ using UserTimingMark = mozilla::baseprofiler::markers::UserTimingMark;
using UserTimingMeasure = mozilla::baseprofiler::markers::UserTimingMeasure;
using MediaSample = mozilla::baseprofiler::markers::MediaSample;
struct Network {
static constexpr mozilla::Span<const char> 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<const char> typeString = GetNetworkState(aType);
mozilla::Span<const char> 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<const char> 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<const char> 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<const char> MarkerTypeName() {
return mozilla::MakeStringSpan("CompositorScreenshot");

View File

@ -803,23 +803,71 @@ TEST(GeckoProfiler, Markers)
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));
nsCOMPtr<nsIURI> uri;
ASSERT_TRUE(
NS_SUCCEEDED(NS_NewURI(getter_AddRefs(uri), "http://mozilla.org/"_ns)));
// The marker name will be "Load <aChannelId>: <aURI>".
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<mozilla::ProfileChunkedBuffer> aSource =
nullptr */
/* const mozilla::Maybe<nsDependentCString>& aContentType =
mozilla::Nothing() */);
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_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<mozilla::ProfileChunkedBuffer> aSource =
nullptr */
nullptr,
/* const mozilla::Maybe<nsDependentCString>& aContentType =
mozilla::Nothing() */
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/"));
nsCOMPtr<nsIURI> 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<mozilla::ProfileChunkedBuffer> aSource =
nullptr */
/* const mozilla::Maybe<nsDependentCString>& aContentType =
mozilla::Nothing() */);
nsCString screenshotURL = "url"_ns;
PROFILER_ADD_MARKER_WITH_PAYLOAD(
@ -1337,36 +1385,42 @@ TEST(GeckoProfiler, Markers)
EXPECT_EQ_JSON(payload["memoryAddress"], Int64, 1234);
EXPECT_EQ_JSON(payload["threadId"], Int64, 5678);
} else if (nameString == "NetworkMarkerPayload start marker") {
} else if (nameString == "Load 1: http://mozilla.org/") {
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_EQ_JSON(payload["RedirectURI"], String, "");
EXPECT_TRUE(payload["RedirectURI"].isNull());
EXPECT_TRUE(payload["contentType"].isNull());
} else if (nameString == "NetworkMarkerPayload stop marker") {
} else if (nameString == "Load 12: http://mozilla.org/") {
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_EQ_JSON(payload["RedirectURI"], String, "");
EXPECT_TRUE(payload["RedirectURI"].isNull());
EXPECT_EQ_JSON(payload["contentType"], String, "text/html");
} else if (nameString == "NetworkMarkerPayload redirect marker") {
} else if (nameString == "Load 123: http://mozilla.org/") {
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");