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