ProfileBuffer.h (9271B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 #ifndef MOZ_PROFILE_BUFFER_H 7 #define MOZ_PROFILE_BUFFER_H 8 9 #include "ProfileBufferEntry.h" 10 11 #include "mozilla/BaseProfiler.h" 12 #include "mozilla/Maybe.h" 13 #include "mozilla/PowerOfTwo.h" 14 #include "mozilla/ProfileBufferChunkManagerSingle.h" 15 #include "mozilla/ProfileChunkedBuffer.h" 16 17 namespace mozilla { 18 namespace baseprofiler { 19 20 // Class storing most profiling data in a ProfileChunkedBuffer. 21 // 22 // This class is used as a queue of entries which, after construction, never 23 // allocates. This makes it safe to use in the profiler's "critical section". 24 class ProfileBuffer final { 25 public: 26 // ProfileBuffer constructor 27 // @param aBuffer The in-session ProfileChunkedBuffer to use as buffer 28 // manager. 29 explicit ProfileBuffer(ProfileChunkedBuffer& aBuffer); 30 31 ProfileChunkedBuffer& UnderlyingChunkedBuffer() const { return mEntries; } 32 33 bool IsThreadSafe() const { return mEntries.IsThreadSafe(); } 34 35 // Add |aEntry| to the buffer, ignoring what kind of entry it is. 36 // Returns the position of the entry. 37 uint64_t AddEntry(const ProfileBufferEntry& aEntry); 38 39 // Add to the buffer a sample start (ThreadId) entry for aThreadId. 40 // Returns the position of the entry. 41 uint64_t AddThreadIdEntry(BaseProfilerThreadId aThreadId); 42 43 void CollectCodeLocation(const char* aLabel, const char* aStr, 44 uint32_t aFrameFlags, uint64_t aInnerWindowID, 45 uint32_t aSourceId, 46 const Maybe<uint32_t>& aLineNumber, 47 const Maybe<uint32_t>& aColumnNumber, 48 const Maybe<ProfilingCategoryPair>& aCategoryPair); 49 50 // Maximum size of a frameKey string that we'll handle. 51 static constexpr size_t kMaxFrameKeyLength = 512; 52 53 // Stream JSON for samples in the buffer to aWriter, using the supplied 54 // UniqueStacks object. 55 // Only streams samples for the given thread ID and which were taken at or 56 // after aSinceTime. If ID is 0, ignore the stored thread ID; this should only 57 // be used when the buffer contains only one sample. 58 // Return the thread ID of the streamed sample(s), or 0. 59 BaseProfilerThreadId StreamSamplesToJSON(SpliceableJSONWriter& aWriter, 60 BaseProfilerThreadId aThreadId, 61 double aSinceTime, 62 UniqueStacks& aUniqueStacks) const; 63 64 void StreamMarkersToJSON(SpliceableJSONWriter& aWriter, 65 BaseProfilerThreadId aThreadId, 66 const TimeStamp& aProcessStartTime, 67 double aSinceTime, 68 UniqueStacks& aUniqueStacks) const; 69 void StreamPausedRangesToJSON(SpliceableJSONWriter& aWriter, 70 double aSinceTime) const; 71 void StreamProfilerOverheadToJSON(SpliceableJSONWriter& aWriter, 72 const TimeStamp& aProcessStartTime, 73 double aSinceTime) const; 74 void StreamCountersToJSON(SpliceableJSONWriter& aWriter, 75 const TimeStamp& aProcessStartTime, 76 double aSinceTime) const; 77 78 // Find (via |aLastSample|) the most recent sample for the thread denoted by 79 // |aThreadId| and clone it, patching in the current time as appropriate. 80 // Mutate |aLastSample| to point to the newly inserted sample. 81 // Returns whether duplication was successful. 82 bool DuplicateLastSample(BaseProfilerThreadId aThreadId, 83 const TimeStamp& aProcessStartTime, 84 Maybe<uint64_t>& aLastSample); 85 86 void DiscardSamplesBeforeTime(double aTime); 87 88 size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const; 89 size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const; 90 91 void CollectOverheadStats(TimeDuration aSamplingTime, TimeDuration aLocking, 92 TimeDuration aCleaning, TimeDuration aCounters, 93 TimeDuration aThreads); 94 95 ProfilerBufferInfo GetProfilerBufferInfo() const; 96 97 private: 98 // Add |aEntry| to the provider ProfileChunkedBuffer. 99 // `static` because it may be used to add an entry to a `ProfileChunkedBuffer` 100 // that is not attached to a `ProfileBuffer`. 101 static ProfileBufferBlockIndex AddEntry( 102 ProfileChunkedBuffer& aProfileChunkedBuffer, 103 const ProfileBufferEntry& aEntry); 104 105 // Add a sample start (ThreadId) entry for aThreadId to the provided 106 // ProfileChunkedBuffer. Returns the position of the entry. 107 // `static` because it may be used to add an entry to a `ProfileChunkedBuffer` 108 // that is not attached to a `ProfileBuffer`. 109 static ProfileBufferBlockIndex AddThreadIdEntry( 110 ProfileChunkedBuffer& aProfileChunkedBuffer, 111 BaseProfilerThreadId aThreadId); 112 113 // The storage in which this ProfileBuffer stores its entries. 114 ProfileChunkedBuffer& mEntries; 115 116 public: 117 // `BufferRangeStart()` and `BufferRangeEnd()` return `uint64_t` values 118 // corresponding to the first entry and past the last entry stored in 119 // `mEntries`. 120 // 121 // The returned values are not guaranteed to be stable, because other threads 122 // may also be accessing the buffer concurrently. But they will always 123 // increase, and can therefore give an indication of how far these values have 124 // *at least* reached. In particular: 125 // - Entries whose index is strictly less that `BufferRangeStart()` have been 126 // discarded by now, so any related data may also be safely discarded. 127 // - It is safe to try and read entries at any index strictly less than 128 // `BufferRangeEnd()` -- but note that these reads may fail by the time you 129 // request them, as old entries get overwritten by new ones. 130 uint64_t BufferRangeStart() const { return mEntries.GetState().mRangeStart; } 131 uint64_t BufferRangeEnd() const { return mEntries.GetState().mRangeEnd; } 132 133 private: 134 // Single pre-allocated chunk (to avoid spurious mallocs), used when: 135 // - Duplicating sleeping stacks (hence scExpectedMaximumStackSize). 136 // - Adding JIT info. 137 // - Streaming stacks to JSON. 138 // Mutable because it's accessed from non-multithreaded const methods. 139 mutable Maybe<ProfileBufferChunkManagerSingle> mMaybeWorkerChunkManager; 140 ProfileBufferChunkManagerSingle& WorkerChunkManager() const { 141 if (mMaybeWorkerChunkManager.isNothing()) { 142 // Only actually allocate it on first use. (Some ProfileBuffers are 143 // temporary and don't actually need this.) 144 mMaybeWorkerChunkManager.emplace( 145 ProfileBufferChunk::SizeofChunkMetadata() + 146 ProfileBufferChunkManager::scExpectedMaximumStackSize); 147 } 148 return *mMaybeWorkerChunkManager; 149 } 150 151 // Time from launch (us) when first sampling was recorded. 152 double mFirstSamplingTimeUs = 0.0; 153 // Time from launch (us) when last sampling was recorded. 154 double mLastSamplingTimeUs = 0.0; 155 // Sampling stats: Interval (us) between successive samplings. 156 ProfilerStats mIntervalsUs; 157 // Sampling stats: Total duration (us) of each sampling. (Split detail below.) 158 ProfilerStats mOverheadsUs; 159 // Sampling stats: Time (us) to acquire the lock before sampling. 160 ProfilerStats mLockingsUs; 161 // Sampling stats: Time (us) to discard expired data. 162 ProfilerStats mCleaningsUs; 163 // Sampling stats: Time (us) to collect counter data. 164 ProfilerStats mCountersUs; 165 // Sampling stats: Time (us) to sample thread stacks. 166 ProfilerStats mThreadsUs; 167 }; 168 169 /** 170 * Helper type used to implement ProfilerStackCollector. This type is used as 171 * the collector for MergeStacks by ProfileBuffer. It holds a reference to the 172 * buffer, as well as additional feature flags which are needed to control the 173 * data collection strategy 174 */ 175 class ProfileBufferCollector final : public ProfilerStackCollector { 176 public: 177 ProfileBufferCollector(ProfileBuffer& aBuf, uint64_t aSamplePos, 178 uint64_t aBufferRangeStart) 179 : mBuf(aBuf), 180 mSamplePositionInBuffer(aSamplePos), 181 mBufferRangeStart(aBufferRangeStart) { 182 MOZ_ASSERT( 183 mSamplePositionInBuffer >= mBufferRangeStart, 184 "The sample position should always be after the buffer range start"); 185 } 186 187 // Position at which the sample starts in the profiler buffer (which may be 188 // different from the buffer in which the sample data is collected here). 189 Maybe<uint64_t> SamplePositionInBuffer() override { 190 return Some(mSamplePositionInBuffer); 191 } 192 193 // Profiler buffer's range start (which may be different from the buffer in 194 // which the sample data is collected here). 195 Maybe<uint64_t> BufferRangeStart() override { 196 return Some(mBufferRangeStart); 197 } 198 199 virtual void CollectNativeLeafAddr(void* aAddr) override; 200 virtual void CollectProfilingStackFrame( 201 const ProfilingStackFrame& aFrame) override; 202 203 private: 204 ProfileBuffer& mBuf; 205 uint64_t mSamplePositionInBuffer; 206 uint64_t mBufferRangeStart; 207 }; 208 209 } // namespace baseprofiler 210 } // namespace mozilla 211 212 #endif