mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 22:01:30 +00:00
Backed out 20 changesets (bug 1646266) for build bustage at baseprofiler/core/ProfilerMarkers.cpp on a CLOSED TREE
Backed out changeset a2734d73264c (bug 1646266) Backed out changeset a0c2db6f73c7 (bug 1646266) Backed out changeset 6b71d7b09641 (bug 1646266) Backed out changeset fcf3c271d0fc (bug 1646266) Backed out changeset b4a39ef38261 (bug 1646266) Backed out changeset 6c2b59568703 (bug 1646266) Backed out changeset 5e7a28a727a1 (bug 1646266) Backed out changeset b51bc775d1e3 (bug 1646266) Backed out changeset a01a466e464c (bug 1646266) Backed out changeset 2c8828fab7a0 (bug 1646266) Backed out changeset 9fd6a871374f (bug 1646266) Backed out changeset 3b88d838b252 (bug 1646266) Backed out changeset bde14a8b0660 (bug 1646266) Backed out changeset dfd7e13e9e0b (bug 1646266) Backed out changeset 22bdc0172356 (bug 1646266) Backed out changeset 4ea14ca3d492 (bug 1646266) Backed out changeset 25f8e4b67b32 (bug 1646266) Backed out changeset 3d0160207591 (bug 1646266) Backed out changeset 790ed86c1a6c (bug 1646266) Backed out changeset 4c38607ea1ba (bug 1646266)
This commit is contained in:
parent
4256c8d7fc
commit
91699791f8
@ -15,10 +15,8 @@
|
||||
|
||||
#include "BaseProfiler.h"
|
||||
#include "BaseProfilerMarkerPayload.h"
|
||||
#include "mozilla/BaseProfilerMarkers.h"
|
||||
#include "platform.h"
|
||||
#include "ProfileBuffer.h"
|
||||
#include "ProfilerBacktrace.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace baseprofiler {
|
||||
@ -814,61 +812,33 @@ void ProfileBuffer::StreamMarkersToJSON(SpliceableJSONWriter& aWriter,
|
||||
MOZ_ASSERT(static_cast<ProfileBufferEntry::KindUnderlyingType>(type) <
|
||||
static_cast<ProfileBufferEntry::KindUnderlyingType>(
|
||||
ProfileBufferEntry::Kind::MODERN_LIMIT));
|
||||
// Code should *break* from the switch if the entry was not fully read.
|
||||
// Code should *return* from the switch if the entry was fully read.
|
||||
switch (type) {
|
||||
case ProfileBufferEntry::Kind::MarkerData:
|
||||
if (aER.ReadObject<int>() != aThreadId) {
|
||||
break;
|
||||
}
|
||||
// Schema:
|
||||
// [name, time, category, data]
|
||||
aWriter.StartArrayElement();
|
||||
{
|
||||
std::string name = aER.ReadObject<std::string>();
|
||||
const ProfilingCategoryPairInfo& info = GetProfilingCategoryPairInfo(
|
||||
static_cast<ProfilingCategoryPair>(aER.ReadObject<uint32_t>()));
|
||||
auto payload = aER.ReadObject<UniquePtr<ProfilerMarkerPayload>>();
|
||||
double time = aER.ReadObject<double>();
|
||||
MOZ_ASSERT(aER.RemainingBytes() == 0);
|
||||
if (type == ProfileBufferEntry::Kind::MarkerData &&
|
||||
aER.ReadObject<int>() == aThreadId) {
|
||||
// Schema:
|
||||
// [name, time, category, data]
|
||||
|
||||
aUniqueStacks.mUniqueStrings->WriteElement(aWriter, name.c_str());
|
||||
aWriter.DoubleElement(time);
|
||||
aWriter.IntElement(unsigned(info.mCategory));
|
||||
if (payload) {
|
||||
aWriter.StartObjectElement(SpliceableJSONWriter::SingleLineStyle);
|
||||
{
|
||||
payload->StreamPayload(aWriter, aProcessStartTime, aUniqueStacks);
|
||||
}
|
||||
aWriter.EndObject();
|
||||
}
|
||||
}
|
||||
aWriter.EndArray();
|
||||
return;
|
||||
aWriter.StartArrayElement();
|
||||
{
|
||||
std::string name = aER.ReadObject<std::string>();
|
||||
const ProfilingCategoryPairInfo& info = GetProfilingCategoryPairInfo(
|
||||
static_cast<ProfilingCategoryPair>(aER.ReadObject<uint32_t>()));
|
||||
auto payload = aER.ReadObject<UniquePtr<ProfilerMarkerPayload>>();
|
||||
double time = aER.ReadObject<double>();
|
||||
MOZ_ASSERT(aER.RemainingBytes() == 0);
|
||||
|
||||
case ProfileBufferEntry::Kind::Marker:
|
||||
if (::mozilla::base_profiler_markers_detail::
|
||||
DeserializeAfterKindAndStream(
|
||||
aER, aWriter, aThreadId,
|
||||
[&](const mozilla::ProfilerString8View& aName) {
|
||||
aUniqueStacks.mUniqueStrings->WriteElement(
|
||||
aWriter, aName.String().c_str());
|
||||
},
|
||||
[&](ProfileChunkedBuffer& aChunkedBuffer) {
|
||||
ProfilerBacktrace backtrace("", aThreadId,
|
||||
&aChunkedBuffer);
|
||||
backtrace.StreamJSON(
|
||||
aWriter, TimeStamp::ProcessCreation(), aUniqueStacks);
|
||||
})) {
|
||||
return;
|
||||
aUniqueStacks.mUniqueStrings->WriteElement(aWriter, name.c_str());
|
||||
aWriter.DoubleElement(time);
|
||||
aWriter.IntElement(unsigned(info.mCategory));
|
||||
if (payload) {
|
||||
aWriter.StartObjectElement(SpliceableJSONWriter::SingleLineStyle);
|
||||
{ payload->StreamPayload(aWriter, aProcessStartTime, aUniqueStacks); }
|
||||
aWriter.EndObject();
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
aWriter.EndArray();
|
||||
} else {
|
||||
aER.SetRemainingBytes(0);
|
||||
}
|
||||
|
||||
aER.SetRemainingBytes(0);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1,58 +0,0 @@
|
||||
/* -*- 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 "mozilla/BaseProfilerMarkers.h"
|
||||
|
||||
#include "mozilla/Likely.h"
|
||||
|
||||
#include <limits>
|
||||
|
||||
namespace mozilla {
|
||||
namespace base_profiler_markers_detail {
|
||||
|
||||
// We need an atomic type that can hold a `DeserializerTag`. (Atomic doesn't
|
||||
// work with too-small types.)
|
||||
using DeserializerTagAtomic = unsigned;
|
||||
|
||||
// Number of currently-registered deserializers.
|
||||
static Atomic<DeserializerTagAtomic, MemoryOrdering::Relaxed>
|
||||
sDeserializerCount{0};
|
||||
|
||||
// This needs to be big enough to handle all possible sub-types of
|
||||
// ProfilerMarkerPayload.
|
||||
static constexpr Streaming::DeserializerTag DeserializerMax = 255;
|
||||
|
||||
static_assert(
|
||||
DeserializerMax <= std::numeric_limits<Streaming::DeserializerTag>::max(),
|
||||
"The maximum number of deserializers must fit in the DeserializerTag type");
|
||||
|
||||
// Array of deserializer.
|
||||
// 1-based, i.e.: [0] -> tag 1, [DeserializerMax - 1] -> tag DeserializerMax.
|
||||
static Streaming::Deserializer sDeserializers1Based[DeserializerMax];
|
||||
|
||||
/* static */ Streaming::DeserializerTag Streaming::TagForDeserializer(
|
||||
Streaming::Deserializer aDeserializer) {
|
||||
MOZ_RELEASE_ASSERT(!!aDeserializer);
|
||||
|
||||
DeserializerTagAtomic tag = ++sDeserializerCount;
|
||||
MOZ_RELEASE_ASSERT(
|
||||
tag <= DeserializerMax,
|
||||
"Too many deserializers, consider increasing DeserializerMax. "
|
||||
"Or is a deserializer stored again and again?");
|
||||
sDeserializers1Based[tag - 1] = aDeserializer;
|
||||
|
||||
return static_cast<DeserializerTag>(tag);
|
||||
}
|
||||
|
||||
/* static */ Streaming::Deserializer Streaming::DeserializerForTag(
|
||||
Streaming::DeserializerTag aTag) {
|
||||
MOZ_RELEASE_ASSERT(aTag > 0);
|
||||
MOZ_RELEASE_ASSERT(aTag <= DeserializerMax);
|
||||
MOZ_RELEASE_ASSERT(aTag <= sDeserializerCount);
|
||||
return sDeserializers1Based[aTag - 1];
|
||||
}
|
||||
|
||||
} // namespace base_profiler_markers_detail
|
||||
} // namespace mozilla
|
@ -419,7 +419,7 @@ class CorePS {
|
||||
PS_GET_LOCKLESS(int, MainThreadId)
|
||||
|
||||
// No PSLockRef is needed for this field because it's immutable.
|
||||
PS_GET_LOCKLESS(const TimeStamp&, ProcessStartTime)
|
||||
PS_GET_LOCKLESS(TimeStamp, ProcessStartTime)
|
||||
|
||||
// No PSLockRef is needed for this field because it's thread-safe.
|
||||
PS_GET_LOCKLESS(ProfileChunkedBuffer&, CoreBuffer)
|
||||
@ -568,11 +568,6 @@ class CorePS {
|
||||
|
||||
CorePS* CorePS::sInstance = nullptr;
|
||||
|
||||
ProfileChunkedBuffer& profiler_get_core_buffer() {
|
||||
MOZ_ASSERT(CorePS::Exists());
|
||||
return CorePS::CoreBuffer();
|
||||
}
|
||||
|
||||
class SamplerThread;
|
||||
|
||||
static SamplerThread* NewSamplerThread(PSLockRef aLock, uint32_t aGeneration,
|
||||
@ -3535,7 +3530,59 @@ double profiler_time() {
|
||||
return delta.ToMilliseconds();
|
||||
}
|
||||
|
||||
bool profiler_capture_backtrace_into(ProfileChunkedBuffer& aChunkedBuffer) {
|
||||
static void locked_profiler_fill_backtrace(PSLockRef aLock,
|
||||
RegisteredThread& aRegisteredThread,
|
||||
ProfileBuffer& aProfileBuffer) {
|
||||
Registers regs;
|
||||
#if defined(HAVE_NATIVE_UNWIND)
|
||||
regs.SyncPopulate();
|
||||
#else
|
||||
regs.Clear();
|
||||
#endif
|
||||
|
||||
DoSyncSample(aLock, aRegisteredThread, TimeStamp::NowUnfuzzed(), regs,
|
||||
aProfileBuffer);
|
||||
}
|
||||
|
||||
static UniqueProfilerBacktrace locked_profiler_get_backtrace(PSLockRef aLock) {
|
||||
MOZ_RELEASE_ASSERT(CorePS::Exists());
|
||||
|
||||
if (!ActivePS::Exists(aLock)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RegisteredThread* registeredThread =
|
||||
TLSRegisteredThread::RegisteredThread(aLock);
|
||||
if (!registeredThread) {
|
||||
MOZ_ASSERT(registeredThread);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto bufferManager = MakeUnique<ProfileChunkedBuffer>(
|
||||
ProfileChunkedBuffer::ThreadSafety::WithoutMutex,
|
||||
MakeUnique<ProfileBufferChunkManagerSingle>(scExpectedMaximumStackSize));
|
||||
ProfileBuffer buffer(*bufferManager);
|
||||
|
||||
locked_profiler_fill_backtrace(aLock, *registeredThread, buffer);
|
||||
|
||||
return UniqueProfilerBacktrace(
|
||||
new ProfilerBacktrace("SyncProfile", registeredThread->Info()->ThreadId(),
|
||||
std::move(bufferManager)));
|
||||
}
|
||||
|
||||
UniqueProfilerBacktrace profiler_get_backtrace() {
|
||||
MOZ_RELEASE_ASSERT(CorePS::Exists());
|
||||
|
||||
PSAutoLock lock;
|
||||
|
||||
return locked_profiler_get_backtrace(lock);
|
||||
}
|
||||
|
||||
void ProfilerBacktraceDestructor::operator()(ProfilerBacktrace* aBacktrace) {
|
||||
delete aBacktrace;
|
||||
}
|
||||
|
||||
bool profiler_capture_backtrace(ProfileChunkedBuffer& aChunkedBuffer) {
|
||||
MOZ_RELEASE_ASSERT(CorePS::Exists());
|
||||
|
||||
PSAutoLock lock;
|
||||
@ -3553,53 +3600,11 @@ bool profiler_capture_backtrace_into(ProfileChunkedBuffer& aChunkedBuffer) {
|
||||
|
||||
ProfileBuffer profileBuffer(aChunkedBuffer);
|
||||
|
||||
Registers regs;
|
||||
#if defined(HAVE_NATIVE_UNWIND)
|
||||
regs.SyncPopulate();
|
||||
#else
|
||||
regs.Clear();
|
||||
#endif
|
||||
|
||||
DoSyncSample(lock, *registeredThread, TimeStamp::NowUnfuzzed(), regs,
|
||||
profileBuffer);
|
||||
locked_profiler_fill_backtrace(lock, *registeredThread, profileBuffer);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
UniquePtr<ProfileChunkedBuffer> profiler_capture_backtrace() {
|
||||
MOZ_RELEASE_ASSERT(CorePS::Exists());
|
||||
|
||||
// Quick is-active check before allocating a buffer.
|
||||
if (!profiler_is_active()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto buffer = MakeUnique<ProfileChunkedBuffer>(
|
||||
ProfileChunkedBuffer::ThreadSafety::WithoutMutex,
|
||||
MakeUnique<ProfileBufferChunkManagerSingle>(scExpectedMaximumStackSize));
|
||||
|
||||
if (!profiler_capture_backtrace_into(*buffer)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
UniqueProfilerBacktrace profiler_get_backtrace() {
|
||||
UniquePtr<ProfileChunkedBuffer> buffer = profiler_capture_backtrace();
|
||||
|
||||
if (!buffer) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return UniqueProfilerBacktrace(new ProfilerBacktrace(
|
||||
"SyncProfile", profiler_current_thread_id(), std::move(buffer)));
|
||||
}
|
||||
|
||||
void ProfilerBacktraceDestructor::operator()(ProfilerBacktrace* aBacktrace) {
|
||||
delete aBacktrace;
|
||||
}
|
||||
|
||||
bool profiler_is_locked_on_current_thread() {
|
||||
// This function is used to help users avoid calling `profiler_...` functions
|
||||
// when the profiler may already have a lock in place, which would prevent a
|
||||
|
@ -26,7 +26,6 @@ if CONFIG['MOZ_GECKO_PROFILER']:
|
||||
'core/ProfiledThreadData.cpp',
|
||||
'core/ProfilerBacktrace.cpp',
|
||||
'core/ProfilerMarkerPayload.cpp',
|
||||
'core/ProfilerMarkers.cpp',
|
||||
'core/ProfilingCategory.cpp',
|
||||
'core/ProfilingStack.cpp',
|
||||
'core/RegisteredThread.cpp',
|
||||
@ -83,10 +82,6 @@ EXPORTS.mozilla += [
|
||||
'public/BaseProfileJSONWriter.h',
|
||||
'public/BaseProfilerCounts.h',
|
||||
'public/BaseProfilerDetail.h',
|
||||
'public/BaseProfilerMarkers.h',
|
||||
'public/BaseProfilerMarkersDetail.h',
|
||||
'public/BaseProfilerMarkersPrerequisites.h',
|
||||
'public/BaseProfilerMarkerTypes.h',
|
||||
'public/BlocksRingBuffer.h',
|
||||
'public/leb128iterator.h',
|
||||
'public/ModuloBuffer.h',
|
||||
|
@ -19,22 +19,18 @@
|
||||
// This file is safe to include unconditionally, and only defines
|
||||
// empty macros if MOZ_GECKO_PROFILER is not set.
|
||||
|
||||
// These headers are also safe to include unconditionally, with empty macros if
|
||||
// MOZ_GECKO_PROFILER is not set.
|
||||
// BaseProfilerCounts.h is also safe to include unconditionally, with empty
|
||||
// macros if MOZ_GECKO_PROFILER is not set.
|
||||
#include "mozilla/BaseProfilerCounts.h"
|
||||
|
||||
// BaseProfilerMarkers.h is #included in the middle of this header!
|
||||
// #include "mozilla/BaseProfilerMarkers.h"
|
||||
|
||||
#ifndef MOZ_GECKO_PROFILER
|
||||
|
||||
# include "mozilla/BaseProfilerMarkers.h"
|
||||
# include "mozilla/UniquePtr.h"
|
||||
|
||||
// This file can be #included unconditionally. However, everything within this
|
||||
// file must be guarded by a #ifdef MOZ_GECKO_PROFILER, *except* for the
|
||||
// following macros and functions, which encapsulate the most common operations
|
||||
// and thus avoid the need for many #ifdefs.
|
||||
// following macros, which encapsulate the most common operations and thus
|
||||
// avoid the need for many #ifdefs.
|
||||
|
||||
# define AUTO_BASE_PROFILER_INIT
|
||||
|
||||
@ -66,30 +62,20 @@
|
||||
|
||||
# define AUTO_PROFILER_STATS(name)
|
||||
|
||||
// Function stubs for when MOZ_GECKO_PROFILER is not defined.
|
||||
|
||||
namespace mozilla {
|
||||
class ProfileChunkedBuffer;
|
||||
|
||||
namespace baseprofiler {
|
||||
struct ProfilerBacktrace {};
|
||||
using UniqueProfilerBacktrace = UniquePtr<ProfilerBacktrace>;
|
||||
|
||||
// Get/Capture-backtrace functions can return nullptr or false, the result
|
||||
// should be fed to another empty macro or stub anyway.
|
||||
|
||||
static inline UniqueProfilerBacktrace profiler_get_backtrace() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static inline bool profiler_capture_backtrace_into(
|
||||
static inline bool profiler_capture_backtrace(
|
||||
ProfileChunkedBuffer& aChunkedBuffer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline UniquePtr<ProfileChunkedBuffer> profiler_capture_backtrace() {
|
||||
return nullptr;
|
||||
}
|
||||
} // namespace baseprofiler
|
||||
} // namespace mozilla
|
||||
|
||||
@ -549,22 +535,10 @@ struct ProfilerBacktraceDestructor {
|
||||
using UniqueProfilerBacktrace =
|
||||
UniquePtr<ProfilerBacktrace, ProfilerBacktraceDestructor>;
|
||||
|
||||
// Immediately capture the current thread's call stack, store it in the provided
|
||||
// buffer (usually to avoid allocations if you can construct the buffer on the
|
||||
// stack). Returns false if unsuccessful, or if the profiler is inactive.
|
||||
MFBT_API bool profiler_capture_backtrace_into(
|
||||
ProfileChunkedBuffer& aChunkedBuffer);
|
||||
|
||||
// Immediately capture the current thread's call stack, and return it in a
|
||||
// ProfileChunkedBuffer (usually for later use in MarkerStack::TakeBacktrace()).
|
||||
// May be null if unsuccessful, or if the profiler is inactive.
|
||||
MFBT_API UniquePtr<ProfileChunkedBuffer> profiler_capture_backtrace();
|
||||
|
||||
// Immediately capture the current thread's call stack, and return it in a
|
||||
// ProfilerBacktrace (usually for later use in marker function that take a
|
||||
// ProfilerBacktrace). May be null if unsuccessful, or if the profiler is
|
||||
// inactive.
|
||||
// Immediately capture the current thread's call stack and return it. A no-op
|
||||
// if the profiler is inactive.
|
||||
MFBT_API UniqueProfilerBacktrace profiler_get_backtrace();
|
||||
MFBT_API bool profiler_capture_backtrace(ProfileChunkedBuffer& aChunkedBuffer);
|
||||
|
||||
struct ProfilerStats {
|
||||
unsigned n = 0;
|
||||
@ -702,16 +676,6 @@ class MOZ_RAII AutoProfilerStats {
|
||||
|
||||
# endif // PROFILER_RUNTIME_STATS else
|
||||
|
||||
} // namespace baseprofiler
|
||||
} // namespace mozilla
|
||||
|
||||
// BaseProfilerMarkers.h requires some stuff from this header.
|
||||
// TODO: Move common stuff to shared header, and move this #include to the top.
|
||||
# include "mozilla/BaseProfilerMarkers.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace baseprofiler {
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Put profiling data into the profiler (labels and markers)
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -1,129 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; 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 BaseProfilerMarkerTypes_h
|
||||
#define BaseProfilerMarkerTypes_h
|
||||
|
||||
// This header contains common marker type definitions.
|
||||
//
|
||||
// It #include's "mozilla/BaseProfilerMarkers.h", see that file for how to
|
||||
// define other marker types, and how to add markers to the profiler buffers.
|
||||
//
|
||||
// If you don't need to use these common types, #include
|
||||
// "mozilla/BaseProfilerMarkers.h" instead.
|
||||
//
|
||||
// Types in this files can be defined without relying on xpcom.
|
||||
// Others are defined in "ProfilerMarkerTypes.h".
|
||||
|
||||
// /!\ WORK IN PROGRESS /!\
|
||||
// This file contains draft marker definitions, but most are not used yet.
|
||||
// Further work is needed to complete these definitions, and use them to convert
|
||||
// existing PROFILER_ADD_MARKER calls. See meta bug 1661394.
|
||||
|
||||
#include "mozilla/BaseProfilerMarkers.h"
|
||||
|
||||
#ifdef MOZ_GECKO_PROFILER
|
||||
|
||||
namespace mozilla::baseprofiler::markers {
|
||||
|
||||
struct Tracing {
|
||||
static constexpr const char* MarkerTypeName() { return "tracing"; }
|
||||
static void StreamJSONMarkerData(JSONWriter& aWriter,
|
||||
const ProfilerString8View& aCategory) {
|
||||
if (aCategory.Length() != 0) {
|
||||
aWriter.StringProperty("category", aCategory.String().c_str());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct FileIO {
|
||||
static constexpr const char* MarkerTypeName() { return "FileIO"; }
|
||||
static void StreamJSONMarkerData(JSONWriter& aWriter,
|
||||
const ProfilerString8View& aOperation,
|
||||
const ProfilerString8View& aSource,
|
||||
const ProfilerString8View& aFilename,
|
||||
MarkerThreadId aOperationThreadId) {
|
||||
aWriter.StringProperty("operation", aOperation.String().c_str());
|
||||
aWriter.StringProperty("source", aSource.String().c_str());
|
||||
if (aFilename.Length() != 0) {
|
||||
aWriter.StringProperty("filename", aFilename.String().c_str());
|
||||
}
|
||||
if (!aOperationThreadId.IsUnspecified()) {
|
||||
aWriter.IntProperty("threadId", aOperationThreadId.ThreadId());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct UserTimingMark {
|
||||
static constexpr const char* MarkerTypeName() { return "UserTiming"; }
|
||||
static void StreamJSONMarkerData(JSONWriter& aWriter,
|
||||
const ProfilerString8View& aName) {
|
||||
aWriter.StringProperty("name", aName.String().c_str());
|
||||
aWriter.StringProperty("entryType", "mark");
|
||||
aWriter.NullProperty("startMark");
|
||||
aWriter.NullProperty("endMark");
|
||||
}
|
||||
};
|
||||
|
||||
struct UserTimingMeasure {
|
||||
static constexpr const char* MarkerTypeName() { return "UserTiming"; }
|
||||
static void StreamJSONMarkerData(JSONWriter& aWriter,
|
||||
const ProfilerString8View& aName,
|
||||
const Maybe<ProfilerString8View>& aStartMark,
|
||||
const Maybe<ProfilerString8View>& aEndMark) {
|
||||
aWriter.StringProperty("name", aName.String().c_str());
|
||||
aWriter.StringProperty("entryType", "measure");
|
||||
|
||||
if (aStartMark.isSome()) {
|
||||
aWriter.StringProperty("startMark", aStartMark->String().c_str());
|
||||
} else {
|
||||
aWriter.NullProperty("startMark");
|
||||
}
|
||||
if (aEndMark.isSome()) {
|
||||
aWriter.StringProperty("endMark", aEndMark->String().c_str());
|
||||
} else {
|
||||
aWriter.NullProperty("endMark");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct Hang {
|
||||
static constexpr const char* MarkerTypeName() { return "BHR-detected hang"; }
|
||||
static void StreamJSONMarkerData(JSONWriter& aWriter) {}
|
||||
};
|
||||
|
||||
struct LongTask {
|
||||
static constexpr const char* MarkerTypeName() { return "MainThreadLongTask"; }
|
||||
static void StreamJSONMarkerData(JSONWriter& aWriter) {
|
||||
aWriter.StringProperty("category", "LongTask");
|
||||
}
|
||||
};
|
||||
|
||||
struct Log {
|
||||
static constexpr const char* MarkerTypeName() { return "Log"; }
|
||||
static void StreamJSONMarkerData(JSONWriter& aWriter,
|
||||
const ProfilerString8View& aModule,
|
||||
const ProfilerString8View& aText) {
|
||||
aWriter.StringProperty("module", aModule.String().c_str());
|
||||
aWriter.StringProperty("name", aText.String().c_str());
|
||||
}
|
||||
};
|
||||
|
||||
struct MediaSample {
|
||||
static constexpr const char* MarkerTypeName() { return "MediaSample"; }
|
||||
static void StreamJSONMarkerData(JSONWriter& aWriter,
|
||||
int64_t aSampleStartTimeUs,
|
||||
int64_t aSampleEndTimeUs) {
|
||||
aWriter.IntProperty("sampleStartTimeUs", aSampleStartTimeUs);
|
||||
aWriter.IntProperty("sampleEndTimeUs", aSampleEndTimeUs);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace mozilla::baseprofiler::markers
|
||||
|
||||
#endif // MOZ_GECKO_PROFILER
|
||||
|
||||
#endif // BaseProfilerMarkerTypes_h
|
@ -1,123 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; 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/. */
|
||||
|
||||
// This header contains basic definitions necessary to create marker types, and
|
||||
// to add markers to the profiler buffers.
|
||||
//
|
||||
// If basic marker types are needed, #include
|
||||
// "mozilla/BaseProfilerMarkerTypes.h" instead.
|
||||
//
|
||||
// But if you want to create your own marker type locally, you can #include this
|
||||
// header only; look at mozilla/BaseProfilerMarkerTypes.h for examples of how to
|
||||
// define types, and mozilla/BaseProfilerMarkerPrerequisites.h for some
|
||||
// supporting types.
|
||||
//
|
||||
// To then record markers:
|
||||
// - Use `baseprofiler::AddMarker<ChosenMarkerType>(...)` from
|
||||
// mozglue or other libraries that are outside of xul, especially if they may
|
||||
// happen outside of xpcom's lifetime (typically startup, shutdown, or tests).
|
||||
// - Otherwise #include "ProfilerMarkers.h" instead, and use
|
||||
// `profiler_add_marker<ChosenMarkerType>(...)`.
|
||||
// See these functions for more details.
|
||||
|
||||
#ifndef BaseProfilerMarkers_h
|
||||
#define BaseProfilerMarkers_h
|
||||
|
||||
#include "mozilla/BaseProfilerMarkersDetail.h"
|
||||
|
||||
// TODO: Move common stuff to shared header instead.
|
||||
#include "BaseProfiler.h"
|
||||
|
||||
#ifndef MOZ_GECKO_PROFILER
|
||||
|
||||
# define BASE_PROFILER_MARKER_UNTYPED(markerName, options)
|
||||
# define BASE_PROFILER_MARKER(markerName, options, MarkerType, ...)
|
||||
# define BASE_PROFILER_MARKER_TEXT(markerName, options, text)
|
||||
|
||||
#else // ndef MOZ_GECKO_PROFILER
|
||||
|
||||
# include "mozilla/ProfileChunkedBuffer.h"
|
||||
# include "mozilla/TimeStamp.h"
|
||||
|
||||
# include <functional>
|
||||
# include <string>
|
||||
# include <utility>
|
||||
|
||||
namespace mozilla::baseprofiler {
|
||||
|
||||
template <typename MarkerType = ::mozilla::baseprofiler::markers::NoPayload,
|
||||
typename... Ts>
|
||||
ProfileBufferBlockIndex AddMarkerToBuffer(ProfileChunkedBuffer& aBuffer,
|
||||
const ProfilerString8View& aName,
|
||||
MarkerOptions&& aOptions,
|
||||
const Ts&... aTs) {
|
||||
return base_profiler_markers_detail::AddMarkerToBuffer<MarkerType>(
|
||||
aBuffer, aName, std::move(aOptions),
|
||||
::mozilla::baseprofiler::profiler_capture_backtrace_into, aTs...);
|
||||
}
|
||||
|
||||
template <typename MarkerType = ::mozilla::baseprofiler::markers::NoPayload,
|
||||
typename... Ts>
|
||||
ProfileBufferBlockIndex AddMarker(const ProfilerString8View& aName,
|
||||
MarkerOptions&& aOptions, const Ts&... aTs) {
|
||||
if (!baseprofiler::profiler_can_accept_markers()) {
|
||||
return {};
|
||||
}
|
||||
return ::mozilla::baseprofiler::AddMarkerToBuffer<MarkerType>(
|
||||
base_profiler_markers_detail::CachedBaseCoreBuffer(), aName,
|
||||
std::move(aOptions), aTs...);
|
||||
}
|
||||
|
||||
// Marker types' StreamJSONMarkerData functions should use this to correctly
|
||||
// output timestamps as a JSON property.
|
||||
inline void WritePropertyTime(JSONWriter& aWriter, const char* aName,
|
||||
const TimeStamp& aTime) {
|
||||
if (!aTime.IsNull()) {
|
||||
aWriter.DoubleProperty(
|
||||
aName, (aTime - TimeStamp::ProcessCreation()).ToMilliseconds());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mozilla::baseprofiler
|
||||
|
||||
# define BASE_PROFILER_MARKER_UNTYPED(markerName, options) \
|
||||
do { \
|
||||
AUTO_PROFILER_STATS(base_add_marker_v2); \
|
||||
::mozilla::baseprofiler::AddMarker<>( \
|
||||
markerName, ::mozilla::baseprofiler::category::options); \
|
||||
} while (false)
|
||||
|
||||
# define BASE_PROFILER_MARKER(markerName, options, MarkerType, ...) \
|
||||
do { \
|
||||
AUTO_PROFILER_STATS(base_add_marker_v2_with_##MarkerType); \
|
||||
::mozilla::baseprofiler::AddMarker< \
|
||||
::mozilla::baseprofiler::markers::MarkerType>( \
|
||||
markerName, ::mozilla::baseprofiler::category::options, \
|
||||
##__VA_ARGS__); \
|
||||
} while (false)
|
||||
|
||||
namespace mozilla::baseprofiler::markers {
|
||||
// Most common marker type. Others are in BaseProfilerMarkerTypes.h.
|
||||
struct Text {
|
||||
static constexpr const char* MarkerTypeName() { return "Text"; }
|
||||
static void StreamJSONMarkerData(JSONWriter& aWriter,
|
||||
const ProfilerString8View& aText) {
|
||||
aWriter.StringProperty("name", aText.String().c_str());
|
||||
}
|
||||
};
|
||||
} // namespace mozilla::baseprofiler::markers
|
||||
|
||||
# define BASE_PROFILER_MARKER_TEXT(markerName, options, text) \
|
||||
do { \
|
||||
AUTO_PROFILER_STATS(base_add_marker_v2_with_Text); \
|
||||
::mozilla::baseprofiler::AddMarker< \
|
||||
::mozilla::baseprofiler::markers::Text>( \
|
||||
markerName, ::mozilla::baseprofiler::category::options, text); \
|
||||
} while (false)
|
||||
|
||||
#endif // nfed MOZ_GECKO_PROFILER else
|
||||
|
||||
#endif // BaseProfilerMarkers_h
|
@ -1,624 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; 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 BaseProfilerMarkersDetail_h
|
||||
#define BaseProfilerMarkersDetail_h
|
||||
|
||||
#ifndef BaseProfilerMarkers_h
|
||||
# error "This header should only be #included by BaseProfilerMarkers.h"
|
||||
#endif
|
||||
|
||||
#include "mozilla/BaseProfilerMarkersPrerequisites.h"
|
||||
|
||||
#ifdef MOZ_GECKO_PROFILER
|
||||
|
||||
// ~~ HERE BE DRAGONS ~~
|
||||
//
|
||||
// Everything below is internal implementation detail, you shouldn't need to
|
||||
// look at it unless working on the profiler code.
|
||||
|
||||
# include "mozilla/JSONWriter.h"
|
||||
# include "mozilla/ProfileBufferEntryKinds.h"
|
||||
|
||||
# include <limits>
|
||||
# include <tuple>
|
||||
|
||||
namespace mozilla::baseprofiler {
|
||||
// Implemented in platform.cpp
|
||||
MFBT_API ProfileChunkedBuffer& profiler_get_core_buffer();
|
||||
} // namespace mozilla::baseprofiler
|
||||
|
||||
namespace mozilla::base_profiler_markers_detail {
|
||||
|
||||
// Get the core buffer from the profiler, and cache it in a
|
||||
// non-templated-function static reference.
|
||||
inline ProfileChunkedBuffer& CachedBaseCoreBuffer() {
|
||||
static ProfileChunkedBuffer& coreBuffer =
|
||||
baseprofiler::profiler_get_core_buffer();
|
||||
return coreBuffer;
|
||||
}
|
||||
|
||||
struct Streaming {
|
||||
// A `Deserializer` is a free function that can read a serialized payload from
|
||||
// an `EntryReader` and streams it as JSON object properties.
|
||||
using Deserializer = void (*)(ProfileBufferEntryReader&, JSONWriter&);
|
||||
|
||||
// A `DeserializerTag` will be added before the payload, to help select the
|
||||
// correct deserializer when reading back the payload.
|
||||
using DeserializerTag = unsigned char;
|
||||
|
||||
// Store a deserializer and get its `DeserializerTag`.
|
||||
// This is intended to be only used once per deserializer, so it should be
|
||||
// called to initialize a `static const` tag that will be re-used by all
|
||||
// markers of the corresponding payload type.
|
||||
MFBT_API static DeserializerTag TagForDeserializer(
|
||||
Deserializer aDeserializer);
|
||||
|
||||
// Get the `Deserializer` for a given `DeserializerTag`.
|
||||
MFBT_API static Deserializer DeserializerForTag(DeserializerTag aTag);
|
||||
};
|
||||
|
||||
// This helper will examine a marker type's `StreamJSONMarkerData` function, see
|
||||
// specialization below.
|
||||
template <typename T>
|
||||
struct StreamFunctionTypeHelper;
|
||||
|
||||
// Helper specialization that takes the expected
|
||||
// `StreamJSONMarkerData(JSONWriter&, ...)` function and provide information
|
||||
// about the `...` parameters.
|
||||
template <typename R, typename... As>
|
||||
struct StreamFunctionTypeHelper<R(JSONWriter&, As...)> {
|
||||
constexpr static size_t scArity = sizeof...(As);
|
||||
using TupleType =
|
||||
std::tuple<std::remove_cv_t<std::remove_reference_t<As>>...>;
|
||||
|
||||
// Serialization function that takes the exact same parameter types
|
||||
// (const-ref'd) as `StreamJSONMarkerData`. This has to be inside the helper
|
||||
// because only here can we access the raw parameter pack `As...`.
|
||||
// And because we're using the same argument types through
|
||||
// references-to-const, permitted implicit conversions can happen.
|
||||
static ProfileBufferBlockIndex Serialize(
|
||||
ProfileChunkedBuffer& aBuffer, const ProfilerString8View& aName,
|
||||
MarkerOptions&& aOptions, Streaming::DeserializerTag aDeserializerTag,
|
||||
const As&... aAs) {
|
||||
// Note that options are first after the entry kind, because they contain
|
||||
// the thread id, which is handled first to filter markers by threads.
|
||||
return aBuffer.PutObjects(ProfileBufferEntryKind::Marker, aOptions, aName,
|
||||
aDeserializerTag, aAs...);
|
||||
}
|
||||
};
|
||||
|
||||
// Helper for a marker type.
|
||||
// A marker type is defined in a `struct` with some expected static member
|
||||
// functions. See example in BaseProfilerMarkers.h.
|
||||
template <typename MarkerType>
|
||||
struct MarkerTypeSerialization {
|
||||
// Definitions to access the expected `StreamJSONMarkerData(JSONWriter&, ...)`
|
||||
// function and its parameters.
|
||||
using StreamFunctionType =
|
||||
StreamFunctionTypeHelper<decltype(MarkerType::StreamJSONMarkerData)>;
|
||||
constexpr static size_t scStreamFunctionParameterCount =
|
||||
StreamFunctionType::scArity;
|
||||
using StreamFunctionUserParametersTuple =
|
||||
typename StreamFunctionType::TupleType;
|
||||
template <size_t i>
|
||||
using StreamFunctionParameter =
|
||||
std::tuple_element_t<i, StreamFunctionUserParametersTuple>;
|
||||
|
||||
template <typename... Ts>
|
||||
static ProfileBufferBlockIndex Serialize(ProfileChunkedBuffer& aBuffer,
|
||||
const ProfilerString8View& aName,
|
||||
MarkerOptions&& aOptions,
|
||||
const Ts&... aTs) {
|
||||
static_assert(!std::is_same_v<MarkerType,
|
||||
::mozilla::baseprofiler::markers::NoPayload>,
|
||||
"NoPayload should have been handled in the caller.");
|
||||
// Note that the tag is stored in a function-static object, and this
|
||||
// function is static in a templated struct, so there should only be one tag
|
||||
// per MarkerType.
|
||||
// Making the tag class-static may have been more efficient (to avoid a
|
||||
// thread-safe init check at every call), but random global static
|
||||
// initialization order would make it more complex to coordinate with
|
||||
// `Streaming::TagForDeserializer()`, and also would add a (small) cost for
|
||||
// everybody, even the majority of users not using the profiler.
|
||||
static const Streaming::DeserializerTag tag =
|
||||
Streaming::TagForDeserializer(Deserialize);
|
||||
return StreamFunctionType::Serialize(aBuffer, aName, std::move(aOptions),
|
||||
tag, aTs...);
|
||||
}
|
||||
|
||||
private:
|
||||
// This templated function will recursively deserialize each argument expected
|
||||
// by `MarkerType::StreamJSONMarkerData()` on the stack, and call it at the
|
||||
// end. E.g., for `StreamJSONMarkerData(int, char)`:
|
||||
// - DeserializeArguments<0>(aER, aWriter) reads an int and calls:
|
||||
// - DeserializeArguments<1>(aER, aWriter, const int&) reads a char and calls:
|
||||
// - MarkerType::StreamJSONMarkerData(aWriter, const int&, const char&).
|
||||
// Prototyping on godbolt showed that clang and gcc can flatten these
|
||||
// recursive calls into one function with successive reads followed by the one
|
||||
// stream call; tested up to 40 arguments: https://godbolt.org/z/5KeeM4
|
||||
template <size_t i = 0, typename... Args>
|
||||
static void DeserializeArguments(ProfileBufferEntryReader& aEntryReader,
|
||||
JSONWriter& aWriter, const Args&... aArgs) {
|
||||
static_assert(sizeof...(Args) == i,
|
||||
"We should have collected `i` arguments so far");
|
||||
if constexpr (i < scStreamFunctionParameterCount) {
|
||||
// Deserialize the i-th argument on this stack.
|
||||
auto argument = aEntryReader.ReadObject<StreamFunctionParameter<i>>();
|
||||
// Add our local argument to the next recursive call.
|
||||
DeserializeArguments<i + 1>(aEntryReader, aWriter, aArgs..., argument);
|
||||
} else {
|
||||
// We've read all the arguments, finally call the `StreamJSONMarkerData`
|
||||
// function, which should write the appropriate JSON elements for this
|
||||
// marker type. Note that the MarkerType-specific "type" element is
|
||||
// already written.
|
||||
MarkerType::StreamJSONMarkerData(aWriter, aArgs...);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
static void Deserialize(ProfileBufferEntryReader& aEntryReader,
|
||||
JSONWriter& aWriter) {
|
||||
aWriter.StringProperty("type", MarkerType::MarkerTypeName());
|
||||
DeserializeArguments(aEntryReader, aWriter);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct MarkerTypeSerialization<::mozilla::baseprofiler::markers::NoPayload> {
|
||||
// Nothing! NoPayload has special handling avoiding payload work.
|
||||
};
|
||||
|
||||
template <typename MarkerType, typename... Ts>
|
||||
static ProfileBufferBlockIndex AddMarkerWithOptionalStackToBuffer(
|
||||
ProfileChunkedBuffer& aBuffer, const ProfilerString8View& aName,
|
||||
MarkerOptions&& aOptions, const Ts&... aTs) {
|
||||
if constexpr (std::is_same_v<MarkerType,
|
||||
::mozilla::baseprofiler::markers::NoPayload>) {
|
||||
static_assert(sizeof...(Ts) == 0,
|
||||
"NoPayload does not accept any payload arguments.");
|
||||
// Note that options are first after the entry kind, because they contain
|
||||
// the thread id, which is handled first to filter markers by threads.
|
||||
return aBuffer.PutObjects(
|
||||
ProfileBufferEntryKind::Marker, std::move(aOptions), aName,
|
||||
base_profiler_markers_detail::Streaming::DeserializerTag(0));
|
||||
} else {
|
||||
return MarkerTypeSerialization<MarkerType>::Serialize(
|
||||
aBuffer, aName, std::move(aOptions), aTs...);
|
||||
}
|
||||
}
|
||||
|
||||
// Pointer to a function that can capture a backtrace into the provided
|
||||
// `ProfileChunkedBuffer`, and returns true when successful.
|
||||
using BacktraceCaptureFunction = bool (*)(ProfileChunkedBuffer&);
|
||||
|
||||
// Add a marker with the given name, options, and arguments to the given buffer.
|
||||
// Because this may be called from either Base or Gecko Profiler functions, the
|
||||
// appropriate backtrace-capturing function must also be provided.
|
||||
template <typename MarkerType, typename... Ts>
|
||||
ProfileBufferBlockIndex AddMarkerToBuffer(
|
||||
ProfileChunkedBuffer& aBuffer, const ProfilerString8View& aName,
|
||||
MarkerOptions&& aOptions,
|
||||
BacktraceCaptureFunction aBacktraceCaptureFunction, const Ts&... aTs) {
|
||||
if (aOptions.ThreadId().IsUnspecified()) {
|
||||
// If yet unspecified, set thread to this thread where the marker is added.
|
||||
aOptions.Set(MarkerThreadId::CurrentThread());
|
||||
}
|
||||
|
||||
if (aOptions.IsTimingUnspecified()) {
|
||||
// If yet unspecified, set timing to this instant of adding the marker.
|
||||
aOptions.Set(MarkerTiming::InstantNow());
|
||||
}
|
||||
|
||||
if (aOptions.Stack().IsCaptureNeeded()) {
|
||||
// A capture was requested, let's attempt to do it here&now. This avoids a
|
||||
// lot of allocations that would be necessary if capturing a backtrace
|
||||
// separately.
|
||||
// TODO use a local on-stack byte buffer to remove last allocation.
|
||||
// TODO reduce internal profiler stack levels, see bug 1659872.
|
||||
ProfileBufferChunkManagerSingle chunkManager(64 * 1024);
|
||||
ProfileChunkedBuffer chunkedBuffer(
|
||||
ProfileChunkedBuffer::ThreadSafety::WithoutMutex, chunkManager);
|
||||
aOptions.Stack().UseRequestedBacktrace(
|
||||
aBacktraceCaptureFunction(chunkedBuffer) ? &chunkedBuffer : nullptr);
|
||||
// This call must be made from here, while chunkedBuffer is in scope.
|
||||
return AddMarkerWithOptionalStackToBuffer<MarkerType>(
|
||||
aBuffer, aName, std::move(aOptions), aTs...);
|
||||
}
|
||||
|
||||
return AddMarkerWithOptionalStackToBuffer<MarkerType>(
|
||||
aBuffer, aName, std::move(aOptions), aTs...);
|
||||
}
|
||||
|
||||
template <typename NameCallback, typename StackCallback>
|
||||
[[nodiscard]] bool DeserializeAfterKindAndStream(
|
||||
ProfileBufferEntryReader& aEntryReader, JSONWriter& aWriter,
|
||||
int aThreadIdOrZero, NameCallback&& aNameCallback,
|
||||
StackCallback&& aStackCallback) {
|
||||
// Each entry is made up of the following:
|
||||
// ProfileBufferEntry::Kind::Marker, <- already read by caller
|
||||
// options, <- next location in entries
|
||||
// name,
|
||||
// payload
|
||||
const MarkerOptions options = aEntryReader.ReadObject<MarkerOptions>();
|
||||
if (aThreadIdOrZero != 0 &&
|
||||
options.ThreadId().ThreadId() != aThreadIdOrZero) {
|
||||
// A specific thread is being read, we're not in it.
|
||||
return false;
|
||||
}
|
||||
// Write the information to JSON with the following schema:
|
||||
// [name, startTime, endTime, phase, category, data]
|
||||
aWriter.StartArrayElement();
|
||||
{
|
||||
std::forward<NameCallback>(aNameCallback)(
|
||||
aEntryReader.ReadObject<mozilla::ProfilerString8View>());
|
||||
|
||||
const double startTime = options.Timing().GetStartTime();
|
||||
aWriter.DoubleElement(startTime);
|
||||
|
||||
const double endTime = options.Timing().GetEndTime();
|
||||
aWriter.DoubleElement(endTime);
|
||||
|
||||
aWriter.IntElement(static_cast<int64_t>(options.Timing().MarkerPhase()));
|
||||
|
||||
aWriter.IntElement(static_cast<int64_t>(options.Category().Category()));
|
||||
|
||||
if (const auto tag =
|
||||
aEntryReader.ReadObject<mozilla::base_profiler_markers_detail::
|
||||
Streaming::DeserializerTag>();
|
||||
tag != 0) {
|
||||
aWriter.StartObjectElement(JSONWriter::SingleLineStyle);
|
||||
{
|
||||
// Stream "common props".
|
||||
|
||||
// TODO: Move this to top-level tuple, when frontend supports it.
|
||||
if (!options.InnerWindowId().IsUnspecified()) {
|
||||
// Here, we are converting uint64_t to double. Both Browsing Context
|
||||
// and Inner Window IDs are created 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",
|
||||
static_cast<double>(options.InnerWindowId().Id()));
|
||||
}
|
||||
|
||||
// TODO: Move this to top-level tuple, when frontend supports it.
|
||||
if (ProfileChunkedBuffer* chunkedBuffer =
|
||||
options.Stack().GetChunkedBuffer();
|
||||
chunkedBuffer) {
|
||||
aWriter.StartObjectProperty("stack");
|
||||
{ std::forward<StackCallback>(aStackCallback)(*chunkedBuffer); }
|
||||
aWriter.EndObject();
|
||||
}
|
||||
|
||||
// Stream the payload, including the type.
|
||||
mozilla::base_profiler_markers_detail::Streaming::Deserializer
|
||||
deserializer = mozilla::base_profiler_markers_detail::Streaming::
|
||||
DeserializerForTag(tag);
|
||||
MOZ_RELEASE_ASSERT(deserializer);
|
||||
deserializer(aEntryReader, aWriter);
|
||||
}
|
||||
aWriter.EndObject();
|
||||
}
|
||||
}
|
||||
aWriter.EndArray();
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace mozilla::base_profiler_markers_detail
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Serializer, Deserializer: ProfilerStringView<CHAR>
|
||||
|
||||
// The serialization starts with a ULEB128 number that encodes both whether the
|
||||
// ProfilerStringView is literal (Least Significant Bit = 0) or not (LSB = 1),
|
||||
// plus the string length (excluding null terminator) in bytes, shifted left by
|
||||
// 1 bit. Following that number:
|
||||
// - If literal, the string pointer value.
|
||||
// - If non-literal, the contents as bytes (excluding null terminator if any).
|
||||
template <typename CHAR>
|
||||
struct ProfileBufferEntryWriter::Serializer<ProfilerStringView<CHAR>> {
|
||||
static Length Bytes(const ProfilerStringView<CHAR>& aString) {
|
||||
MOZ_RELEASE_ASSERT(
|
||||
aString.Length() < std::numeric_limits<Length>::max() / 2,
|
||||
"Double the string length doesn't fit in Length type");
|
||||
const Length stringLength = static_cast<Length>(aString.Length());
|
||||
if (aString.IsLiteral()) {
|
||||
// Literal -> Length shifted left and LSB=0, then pointer.
|
||||
return ULEB128Size(stringLength << 1 | 0u) +
|
||||
static_cast<ProfileChunkedBuffer::Length>(sizeof(const CHAR*));
|
||||
}
|
||||
// Non-literal -> Length shifted left and LSB=1, then string size in bytes.
|
||||
return ULEB128Size((stringLength << 1) | 1u) + stringLength * sizeof(CHAR);
|
||||
}
|
||||
|
||||
static void Write(ProfileBufferEntryWriter& aEW,
|
||||
const ProfilerStringView<CHAR>& aString) {
|
||||
MOZ_RELEASE_ASSERT(
|
||||
aString.Length() < std::numeric_limits<Length>::max() / 2,
|
||||
"Double the string length doesn't fit in Length type");
|
||||
const Length stringLength = static_cast<Length>(aString.Length());
|
||||
if (aString.IsLiteral()) {
|
||||
// Literal -> Length shifted left and LSB=0, then pointer.
|
||||
aEW.WriteULEB128(stringLength << 1 | 0u);
|
||||
aEW.WriteObject(WrapProfileBufferRawPointer(aString.Data()));
|
||||
return;
|
||||
}
|
||||
// Non-literal -> Length shifted left and LSB=1, then string size in bytes.
|
||||
aEW.WriteULEB128(stringLength << 1 | 1u);
|
||||
aEW.WriteBytes(aString.Data(), stringLength * sizeof(CHAR));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename CHAR>
|
||||
struct ProfileBufferEntryReader::Deserializer<ProfilerStringView<CHAR>> {
|
||||
static void ReadInto(ProfileBufferEntryReader& aER,
|
||||
ProfilerStringView<CHAR>& aString) {
|
||||
const Length lengthAndIsLiteral = aER.ReadULEB128<Length>();
|
||||
const Length stringLength = lengthAndIsLiteral >> 1;
|
||||
if ((lengthAndIsLiteral & 1u) == 0u) {
|
||||
// LSB==0 -> Literal string, read the string pointer.
|
||||
aString.mStringView = std::basic_string_view<CHAR>(
|
||||
aER.ReadObject<const CHAR*>(), stringLength);
|
||||
aString.mOwnership = ProfilerStringView<CHAR>::Ownership::Literal;
|
||||
return;
|
||||
}
|
||||
// LSB==1 -> Not a literal string, allocate a buffer to store the string
|
||||
// (plus terminal, for safety), and give it to the ProfilerStringView; Note
|
||||
// that this is a secret use of ProfilerStringView, which is intended to
|
||||
// only be used between deserialization and JSON streaming.
|
||||
CHAR* buffer = new CHAR[stringLength + 1];
|
||||
aER.ReadBytes(buffer, stringLength * sizeof(CHAR));
|
||||
buffer[stringLength] = CHAR(0);
|
||||
aString.mStringView = std::basic_string_view<CHAR>(buffer, stringLength);
|
||||
aString.mOwnership =
|
||||
ProfilerStringView<CHAR>::Ownership::OwnedThroughStringView;
|
||||
}
|
||||
|
||||
static ProfilerStringView<CHAR> Read(ProfileBufferEntryReader& aER) {
|
||||
const Length lengthAndIsLiteral = aER.ReadULEB128<Length>();
|
||||
const Length stringLength = lengthAndIsLiteral >> 1;
|
||||
if ((lengthAndIsLiteral & 1u) == 0u) {
|
||||
// LSB==0 -> Literal string, read the string pointer.
|
||||
return ProfilerStringView<CHAR>(
|
||||
aER.ReadObject<const CHAR*>(), stringLength,
|
||||
ProfilerStringView<CHAR>::Ownership::Literal);
|
||||
}
|
||||
// LSB==1 -> Not a literal string, allocate a buffer to store the string
|
||||
// (plus terminal, for safety), and give it to the ProfilerStringView; Note
|
||||
// that this is a secret use of ProfilerStringView, which is intended to
|
||||
// only be used between deserialization and JSON streaming.
|
||||
CHAR* buffer = new CHAR[stringLength + 1];
|
||||
aER.ReadBytes(buffer, stringLength * sizeof(CHAR));
|
||||
buffer[stringLength] = CHAR(0);
|
||||
return ProfilerStringView<CHAR>(
|
||||
buffer, stringLength,
|
||||
ProfilerStringView<CHAR>::Ownership::OwnedThroughStringView);
|
||||
}
|
||||
};
|
||||
|
||||
// Serializer, Deserializer: MarkerCategory
|
||||
|
||||
// The serialization contains both category numbers encoded as ULEB128.
|
||||
template <>
|
||||
struct ProfileBufferEntryWriter::Serializer<MarkerCategory> {
|
||||
static Length Bytes(const MarkerCategory& aCategory) {
|
||||
return ULEB128Size(static_cast<uint32_t>(aCategory.CategoryPair())) +
|
||||
ULEB128Size(static_cast<uint32_t>(aCategory.Category()));
|
||||
}
|
||||
|
||||
static void Write(ProfileBufferEntryWriter& aEW,
|
||||
const MarkerCategory& aCategory) {
|
||||
aEW.WriteULEB128(static_cast<uint32_t>(aCategory.CategoryPair()));
|
||||
aEW.WriteULEB128(static_cast<uint32_t>(aCategory.Category()));
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct ProfileBufferEntryReader::Deserializer<MarkerCategory> {
|
||||
static void ReadInto(ProfileBufferEntryReader& aER,
|
||||
MarkerCategory& aCategory) {
|
||||
aCategory.mCategoryPair = static_cast<baseprofiler::ProfilingCategoryPair>(
|
||||
aER.ReadULEB128<uint32_t>());
|
||||
aCategory.mCategory = static_cast<baseprofiler::ProfilingCategory>(
|
||||
aER.ReadULEB128<uint32_t>());
|
||||
}
|
||||
|
||||
static MarkerCategory Read(ProfileBufferEntryReader& aER) {
|
||||
return MarkerCategory(static_cast<baseprofiler::ProfilingCategoryPair>(
|
||||
aER.ReadULEB128<uint32_t>()),
|
||||
static_cast<baseprofiler::ProfilingCategory>(
|
||||
aER.ReadULEB128<uint32_t>()));
|
||||
}
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Serializer, Deserializer: MarkerTiming
|
||||
|
||||
// The serialization starts with the marker phase, followed by one or two
|
||||
// timestamps as needed.
|
||||
template <>
|
||||
struct ProfileBufferEntryWriter::Serializer<MarkerTiming> {
|
||||
static Length Bytes(const MarkerTiming& aTiming) {
|
||||
MOZ_ASSERT(!aTiming.IsUnspecified());
|
||||
const auto phase = aTiming.MarkerPhase();
|
||||
switch (phase) {
|
||||
case MarkerTiming::Phase::Instant:
|
||||
return SumBytes(phase, aTiming.StartTime());
|
||||
case MarkerTiming::Phase::Interval:
|
||||
return SumBytes(phase, aTiming.StartTime(), aTiming.EndTime());
|
||||
case MarkerTiming::Phase::IntervalStart:
|
||||
return SumBytes(phase, aTiming.StartTime());
|
||||
case MarkerTiming::Phase::IntervalEnd:
|
||||
return SumBytes(phase, aTiming.EndTime());
|
||||
default:
|
||||
MOZ_RELEASE_ASSERT(phase == MarkerTiming::Phase::Instant ||
|
||||
phase == MarkerTiming::Phase::Interval ||
|
||||
phase == MarkerTiming::Phase::IntervalStart ||
|
||||
phase == MarkerTiming::Phase::IntervalEnd);
|
||||
return 0; // Only to avoid build errors.
|
||||
}
|
||||
}
|
||||
|
||||
static void Write(ProfileBufferEntryWriter& aEW,
|
||||
const MarkerTiming& aTiming) {
|
||||
MOZ_ASSERT(!aTiming.IsUnspecified());
|
||||
const auto phase = aTiming.MarkerPhase();
|
||||
switch (phase) {
|
||||
case MarkerTiming::Phase::Instant:
|
||||
aEW.WriteObjects(phase, aTiming.StartTime());
|
||||
return;
|
||||
case MarkerTiming::Phase::Interval:
|
||||
aEW.WriteObjects(phase, aTiming.StartTime(), aTiming.EndTime());
|
||||
return;
|
||||
case MarkerTiming::Phase::IntervalStart:
|
||||
aEW.WriteObjects(phase, aTiming.StartTime());
|
||||
return;
|
||||
case MarkerTiming::Phase::IntervalEnd:
|
||||
aEW.WriteObjects(phase, aTiming.EndTime());
|
||||
return;
|
||||
default:
|
||||
MOZ_RELEASE_ASSERT(phase == MarkerTiming::Phase::Instant ||
|
||||
phase == MarkerTiming::Phase::Interval ||
|
||||
phase == MarkerTiming::Phase::IntervalStart ||
|
||||
phase == MarkerTiming::Phase::IntervalEnd);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct ProfileBufferEntryReader::Deserializer<MarkerTiming> {
|
||||
static void ReadInto(ProfileBufferEntryReader& aER, MarkerTiming& aTiming) {
|
||||
aTiming.mPhase = aER.ReadObject<MarkerTiming::Phase>();
|
||||
switch (aTiming.mPhase) {
|
||||
case MarkerTiming::Phase::Instant:
|
||||
aTiming.mStartTime = aER.ReadObject<TimeStamp>();
|
||||
aTiming.mEndTime = TimeStamp{};
|
||||
break;
|
||||
case MarkerTiming::Phase::Interval:
|
||||
aTiming.mStartTime = aER.ReadObject<TimeStamp>();
|
||||
aTiming.mEndTime = aER.ReadObject<TimeStamp>();
|
||||
break;
|
||||
case MarkerTiming::Phase::IntervalStart:
|
||||
aTiming.mStartTime = aER.ReadObject<TimeStamp>();
|
||||
aTiming.mEndTime = TimeStamp{};
|
||||
break;
|
||||
case MarkerTiming::Phase::IntervalEnd:
|
||||
aTiming.mStartTime = TimeStamp{};
|
||||
aTiming.mEndTime = aER.ReadObject<TimeStamp>();
|
||||
break;
|
||||
default:
|
||||
MOZ_RELEASE_ASSERT(aTiming.mPhase == MarkerTiming::Phase::Instant ||
|
||||
aTiming.mPhase == MarkerTiming::Phase::Interval ||
|
||||
aTiming.mPhase ==
|
||||
MarkerTiming::Phase::IntervalStart ||
|
||||
aTiming.mPhase == MarkerTiming::Phase::IntervalEnd);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static MarkerTiming Read(ProfileBufferEntryReader& aER) {
|
||||
TimeStamp start;
|
||||
TimeStamp end;
|
||||
auto phase = aER.ReadObject<MarkerTiming::Phase>();
|
||||
switch (phase) {
|
||||
case MarkerTiming::Phase::Instant:
|
||||
start = aER.ReadObject<TimeStamp>();
|
||||
break;
|
||||
case MarkerTiming::Phase::Interval:
|
||||
start = aER.ReadObject<TimeStamp>();
|
||||
end = aER.ReadObject<TimeStamp>();
|
||||
break;
|
||||
case MarkerTiming::Phase::IntervalStart:
|
||||
start = aER.ReadObject<TimeStamp>();
|
||||
break;
|
||||
case MarkerTiming::Phase::IntervalEnd:
|
||||
end = aER.ReadObject<TimeStamp>();
|
||||
break;
|
||||
default:
|
||||
MOZ_RELEASE_ASSERT(phase == MarkerTiming::Phase::Instant ||
|
||||
phase == MarkerTiming::Phase::Interval ||
|
||||
phase == MarkerTiming::Phase::IntervalStart ||
|
||||
phase == MarkerTiming::Phase::IntervalEnd);
|
||||
break;
|
||||
}
|
||||
return MarkerTiming(start, end, phase);
|
||||
}
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Serializer, Deserializer: MarkerStack
|
||||
|
||||
// The serialization only contains the `ProfileChunkedBuffer` from the
|
||||
// backtrace; if there is no backtrace or if it's empty, this will implicitly
|
||||
// store a nullptr (see
|
||||
// `ProfileBufferEntryWriter::Serializer<ProfilerChunkedBuffer*>`).
|
||||
template <>
|
||||
struct ProfileBufferEntryWriter::Serializer<MarkerStack> {
|
||||
static Length Bytes(const MarkerStack& aStack) {
|
||||
return SumBytes(aStack.GetChunkedBuffer());
|
||||
}
|
||||
|
||||
static void Write(ProfileBufferEntryWriter& aEW, const MarkerStack& aStack) {
|
||||
aEW.WriteObject(aStack.GetChunkedBuffer());
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct ProfileBufferEntryReader::Deserializer<MarkerStack> {
|
||||
static void ReadInto(ProfileBufferEntryReader& aER, MarkerStack& aStack) {
|
||||
aStack = Read(aER);
|
||||
}
|
||||
|
||||
static MarkerStack Read(ProfileBufferEntryReader& aER) {
|
||||
return MarkerStack(aER.ReadObject<UniquePtr<ProfileChunkedBuffer>>());
|
||||
}
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Serializer, Deserializer: MarkerOptions
|
||||
|
||||
// The serialization contains all members (either trivially-copyable, or they
|
||||
// provide their specialization above).
|
||||
template <>
|
||||
struct ProfileBufferEntryWriter::Serializer<MarkerOptions> {
|
||||
static Length Bytes(const MarkerOptions& aOptions) {
|
||||
return SumBytes(aOptions.Category(), aOptions.ThreadId(), aOptions.Timing(),
|
||||
aOptions.Stack(), aOptions.InnerWindowId());
|
||||
}
|
||||
|
||||
static void Write(ProfileBufferEntryWriter& aEW,
|
||||
const MarkerOptions& aOptions) {
|
||||
aEW.WriteObjects(aOptions.Category(), aOptions.ThreadId(),
|
||||
aOptions.Timing(), aOptions.Stack(),
|
||||
aOptions.InnerWindowId());
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct ProfileBufferEntryReader::Deserializer<MarkerOptions> {
|
||||
static void ReadInto(ProfileBufferEntryReader& aER, MarkerOptions& aOptions) {
|
||||
aER.ReadIntoObjects(aOptions.mCategory, aOptions.mThreadId,
|
||||
aOptions.mTiming, aOptions.mStack,
|
||||
aOptions.mInnerWindowId);
|
||||
}
|
||||
|
||||
static MarkerOptions Read(ProfileBufferEntryReader& aER) {
|
||||
MarkerOptions options;
|
||||
ReadInto(aER, options);
|
||||
return options;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // MOZ_GECKO_PROFILER
|
||||
|
||||
#endif // BaseProfilerMarkersDetail_h
|
@ -1,646 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; 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/. */
|
||||
|
||||
// This header contains basic definitions required to create marker types, and
|
||||
// to add markers to the profiler buffers.
|
||||
//
|
||||
// In most cases, #include "mozilla/BaseProfilerMarkers.h" instead, or
|
||||
// #include "mozilla/BaseProfilerMarkerTypes.h" for common marker types.
|
||||
|
||||
#ifndef BaseProfilerMarkersPrerequisites_h
|
||||
#define BaseProfilerMarkersPrerequisites_h
|
||||
|
||||
#ifdef MOZ_GECKO_PROFILER
|
||||
|
||||
# include "BaseProfilingCategory.h"
|
||||
# include "mozilla/ProfileChunkedBuffer.h"
|
||||
# include "mozilla/TimeStamp.h"
|
||||
# include "mozilla/UniquePtr.h"
|
||||
|
||||
# include <string_view>
|
||||
# include <string>
|
||||
# include <type_traits>
|
||||
# include <utility>
|
||||
|
||||
namespace mozilla::baseprofiler {
|
||||
// Implemented in platform.cpp
|
||||
MFBT_API int profiler_current_thread_id();
|
||||
} // namespace mozilla::baseprofiler
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
// Return a NotNull<const CHAR*> pointing at the literal empty string `""`.
|
||||
template <typename CHAR>
|
||||
constexpr const CHAR* LiteralEmptyStringPointer() {
|
||||
static_assert(std::is_same_v<CHAR, char> || std::is_same_v<CHAR, char16_t>,
|
||||
"Only char and char16_t are supported in Firefox");
|
||||
if constexpr (std::is_same_v<CHAR, char>) {
|
||||
return "";
|
||||
}
|
||||
if constexpr (std::is_same_v<CHAR, char16_t>) {
|
||||
return u"";
|
||||
}
|
||||
}
|
||||
|
||||
// Return a string_view<CHAR> pointing at the literal empty string.
|
||||
template <typename CHAR>
|
||||
constexpr std::basic_string_view<CHAR> LiteralEmptyStringView() {
|
||||
static_assert(std::is_same_v<CHAR, char> || std::is_same_v<CHAR, char16_t>,
|
||||
"Only char and char16_t are supported in Firefox");
|
||||
// Use `operator""sv()` from <string_view>.
|
||||
using namespace std::literals::string_view_literals;
|
||||
if constexpr (std::is_same_v<CHAR, char>) {
|
||||
return ""sv;
|
||||
}
|
||||
if constexpr (std::is_same_v<CHAR, char16_t>) {
|
||||
return u""sv;
|
||||
}
|
||||
}
|
||||
|
||||
// General string view, optimized for short on-stack life before serialization,
|
||||
// and between deserialization and JSON-streaming.
|
||||
template <typename CHAR>
|
||||
class MOZ_STACK_CLASS ProfilerStringView {
|
||||
public:
|
||||
// Default constructor points at "" (literal empty string).
|
||||
constexpr ProfilerStringView() = default;
|
||||
|
||||
// Don't allow copy.
|
||||
ProfilerStringView(const ProfilerStringView&) = delete;
|
||||
ProfilerStringView& operator=(const ProfilerStringView&) = delete;
|
||||
|
||||
// Allow move. For consistency the moved-from string is always reset to "".
|
||||
constexpr ProfilerStringView(ProfilerStringView&& aOther)
|
||||
: mStringView(std::move(aOther.mStringView)),
|
||||
mOwnership(aOther.mOwnership) {
|
||||
if (mOwnership == Ownership::OwnedThroughStringView) {
|
||||
// We now own the buffer, make the other point at the literal "".
|
||||
aOther.mStringView = LiteralEmptyStringView<CHAR>();
|
||||
aOther.mOwnership = Ownership::Literal;
|
||||
}
|
||||
}
|
||||
constexpr ProfilerStringView& operator=(ProfilerStringView&& aOther) {
|
||||
mStringView = std::move(aOther.mStringView);
|
||||
mOwnership = aOther.mOwnership;
|
||||
if (mOwnership == Ownership::OwnedThroughStringView) {
|
||||
// We now own the buffer, make the other point at the literal "".
|
||||
aOther.mStringView = LiteralEmptyStringView<CHAR>();
|
||||
aOther.mOwnership = Ownership::Literal;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
~ProfilerStringView() {
|
||||
if (MOZ_UNLIKELY(mOwnership == Ownership::OwnedThroughStringView)) {
|
||||
// We own the buffer pointed at by mStringView, destroy it.
|
||||
// This is only used between deserialization and streaming.
|
||||
delete mStringView.data();
|
||||
}
|
||||
}
|
||||
|
||||
// Implicit construction from nullptr, points at "" (literal empty string).
|
||||
constexpr MOZ_IMPLICIT ProfilerStringView(decltype(nullptr)) {}
|
||||
|
||||
// Implicit constructor from a literal string.
|
||||
template <size_t Np1>
|
||||
constexpr MOZ_IMPLICIT ProfilerStringView(const CHAR (&aLiteralString)[Np1])
|
||||
: ProfilerStringView(aLiteralString, Np1 - 1, Ownership::Literal) {}
|
||||
|
||||
// Constructor from a non-literal string.
|
||||
constexpr ProfilerStringView(const CHAR* aString, size_t aLength)
|
||||
: ProfilerStringView(aString, aLength, Ownership::Reference) {}
|
||||
|
||||
// Implicit constructor from a string_view.
|
||||
constexpr MOZ_IMPLICIT ProfilerStringView(
|
||||
const std::basic_string_view<CHAR>& aStringView)
|
||||
: ProfilerStringView(aStringView.data(), aStringView.length(),
|
||||
Ownership::Reference) {}
|
||||
|
||||
// Implicit constructor from an expiring string_view. We assume that the
|
||||
// pointed-at string will outlive this ProfilerStringView.
|
||||
constexpr MOZ_IMPLICIT ProfilerStringView(
|
||||
std::basic_string_view<CHAR>&& aStringView)
|
||||
: ProfilerStringView(aStringView.data(), aStringView.length(),
|
||||
Ownership::Reference) {}
|
||||
|
||||
// Implicit constructor from std::string.
|
||||
constexpr MOZ_IMPLICIT ProfilerStringView(const std::string& aString)
|
||||
: ProfilerStringView(aString.data(), aString.length(),
|
||||
Ownership::Reference) {}
|
||||
|
||||
// Construction from a raw pointer to a null-terminated string.
|
||||
// This is a named class-static function to make it more obvious where work is
|
||||
// being done (to determine the string length), and encourage users to instead
|
||||
// provide a length, if already known.
|
||||
// TODO: Find callers and convert them to constructor instead if possible.
|
||||
static constexpr ProfilerStringView WrapNullTerminatedString(
|
||||
const CHAR* aString) {
|
||||
return ProfilerStringView(
|
||||
aString, aString ? std::char_traits<char>::length(aString) : 0,
|
||||
Ownership::Reference);
|
||||
}
|
||||
|
||||
// Implicit constructor for an object with member functions `Data()`
|
||||
// `Length()`, and `IsLiteral()`, common in xpcom strings.
|
||||
template <
|
||||
typename String,
|
||||
typename DataReturnType = decltype(std::declval<const String>().Data()),
|
||||
typename LengthReturnType =
|
||||
decltype(std::declval<const String>().Length()),
|
||||
typename IsLiteralReturnType =
|
||||
decltype(std::declval<const String>().IsLiteral()),
|
||||
typename =
|
||||
std::enable_if_t<std::is_convertible_v<DataReturnType, const CHAR*> &&
|
||||
std::is_integral_v<LengthReturnType> &&
|
||||
std::is_same_v<IsLiteralReturnType, bool>>>
|
||||
constexpr MOZ_IMPLICIT ProfilerStringView(const String& aString)
|
||||
: ProfilerStringView(
|
||||
static_cast<const CHAR*>(aString.Data()), aString.Length(),
|
||||
aString.IsLiteral() ? Ownership::Literal : Ownership::Reference) {}
|
||||
|
||||
[[nodiscard]] constexpr const std::basic_string_view<CHAR>& StringView()
|
||||
const {
|
||||
return mStringView;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr const CHAR* Data() const {
|
||||
return mStringView.data();
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr size_t Length() const { return mStringView.length(); }
|
||||
|
||||
[[nodiscard]] constexpr bool IsLiteral() const {
|
||||
return mOwnership == Ownership::Literal;
|
||||
}
|
||||
[[nodiscard]] constexpr bool IsReference() const {
|
||||
return mOwnership == Ownership::Reference;
|
||||
}
|
||||
// No `IsOwned...()` because it's a secret, only used internally!
|
||||
|
||||
[[nodiscard]] std::basic_string<CHAR> String() const {
|
||||
return std::basic_string<CHAR>(mStringView);
|
||||
}
|
||||
|
||||
private:
|
||||
enum class Ownership { Literal, Reference, OwnedThroughStringView };
|
||||
|
||||
// Allow deserializer to store anything here.
|
||||
friend ProfileBufferEntryReader::Deserializer<ProfilerStringView>;
|
||||
|
||||
constexpr ProfilerStringView(const CHAR* aString, size_t aLength,
|
||||
Ownership aOwnership)
|
||||
: mStringView(aString ? std::basic_string_view<CHAR>(aString, aLength)
|
||||
: LiteralEmptyStringView<CHAR>()),
|
||||
mOwnership(aString ? aOwnership : Ownership::Literal) {}
|
||||
|
||||
// String view to an outside string (literal or reference).
|
||||
// We may actually own the pointed-at buffer, but it is only used internally
|
||||
// between deserialization and JSON streaming.
|
||||
std::basic_string_view<CHAR> mStringView = LiteralEmptyStringView<CHAR>();
|
||||
|
||||
Ownership mOwnership = Ownership::Literal;
|
||||
};
|
||||
|
||||
using ProfilerString8View = ProfilerStringView<char>;
|
||||
using ProfilerString16View = ProfilerStringView<char16_t>;
|
||||
|
||||
// The classes below are all embedded in a `MarkerOptions` object.
|
||||
class MarkerOptions;
|
||||
|
||||
// This compulsory marker option contains the required category information.
|
||||
class MarkerCategory {
|
||||
public:
|
||||
// Constructor from category pair (aka sub-category) and category.
|
||||
constexpr MarkerCategory(baseprofiler::ProfilingCategoryPair aCategoryPair,
|
||||
baseprofiler::ProfilingCategory aCategory)
|
||||
: mCategoryPair(aCategoryPair), mCategory(aCategory) {}
|
||||
|
||||
constexpr baseprofiler::ProfilingCategoryPair CategoryPair() const {
|
||||
return mCategoryPair;
|
||||
}
|
||||
|
||||
constexpr baseprofiler::ProfilingCategory Category() const {
|
||||
return mCategory;
|
||||
}
|
||||
|
||||
// Create a MarkerOptions object from this category and options.
|
||||
// Definition under MarkerOptions below.
|
||||
template <typename... Options>
|
||||
MarkerOptions WithOptions(Options&&... aOptions) const;
|
||||
|
||||
private:
|
||||
// The default constructor is only used during deserialization of
|
||||
// MarkerOptions.
|
||||
friend MarkerOptions;
|
||||
constexpr MarkerCategory() = default;
|
||||
|
||||
friend ProfileBufferEntryReader::Deserializer<MarkerCategory>;
|
||||
|
||||
baseprofiler::ProfilingCategoryPair mCategoryPair =
|
||||
baseprofiler::ProfilingCategoryPair::OTHER;
|
||||
baseprofiler::ProfilingCategory mCategory =
|
||||
baseprofiler::ProfilingCategory::OTHER;
|
||||
};
|
||||
|
||||
namespace baseprofiler::category {
|
||||
// Each category-pair (aka subcategory) name constructs a MarkerCategory.
|
||||
// E.g.: mozilla::baseprofiler::category::OTHER_Profiling
|
||||
// Profiler macros will take the category name alone.
|
||||
// E.g.: `PROFILER_MARKER_UNTYPED("name", OTHER_Profiling)`
|
||||
# define CATEGORY_ENUM_BEGIN_CATEGORY(name, labelAsString, color)
|
||||
# define CATEGORY_ENUM_SUBCATEGORY(supercategory, name, labelAsString) \
|
||||
static constexpr MarkerCategory name{ProfilingCategoryPair::name, \
|
||||
ProfilingCategory::supercategory};
|
||||
# define CATEGORY_ENUM_END_CATEGORY
|
||||
MOZ_PROFILING_CATEGORY_LIST(CATEGORY_ENUM_BEGIN_CATEGORY,
|
||||
CATEGORY_ENUM_SUBCATEGORY,
|
||||
CATEGORY_ENUM_END_CATEGORY)
|
||||
# undef CATEGORY_ENUM_BEGIN_CATEGORY
|
||||
# undef CATEGORY_ENUM_SUBCATEGORY
|
||||
# undef CATEGORY_ENUM_END_CATEGORY
|
||||
} // namespace baseprofiler::category
|
||||
|
||||
// This marker option captures a given thread id.
|
||||
// If left unspecified (by default construction) during the add-marker call, the
|
||||
// current thread id will be used then.
|
||||
class MarkerThreadId {
|
||||
public:
|
||||
// Default constructor, keeps the thread id unspecified.
|
||||
constexpr MarkerThreadId() = default;
|
||||
|
||||
// Constructor from a given thread id.
|
||||
constexpr explicit MarkerThreadId(int aThreadId) : mThreadId(aThreadId) {}
|
||||
|
||||
// Use the current thread's id.
|
||||
static MarkerThreadId CurrentThread() {
|
||||
return MarkerThreadId(baseprofiler::profiler_current_thread_id());
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr int ThreadId() const { return mThreadId; }
|
||||
|
||||
[[nodiscard]] constexpr bool IsUnspecified() const { return mThreadId == 0; }
|
||||
|
||||
private:
|
||||
int mThreadId = 0;
|
||||
};
|
||||
|
||||
// This marker option contains marker timing information.
|
||||
// This class encapsulates the logic for correctly storing a marker based on its
|
||||
// Use the static methods to create the MarkerTiming. This is a transient object
|
||||
// that is being used to enforce the constraints of the combinations of the
|
||||
// data.
|
||||
class MarkerTiming {
|
||||
public:
|
||||
// The following static methods are used to create the MarkerTiming based on
|
||||
// the type that it is.
|
||||
|
||||
static MarkerTiming InstantAt(const TimeStamp& aTime) {
|
||||
MOZ_ASSERT(!aTime.IsNull(), "Time is null for an instant marker.");
|
||||
return MarkerTiming{aTime, TimeStamp{}, MarkerTiming::Phase::Instant};
|
||||
}
|
||||
|
||||
static MarkerTiming InstantNow() {
|
||||
return InstantAt(TimeStamp::NowUnfuzzed());
|
||||
}
|
||||
|
||||
static MarkerTiming Interval(const TimeStamp& aStartTime,
|
||||
const TimeStamp& aEndTime) {
|
||||
MOZ_ASSERT(!aStartTime.IsNull(),
|
||||
"Start time is null for an interval marker.");
|
||||
MOZ_ASSERT(!aEndTime.IsNull(), "End time is null for an interval marker.");
|
||||
return MarkerTiming{aStartTime, aEndTime, MarkerTiming::Phase::Interval};
|
||||
}
|
||||
|
||||
static MarkerTiming IntervalUntilNowFrom(const TimeStamp& aStartTime) {
|
||||
return Interval(aStartTime, TimeStamp::NowUnfuzzed());
|
||||
}
|
||||
|
||||
static MarkerTiming IntervalStart(
|
||||
const TimeStamp& aTime = TimeStamp::NowUnfuzzed()) {
|
||||
MOZ_ASSERT(!aTime.IsNull(), "Time is null for an interval start marker.");
|
||||
return MarkerTiming{aTime, TimeStamp{}, MarkerTiming::Phase::IntervalStart};
|
||||
}
|
||||
|
||||
static MarkerTiming IntervalEnd(
|
||||
const TimeStamp& aTime = TimeStamp::NowUnfuzzed()) {
|
||||
MOZ_ASSERT(!aTime.IsNull(), "Time is null for an interval end marker.");
|
||||
return MarkerTiming{TimeStamp{}, aTime, MarkerTiming::Phase::IntervalEnd};
|
||||
}
|
||||
|
||||
[[nodiscard]] const TimeStamp& StartTime() const { return mStartTime; }
|
||||
[[nodiscard]] const TimeStamp& EndTime() const { return mEndTime; }
|
||||
|
||||
enum class Phase : uint8_t {
|
||||
Instant = 0,
|
||||
Interval = 1,
|
||||
IntervalStart = 2,
|
||||
IntervalEnd = 3,
|
||||
};
|
||||
|
||||
[[nodiscard]] Phase MarkerPhase() const {
|
||||
MOZ_ASSERT(!IsUnspecified());
|
||||
return mPhase;
|
||||
}
|
||||
|
||||
// The following getter methods are used to put the value into the buffer for
|
||||
// storage.
|
||||
[[nodiscard]] double GetStartTime() const {
|
||||
MOZ_ASSERT(!IsUnspecified());
|
||||
// If mStartTime is null (e.g., for IntervalEnd), this will output 0.0 as
|
||||
// expected.
|
||||
return MarkerTiming::timeStampToDouble(mStartTime);
|
||||
}
|
||||
|
||||
[[nodiscard]] double GetEndTime() const {
|
||||
MOZ_ASSERT(!IsUnspecified());
|
||||
// If mEndTime is null (e.g., for Instant or IntervalStart), this will
|
||||
// output 0.0 as expected.
|
||||
return MarkerTiming::timeStampToDouble(mEndTime);
|
||||
}
|
||||
|
||||
[[nodiscard]] uint8_t GetPhase() const {
|
||||
MOZ_ASSERT(!IsUnspecified());
|
||||
return static_cast<uint8_t>(mPhase);
|
||||
}
|
||||
|
||||
private:
|
||||
friend ProfileBufferEntryWriter::Serializer<MarkerTiming>;
|
||||
friend ProfileBufferEntryReader::Deserializer<MarkerTiming>;
|
||||
friend MarkerOptions;
|
||||
|
||||
// Default timing leaves it internally "unspecified", serialization getters
|
||||
// and add-marker functions will default to `InstantNow()`.
|
||||
constexpr MarkerTiming() = default;
|
||||
|
||||
// This should only be used by internal profiler code.
|
||||
[[nodiscard]] bool IsUnspecified() const {
|
||||
return mStartTime.IsNull() && mEndTime.IsNull();
|
||||
}
|
||||
|
||||
// Full constructor, used by static factory functions.
|
||||
constexpr MarkerTiming(const TimeStamp& aStartTime, const TimeStamp& aEndTime,
|
||||
Phase aPhase)
|
||||
: mStartTime(aStartTime), mEndTime(aEndTime), mPhase(aPhase) {}
|
||||
|
||||
static double timeStampToDouble(const TimeStamp& time) {
|
||||
if (time.IsNull()) {
|
||||
// The Phase lets us know not to use this value.
|
||||
return 0;
|
||||
}
|
||||
return (time - TimeStamp::ProcessCreation()).ToMilliseconds();
|
||||
}
|
||||
|
||||
TimeStamp mStartTime;
|
||||
TimeStamp mEndTime;
|
||||
Phase mPhase = Phase::Instant;
|
||||
};
|
||||
|
||||
// This marker option allows three cases:
|
||||
// - By default, no stacks are captured.
|
||||
// - The caller can request a stack capture, and the add-marker code will take
|
||||
// care of it in the most efficient way.
|
||||
// - The caller can still provide an existing backtrace, for cases where a
|
||||
// marker reports something that happened elsewhere.
|
||||
class MarkerStack {
|
||||
public:
|
||||
// Default constructor, no capture.
|
||||
constexpr MarkerStack() = default;
|
||||
|
||||
// Disallow copy.
|
||||
MarkerStack(const MarkerStack&) = delete;
|
||||
MarkerStack& operator=(const MarkerStack&) = delete;
|
||||
|
||||
// Allow move.
|
||||
MarkerStack(MarkerStack&& aOther)
|
||||
: mIsCaptureRequested(aOther.mIsCaptureRequested),
|
||||
mOptionalChunkedBufferStorage(
|
||||
std::move(aOther.mOptionalChunkedBufferStorage)),
|
||||
mChunkedBuffer(aOther.mChunkedBuffer) {
|
||||
AssertInvariants();
|
||||
aOther.Clear();
|
||||
}
|
||||
MarkerStack& operator=(MarkerStack&& aOther) {
|
||||
mIsCaptureRequested = aOther.mIsCaptureRequested;
|
||||
mOptionalChunkedBufferStorage =
|
||||
std::move(aOther.mOptionalChunkedBufferStorage);
|
||||
mChunkedBuffer = aOther.mChunkedBuffer;
|
||||
AssertInvariants();
|
||||
aOther.Clear();
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Take ownership of a backtrace. If null or empty, equivalent to NoStack().
|
||||
explicit MarkerStack(UniquePtr<ProfileChunkedBuffer>&& aExternalChunkedBuffer)
|
||||
: mIsCaptureRequested(false),
|
||||
mOptionalChunkedBufferStorage(
|
||||
(!aExternalChunkedBuffer || aExternalChunkedBuffer->IsEmpty())
|
||||
? nullptr
|
||||
: std::move(aExternalChunkedBuffer)),
|
||||
mChunkedBuffer(mOptionalChunkedBufferStorage.get()) {
|
||||
AssertInvariants();
|
||||
}
|
||||
|
||||
// Use an existing backtrace stored elsewhere, which the user must guarantee
|
||||
// is alive during the add-marker call. If empty, equivalent to NoStack().
|
||||
explicit MarkerStack(ProfileChunkedBuffer& aExternalChunkedBuffer)
|
||||
: mIsCaptureRequested(false),
|
||||
mChunkedBuffer(aExternalChunkedBuffer.IsEmpty()
|
||||
? nullptr
|
||||
: &aExternalChunkedBuffer) {
|
||||
AssertInvariants();
|
||||
}
|
||||
|
||||
// Don't capture a stack in this marker.
|
||||
static MarkerStack NoStack() { return MarkerStack(false); }
|
||||
|
||||
// Capture a stack when adding this marker.
|
||||
static MarkerStack Capture() {
|
||||
// Actual capture will be handled inside profiler_add_marker.
|
||||
return MarkerStack(true);
|
||||
}
|
||||
|
||||
// Use an existing backtrace stored elsewhere, which the user must guarantee
|
||||
// is alive during the add-marker call. If empty, equivalent to NoStack().
|
||||
static MarkerStack UseBacktrace(
|
||||
ProfileChunkedBuffer& aExternalChunkedBuffer) {
|
||||
return MarkerStack(aExternalChunkedBuffer);
|
||||
}
|
||||
|
||||
// Take ownership of a backtrace previously captured with
|
||||
// `profiler_capture_backtrace()`. If null, equivalent to NoStack().
|
||||
static MarkerStack TakeBacktrace(
|
||||
UniquePtr<ProfileChunkedBuffer>&& aExternalChunkedBuffer) {
|
||||
return MarkerStack(std::move(aExternalChunkedBuffer));
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsCaptureNeeded() const {
|
||||
// If the chunked buffer already contains something, consider the capture
|
||||
// request already fulfilled.
|
||||
return mIsCaptureRequested;
|
||||
}
|
||||
|
||||
ProfileChunkedBuffer* GetChunkedBuffer() const { return mChunkedBuffer; }
|
||||
|
||||
// Use backtrace after a request. If null, equivalent to NoStack().
|
||||
void UseRequestedBacktrace(ProfileChunkedBuffer* aExternalChunkedBuffer) {
|
||||
MOZ_RELEASE_ASSERT(IsCaptureNeeded());
|
||||
mIsCaptureRequested = false;
|
||||
if (aExternalChunkedBuffer && !aExternalChunkedBuffer->IsEmpty()) {
|
||||
// We only need to use the provided buffer if it is not empty.
|
||||
mChunkedBuffer = aExternalChunkedBuffer;
|
||||
}
|
||||
AssertInvariants();
|
||||
}
|
||||
|
||||
void Clear() {
|
||||
mIsCaptureRequested = false;
|
||||
mOptionalChunkedBufferStorage.reset();
|
||||
mChunkedBuffer = nullptr;
|
||||
AssertInvariants();
|
||||
}
|
||||
|
||||
private:
|
||||
explicit MarkerStack(bool aIsCaptureRequested)
|
||||
: mIsCaptureRequested(aIsCaptureRequested) {
|
||||
AssertInvariants();
|
||||
}
|
||||
|
||||
// This should be called after every constructor and non-const function.
|
||||
void AssertInvariants() const {
|
||||
# ifdef DEBUG
|
||||
if (mIsCaptureRequested) {
|
||||
MOZ_ASSERT(!mOptionalChunkedBufferStorage,
|
||||
"We should not hold a buffer when capture is requested");
|
||||
MOZ_ASSERT(!mChunkedBuffer,
|
||||
"We should not point at a buffer when capture is requested");
|
||||
} else {
|
||||
if (mOptionalChunkedBufferStorage) {
|
||||
MOZ_ASSERT(mChunkedBuffer == mOptionalChunkedBufferStorage.get(),
|
||||
"Non-null mOptionalChunkedBufferStorage must be pointed-at "
|
||||
"by mChunkedBuffer");
|
||||
}
|
||||
if (mChunkedBuffer) {
|
||||
MOZ_ASSERT(!mChunkedBuffer->IsEmpty(),
|
||||
"Non-null mChunkedBuffer must not be empty");
|
||||
}
|
||||
}
|
||||
# endif // DEBUG
|
||||
}
|
||||
|
||||
// True if a capture is requested when marker is added to the profile buffer.
|
||||
bool mIsCaptureRequested = false;
|
||||
|
||||
// Optional storage for the backtrace, in case it was captured before the
|
||||
// add-marker call.
|
||||
UniquePtr<ProfileChunkedBuffer> mOptionalChunkedBufferStorage;
|
||||
|
||||
// If not null, this points to the backtrace. It may point to a backtrace
|
||||
// temporarily stored on the stack, or to mOptionalChunkedBufferStorage.
|
||||
ProfileChunkedBuffer* mChunkedBuffer = nullptr;
|
||||
};
|
||||
|
||||
// This marker option captures a given inner window id.
|
||||
class MarkerInnerWindowId {
|
||||
public:
|
||||
// Default constructor, it leaves the id unspecified.
|
||||
constexpr MarkerInnerWindowId() = default;
|
||||
|
||||
// Constructor with a specified inner window id.
|
||||
constexpr explicit MarkerInnerWindowId(uint64_t i) : mInnerWindowId(i) {}
|
||||
|
||||
// Explicit option with unspecified id.
|
||||
constexpr static MarkerInnerWindowId NoId() { return MarkerInnerWindowId{}; }
|
||||
|
||||
[[nodiscard]] bool IsUnspecified() const { return mInnerWindowId == scNoId; }
|
||||
|
||||
[[nodiscard]] constexpr uint64_t Id() const { return mInnerWindowId; }
|
||||
|
||||
private:
|
||||
static constexpr uint64_t scNoId = 0;
|
||||
uint64_t mInnerWindowId = scNoId;
|
||||
};
|
||||
|
||||
// This class combines a compulsory category with the above marker options.
|
||||
// To provide options to add-marker functions, first pick a MarkerCategory
|
||||
// object, then options may be added with WithOptions(), e.g.:
|
||||
// `mozilla::baseprofiler::category::OTHER_profiling`
|
||||
// `mozilla::baseprofiler::category::DOM.WithOptions(
|
||||
// MarkerThreadId(1), MarkerTiming::IntervalStart())`
|
||||
class MarkerOptions {
|
||||
public:
|
||||
// Implicit constructor from category.
|
||||
constexpr MOZ_IMPLICIT MarkerOptions(const MarkerCategory& aCategory)
|
||||
: mCategory(aCategory) {}
|
||||
|
||||
// Constructor from category and other options.
|
||||
template <typename... Options>
|
||||
explicit MarkerOptions(const MarkerCategory& aCategory, Options&&... aOptions)
|
||||
: mCategory(aCategory) {
|
||||
(Set(std::forward<Options>(aOptions)), ...);
|
||||
}
|
||||
|
||||
// Disallow copy.
|
||||
MarkerOptions(const MarkerOptions&) = delete;
|
||||
MarkerOptions& operator=(const MarkerOptions&) = delete;
|
||||
|
||||
// Allow move.
|
||||
MarkerOptions(MarkerOptions&&) = default;
|
||||
MarkerOptions& operator=(MarkerOptions&&) = default;
|
||||
|
||||
// The embedded `MarkerTiming` hasn't been specified yet.
|
||||
[[nodiscard]] bool IsTimingUnspecified() const {
|
||||
return mTiming.IsUnspecified();
|
||||
}
|
||||
|
||||
// Each options may be added in a chain by e.g.: `Set(MarkerThreadId(123))`.
|
||||
// Read them with the option name (without "Marker"), e.g.: `ThreadId()`.
|
||||
# define FUNCTION_ON_MEMBER(NAME) \
|
||||
MarkerOptions&& Set(Marker##NAME&& a##NAME) { \
|
||||
m##NAME = std::move(a##NAME); \
|
||||
return std::move(*this); \
|
||||
} \
|
||||
\
|
||||
const Marker##NAME& NAME() const { return m##NAME; } \
|
||||
Marker##NAME& NAME() { return m##NAME; }
|
||||
|
||||
FUNCTION_ON_MEMBER(Category);
|
||||
FUNCTION_ON_MEMBER(ThreadId);
|
||||
FUNCTION_ON_MEMBER(Timing);
|
||||
FUNCTION_ON_MEMBER(Stack);
|
||||
FUNCTION_ON_MEMBER(InnerWindowId);
|
||||
# undef FUNCTION_ON_MEMBER
|
||||
|
||||
private:
|
||||
friend ProfileBufferEntryReader::Deserializer<MarkerOptions>;
|
||||
|
||||
// The default constructor is only used during deserialization.
|
||||
constexpr MarkerOptions() = default;
|
||||
|
||||
MarkerCategory mCategory;
|
||||
MarkerThreadId mThreadId;
|
||||
MarkerTiming mTiming;
|
||||
MarkerStack mStack;
|
||||
MarkerInnerWindowId mInnerWindowId;
|
||||
};
|
||||
|
||||
template <typename... Options>
|
||||
MarkerOptions MarkerCategory::WithOptions(Options&&... aOptions) const {
|
||||
return MarkerOptions(*this, std::forward<Options>(aOptions)...);
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
namespace mozilla::baseprofiler::markers {
|
||||
|
||||
// Default marker payload types, with no extra information, not even a marker
|
||||
// type and payload. This is intended for label-only markers.
|
||||
struct NoPayload final {};
|
||||
|
||||
} // namespace mozilla::baseprofiler::markers
|
||||
|
||||
#endif // MOZ_GECKO_PROFILER
|
||||
|
||||
#endif // BaseProfilerMarkersPrerequisites_h
|
@ -73,9 +73,6 @@ enum class ProfileBufferEntryKind : ProfileBufferEntryKindUnderlyingType {
|
||||
// Marker data, including payload.
|
||||
MarkerData = LEGACY_LIMIT,
|
||||
|
||||
// Markers from 2.0 specs.
|
||||
Marker,
|
||||
|
||||
// Optional between TimeBeforeCompactStack and CompactStack.
|
||||
UnresponsiveDurationMs,
|
||||
|
||||
|
@ -587,11 +587,6 @@ class ProfileChunkedBuffer {
|
||||
return {mRangeStart, mRangeEnd, mPushedBlockCount, mClearedBlockCount};
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsEmpty() const {
|
||||
baseprofiler::detail::BaseProfilerMaybeAutoLock lock(mMutex);
|
||||
return mRangeStart == mRangeEnd;
|
||||
}
|
||||
|
||||
// True if this buffer is already locked on this thread.
|
||||
// This should be used if some functions may call an already-locked buffer,
|
||||
// e.g.: Put -> memory hook -> profiler_add_native_allocation_marker -> Put.
|
||||
@ -1749,33 +1744,6 @@ struct ProfileBufferEntryWriter::Serializer<UniquePtr<ProfileChunkedBuffer>> {
|
||||
}
|
||||
};
|
||||
|
||||
// Serialization of a raw pointer to ProfileChunkedBuffer.
|
||||
// Use Deserializer<UniquePtr<ProfileChunkedBuffer>> to read it back.
|
||||
template <>
|
||||
struct ProfileBufferEntryWriter::Serializer<ProfileChunkedBuffer*> {
|
||||
static Length Bytes(ProfileChunkedBuffer* aBufferUPtr) {
|
||||
if (!aBufferUPtr) {
|
||||
// Null pointer, treat it like an empty buffer, i.e., write length of 0.
|
||||
return ULEB128Size<Length>(0);
|
||||
}
|
||||
// Otherwise write the pointed-at ProfileChunkedBuffer (which could be
|
||||
// out-of-session or empty.)
|
||||
return SumBytes(*aBufferUPtr);
|
||||
}
|
||||
|
||||
static void Write(ProfileBufferEntryWriter& aEW,
|
||||
ProfileChunkedBuffer* aBufferUPtr) {
|
||||
if (!aBufferUPtr) {
|
||||
// Null pointer, treat it like an empty buffer, i.e., write length of 0.
|
||||
aEW.WriteULEB128<Length>(0);
|
||||
return;
|
||||
}
|
||||
// Otherwise write the pointed-at ProfileChunkedBuffer (which could be
|
||||
// out-of-session or empty.)
|
||||
aEW.WriteObject(*aBufferUPtr);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct ProfileBufferEntryReader::Deserializer<UniquePtr<ProfileChunkedBuffer>> {
|
||||
static void ReadInto(ProfileBufferEntryReader& aER,
|
||||
|
@ -11,7 +11,6 @@
|
||||
|
||||
#ifdef MOZ_GECKO_PROFILER
|
||||
# include "BaseProfilerMarkerPayload.h"
|
||||
# include "mozilla/BaseProfilerMarkerTypes.h"
|
||||
# include "mozilla/BlocksRingBuffer.h"
|
||||
# include "mozilla/leb128iterator.h"
|
||||
# include "mozilla/ModuloBuffer.h"
|
||||
@ -37,7 +36,6 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
@ -2985,215 +2983,6 @@ void TestBlocksRingBufferSerialization() {
|
||||
printf("TestBlocksRingBufferSerialization done\n");
|
||||
}
|
||||
|
||||
void TestLiteralEmptyStringView() {
|
||||
printf("TestLiteralEmptyStringView...\n");
|
||||
|
||||
static_assert(mozilla::LiteralEmptyStringView<char>() ==
|
||||
std::string_view(""));
|
||||
static_assert(!!mozilla::LiteralEmptyStringView<char>().data());
|
||||
static_assert(mozilla::LiteralEmptyStringView<char>().length() == 0);
|
||||
|
||||
static_assert(mozilla::LiteralEmptyStringView<char16_t>() ==
|
||||
std::basic_string_view<char16_t>(u""));
|
||||
static_assert(!!mozilla::LiteralEmptyStringView<char16_t>().data());
|
||||
static_assert(mozilla::LiteralEmptyStringView<char16_t>().length() == 0);
|
||||
|
||||
printf("TestLiteralEmptyStringView done\n");
|
||||
}
|
||||
|
||||
void TestProfilerStringView() {
|
||||
printf("TestProfilerStringView...\n");
|
||||
|
||||
// Used to verify implicit constructions, as this will normally be used in
|
||||
// function parameters.
|
||||
auto BS8V = [](mozilla::ProfilerString8View&& aBS8V) {
|
||||
return std::move(aBS8V);
|
||||
};
|
||||
|
||||
// Literal empty string.
|
||||
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(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(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(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::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(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(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:
|
||||
FakeNsCString(const char* aData, size_t aLength, bool aIsLiteral)
|
||||
: mData(aData), mLength(aLength), mIsLiteral(aIsLiteral) {}
|
||||
|
||||
const char* Data() const { return mData; }
|
||||
size_t Length() const { return mLength; }
|
||||
bool IsLiteral() const { return mIsLiteral; }
|
||||
|
||||
private:
|
||||
const char* mData;
|
||||
size_t mLength;
|
||||
bool mIsLiteral;
|
||||
};
|
||||
|
||||
// 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;
|
||||
constexpr ProfileChunkedBuffer::Length chunkMinSize = 128;
|
||||
ProfileBufferChunkManagerWithLocalLimit cm(bufferMaxSize, chunkMinSize);
|
||||
ProfileChunkedBuffer cb(ProfileChunkedBuffer::ThreadSafety::WithMutex, cm);
|
||||
|
||||
// Literal string, serialized as raw pointer.
|
||||
MOZ_RELEASE_ASSERT(cb.PutObject(BS8V("hi")));
|
||||
{
|
||||
unsigned read = 0;
|
||||
ProfilerString8View outerBS8V;
|
||||
cb.ReadEach([&](ProfileBufferEntryReader& aER) {
|
||||
++read;
|
||||
auto bs8v = aER.ReadObject<ProfilerString8View>();
|
||||
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(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::string hiString("hi");
|
||||
MOZ_RELEASE_ASSERT(cb.PutObject(BS8V(hiString)));
|
||||
{
|
||||
unsigned read = 0;
|
||||
ProfilerString8View outerBS8V;
|
||||
cb.ReadEach([&](ProfileBufferEntryReader& aER) {
|
||||
++read;
|
||||
auto bs8v = aER.ReadObject<ProfilerString8View>();
|
||||
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(!bs8v.IsLiteral());
|
||||
MOZ_RELEASE_ASSERT(!bs8v.IsReference());
|
||||
// Test move of ownership.
|
||||
outerBS8V = std::move(bs8v);
|
||||
// NOLINTNEXTLINE(bugprone-use-after-move, clang-analyzer-cplusplus.Move)
|
||||
MOZ_RELEASE_ASSERT(bs8v.Length() == 0);
|
||||
});
|
||||
MOZ_RELEASE_ASSERT(read == 1);
|
||||
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());
|
||||
}
|
||||
|
||||
printf("TestProfilerStringView done\n");
|
||||
}
|
||||
|
||||
void TestProfilerDependencies() {
|
||||
TestPowerOfTwoMask();
|
||||
TestPowerOfTwo();
|
||||
@ -3210,8 +2999,6 @@ void TestProfilerDependencies() {
|
||||
TestBlocksRingBufferUnderlyingBufferChanges();
|
||||
TestBlocksRingBufferThreading();
|
||||
TestBlocksRingBufferSerialization();
|
||||
TestLiteralEmptyStringView();
|
||||
TestProfilerStringView();
|
||||
}
|
||||
|
||||
class BaseTestMarkerPayload : public baseprofiler::ProfilerMarkerPayload {
|
||||
@ -3398,7 +3185,6 @@ void TestProfiler() {
|
||||
static const unsigned long long fibStart = 37;
|
||||
printf("Fibonacci(%llu)...\n", fibStart);
|
||||
AUTO_BASE_PROFILER_LABEL("Label around Fibonacci", OTHER);
|
||||
|
||||
unsigned long long f = Fibonacci(fibStart);
|
||||
printf("Fibonacci(%llu) = %llu\n", fibStart, f);
|
||||
});
|
||||
@ -3456,20 +3242,6 @@ void TestProfiler() {
|
||||
baseprofiler::FileIOMarkerPayload(
|
||||
"operation", "source", "filename", TimeStamp::NowUnfuzzed(),
|
||||
TimeStamp::NowUnfuzzed(), std::move(cause)));
|
||||
baseprofiler::AddMarker<mozilla::baseprofiler::markers::FileIO>(
|
||||
"m2fileio", mozilla::baseprofiler::category::OTHER, "op2", "src2", "f2",
|
||||
MarkerThreadId{});
|
||||
baseprofiler::AddMarker<mozilla::baseprofiler::markers::FileIO>(
|
||||
"m2fileio-capture",
|
||||
mozilla::baseprofiler::category::OTHER.WithOptions(
|
||||
MarkerStack::Capture()),
|
||||
"op2", "src2", "f2", MarkerThreadId{});
|
||||
baseprofiler::AddMarker<mozilla::baseprofiler::markers::FileIO>(
|
||||
"m2fileio-take-backtrace",
|
||||
mozilla::baseprofiler::category::OTHER.WithOptions(
|
||||
MarkerStack::TakeBacktrace(
|
||||
baseprofiler::profiler_capture_backtrace())),
|
||||
"op2", "src2", "f2", MarkerThreadId{});
|
||||
|
||||
baseprofiler::profiler_add_marker(
|
||||
"UserTimingMarkerPayload", baseprofiler::ProfilingCategoryPair::OTHER,
|
||||
@ -3499,67 +3271,6 @@ void TestProfiler() {
|
||||
baseprofiler::LogMarkerPayload("module", "text",
|
||||
TimeStamp::NowUnfuzzed()));
|
||||
|
||||
MOZ_RELEASE_ASSERT(baseprofiler::AddMarker(
|
||||
"markers 2.0 without options", mozilla::baseprofiler::category::OTHER));
|
||||
|
||||
MOZ_RELEASE_ASSERT(baseprofiler::AddMarker(
|
||||
"markers 2.0 with option",
|
||||
mozilla::baseprofiler::category::OTHER.WithOptions(
|
||||
MarkerInnerWindowId(123))));
|
||||
|
||||
MOZ_RELEASE_ASSERT(baseprofiler::AddMarker<>(
|
||||
"default-templated markers 2.0 without options",
|
||||
mozilla::baseprofiler::category::OTHER));
|
||||
|
||||
MOZ_RELEASE_ASSERT(baseprofiler::AddMarker<>(
|
||||
"default-templated markers 2.0 with option",
|
||||
mozilla::baseprofiler::category::OTHER.WithOptions(
|
||||
MarkerInnerWindowId(123))));
|
||||
|
||||
MOZ_RELEASE_ASSERT(
|
||||
baseprofiler::AddMarker<::mozilla::baseprofiler::markers::NoPayload>(
|
||||
"explicitly-default-templated markers 2.0 without options",
|
||||
mozilla::baseprofiler::category::OTHER));
|
||||
|
||||
MOZ_RELEASE_ASSERT(
|
||||
baseprofiler::AddMarker<::mozilla::baseprofiler::markers::NoPayload>(
|
||||
"explicitly-default-templated markers 2.0 with option",
|
||||
mozilla::baseprofiler::category::OTHER.WithOptions(
|
||||
MarkerInnerWindowId(123))));
|
||||
|
||||
MOZ_RELEASE_ASSERT(
|
||||
baseprofiler::AddMarker<mozilla::baseprofiler::markers::Tracing>(
|
||||
"tracing", mozilla::baseprofiler::category::OTHER, "category"));
|
||||
|
||||
MOZ_RELEASE_ASSERT(
|
||||
baseprofiler::AddMarker<mozilla::baseprofiler::markers::UserTimingMark>(
|
||||
"mark", mozilla::baseprofiler::category::OTHER, "mark name"));
|
||||
|
||||
MOZ_RELEASE_ASSERT(baseprofiler::AddMarker<
|
||||
mozilla::baseprofiler::markers::UserTimingMeasure>(
|
||||
"measure", mozilla::baseprofiler::category::OTHER, "measure name",
|
||||
Some(ProfilerString8View("start")), Some(ProfilerString8View("end"))));
|
||||
|
||||
MOZ_RELEASE_ASSERT(
|
||||
baseprofiler::AddMarker<mozilla::baseprofiler::markers::Hang>(
|
||||
"hang", mozilla::baseprofiler::category::OTHER));
|
||||
|
||||
MOZ_RELEASE_ASSERT(
|
||||
baseprofiler::AddMarker<mozilla::baseprofiler::markers::LongTask>(
|
||||
"longtask", mozilla::baseprofiler::category::OTHER));
|
||||
|
||||
MOZ_RELEASE_ASSERT(
|
||||
baseprofiler::AddMarker<mozilla::baseprofiler::markers::Text>(
|
||||
"text", mozilla::baseprofiler::category::OTHER, "text text"));
|
||||
|
||||
MOZ_RELEASE_ASSERT(
|
||||
baseprofiler::AddMarker<mozilla::baseprofiler::markers::Log>(
|
||||
"log", mozilla::baseprofiler::category::OTHER, "module", "text"));
|
||||
|
||||
MOZ_RELEASE_ASSERT(
|
||||
baseprofiler::AddMarker<mozilla::baseprofiler::markers::MediaSample>(
|
||||
"media sample", mozilla::baseprofiler::category::OTHER, 123, 456));
|
||||
|
||||
printf("Sleep 1s...\n");
|
||||
{
|
||||
AUTO_BASE_PROFILER_THREAD_SLEEP;
|
||||
@ -3615,252 +3326,6 @@ void TestProfiler() {
|
||||
printf("TestProfiler done\n");
|
||||
}
|
||||
|
||||
void StreamMarkers(const mozilla::ProfileChunkedBuffer& aBuffer,
|
||||
mozilla::JSONWriter& aWriter) {
|
||||
aWriter.Start();
|
||||
{
|
||||
aWriter.StartArrayProperty("data");
|
||||
{
|
||||
aBuffer.ReadEach([&](mozilla::ProfileBufferEntryReader& aEntryReader) {
|
||||
mozilla::ProfileBufferEntryKind entryKind =
|
||||
aEntryReader.ReadObject<mozilla::ProfileBufferEntryKind>();
|
||||
MOZ_RELEASE_ASSERT(entryKind ==
|
||||
mozilla::ProfileBufferEntryKind::Marker);
|
||||
|
||||
const bool success = mozilla::base_profiler_markers_detail::
|
||||
DeserializeAfterKindAndStream(
|
||||
aEntryReader, aWriter, 0,
|
||||
[&](const mozilla::ProfilerString8View& aName) {
|
||||
aWriter.StringElement(aName.String().c_str());
|
||||
},
|
||||
[&](mozilla::ProfileChunkedBuffer&) {
|
||||
aWriter.StringElement("Real backtrace would be here");
|
||||
});
|
||||
MOZ_RELEASE_ASSERT(success);
|
||||
});
|
||||
}
|
||||
aWriter.EndArray();
|
||||
}
|
||||
aWriter.End();
|
||||
}
|
||||
|
||||
void PrintMarkers(const mozilla::ProfileChunkedBuffer& aBuffer) {
|
||||
mozilla::baseprofiler::SpliceableJSONWriter writer(
|
||||
mozilla::MakeUnique<mozilla::baseprofiler::OStreamJSONWriteFunc>(
|
||||
std::cout));
|
||||
StreamMarkers(aBuffer, writer);
|
||||
}
|
||||
|
||||
void TestMarkerNoPayload() {
|
||||
printf("TestMarkerNoPayload...\n");
|
||||
|
||||
mozilla::ProfileBufferChunkManagerSingle chunkManager(512);
|
||||
mozilla::ProfileChunkedBuffer buffer(
|
||||
mozilla::ProfileChunkedBuffer::ThreadSafety::WithoutMutex, chunkManager);
|
||||
|
||||
mozilla::ProfileBufferBlockIndex i0 =
|
||||
mozilla::baseprofiler::AddMarkerToBuffer<>(
|
||||
buffer, "literal", mozilla::baseprofiler::category::OTHER_Profiling);
|
||||
MOZ_RELEASE_ASSERT(i0);
|
||||
|
||||
const std::string dynamic = "dynamic";
|
||||
mozilla::ProfileBufferBlockIndex i1 =
|
||||
mozilla::baseprofiler::AddMarkerToBuffer<>(
|
||||
buffer, dynamic,
|
||||
mozilla::baseprofiler::category::GRAPHICS_FlushingAsyncPaints);
|
||||
MOZ_RELEASE_ASSERT(i1);
|
||||
MOZ_RELEASE_ASSERT(i1 > i0);
|
||||
|
||||
mozilla::ProfileBufferBlockIndex i2 =
|
||||
mozilla::baseprofiler::AddMarkerToBuffer<>(
|
||||
buffer, std::string_view("string_view"),
|
||||
mozilla::baseprofiler::category::GRAPHICS_FlushingAsyncPaints);
|
||||
MOZ_RELEASE_ASSERT(i2);
|
||||
MOZ_RELEASE_ASSERT(i2 > i1);
|
||||
|
||||
# ifdef DEBUG
|
||||
buffer.Dump();
|
||||
# endif
|
||||
|
||||
PrintMarkers(buffer);
|
||||
|
||||
printf("TestMarkerNoPayload done\n");
|
||||
}
|
||||
|
||||
void TestUserMarker() {
|
||||
printf("TestUserMarker...\n");
|
||||
|
||||
// User-defined marker type with text. If there are no `Convert` functions,
|
||||
// it's fine to define it right in the function where it's used.
|
||||
struct MarkerTypeTestMinimal {
|
||||
static constexpr const char* MarkerTypeName() { return "test-minimal"; }
|
||||
static void StreamJSONMarkerData(mozilla::JSONWriter& aWriter,
|
||||
const std::string& aText) {
|
||||
aWriter.StringProperty("text", aText.c_str());
|
||||
}
|
||||
};
|
||||
|
||||
mozilla::ProfileBufferChunkManagerSingle chunkManager(1024);
|
||||
mozilla::ProfileChunkedBuffer buffer(
|
||||
mozilla::ProfileChunkedBuffer::ThreadSafety::WithoutMutex, chunkManager);
|
||||
|
||||
MOZ_RELEASE_ASSERT(
|
||||
mozilla::baseprofiler::AddMarkerToBuffer<MarkerTypeTestMinimal>(
|
||||
buffer, "test2", mozilla::baseprofiler::category::OTHER_Profiling,
|
||||
std::string("payload text")));
|
||||
|
||||
MOZ_RELEASE_ASSERT(
|
||||
mozilla::baseprofiler::AddMarkerToBuffer<MarkerTypeTestMinimal>(
|
||||
buffer, "test2",
|
||||
mozilla::baseprofiler::category::OTHER_Profiling.WithOptions(
|
||||
mozilla::MarkerThreadId(123)),
|
||||
std::string("ThreadId(123)")));
|
||||
|
||||
auto start = mozilla::TimeStamp::NowUnfuzzed();
|
||||
|
||||
MOZ_RELEASE_ASSERT(
|
||||
mozilla::baseprofiler::AddMarkerToBuffer<MarkerTypeTestMinimal>(
|
||||
buffer, "test2",
|
||||
mozilla::baseprofiler::category::OTHER_Profiling.WithOptions(
|
||||
mozilla::MarkerTiming::InstantAt(start)),
|
||||
std::string("InstantAt(start)")));
|
||||
|
||||
auto then = mozilla::TimeStamp::NowUnfuzzed();
|
||||
|
||||
MOZ_RELEASE_ASSERT(
|
||||
mozilla::baseprofiler::AddMarkerToBuffer<MarkerTypeTestMinimal>(
|
||||
buffer, "test2",
|
||||
mozilla::baseprofiler::category::OTHER_Profiling.WithOptions(
|
||||
mozilla::MarkerTiming::IntervalStart(start)),
|
||||
std::string("IntervalStart(start)")));
|
||||
|
||||
MOZ_RELEASE_ASSERT(
|
||||
mozilla::baseprofiler::AddMarkerToBuffer<MarkerTypeTestMinimal>(
|
||||
buffer, "test2",
|
||||
mozilla::baseprofiler::category::OTHER_Profiling.WithOptions(
|
||||
mozilla::MarkerTiming::IntervalEnd(then)),
|
||||
std::string("IntervalEnd(then)")));
|
||||
|
||||
MOZ_RELEASE_ASSERT(
|
||||
mozilla::baseprofiler::AddMarkerToBuffer<MarkerTypeTestMinimal>(
|
||||
buffer, "test2",
|
||||
mozilla::baseprofiler::category::OTHER_Profiling.WithOptions(
|
||||
mozilla::MarkerTiming::Interval(start, then)),
|
||||
std::string("Interval(start, then)")));
|
||||
|
||||
MOZ_RELEASE_ASSERT(
|
||||
mozilla::baseprofiler::AddMarkerToBuffer<MarkerTypeTestMinimal>(
|
||||
buffer, "test2",
|
||||
mozilla::baseprofiler::category::OTHER_Profiling.WithOptions(
|
||||
mozilla::MarkerTiming::IntervalUntilNowFrom(start)),
|
||||
std::string("IntervalUntilNowFrom(start)")));
|
||||
|
||||
MOZ_RELEASE_ASSERT(
|
||||
mozilla::baseprofiler::AddMarkerToBuffer<MarkerTypeTestMinimal>(
|
||||
buffer, "test2",
|
||||
mozilla::baseprofiler::category::OTHER_Profiling.WithOptions(
|
||||
mozilla::MarkerStack::NoStack()),
|
||||
std::string("NoStack")));
|
||||
// Note: We cannot test stack-capture here, because the profiler is not
|
||||
// initialized.
|
||||
|
||||
MOZ_RELEASE_ASSERT(
|
||||
mozilla::baseprofiler::AddMarkerToBuffer<MarkerTypeTestMinimal>(
|
||||
buffer, "test2",
|
||||
mozilla::baseprofiler::category::OTHER_Profiling.WithOptions(
|
||||
mozilla::MarkerInnerWindowId(123)),
|
||||
std::string("InnerWindowId(123)")));
|
||||
|
||||
# ifdef DEBUG
|
||||
buffer.Dump();
|
||||
# endif
|
||||
|
||||
PrintMarkers(buffer);
|
||||
|
||||
printf("TestUserMarker done\n");
|
||||
}
|
||||
|
||||
void TestPredefinedMarkers() {
|
||||
printf("TestPredefinedMarkers...\n");
|
||||
|
||||
// User-defined marker type with text. If there are no `Convert` functions,
|
||||
// it's fine to define it right in the function where it's used.
|
||||
struct MarkerTypeTestMinimal {
|
||||
static constexpr const char* MarkerTypeName() { return "test-minimal"; }
|
||||
static void StreamJSONMarkerData(mozilla::JSONWriter& aWriter,
|
||||
const std::string& aText) {
|
||||
aWriter.StringProperty("text", aText.c_str());
|
||||
}
|
||||
};
|
||||
|
||||
mozilla::ProfileBufferChunkManagerSingle chunkManager(1024);
|
||||
mozilla::ProfileChunkedBuffer buffer(
|
||||
mozilla::ProfileChunkedBuffer::ThreadSafety::WithoutMutex, chunkManager);
|
||||
|
||||
MOZ_RELEASE_ASSERT(mozilla::baseprofiler::AddMarkerToBuffer<
|
||||
mozilla::baseprofiler::markers::Tracing>(
|
||||
buffer, std::string_view("tracing"),
|
||||
mozilla::baseprofiler::category::OTHER, "category"));
|
||||
|
||||
MOZ_RELEASE_ASSERT(mozilla::baseprofiler::AddMarkerToBuffer<
|
||||
mozilla::baseprofiler::markers::UserTimingMark>(
|
||||
buffer, std::string_view("mark"), mozilla::baseprofiler::category::OTHER,
|
||||
"mark name"));
|
||||
|
||||
MOZ_RELEASE_ASSERT(mozilla::baseprofiler::AddMarkerToBuffer<
|
||||
mozilla::baseprofiler::markers::UserTimingMeasure>(
|
||||
buffer, std::string_view("measure"),
|
||||
mozilla::baseprofiler::category::OTHER, "measure name ",
|
||||
mozilla::Some(mozilla::ProfilerString8View(" start ")),
|
||||
mozilla::Some(mozilla::ProfilerString8View("end"))));
|
||||
|
||||
MOZ_RELEASE_ASSERT(mozilla::baseprofiler::AddMarkerToBuffer<
|
||||
mozilla::baseprofiler::markers::Hang>(
|
||||
buffer, std::string_view("hang"),
|
||||
mozilla::baseprofiler::category::OTHER));
|
||||
|
||||
MOZ_RELEASE_ASSERT(mozilla::baseprofiler::AddMarkerToBuffer<
|
||||
mozilla::baseprofiler::markers::LongTask>(
|
||||
buffer, std::string_view("long task"),
|
||||
mozilla::baseprofiler::category::OTHER));
|
||||
|
||||
MOZ_RELEASE_ASSERT(mozilla::baseprofiler::AddMarkerToBuffer<
|
||||
mozilla::baseprofiler::markers::Text>(
|
||||
buffer, std::string_view("text"), mozilla::baseprofiler::category::OTHER,
|
||||
"text text"));
|
||||
|
||||
MOZ_RELEASE_ASSERT(mozilla::baseprofiler::AddMarkerToBuffer<
|
||||
mozilla::baseprofiler::markers::Log>(
|
||||
buffer, std::string_view("log"), mozilla::baseprofiler::category::OTHER,
|
||||
"module", "text"));
|
||||
|
||||
MOZ_RELEASE_ASSERT(mozilla::baseprofiler::AddMarkerToBuffer<
|
||||
mozilla::baseprofiler::markers::MediaSample>(
|
||||
buffer, std::string_view("media"), mozilla::baseprofiler::category::OTHER,
|
||||
123, 456));
|
||||
|
||||
# ifdef DEBUG
|
||||
buffer.Dump();
|
||||
# endif
|
||||
|
||||
PrintMarkers(buffer);
|
||||
|
||||
printf("TestPredefinedMarkers done\n");
|
||||
}
|
||||
|
||||
void TestProfilerMarkers() {
|
||||
printf("TestProfilerMarkers -- pid: %d, tid: %d\n",
|
||||
mozilla::baseprofiler::profiler_current_process_id(),
|
||||
mozilla::baseprofiler::profiler_current_thread_id());
|
||||
// ::SleepMilli(10000);
|
||||
|
||||
TestMarkerNoPayload();
|
||||
TestUserMarker();
|
||||
TestPredefinedMarkers();
|
||||
|
||||
printf("TestProfilerMarkers done\n");
|
||||
}
|
||||
|
||||
#else // MOZ_GECKO_PROFILER
|
||||
|
||||
// Testing that macros are still #defined (but do nothing) when
|
||||
@ -3868,50 +3333,14 @@ void TestProfilerMarkers() {
|
||||
void TestProfiler() {
|
||||
// These don't need to make sense, we just want to know that they're defined
|
||||
// and don't do anything.
|
||||
|
||||
# ifndef AUTO_BASE_PROFILER_INIT
|
||||
# error AUTO_BASE_PROFILER_INIT not #defined
|
||||
# endif // AUTO_BASE_PROFILER_INIT
|
||||
AUTO_BASE_PROFILER_INIT;
|
||||
|
||||
// This wouldn't build if the macro did output its arguments.
|
||||
# ifndef AUTO_BASE_PROFILER_TEXT_MARKER_CAUSE
|
||||
# error AUTO_BASE_PROFILER_TEXT_MARKER_CAUSE not #defined
|
||||
# endif // AUTO_BASE_PROFILER_TEXT_MARKER_CAUSE
|
||||
AUTO_BASE_PROFILER_TEXT_MARKER_CAUSE(catch, catch, catch, catch);
|
||||
|
||||
# ifndef AUTO_BASE_PROFILER_LABEL
|
||||
# error AUTO_BASE_PROFILER_LABEL not #defined
|
||||
# endif // AUTO_BASE_PROFILER_LABEL
|
||||
AUTO_BASE_PROFILER_LABEL(catch, catch);
|
||||
|
||||
# ifndef AUTO_BASE_PROFILER_THREAD_SLEEP
|
||||
# error AUTO_BASE_PROFILER_THREAD_SLEEP not #defined
|
||||
# endif // AUTO_BASE_PROFILER_THREAD_SLEEP
|
||||
AUTO_BASE_PROFILER_THREAD_SLEEP;
|
||||
|
||||
# ifndef BASE_PROFILER_MARKER_UNTYPED
|
||||
# error BASE_PROFILER_MARKER_UNTYPED not #defined
|
||||
# endif // BASE_PROFILER_MARKER_UNTYPED
|
||||
BASE_PROFILER_MARKER_UNTYPED(catch, catch);
|
||||
|
||||
# ifndef BASE_PROFILER_MARKER
|
||||
# error BASE_PROFILER_MARKER not #defined
|
||||
# endif // BASE_PROFILER_MARKER
|
||||
BASE_PROFILER_MARKER(catch, catch, catch);
|
||||
BASE_PROFILER_MARKER(catch, catch, catch, catch);
|
||||
|
||||
# ifndef BASE_PROFILER_MARKER_TEXT
|
||||
# error BASE_PROFILER_MARKER_TEXT not #defined
|
||||
# endif // BASE_PROFILER_MARKER_TEXT
|
||||
BASE_PROFILER_MARKER_TEXT(catch, catch, catch);
|
||||
}
|
||||
|
||||
// Testing that macros are still #defined (but do nothing) when
|
||||
// MOZ_GECKO_PROFILER is disabled.
|
||||
void TestProfilerMarkers() {
|
||||
// These don't need to make sense, we just want to know that they're defined
|
||||
// and don't do anything.
|
||||
}
|
||||
|
||||
#endif // MOZ_GECKO_PROFILER else
|
||||
@ -3929,10 +3358,9 @@ int main()
|
||||
// ::SleepMilli(10000);
|
||||
#endif // MOZ_GECKO_PROFILER
|
||||
|
||||
// Note that there are two `TestProfiler{,Markers}` functions above, depending
|
||||
// on whether MOZ_GECKO_PROFILER is #defined.
|
||||
// Note that there are two `TestProfiler` functions above, depending on
|
||||
// whether MOZ_GECKO_PROFILER is #defined.
|
||||
TestProfiler();
|
||||
TestProfilerMarkers();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -6,10 +6,8 @@
|
||||
|
||||
#include "ProfileBufferEntry.h"
|
||||
|
||||
#include "mozilla/ProfilerMarkers.h"
|
||||
#include "platform.h"
|
||||
#include "ProfileBuffer.h"
|
||||
#include "ProfilerBacktrace.h"
|
||||
#include "ProfilerMarkerPayload.h"
|
||||
|
||||
#include "jsapi.h"
|
||||
@ -674,7 +672,6 @@ class EntryGetter {
|
||||
// */
|
||||
// )
|
||||
// | MarkerData
|
||||
// | Marker
|
||||
// | ( /* Counters */
|
||||
// CounterId
|
||||
// Time
|
||||
@ -1184,83 +1181,51 @@ void ProfileBuffer::StreamMarkersToJSON(SpliceableJSONWriter& aWriter,
|
||||
MOZ_ASSERT(static_cast<ProfileBufferEntry::KindUnderlyingType>(type) <
|
||||
static_cast<ProfileBufferEntry::KindUnderlyingType>(
|
||||
ProfileBufferEntry::Kind::MODERN_LIMIT));
|
||||
// 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<int>() != aThreadId) {
|
||||
break; // Entry not fully read.
|
||||
if (type == ProfileBufferEntry::Kind::MarkerData &&
|
||||
aER.ReadObject<int>() == aThreadId) {
|
||||
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<std::string>();
|
||||
auto startTime = aER.ReadObject<double>();
|
||||
auto endTime = aER.ReadObject<double>();
|
||||
auto phase = aER.ReadObject<uint8_t>();
|
||||
const JS::ProfilingCategoryPairInfo& info =
|
||||
GetProfilingCategoryPairInfo(static_cast<JS::ProfilingCategoryPair>(
|
||||
aER.ReadObject<uint32_t>()));
|
||||
auto payload = aER.ReadObject<UniquePtr<ProfilerMarkerPayload>>();
|
||||
|
||||
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.c_str());
|
||||
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.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<std::string>();
|
||||
auto startTime = aER.ReadObject<double>();
|
||||
auto endTime = aER.ReadObject<double>();
|
||||
auto phase = aER.ReadObject<uint8_t>();
|
||||
const JS::ProfilingCategoryPairInfo& info =
|
||||
GetProfilingCategoryPairInfo(
|
||||
static_cast<JS::ProfilingCategoryPair>(
|
||||
aER.ReadObject<uint32_t>()));
|
||||
auto payload = aER.ReadObject<UniquePtr<ProfilerMarkerPayload>>();
|
||||
|
||||
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.c_str());
|
||||
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,
|
||||
[&](const mozilla::ProfilerString8View& aName) {
|
||||
aUniqueStacks.mUniqueStrings->WriteElement(
|
||||
aWriter, aName.String().c_str());
|
||||
},
|
||||
[&](ProfileChunkedBuffer& aChunkedBuffer) {
|
||||
ProfilerBacktrace backtrace("", aThreadId,
|
||||
&aChunkedBuffer);
|
||||
backtrace.StreamJSON(aWriter, aProcessStartTime,
|
||||
aUniqueStacks);
|
||||
})) {
|
||||
return; // Entry fully read.
|
||||
}
|
||||
break; // Entry not fully read.
|
||||
|
||||
default:
|
||||
break; // Entry not fully read.
|
||||
}
|
||||
aWriter.EndArray();
|
||||
} else {
|
||||
aER.SetRemainingBytes(0);
|
||||
}
|
||||
|
||||
aER.SetRemainingBytes(0);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -139,8 +139,8 @@ ProfilerMarkerPayload::DeserializeCommonProps(
|
||||
// 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.
|
||||
// the MarkerPhase 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");
|
||||
|
@ -695,11 +695,6 @@ class CorePS {
|
||||
|
||||
CorePS* CorePS::sInstance = nullptr;
|
||||
|
||||
ProfileChunkedBuffer& profiler_get_core_buffer() {
|
||||
MOZ_ASSERT(CorePS::Exists());
|
||||
return CorePS::CoreBuffer();
|
||||
}
|
||||
|
||||
class SamplerThread;
|
||||
|
||||
static SamplerThread* NewSamplerThread(PSLockRef aLock, uint32_t aGeneration,
|
||||
@ -1588,6 +1583,80 @@ void ProfilingStackOwner::DumpStackAndCrash() const {
|
||||
// The name of the main thread.
|
||||
static const char* const kMainThreadName = "GeckoMain";
|
||||
|
||||
enum class MarkerPhase : uint8_t {
|
||||
Instant = 0,
|
||||
Interval = 1,
|
||||
IntervalStart = 2,
|
||||
IntervalEnd = 3,
|
||||
};
|
||||
|
||||
// This class encapsulates the logic for correctly storing a marker based on its
|
||||
// timing type, based on the constraints of that type. Use the static methods to
|
||||
// create the MarkerTiming. This is a transient object that is being used to
|
||||
// enforce the constraints of the combinations of the data.
|
||||
class MarkerTiming {
|
||||
public:
|
||||
// The following static methods are used to create the MarkerTiming based on
|
||||
// the type that it is.
|
||||
static MarkerTiming Instant(
|
||||
const TimeStamp& aTime = TimeStamp::NowUnfuzzed()) {
|
||||
MOZ_ASSERT(!aTime.IsNull(), "Time is null for an instant marker.");
|
||||
return MarkerTiming{aTime, TimeStamp{}, MarkerPhase::Instant};
|
||||
}
|
||||
|
||||
static MarkerTiming Interval(
|
||||
const TimeStamp& aStartTime,
|
||||
const TimeStamp& aEndTime = TimeStamp::NowUnfuzzed()) {
|
||||
MOZ_ASSERT(!aStartTime.IsNull(),
|
||||
"Start time is null for an interval marker.");
|
||||
MOZ_ASSERT(!aEndTime.IsNull(), "End time is null for an interval marker.");
|
||||
return MarkerTiming{aStartTime, aEndTime, MarkerPhase::Interval};
|
||||
}
|
||||
|
||||
static MarkerTiming IntervalStart(
|
||||
const TimeStamp& aTime = TimeStamp::NowUnfuzzed()) {
|
||||
MOZ_ASSERT(!aTime.IsNull(), "Time is null for an interval start marker.");
|
||||
return MarkerTiming{aTime, TimeStamp{}, MarkerPhase::IntervalStart};
|
||||
}
|
||||
|
||||
static MarkerTiming IntervalEnd(
|
||||
const TimeStamp& aTime = TimeStamp::NowUnfuzzed()) {
|
||||
MOZ_ASSERT(!aTime.IsNull(), "Time is null for an interval end marker.");
|
||||
return MarkerTiming{TimeStamp{}, aTime, MarkerPhase::IntervalEnd};
|
||||
}
|
||||
|
||||
// The following getter methods are used to put the value into the buffer for
|
||||
// storage.
|
||||
double GetStartTime() const {
|
||||
return MarkerTiming::timeStampToDouble(mStartTime);
|
||||
}
|
||||
|
||||
double GetEndTime() const {
|
||||
return MarkerTiming::timeStampToDouble(mEndTime);
|
||||
}
|
||||
|
||||
uint8_t GetMarkerPhase() const { return static_cast<uint8_t>(mMarkerPhase); }
|
||||
|
||||
private:
|
||||
MarkerTiming(TimeStamp aStartTime, TimeStamp aEndTime,
|
||||
MarkerPhase aMarkerPhase)
|
||||
: mStartTime(aStartTime),
|
||||
mEndTime(aEndTime),
|
||||
mMarkerPhase(aMarkerPhase) {}
|
||||
|
||||
static double timeStampToDouble(TimeStamp time) {
|
||||
if (time.IsNull()) {
|
||||
// The MarkerPhase lets us know not to use this value.
|
||||
return 0;
|
||||
}
|
||||
return (time - CorePS::ProcessStartTime()).ToMilliseconds();
|
||||
}
|
||||
|
||||
TimeStamp mStartTime;
|
||||
TimeStamp mEndTime;
|
||||
MarkerPhase mMarkerPhase;
|
||||
};
|
||||
|
||||
// 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
|
||||
@ -1599,7 +1668,7 @@ MarkerTiming get_marker_timing_from_payload(
|
||||
if (start.IsNull()) {
|
||||
if (end.IsNull()) {
|
||||
// The payload contains no time information, use the current time.
|
||||
return MarkerTiming::InstantAt(TimeStamp::NowUnfuzzed());
|
||||
return MarkerTiming::Instant(TimeStamp::NowUnfuzzed());
|
||||
}
|
||||
return MarkerTiming::IntervalEnd(end);
|
||||
}
|
||||
@ -1607,7 +1676,7 @@ MarkerTiming get_marker_timing_from_payload(
|
||||
return MarkerTiming::IntervalStart(start);
|
||||
}
|
||||
if (start == end) {
|
||||
return MarkerTiming::InstantAt(start);
|
||||
return MarkerTiming::Instant(start);
|
||||
}
|
||||
return MarkerTiming::Interval(start, end);
|
||||
}
|
||||
@ -1619,11 +1688,12 @@ static void StoreMarker(ProfileChunkedBuffer& aChunkedBuffer, int aThreadId,
|
||||
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<uint32_t>(aCategoryPair), aPayload);
|
||||
aChunkedBuffer.PutObjects(ProfileBufferEntry::Kind::MarkerData, aThreadId,
|
||||
WrapProfileBufferUnownedCString(aMarkerName),
|
||||
aMarkerTiming.GetStartTime(),
|
||||
aMarkerTiming.GetEndTime(),
|
||||
aMarkerTiming.GetMarkerPhase(),
|
||||
static_cast<uint32_t>(aCategoryPair), aPayload);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
@ -2713,7 +2783,7 @@ static void CollectJavaThreadProfileData(ProfileBuffer& aProfileBuffer) {
|
||||
: CorePS::ProcessStartTime() +
|
||||
TimeDuration::FromMilliseconds(endTimeMs);
|
||||
MarkerTiming timing = endTimeMs == 0
|
||||
? MarkerTiming::InstantAt(startTime)
|
||||
? MarkerTiming::Instant(startTime)
|
||||
: MarkerTiming::Interval(startTime, endTime);
|
||||
|
||||
if (!text) {
|
||||
@ -5279,7 +5349,65 @@ double profiler_time() {
|
||||
return delta.ToMilliseconds();
|
||||
}
|
||||
|
||||
bool profiler_capture_backtrace_into(ProfileChunkedBuffer& aChunkedBuffer) {
|
||||
static void locked_profiler_fill_backtrace(PSLockRef aLock,
|
||||
RegisteredThread& aRegisteredThread,
|
||||
ProfileBuffer& aProfileBuffer) {
|
||||
Registers regs;
|
||||
#if defined(HAVE_NATIVE_UNWIND)
|
||||
regs.SyncPopulate();
|
||||
#else
|
||||
regs.Clear();
|
||||
#endif
|
||||
|
||||
DoSyncSample(aLock, aRegisteredThread, TimeStamp::NowUnfuzzed(), regs,
|
||||
aProfileBuffer);
|
||||
}
|
||||
|
||||
static UniqueProfilerBacktrace locked_profiler_get_backtrace(PSLockRef aLock) {
|
||||
if (!ActivePS::Exists(aLock)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RegisteredThread* registeredThread =
|
||||
TLSRegisteredThread::RegisteredThread(aLock);
|
||||
if (!registeredThread) {
|
||||
// If this was called from a non-registered thread, return a nullptr
|
||||
// and do no more work. This can happen from a memory hook. Before
|
||||
// the allocation tracking there was a MOZ_ASSERT() here checking
|
||||
// for the existence of a registeredThread.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto bufferManager = MakeUnique<ProfileChunkedBuffer>(
|
||||
ProfileChunkedBuffer::ThreadSafety::WithoutMutex,
|
||||
MakeUnique<ProfileBufferChunkManagerSingle>(scExpectedMaximumStackSize));
|
||||
ProfileBuffer buffer(*bufferManager);
|
||||
|
||||
locked_profiler_fill_backtrace(aLock, *registeredThread, buffer);
|
||||
|
||||
return UniqueProfilerBacktrace(
|
||||
new ProfilerBacktrace("SyncProfile", registeredThread->Info()->ThreadId(),
|
||||
std::move(bufferManager)));
|
||||
}
|
||||
|
||||
UniqueProfilerBacktrace profiler_get_backtrace() {
|
||||
MOZ_RELEASE_ASSERT(CorePS::Exists());
|
||||
|
||||
// Fast racy early return.
|
||||
if (!profiler_is_active()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PSAutoLock lock(gPSMutex);
|
||||
|
||||
return locked_profiler_get_backtrace(lock);
|
||||
}
|
||||
|
||||
void ProfilerBacktraceDestructor::operator()(ProfilerBacktrace* aBacktrace) {
|
||||
delete aBacktrace;
|
||||
}
|
||||
|
||||
bool profiler_capture_backtrace(ProfileChunkedBuffer& aChunkedBuffer) {
|
||||
MOZ_RELEASE_ASSERT(CorePS::Exists());
|
||||
|
||||
PSAutoLock lock(gPSMutex);
|
||||
@ -5291,62 +5419,17 @@ bool profiler_capture_backtrace_into(ProfileChunkedBuffer& aChunkedBuffer) {
|
||||
RegisteredThread* registeredThread =
|
||||
TLSRegisteredThread::RegisteredThread(lock);
|
||||
if (!registeredThread) {
|
||||
// If this was called from a non-registered thread, return false and do no
|
||||
// more work. This can happen from a memory hook. Before the allocation
|
||||
// tracking there was a MOZ_ASSERT() here checking for the existence of a
|
||||
// registeredThread.
|
||||
MOZ_ASSERT(registeredThread);
|
||||
return false;
|
||||
}
|
||||
|
||||
ProfileBuffer profileBuffer(aChunkedBuffer);
|
||||
|
||||
Registers regs;
|
||||
#if defined(HAVE_NATIVE_UNWIND)
|
||||
regs.SyncPopulate();
|
||||
#else
|
||||
regs.Clear();
|
||||
#endif
|
||||
|
||||
DoSyncSample(lock, *registeredThread, TimeStamp::NowUnfuzzed(), regs,
|
||||
profileBuffer);
|
||||
locked_profiler_fill_backtrace(lock, *registeredThread, profileBuffer);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
UniquePtr<ProfileChunkedBuffer> profiler_capture_backtrace() {
|
||||
MOZ_RELEASE_ASSERT(CorePS::Exists());
|
||||
|
||||
// Quick is-active check before allocating a buffer.
|
||||
if (!profiler_is_active()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto buffer = MakeUnique<ProfileChunkedBuffer>(
|
||||
ProfileChunkedBuffer::ThreadSafety::WithoutMutex,
|
||||
MakeUnique<ProfileBufferChunkManagerSingle>(scExpectedMaximumStackSize));
|
||||
|
||||
if (!profiler_capture_backtrace_into(*buffer)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
UniqueProfilerBacktrace profiler_get_backtrace() {
|
||||
UniquePtr<ProfileChunkedBuffer> buffer = profiler_capture_backtrace();
|
||||
|
||||
if (!buffer) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return UniqueProfilerBacktrace(new ProfilerBacktrace(
|
||||
"SyncProfile", profiler_current_thread_id(), std::move(buffer)));
|
||||
}
|
||||
|
||||
void ProfilerBacktraceDestructor::operator()(ProfilerBacktrace* aBacktrace) {
|
||||
delete aBacktrace;
|
||||
}
|
||||
|
||||
static void racy_profiler_add_marker(const char* aMarkerName,
|
||||
JS::ProfilingCategoryPair aCategoryPair,
|
||||
const ProfilerMarkerPayload* aPayload) {
|
||||
@ -5369,7 +5452,7 @@ static void racy_profiler_add_marker(const char* aMarkerName,
|
||||
|
||||
const MarkerTiming markerTiming =
|
||||
aPayload ? get_marker_timing_from_payload(*aPayload)
|
||||
: MarkerTiming::InstantNow();
|
||||
: MarkerTiming::Instant();
|
||||
|
||||
StoreMarker(CorePS::CoreBuffer(), racyRegisteredThread->ThreadId(),
|
||||
aMarkerName, markerTiming, aCategoryPair, aPayload);
|
||||
@ -5519,20 +5602,21 @@ bool profiler_add_native_allocation_marker(int aMainThreadId, int64_t aSize,
|
||||
// locking the profiler mutex here could end up causing a deadlock if another
|
||||
// mutex is taken, which the profiler may indirectly need elsewhere.
|
||||
// See bug 1642726 for such a scenario.
|
||||
// So instead we bail out if the mutex is already locked. Native allocations
|
||||
// are statistically sampled anyway, so missing a few because of this is
|
||||
// acceptable.
|
||||
if (gPSMutex.IsLockedOnCurrentThread()) {
|
||||
// So instead we only try to lock, and bail out if the mutex is already
|
||||
// locked. Native allocations are statistically sampled anyway, so missing a
|
||||
// few because of this is acceptable.
|
||||
PSAutoTryLock tryLock(gPSMutex);
|
||||
if (!tryLock.IsLocked()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
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);
|
||||
NativeAllocationMarkerPayload(
|
||||
TimeStamp::Now(), aSize, aMemoryAddress, profiler_current_thread_id(),
|
||||
locked_profiler_get_backtrace(tryLock.LockRef())),
|
||||
&tryLock.LockRef());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -139,10 +139,6 @@ EXPORTS.mozilla += [
|
||||
'public/ProfileJSONWriter.h',
|
||||
# vm/GeckoProfiler.h needs to include this and doesn't like #include "ProfilerCounts.h"
|
||||
'public/ProfilerCounts.h',
|
||||
'public/ProfilerMarkers.h',
|
||||
'public/ProfilerMarkersDetail.h',
|
||||
'public/ProfilerMarkersPrerequisites.h',
|
||||
'public/ProfilerMarkerTypes.h',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_VTUNE']:
|
||||
|
@ -18,21 +18,16 @@
|
||||
|
||||
// everything in here is also safe to include unconditionally, and only defines
|
||||
// empty macros if MOZ_GECKO_PROFILER is unset
|
||||
#include "BaseProfiler.h"
|
||||
#include "mozilla/ProfilerCounts.h"
|
||||
|
||||
// ProfilerMarkers.h is #included in the middle of this header!
|
||||
// #include "mozilla/ProfilerMarkers.h"
|
||||
|
||||
#ifndef MOZ_GECKO_PROFILER
|
||||
|
||||
# include "mozilla/ProfilerMarkers.h"
|
||||
# include "mozilla/UniquePtr.h"
|
||||
|
||||
// This file can be #included unconditionally. However, everything within this
|
||||
// file must be guarded by a #ifdef MOZ_GECKO_PROFILER, *except* for the
|
||||
// following macros and functions, which encapsulate the most common operations
|
||||
// and thus avoid the need for many #ifdefs.
|
||||
// following macros, which encapsulate the most common operations and thus
|
||||
// avoid the need for many #ifdefs.
|
||||
|
||||
# define AUTO_PROFILER_INIT
|
||||
# define AUTO_PROFILER_INIT2
|
||||
@ -82,14 +77,8 @@
|
||||
# define AUTO_PROFILER_TEXT_MARKER_DOCSHELL_CAUSE( \
|
||||
markerName, text, categoryPair, docShell, cause)
|
||||
|
||||
// Function stubs for when MOZ_GECKO_PROFILER is not defined.
|
||||
|
||||
struct ProfilerBacktrace {};
|
||||
using UniqueProfilerBacktrace = mozilla::UniquePtr<int>;
|
||||
|
||||
// Get/Capture-backtrace functions can return nullptr or false, the result
|
||||
// should be fed to another empty macro or stub anyway.
|
||||
|
||||
static inline UniqueProfilerBacktrace profiler_get_backtrace() {
|
||||
return nullptr;
|
||||
}
|
||||
@ -97,17 +86,14 @@ static inline UniqueProfilerBacktrace profiler_get_backtrace() {
|
||||
namespace mozilla {
|
||||
class ProfileChunkedBuffer;
|
||||
} // namespace mozilla
|
||||
static inline bool profiler_capture_backtrace_into(
|
||||
static inline bool profiler_capture_backtrace(
|
||||
mozilla::ProfileChunkedBuffer& aChunkedBuffer) {
|
||||
return false;
|
||||
}
|
||||
static inline mozilla::UniquePtr<mozilla::ProfileChunkedBuffer>
|
||||
profiler_capture_backtrace() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#else // !MOZ_GECKO_PROFILER
|
||||
|
||||
# include "BaseProfiler.h"
|
||||
# include "js/AllocationRecording.h"
|
||||
# include "js/ProfilingFrameIterator.h"
|
||||
# include "js/ProfilingStack.h"
|
||||
@ -698,22 +684,10 @@ struct ProfilerBacktraceDestructor {
|
||||
using UniqueProfilerBacktrace =
|
||||
mozilla::UniquePtr<ProfilerBacktrace, ProfilerBacktraceDestructor>;
|
||||
|
||||
// Immediately capture the current thread's call stack, store it in the provided
|
||||
// buffer (usually to avoid allocations if you can construct the buffer on the
|
||||
// stack). Returns false if unsuccessful, or if the profiler is inactive.
|
||||
bool profiler_capture_backtrace_into(
|
||||
mozilla::ProfileChunkedBuffer& aChunkedBuffer);
|
||||
|
||||
// Immediately capture the current thread's call stack, and return it in a
|
||||
// ProfileChunkedBuffer (usually for later use in MarkerStack::TakeBacktrace()).
|
||||
// May be null if unsuccessful, or if the profiler is inactive.
|
||||
mozilla::UniquePtr<mozilla::ProfileChunkedBuffer> profiler_capture_backtrace();
|
||||
|
||||
// Immediately capture the current thread's call stack, and return it in a
|
||||
// ProfilerBacktrace (usually for later use in marker function that take a
|
||||
// ProfilerBacktrace). May be null if unsuccessful, or if the profiler is
|
||||
// inactive.
|
||||
// Immediately capture the current thread's call stack and return it. A no-op
|
||||
// if the profiler is inactive.
|
||||
UniqueProfilerBacktrace profiler_get_backtrace();
|
||||
bool profiler_capture_backtrace(mozilla::ProfileChunkedBuffer& aChunkedBuffer);
|
||||
|
||||
struct ProfilerStats {
|
||||
unsigned n = 0;
|
||||
@ -761,10 +735,6 @@ struct ProfilerBufferInfo {
|
||||
// buffer is being written to, and how much data is visible.
|
||||
mozilla::Maybe<ProfilerBufferInfo> profiler_get_buffer_info();
|
||||
|
||||
// ProfilerMarkers.h requires some stuff from this header.
|
||||
// TODO: Move common stuff to shared header, and move this #include to the top.
|
||||
# include "mozilla/ProfilerMarkers.h"
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Put profiling data into the profiler (labels and markers)
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -1,437 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; 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 ProfilerMarkerTypes_h
|
||||
#define ProfilerMarkerTypes_h
|
||||
|
||||
// This header contains common marker type definitions that rely on xpcom.
|
||||
//
|
||||
// It #include's "mozilla/BaseProfilerMarkerTypess.h" and "ProfilerMarkers.h",
|
||||
// see these files for more marker types, how to define other marker types, and
|
||||
// how to add markers to the profiler buffers.
|
||||
|
||||
// /!\ WORK IN PROGRESS /!\
|
||||
// This file contains draft marker definitions, but most are not used yet.
|
||||
// Further work is needed to complete these definitions, and use them to convert
|
||||
// existing PROFILER_ADD_MARKER calls. See meta bug 1661394.
|
||||
|
||||
#include "mozilla/BaseProfilerMarkerTypes.h"
|
||||
#include "mozilla/ProfilerMarkers.h"
|
||||
|
||||
#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 Tracing = mozilla::baseprofiler::markers::Tracing;
|
||||
using FileIO = mozilla::baseprofiler::markers::FileIO;
|
||||
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 const char* MarkerTypeName() { return "Budget"; }
|
||||
static void StreamJSONMarkerData(mozilla::JSONWriter& aWriter) {}
|
||||
};
|
||||
|
||||
struct DOMEvent {
|
||||
static constexpr const char* MarkerTypeName() {
|
||||
// Note: DOMEventMarkerPayload wase 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 "tracing";
|
||||
}
|
||||
static void StreamJSONMarkerData(
|
||||
mozilla::JSONWriter& aWriter,
|
||||
const mozilla::ProfilerString16View& aEventType,
|
||||
const mozilla::TimeStamp& aEventTimeStamp,
|
||||
const mozilla::ProfilerString8View& aTracingCategory) {
|
||||
aWriter.StringProperty(
|
||||
"eventType",
|
||||
NS_ConvertUTF16toUTF8(aEventType.Data(), aEventType.Length()).get());
|
||||
// Note: This is the event *creation* timestamp, which should be before the
|
||||
// marker's own timestamp. It is used to compute the event processing
|
||||
// latency.
|
||||
mozilla::baseprofiler::WritePropertyTime(aWriter, "timeStamp",
|
||||
aEventTimeStamp);
|
||||
// TODO: This is from the old TracingMarkerPayload legacy, is it still
|
||||
// needed, once the one location where DOMEvent is used has transitioned?
|
||||
if (aTracingCategory.Length() != 0) {
|
||||
// Note: This is *not* the MarkerCategory, it's a identifier used to
|
||||
// combine pairs of markers. This should disappear after "set index" is
|
||||
// implemented in bug 1661114.
|
||||
aWriter.StringProperty("category", aTracingCategory.String().c_str());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct Pref {
|
||||
static constexpr const char* MarkerTypeName() { return "PreferenceRead"; }
|
||||
static void StreamJSONMarkerData(
|
||||
mozilla::JSONWriter& aWriter,
|
||||
const mozilla::ProfilerString8View& aPrefName,
|
||||
const mozilla::Maybe<mozilla::PrefValueKind>& aPrefKind,
|
||||
const mozilla::Maybe<mozilla::PrefType>& 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.String().c_str());
|
||||
aWriter.StringProperty("prefKind", PrefValueKindToString(aPrefKind));
|
||||
aWriter.StringProperty("prefType", PrefTypeToString(aPrefType));
|
||||
aWriter.StringProperty("prefValue", aPrefValue.String().c_str());
|
||||
}
|
||||
|
||||
private:
|
||||
static const char* PrefValueKindToString(
|
||||
const mozilla::Maybe<mozilla::PrefValueKind>& aKind) {
|
||||
if (aKind) {
|
||||
return *aKind == mozilla::PrefValueKind::Default ? "Default" : "User";
|
||||
}
|
||||
return "Shared";
|
||||
}
|
||||
|
||||
static const char* PrefTypeToString(
|
||||
const mozilla::Maybe<mozilla::PrefType>& 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 const char* MarkerTypeName() { return "LayerTranslation"; }
|
||||
static void StreamJSONMarkerData(mozilla::JSONWriter& 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);
|
||||
}
|
||||
};
|
||||
|
||||
// Tracks when a vsync occurs according to the HardwareComposer.
|
||||
struct Vsync {
|
||||
static constexpr const char* MarkerTypeName() { return "VsyncTimestamp"; }
|
||||
static void StreamJSONMarkerData(mozilla::JSONWriter& aWriter) {}
|
||||
};
|
||||
|
||||
struct Network {
|
||||
static constexpr const char* MarkerTypeName() { return "Network"; }
|
||||
static void StreamJSONMarkerData(
|
||||
mozilla::JSONWriter& 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);
|
||||
const char* typeString = GetNetworkState(aType);
|
||||
const char* cacheString = GetCacheState(aCacheDisposition);
|
||||
// want to use aUniqueStacks.mUniqueStrings->WriteElement(aWriter,
|
||||
// typeString);
|
||||
aWriter.StringProperty("status", typeString);
|
||||
if (cacheString) {
|
||||
aWriter.StringProperty("cache", cacheString);
|
||||
}
|
||||
aWriter.IntProperty("pri", aPri);
|
||||
if (aCount > 0) {
|
||||
aWriter.IntProperty("count", aCount);
|
||||
}
|
||||
if (aURI.Length() != 0) {
|
||||
aWriter.StringProperty("URI", aURI.String().c_str());
|
||||
}
|
||||
if (aRedirectURI.Length() != 0) {
|
||||
aWriter.StringProperty("RedirectURI", aRedirectURI.String().c_str());
|
||||
}
|
||||
|
||||
if (aContentType.Length() != 0) {
|
||||
aWriter.StringProperty("contentType", aContentType.String().c_str());
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static const char* GetNetworkState(NetworkLoadType aType) {
|
||||
switch (aType) {
|
||||
case NetworkLoadType::LOAD_START:
|
||||
return "STATUS_START";
|
||||
case NetworkLoadType::LOAD_STOP:
|
||||
return "STATUS_STOP";
|
||||
case NetworkLoadType::LOAD_REDIRECT:
|
||||
return "STATUS_REDIRECT";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
static const char* GetCacheState(
|
||||
mozilla::net::CacheDisposition aCacheDisposition) {
|
||||
switch (aCacheDisposition) {
|
||||
case mozilla::net::kCacheUnresolved:
|
||||
return "Unresolved";
|
||||
case mozilla::net::kCacheHit:
|
||||
return "Hit";
|
||||
case mozilla::net::kCacheHitViaReval:
|
||||
return "HitViaReval";
|
||||
case mozilla::net::kCacheMissedViaReval:
|
||||
return "MissedViaReval";
|
||||
case mozilla::net::kCacheMissed:
|
||||
return "Missed";
|
||||
case mozilla::net::kCacheUnknown:
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct ScreenshotPayload {
|
||||
static constexpr const char* MarkerTypeName() {
|
||||
return "CompositorScreenshot";
|
||||
}
|
||||
static void StreamJSONMarkerData(
|
||||
mozilla::JSONWriter& 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.String().c_str());
|
||||
|
||||
char hexWindowID[32];
|
||||
SprintfLiteral(hexWindowID, "0x%" PRIXPTR, aWindowIdentifier);
|
||||
aWriter.StringProperty("windowID", hexWindowID);
|
||||
aWriter.DoubleProperty("windowWidth", aWindowSize.width);
|
||||
aWriter.DoubleProperty("windowHeight", aWindowSize.height);
|
||||
}
|
||||
};
|
||||
|
||||
struct GCSlice {
|
||||
static constexpr const char* MarkerTypeName() { return "GCSlice"; }
|
||||
static void StreamJSONMarkerData(
|
||||
mozilla::JSONWriter& aWriter,
|
||||
const mozilla::ProfilerString8View& aTimingJSON) {
|
||||
if (aTimingJSON.Length() != 0) {
|
||||
// TODO: Is SplicedJSONProperty necessary here? (Guessing yes!)
|
||||
// aWriter.SplicedJSONProperty("timings", aTimingJSON.String().c_str());
|
||||
aWriter.StringProperty("timings", aTimingJSON.String().c_str());
|
||||
} else {
|
||||
aWriter.NullProperty("timings");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct GCMajor {
|
||||
static constexpr const char* MarkerTypeName() { return "GCMajor"; }
|
||||
static void StreamJSONMarkerData(
|
||||
mozilla::JSONWriter& aWriter,
|
||||
const mozilla::ProfilerString8View& aTimingJSON) {
|
||||
if (aTimingJSON.Length() != 0) {
|
||||
// TODO: Is SplicedJSONProperty necessary here? (Guessing yes!)
|
||||
// aWriter.SplicedJSONProperty("timings", aTimingJSON.String().c_str());
|
||||
aWriter.StringProperty("timings", aTimingJSON.String().c_str());
|
||||
} else {
|
||||
aWriter.NullProperty("timings");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct GCMinor {
|
||||
static constexpr const char* MarkerTypeName() { return "GCMinor"; }
|
||||
static void StreamJSONMarkerData(
|
||||
mozilla::JSONWriter& aWriter,
|
||||
const mozilla::ProfilerString8View& aTimingJSON) {
|
||||
if (aTimingJSON.Length() != 0) {
|
||||
// TODO: Is SplicedJSONProperty necessary here? (Guessing yes!)
|
||||
// aWriter.SplicedJSONProperty("nursery", aTimingJSON.String().c_str());
|
||||
aWriter.StringProperty("nursery", aTimingJSON.String().c_str());
|
||||
} else {
|
||||
aWriter.NullProperty("nursery");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct StyleMarkerPayload {
|
||||
static constexpr const char* MarkerTypeName() { return "Styles"; }
|
||||
static void StreamJSONMarkerData(
|
||||
mozilla::JSONWriter& aWriter,
|
||||
const mozilla::ServoTraversalStatistics& aStats) {
|
||||
aWriter.StringProperty("category", "Paint");
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
class JsAllocationMarkerPayload {
|
||||
static constexpr const char* MarkerTypeName() { return "JS allocation"; }
|
||||
static void StreamJSONMarkerData(
|
||||
mozilla::JSONWriter& 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.String().c_str());
|
||||
}
|
||||
if (aTypeName.Length() != 0) {
|
||||
aWriter.StringProperty(
|
||||
"typeName",
|
||||
NS_ConvertUTF16toUTF8(aTypeName.Data(), aTypeName.Length()).get());
|
||||
}
|
||||
if (aDescriptiveTypeName.Length() != 0) {
|
||||
aWriter.StringProperty(
|
||||
"descriptiveTypeName",
|
||||
NS_ConvertUTF16toUTF8(aDescriptiveTypeName.Data(),
|
||||
aDescriptiveTypeName.Length())
|
||||
.get());
|
||||
}
|
||||
aWriter.StringProperty("coarseType", aCoarseType.String().c_str());
|
||||
aWriter.IntProperty("size", aSize);
|
||||
aWriter.BoolProperty("inNursery", aInNursery);
|
||||
}
|
||||
};
|
||||
|
||||
// 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 const char* MarkerTypeName() { return "Native allocation"; }
|
||||
static void StreamJSONMarkerData(mozilla::JSONWriter& aWriter, int64_t aSize,
|
||||
uintptr_t aMemoryAddress, int aThreadId) {
|
||||
aWriter.IntProperty("size", aSize);
|
||||
aWriter.IntProperty("memoryAddress", static_cast<int64_t>(aMemoryAddress));
|
||||
aWriter.IntProperty("threadId", aThreadId);
|
||||
}
|
||||
};
|
||||
|
||||
struct IPCMarkerPayload {
|
||||
static constexpr const char* MarkerTypeName() { return "IPC"; }
|
||||
static void StreamJSONMarkerData(mozilla::JSONWriter& 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",
|
||||
IPC::StringFromIPCMessageType(aMessageType));
|
||||
aWriter.StringProperty("side", IPCSideToString(aSide));
|
||||
aWriter.StringProperty("direction", aDirection == MessageDirection::eSending
|
||||
? "sending"
|
||||
: "receiving");
|
||||
aWriter.StringProperty("phase", IPCPhaseToString(aPhase));
|
||||
aWriter.BoolProperty("sync", aSync);
|
||||
}
|
||||
|
||||
private:
|
||||
static const char* IPCSideToString(mozilla::ipc::Side aSide) {
|
||||
switch (aSide) {
|
||||
case mozilla::ipc::ParentSide:
|
||||
return "parent";
|
||||
case mozilla::ipc::ChildSide:
|
||||
return "child";
|
||||
case mozilla::ipc::UnknownSide:
|
||||
return "unknown";
|
||||
default:
|
||||
MOZ_ASSERT_UNREACHABLE("Invalid IPC side");
|
||||
return "<invalid IPC side>";
|
||||
}
|
||||
}
|
||||
|
||||
static const char* IPCPhaseToString(mozilla::ipc::MessagePhase aPhase) {
|
||||
switch (aPhase) {
|
||||
case mozilla::ipc::MessagePhase::Endpoint:
|
||||
return "endpoint";
|
||||
case mozilla::ipc::MessagePhase::TransferStart:
|
||||
return "transferStart";
|
||||
case mozilla::ipc::MessagePhase::TransferEnd:
|
||||
return "transferEnd";
|
||||
default:
|
||||
MOZ_ASSERT_UNREACHABLE("Invalid IPC phase");
|
||||
return "<invalid IPC phase>";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace geckoprofiler::markers
|
||||
|
||||
#endif // MOZ_GECKO_PROFILER
|
||||
|
||||
#endif // ProfilerMarkerTypes_h
|
@ -1,101 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; 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/. */
|
||||
|
||||
// This header contains definitions necessary to add markers to the Gecko
|
||||
// Profiler buffer.
|
||||
//
|
||||
// It #include's "mozilla/BaseProfilerMarkers.h", see that header for base
|
||||
// definitions necessary to create marker types.
|
||||
//
|
||||
// If common marker types are needed, #include "ProfilerMarkerTypes.h" instead.
|
||||
//
|
||||
// But if you want to create your own marker type locally, you can #include this
|
||||
// header only; look at ProfilerMarkerTypes.h for examples of how to define
|
||||
// types.
|
||||
//
|
||||
// To then record markers:
|
||||
// - Use `baseprofiler::AddMarker<ChosenMarkerType>(...)` from
|
||||
// mozglue or other libraries that are outside of xul, especially if they may
|
||||
// happen outside of xpcom's lifetime (typically startup, shutdown, or tests).
|
||||
// - Otherwise #include "ProfilerMarkers.h" instead, and use
|
||||
// `profiler_add_marker<ChosenMarkerType>(...)`.
|
||||
// See these functions for more details.
|
||||
|
||||
#ifndef ProfilerMarkers_h
|
||||
#define ProfilerMarkers_h
|
||||
|
||||
#include "mozilla/BaseProfilerMarkers.h"
|
||||
#include "mozilla/ProfilerMarkersDetail.h"
|
||||
|
||||
// TODO: Move common stuff to shared header instead.
|
||||
#include "GeckoProfiler.h"
|
||||
|
||||
#ifndef MOZ_GECKO_PROFILER
|
||||
|
||||
# define PROFILER_MARKER_UNTYPED(markerName, options)
|
||||
# define PROFILER_MARKER(markerName, options, MarkerType, ...)
|
||||
# define PROFILER_MARKER_TEXT(markerName, options, text)
|
||||
|
||||
#else // ndef MOZ_GECKO_PROFILER
|
||||
|
||||
// Bring category names from Base Profiler into the geckoprofiler::category
|
||||
// namespace, for consistency with other Gecko Profiler identifiers.
|
||||
namespace geckoprofiler::category {
|
||||
using namespace ::mozilla::baseprofiler::category;
|
||||
}
|
||||
|
||||
template <typename MarkerType = ::mozilla::baseprofiler::markers::NoPayload,
|
||||
typename... Ts>
|
||||
mozilla::ProfileBufferBlockIndex AddMarkerToBuffer(
|
||||
mozilla::ProfileChunkedBuffer& aBuffer,
|
||||
const mozilla::ProfilerString8View& aName,
|
||||
mozilla::MarkerOptions&& aOptions, const Ts&... aTs) {
|
||||
return mozilla::base_profiler_markers_detail::AddMarkerToBuffer<MarkerType>(
|
||||
aBuffer, aName, std::move(aOptions), ::profiler_capture_backtrace_into,
|
||||
aTs...);
|
||||
}
|
||||
|
||||
template <typename MarkerType = ::mozilla::baseprofiler::markers::NoPayload,
|
||||
typename... Ts>
|
||||
mozilla::ProfileBufferBlockIndex profiler_add_marker(
|
||||
const mozilla::ProfilerString8View& aName,
|
||||
mozilla::MarkerOptions&& aOptions, const Ts&... aTs) {
|
||||
if (!profiler_can_accept_markers()) {
|
||||
return {};
|
||||
}
|
||||
return ::AddMarkerToBuffer<MarkerType>(
|
||||
profiler_markers_detail::CachedCoreBuffer(), aName, std::move(aOptions),
|
||||
aTs...);
|
||||
}
|
||||
|
||||
# define PROFILER_MARKER_UNTYPED(markerName, options) \
|
||||
do { \
|
||||
AUTO_PROFILER_STATS(add_marker_v2); \
|
||||
::profiler_add_marker<>(markerName, ::geckoprofiler::category::options); \
|
||||
} while (false)
|
||||
|
||||
# define PROFILER_MARKER(markerName, options, MarkerType, ...) \
|
||||
do { \
|
||||
AUTO_PROFILER_STATS(add_marker_v2_with_##MarkerType); \
|
||||
::profiler_add_marker<::geckoprofiler::markers::MarkerType>( \
|
||||
markerName, ::geckoprofiler::category::options, ##__VA_ARGS__); \
|
||||
} while (false)
|
||||
|
||||
namespace geckoprofiler::markers {
|
||||
// Most common marker type. Others are in ProfilerMarkerTypes.h.
|
||||
using Text = ::mozilla::baseprofiler::markers::Text;
|
||||
} // namespace geckoprofiler::markers
|
||||
|
||||
# define PROFILER_MARKER_TEXT(markerName, options, text) \
|
||||
do { \
|
||||
AUTO_PROFILER_STATS(add_marker_v2_with_Text); \
|
||||
::profiler_add_marker<::geckoprofiler::markers::Text>( \
|
||||
markerName, ::geckoprofiler::category::options, text); \
|
||||
} while (false)
|
||||
|
||||
#endif // nfed MOZ_GECKO_PROFILER else
|
||||
|
||||
#endif // ProfilerMarkers_h
|
@ -1,42 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; 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 ProfilerMarkersDetail_h
|
||||
#define ProfilerMarkersDetail_h
|
||||
|
||||
#ifndef ProfilerMarkers_h
|
||||
# error "This header should only be #included by ProfilerMarkers.h"
|
||||
#endif
|
||||
|
||||
#include "mozilla/ProfilerMarkersPrerequisites.h"
|
||||
|
||||
#ifdef MOZ_GECKO_PROFILER
|
||||
|
||||
// ~~ HERE BE DRAGONS ~~
|
||||
//
|
||||
// Everything below is internal implementation detail, you shouldn't need to
|
||||
// look at it unless working on the profiler code.
|
||||
|
||||
// Header that specializes the (de)serializers for xpcom types.
|
||||
# include "mozilla/ProfileBufferEntrySerializationGeckoExtensions.h"
|
||||
|
||||
// Implemented in platform.cpp
|
||||
mozilla::ProfileChunkedBuffer& profiler_get_core_buffer();
|
||||
|
||||
namespace profiler_markers_detail {
|
||||
|
||||
// Get the core buffer from the profiler, and cache it in a
|
||||
// non-templated-function static reference.
|
||||
inline mozilla::ProfileChunkedBuffer& CachedCoreBuffer() {
|
||||
static mozilla::ProfileChunkedBuffer& coreBuffer = profiler_get_core_buffer();
|
||||
return coreBuffer;
|
||||
}
|
||||
|
||||
} // namespace profiler_markers_detail
|
||||
|
||||
#endif // MOZ_GECKO_PROFILER
|
||||
|
||||
#endif // ProfilerMarkersDetail_h
|
@ -1,30 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; 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/. */
|
||||
|
||||
// This header contains basic definitions required to create marker types, and
|
||||
// to add markers to the profiler buffers.
|
||||
//
|
||||
// In most cases, #include "mozilla/ProfilerMarkers.h" instead, or
|
||||
// #include "mozilla/ProfilerMarkerTypes.h" for common marker types.
|
||||
|
||||
#ifndef ProfilerMarkersPrerequisites_h
|
||||
#define ProfilerMarkersPrerequisites_h
|
||||
|
||||
#include "mozilla/BaseProfilerMarkersPrerequisites.h"
|
||||
|
||||
#ifdef MOZ_GECKO_PROFILER
|
||||
|
||||
namespace geckoprofiler::markers {
|
||||
|
||||
// Default marker payload types, with no extra information, not even a marker
|
||||
// type and payload. This is intended for label-only markers.
|
||||
using NoPayload = ::mozilla::baseprofiler::markers::NoPayload;
|
||||
|
||||
} // namespace geckoprofiler::markers
|
||||
|
||||
#endif // MOZ_GECKO_PROFILER
|
||||
|
||||
#endif // ProfilerMarkersPrerequisites_h
|
@ -10,7 +10,6 @@
|
||||
// profiler internals.
|
||||
|
||||
#include "GeckoProfiler.h"
|
||||
#include "mozilla/ProfilerMarkerTypes.h"
|
||||
#include "platform.h"
|
||||
#include "ProfileBuffer.h"
|
||||
#include "ProfilerMarkerPayload.h"
|
||||
@ -543,9 +542,6 @@ TEST(GeckoProfiler, Pause)
|
||||
PROFILER_ADD_MARKER("Paused", OTHER);
|
||||
info2 = profiler_get_buffer_info();
|
||||
ASSERT_TRUE(info1->mRangeEnd == info2->mRangeEnd);
|
||||
PROFILER_MARKER_UNTYPED("Paused v2", OTHER);
|
||||
Maybe<ProfilerBufferInfo> info3 = profiler_get_buffer_info();
|
||||
ASSERT_TRUE(info2->mRangeEnd == info3->mRangeEnd);
|
||||
|
||||
profiler_resume();
|
||||
|
||||
@ -701,22 +697,6 @@ TEST(GeckoProfiler, Markers)
|
||||
ASSERT_EQ(longstrCut[kMax - 1], 'c');
|
||||
longstrCut[kMax - 1] = '\0';
|
||||
|
||||
// Test basic markers 2.0.
|
||||
MOZ_RELEASE_ASSERT(
|
||||
profiler_add_marker<>("default-templated markers 2.0 with empty options",
|
||||
geckoprofiler::category::OTHER));
|
||||
|
||||
PROFILER_MARKER_UNTYPED("default-templated markers 2.0 with option",
|
||||
OTHER.WithOptions(MarkerStack::TakeBacktrace(
|
||||
profiler_capture_backtrace())));
|
||||
|
||||
PROFILER_MARKER("explicitly-default-templated markers 2.0 with empty options",
|
||||
OTHER, NoPayload);
|
||||
|
||||
MOZ_RELEASE_ASSERT(profiler_add_marker<::geckoprofiler::markers::NoPayload>(
|
||||
"explicitly-default-templated markers 2.0 with option",
|
||||
geckoprofiler::category::OTHER));
|
||||
|
||||
// Used in markers below.
|
||||
TimeStamp ts1 = TimeStamp::NowUnfuzzed();
|
||||
|
||||
@ -737,32 +717,16 @@ TEST(GeckoProfiler, Markers)
|
||||
"FileIOMarkerPayload marker", OTHER, FileIOMarkerPayload,
|
||||
("operation", "source", "filename", ts1, ts2, nullptr));
|
||||
|
||||
MOZ_RELEASE_ASSERT(profiler_add_marker<geckoprofiler::markers::FileIO>(
|
||||
"FileIOMarkerPayload marker 2.0",
|
||||
geckoprofiler::category::OTHER.WithOptions(
|
||||
MarkerTiming::Interval(ts1, ts2)),
|
||||
"operation", "source", "filename", MarkerThreadId{}));
|
||||
|
||||
PROFILER_ADD_MARKER_WITH_PAYLOAD(
|
||||
"FileIOMarkerPayload marker off-MT", OTHER, FileIOMarkerPayload,
|
||||
("operation2", "source2", "filename2", ts1, ts2, nullptr, Some(123)));
|
||||
|
||||
MOZ_RELEASE_ASSERT(profiler_add_marker<geckoprofiler::markers::FileIO>(
|
||||
"FileIOMarkerPayload marker 2.0 off-MT",
|
||||
geckoprofiler::category::OTHER.WithOptions(
|
||||
MarkerTiming::Interval(ts1, ts2)),
|
||||
"operation2", "source2", "filename2", MarkerThreadId{123}));
|
||||
|
||||
// Other markers in alphabetical order of payload class names.
|
||||
|
||||
PROFILER_ADD_MARKER_WITH_PAYLOAD(
|
||||
"DOMEventMarkerPayload marker", OTHER, DOMEventMarkerPayload,
|
||||
(u"dom event"_ns, ts1, "category", TRACING_EVENT, mozilla::Nothing()));
|
||||
|
||||
MOZ_RELEASE_ASSERT(profiler_add_marker<geckoprofiler::markers::DOMEvent>(
|
||||
"DOMEventMarkerPayload marker 2.0", geckoprofiler::category::OTHER,
|
||||
u"dom event"_ns, ts1, "category"));
|
||||
|
||||
{
|
||||
const char gcMajorJSON[] = "42";
|
||||
const auto len = strlen(gcMajorJSON);
|
||||
@ -865,41 +829,6 @@ TEST(GeckoProfiler, Markers)
|
||||
mozilla::ipc::MessageDirection::eSending,
|
||||
mozilla::ipc::MessagePhase::Endpoint, false, ts1));
|
||||
|
||||
MOZ_RELEASE_ASSERT(profiler_add_marker<geckoprofiler::markers::Tracing>(
|
||||
"Tracing", geckoprofiler::category::OTHER, "category"));
|
||||
|
||||
MOZ_RELEASE_ASSERT(
|
||||
profiler_add_marker<geckoprofiler::markers::UserTimingMark>(
|
||||
"UserTimingMark", geckoprofiler::category::OTHER, "mark name"));
|
||||
|
||||
MOZ_RELEASE_ASSERT(
|
||||
profiler_add_marker<geckoprofiler::markers::UserTimingMeasure>(
|
||||
"UserTimingMeasure", geckoprofiler::category::OTHER, "measure name",
|
||||
Some(mozilla::ProfilerString8View("start")),
|
||||
Some(mozilla::ProfilerString8View("end"))));
|
||||
|
||||
MOZ_RELEASE_ASSERT(profiler_add_marker<geckoprofiler::markers::Hang>(
|
||||
"Hang", geckoprofiler::category::OTHER));
|
||||
|
||||
MOZ_RELEASE_ASSERT(profiler_add_marker<geckoprofiler::markers::LongTask>(
|
||||
"LongTask", geckoprofiler::category::OTHER));
|
||||
|
||||
MOZ_RELEASE_ASSERT(profiler_add_marker<geckoprofiler::markers::Text>(
|
||||
"Text", geckoprofiler::category::OTHER, "Text text"));
|
||||
|
||||
MOZ_RELEASE_ASSERT(profiler_add_marker<geckoprofiler::markers::Log>(
|
||||
"Log", geckoprofiler::category::OTHER, "module", "log text"));
|
||||
|
||||
MOZ_RELEASE_ASSERT(profiler_add_marker<geckoprofiler::markers::MediaSample>(
|
||||
"MediaSample", geckoprofiler::category::OTHER, 123, 456));
|
||||
|
||||
MOZ_RELEASE_ASSERT(profiler_add_marker<geckoprofiler::markers::Budget>(
|
||||
"Budget", geckoprofiler::category::OTHER));
|
||||
|
||||
MOZ_RELEASE_ASSERT(profiler_add_marker<geckoprofiler::markers::DOMEvent>(
|
||||
"DOMEvent", geckoprofiler::category::OTHER, u"event"_ns, ts2,
|
||||
"category"));
|
||||
|
||||
SpliceableChunkedJSONWriter w;
|
||||
w.Start();
|
||||
EXPECT_TRUE(::profiler_stream_json_for_this_process(w));
|
||||
@ -938,16 +867,9 @@ TEST(GeckoProfiler, Markers)
|
||||
S_M5_gtest7,
|
||||
S_M5_gtest8,
|
||||
S_M5_gtest9,
|
||||
S_Markers2DefaultEmptyOptions,
|
||||
S_Markers2DefaultWithOptions,
|
||||
S_Markers2ExplicitDefaultEmptyOptions,
|
||||
S_Markers2ExplicitDefaultWithOptions,
|
||||
S_FileIOMarkerPayload,
|
||||
S_FileIOMarker2,
|
||||
S_FileIOMarkerPayloadOffMT,
|
||||
S_FileIOMarker2OffMT,
|
||||
S_DOMEventMarkerPayload,
|
||||
S_DOMEventMarker2,
|
||||
S_GCMajorMarkerPayload,
|
||||
S_GCMinorMarkerPayload,
|
||||
S_GCSliceMarkerPayload,
|
||||
@ -1152,24 +1074,6 @@ TEST(GeckoProfiler, Markers)
|
||||
} else if (nameString == "M3") {
|
||||
ASSERT_EQ(state, S_M3);
|
||||
state = State(state + 1);
|
||||
} else if (nameString ==
|
||||
"default-templated markers 2.0 with empty options") {
|
||||
EXPECT_EQ(state, S_Markers2DefaultEmptyOptions);
|
||||
state = State(S_Markers2DefaultEmptyOptions + 1);
|
||||
} else if (nameString ==
|
||||
"default-templated markers 2.0 with option") {
|
||||
EXPECT_EQ(state, S_Markers2DefaultWithOptions);
|
||||
state = State(S_Markers2DefaultWithOptions + 1);
|
||||
} else if (nameString ==
|
||||
"explicitly-default-templated markers 2.0 with empty "
|
||||
"options") {
|
||||
EXPECT_EQ(state, S_Markers2ExplicitDefaultEmptyOptions);
|
||||
state = State(S_Markers2ExplicitDefaultEmptyOptions + 1);
|
||||
} else if (nameString ==
|
||||
"explicitly-default-templated markers 2.0 with "
|
||||
"option") {
|
||||
EXPECT_EQ(state, S_Markers2ExplicitDefaultWithOptions);
|
||||
state = State(S_Markers2ExplicitDefaultWithOptions + 1);
|
||||
}
|
||||
} else {
|
||||
// root.threads[0].markers.data[i] is an array with 6 elements,
|
||||
@ -1303,17 +1207,6 @@ TEST(GeckoProfiler, Markers)
|
||||
EXPECT_EQ_JSON(payload["filename"], String, "filename");
|
||||
EXPECT_FALSE(payload.isMember("threadId"));
|
||||
|
||||
} else if (nameString == "FileIOMarkerPayload marker 2.0") {
|
||||
EXPECT_EQ(state, S_FileIOMarker2);
|
||||
state = State(S_FileIOMarker2 + 1);
|
||||
EXPECT_EQ(typeString, "FileIO");
|
||||
EXPECT_TIMING_INTERVAL_AT(ts1Double, ts2Double);
|
||||
EXPECT_TRUE(payload["stack"].isNull());
|
||||
EXPECT_EQ_JSON(payload["operation"], String, "operation");
|
||||
EXPECT_EQ_JSON(payload["source"], String, "source");
|
||||
EXPECT_EQ_JSON(payload["filename"], String, "filename");
|
||||
EXPECT_FALSE(payload.isMember("threadId"));
|
||||
|
||||
} else if (nameString == "FileIOMarkerPayload marker off-MT") {
|
||||
EXPECT_EQ(state, S_FileIOMarkerPayloadOffMT);
|
||||
state = State(S_FileIOMarkerPayloadOffMT + 1);
|
||||
@ -1325,18 +1218,6 @@ TEST(GeckoProfiler, Markers)
|
||||
EXPECT_EQ_JSON(payload["filename"], String, "filename2");
|
||||
EXPECT_EQ_JSON(payload["threadId"], Int, 123);
|
||||
|
||||
} else if (nameString ==
|
||||
"FileIOMarkerPayload marker 2.0 off-MT") {
|
||||
EXPECT_EQ(state, S_FileIOMarker2OffMT);
|
||||
state = State(S_FileIOMarker2OffMT + 1);
|
||||
EXPECT_EQ(typeString, "FileIO");
|
||||
EXPECT_TIMING_INTERVAL_AT(ts1Double, ts2Double);
|
||||
EXPECT_TRUE(payload["stack"].isNull());
|
||||
EXPECT_EQ_JSON(payload["operation"], String, "operation2");
|
||||
EXPECT_EQ_JSON(payload["source"], String, "source2");
|
||||
EXPECT_EQ_JSON(payload["filename"], String, "filename2");
|
||||
EXPECT_EQ_JSON(payload["threadId"], Int, 123);
|
||||
|
||||
} else if (nameString == "DOMEventMarkerPayload marker") {
|
||||
EXPECT_EQ(state, S_DOMEventMarkerPayload);
|
||||
state = State(S_DOMEventMarkerPayload + 1);
|
||||
@ -1347,15 +1228,6 @@ TEST(GeckoProfiler, Markers)
|
||||
EXPECT_EQ_JSON(payload["timeStamp"], Double, ts1Double);
|
||||
EXPECT_EQ_JSON(payload["eventType"], String, "dom event");
|
||||
|
||||
} else if (nameString == "DOMEventMarkerPayload marker 2.0") {
|
||||
EXPECT_EQ(state, S_DOMEventMarker2);
|
||||
state = State(S_DOMEventMarker2 + 1);
|
||||
EXPECT_EQ(typeString, "tracing");
|
||||
EXPECT_TRUE(payload["stack"].isNull());
|
||||
EXPECT_EQ_JSON(payload["category"], String, "category");
|
||||
EXPECT_EQ_JSON(payload["timeStamp"], Double, ts1Double);
|
||||
EXPECT_EQ_JSON(payload["eventType"], String, "dom event");
|
||||
|
||||
} else if (nameString == "GCMajorMarkerPayload marker") {
|
||||
EXPECT_EQ(state, S_GCMajorMarkerPayload);
|
||||
state = State(S_GCMajorMarkerPayload + 1);
|
||||
|
Loading…
Reference in New Issue
Block a user