From 26a029d407be480d791972afb5975cf62c9360a6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 02:47:55 +0200 Subject: Adding upstream version 124.0.1. Signed-off-by: Daniel Baumann --- mozglue/baseprofiler/core/ProfileBuffer.h | 211 ++++++++++++++++++++++++++++++ 1 file changed, 211 insertions(+) create mode 100644 mozglue/baseprofiler/core/ProfileBuffer.h (limited to 'mozglue/baseprofiler/core/ProfileBuffer.h') diff --git a/mozglue/baseprofiler/core/ProfileBuffer.h b/mozglue/baseprofiler/core/ProfileBuffer.h new file mode 100644 index 0000000000..f77b429df8 --- /dev/null +++ b/mozglue/baseprofiler/core/ProfileBuffer.h @@ -0,0 +1,211 @@ +/* -*- 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/. */ + +#ifndef MOZ_PROFILE_BUFFER_H +#define MOZ_PROFILE_BUFFER_H + +#include "ProfileBufferEntry.h" + +#include "BaseProfiler.h" +#include "mozilla/Maybe.h" +#include "mozilla/PowerOfTwo.h" +#include "mozilla/ProfileBufferChunkManagerSingle.h" +#include "mozilla/ProfileChunkedBuffer.h" + +namespace mozilla { +namespace baseprofiler { + +// Class storing most profiling data in a ProfileChunkedBuffer. +// +// This class is used as a queue of entries which, after construction, never +// allocates. This makes it safe to use in the profiler's "critical section". +class ProfileBuffer final { + public: + // ProfileBuffer constructor + // @param aBuffer The in-session ProfileChunkedBuffer to use as buffer + // manager. + explicit ProfileBuffer(ProfileChunkedBuffer& aBuffer); + + ProfileChunkedBuffer& UnderlyingChunkedBuffer() const { return mEntries; } + + bool IsThreadSafe() const { return mEntries.IsThreadSafe(); } + + // Add |aEntry| to the buffer, ignoring what kind of entry it is. + // Returns the position of the entry. + uint64_t AddEntry(const ProfileBufferEntry& aEntry); + + // Add to the buffer a sample start (ThreadId) entry for aThreadId. + // Returns the position of the entry. + uint64_t AddThreadIdEntry(BaseProfilerThreadId aThreadId); + + void CollectCodeLocation(const char* aLabel, const char* aStr, + uint32_t aFrameFlags, uint64_t aInnerWindowID, + const Maybe& aLineNumber, + const Maybe& aColumnNumber, + const Maybe& aCategoryPair); + + // Maximum size of a frameKey string that we'll handle. + static const size_t kMaxFrameKeyLength = 512; + + // Stream JSON for samples in the buffer to aWriter, using the supplied + // UniqueStacks object. + // Only streams samples for the given thread ID and which were taken at or + // after aSinceTime. If ID is 0, ignore the stored thread ID; this should only + // be used when the buffer contains only one sample. + // Return the thread ID of the streamed sample(s), or 0. + BaseProfilerThreadId StreamSamplesToJSON(SpliceableJSONWriter& aWriter, + BaseProfilerThreadId aThreadId, + double aSinceTime, + UniqueStacks& aUniqueStacks) const; + + void StreamMarkersToJSON(SpliceableJSONWriter& aWriter, + BaseProfilerThreadId aThreadId, + const TimeStamp& aProcessStartTime, + double aSinceTime, + UniqueStacks& aUniqueStacks) const; + void StreamPausedRangesToJSON(SpliceableJSONWriter& aWriter, + double aSinceTime) const; + void StreamProfilerOverheadToJSON(SpliceableJSONWriter& aWriter, + const TimeStamp& aProcessStartTime, + double aSinceTime) const; + void StreamCountersToJSON(SpliceableJSONWriter& aWriter, + const TimeStamp& aProcessStartTime, + double aSinceTime) const; + + // Find (via |aLastSample|) the most recent sample for the thread denoted by + // |aThreadId| and clone it, patching in the current time as appropriate. + // Mutate |aLastSample| to point to the newly inserted sample. + // Returns whether duplication was successful. + bool DuplicateLastSample(BaseProfilerThreadId aThreadId, + const TimeStamp& aProcessStartTime, + Maybe& aLastSample); + + void DiscardSamplesBeforeTime(double aTime); + + size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const; + size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const; + + void CollectOverheadStats(TimeDuration aSamplingTime, TimeDuration aLocking, + TimeDuration aCleaning, TimeDuration aCounters, + TimeDuration aThreads); + + ProfilerBufferInfo GetProfilerBufferInfo() const; + + private: + // Add |aEntry| to the provider ProfileChunkedBuffer. + // `static` because it may be used to add an entry to a `ProfileChunkedBuffer` + // that is not attached to a `ProfileBuffer`. + static ProfileBufferBlockIndex AddEntry( + ProfileChunkedBuffer& aProfileChunkedBuffer, + const ProfileBufferEntry& aEntry); + + // Add a sample start (ThreadId) entry for aThreadId to the provided + // ProfileChunkedBuffer. Returns the position of the entry. + // `static` because it may be used to add an entry to a `ProfileChunkedBuffer` + // that is not attached to a `ProfileBuffer`. + static ProfileBufferBlockIndex AddThreadIdEntry( + ProfileChunkedBuffer& aProfileChunkedBuffer, + BaseProfilerThreadId aThreadId); + + // The storage in which this ProfileBuffer stores its entries. + ProfileChunkedBuffer& mEntries; + + public: + // `BufferRangeStart()` and `BufferRangeEnd()` return `uint64_t` values + // corresponding to the first entry and past the last entry stored in + // `mEntries`. + // + // The returned values are not guaranteed to be stable, because other threads + // may also be accessing the buffer concurrently. But they will always + // increase, and can therefore give an indication of how far these values have + // *at least* reached. In particular: + // - Entries whose index is strictly less that `BufferRangeStart()` have been + // discarded by now, so any related data may also be safely discarded. + // - It is safe to try and read entries at any index strictly less than + // `BufferRangeEnd()` -- but note that these reads may fail by the time you + // request them, as old entries get overwritten by new ones. + uint64_t BufferRangeStart() const { return mEntries.GetState().mRangeStart; } + uint64_t BufferRangeEnd() const { return mEntries.GetState().mRangeEnd; } + + private: + // Single pre-allocated chunk (to avoid spurious mallocs), used when: + // - Duplicating sleeping stacks (hence scExpectedMaximumStackSize). + // - Adding JIT info. + // - Streaming stacks to JSON. + // Mutable because it's accessed from non-multithreaded const methods. + mutable Maybe mMaybeWorkerChunkManager; + ProfileBufferChunkManagerSingle& WorkerChunkManager() const { + if (mMaybeWorkerChunkManager.isNothing()) { + // Only actually allocate it on first use. (Some ProfileBuffers are + // temporary and don't actually need this.) + mMaybeWorkerChunkManager.emplace( + ProfileBufferChunk::SizeofChunkMetadata() + + ProfileBufferChunkManager::scExpectedMaximumStackSize); + } + return *mMaybeWorkerChunkManager; + } + + // Time from launch (us) when first sampling was recorded. + double mFirstSamplingTimeUs = 0.0; + // Time from launch (us) when last sampling was recorded. + double mLastSamplingTimeUs = 0.0; + // Sampling stats: Interval (us) between successive samplings. + ProfilerStats mIntervalsUs; + // Sampling stats: Total duration (us) of each sampling. (Split detail below.) + ProfilerStats mOverheadsUs; + // Sampling stats: Time (us) to acquire the lock before sampling. + ProfilerStats mLockingsUs; + // Sampling stats: Time (us) to discard expired data. + ProfilerStats mCleaningsUs; + // Sampling stats: Time (us) to collect counter data. + ProfilerStats mCountersUs; + // Sampling stats: Time (us) to sample thread stacks. + ProfilerStats mThreadsUs; +}; + +/** + * Helper type used to implement ProfilerStackCollector. This type is used as + * the collector for MergeStacks by ProfileBuffer. It holds a reference to the + * buffer, as well as additional feature flags which are needed to control the + * data collection strategy + */ +class ProfileBufferCollector final : public ProfilerStackCollector { + public: + ProfileBufferCollector(ProfileBuffer& aBuf, uint64_t aSamplePos, + uint64_t aBufferRangeStart) + : mBuf(aBuf), + mSamplePositionInBuffer(aSamplePos), + mBufferRangeStart(aBufferRangeStart) { + MOZ_ASSERT( + mSamplePositionInBuffer >= mBufferRangeStart, + "The sample position should always be after the buffer range start"); + } + + // Position at which the sample starts in the profiler buffer (which may be + // different from the buffer in which the sample data is collected here). + Maybe SamplePositionInBuffer() override { + return Some(mSamplePositionInBuffer); + } + + // Profiler buffer's range start (which may be different from the buffer in + // which the sample data is collected here). + Maybe BufferRangeStart() override { + return Some(mBufferRangeStart); + } + + virtual void CollectNativeLeafAddr(void* aAddr) override; + virtual void CollectProfilingStackFrame( + const ProfilingStackFrame& aFrame) override; + + private: + ProfileBuffer& mBuf; + uint64_t mSamplePositionInBuffer; + uint64_t mBufferRangeStart; +}; + +} // namespace baseprofiler +} // namespace mozilla + +#endif -- cgit v1.2.3