summaryrefslogtreecommitdiffstats
path: root/tools/profiler/core/ProfileBuffer.h
blob: e172577566b4ca006f3276f6a57f4a8830e965d6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
/* -*- 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 "GeckoProfiler.h"
#include "ProfileBufferEntry.h"

#include "mozilla/Maybe.h"
#include "mozilla/PowerOfTwo.h"
#include "mozilla/ProfileBufferChunkManagerSingle.h"
#include "mozilla/ProfileChunkedBuffer.h"

class RunningTimes;

// 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(mozilla::ProfileChunkedBuffer& aBuffer);

  mozilla::ProfileChunkedBuffer& UnderlyingChunkedBuffer() const {
    return mEntries;
  }

  bool IsThreadSafe() const { return mEntries.IsThreadSafe(); }

  // Add |aEntry| to the buffer, ignoring what kind of entry it is.
  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(int aThreadId);

  void CollectCodeLocation(
      const char* aLabel, const char* aStr, uint32_t aFrameFlags,
      uint64_t aInnerWindowID, const mozilla::Maybe<uint32_t>& aLineNumber,
      const mozilla::Maybe<uint32_t>& aColumnNumber,
      const mozilla::Maybe<JS::ProfilingCategoryPair>& aCategoryPair);

  // Maximum size of a frameKey string that we'll handle.
  static const size_t kMaxFrameKeyLength = 512;

  // Add JIT frame information to aJITFrameInfo for any JitReturnAddr entries
  // that are currently in the buffer at or after aRangeStart, in samples
  // for the given thread.
  void AddJITInfoForRange(uint64_t aRangeStart, int aThreadId,
                          JSContext* aContext,
                          JITFrameInfo& aJITFrameInfo) const;

  // 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.
  // aUniqueStacks needs to contain information about any JIT frames that we
  // might encounter in the buffer, before this method is called. In other
  // words, you need to have called AddJITInfoForRange for every range that
  // might contain JIT frame information before calling this method.
  // Return the thread ID of the streamed sample(s), or 0.
  int StreamSamplesToJSON(SpliceableJSONWriter& aWriter, int aThreadId,
                          double aSinceTime, UniqueStacks& aUniqueStacks) const;

  void StreamMarkersToJSON(SpliceableJSONWriter& aWriter, int aThreadId,
                           const mozilla::TimeStamp& aProcessStartTime,
                           double aSinceTime,
                           UniqueStacks& aUniqueStacks) const;
  void StreamPausedRangesToJSON(SpliceableJSONWriter& aWriter,
                                double aSinceTime) const;
  void StreamProfilerOverheadToJSON(SpliceableJSONWriter& aWriter,
                                    const mozilla::TimeStamp& aProcessStartTime,
                                    double aSinceTime) const;
  void StreamCountersToJSON(SpliceableJSONWriter& aWriter,
                            const mozilla::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(int aThreadId, double aSampleTimeMs,
                           mozilla::Maybe<uint64_t>& aLastSample,
                           const RunningTimes& aRunningTimes);

  void DiscardSamplesBeforeTime(double aTime);

  // Read an entry in the buffer.
  ProfileBufferEntry GetEntry(uint64_t aPosition) const {
    return mEntries.ReadAt(
        mozilla::ProfileBufferBlockIndex::CreateFromProfileBufferIndex(
            aPosition),
        [&](mozilla::Maybe<mozilla::ProfileBufferEntryReader>&& aMER) {
          ProfileBufferEntry entry;
          if (aMER.isSome()) {
            if (aMER->CurrentBlockIndex().ConvertToProfileBufferIndex() ==
                aPosition) {
              // If we're here, it means `aPosition` pointed at a valid block.
              MOZ_RELEASE_ASSERT(aMER->RemainingBytes() <= sizeof(entry));
              aMER->ReadBytes(&entry, aMER->RemainingBytes());
            } else {
              // EntryReader at the wrong position, pretend to have read
              // everything.
              aMER->SetRemainingBytes(0);
            }
          }
          return entry;
        });
  }

  size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
  size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;

  void CollectOverheadStats(double aSamplingTimeMs,
                            mozilla::TimeDuration aLocking,
                            mozilla::TimeDuration aCleaning,
                            mozilla::TimeDuration aCounters,
                            mozilla::TimeDuration aThreads);

  ProfilerBufferInfo GetProfilerBufferInfo() const;

 private:
  // Add |aEntry| to the provided ProfileChunkedBuffer.
  // `static` because it may be used to add an entry to a `ProfileChunkedBuffer`
  // that is not attached to a `ProfileBuffer`.
  static mozilla::ProfileBufferBlockIndex AddEntry(
      mozilla::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 mozilla::ProfileBufferBlockIndex AddThreadIdEntry(
      mozilla::ProfileChunkedBuffer& aProfileChunkedBuffer, int aThreadId);

  // The storage in which this ProfileBuffer stores its entries.
  mozilla::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 mozilla::ProfileBufferChunkManagerSingle mWorkerChunkManager{
      mozilla::ProfileBufferChunk::Create(
          mozilla::ProfileBufferChunk::SizeofChunkMetadata() +
          mozilla::ProfileBufferChunkManager::scExpectedMaximumStackSize)};

  double mFirstSamplingTimeUs = 0.0;
  double mLastSamplingTimeUs = 0.0;
  ProfilerStats mIntervalsUs;
  ProfilerStats mOverheadsUs;
  ProfilerStats mLockingsUs;
  ProfilerStats mCleaningsUs;
  ProfilerStats mCountersUs;
  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)
      : mBuf(aBuf), mSamplePositionInBuffer(aSamplePos) {}

  mozilla::Maybe<uint64_t> SamplePositionInBuffer() override {
    return mozilla::Some(mSamplePositionInBuffer);
  }

  mozilla::Maybe<uint64_t> BufferRangeStart() override {
    return mozilla::Some(mBuf.BufferRangeStart());
  }

  virtual void CollectNativeLeafAddr(void* aAddr) override;
  virtual void CollectJitReturnAddr(void* aAddr) override;
  virtual void CollectWasmFrame(const char* aLabel) override;
  virtual void CollectProfilingStackFrame(
      const js::ProfilingStackFrame& aFrame) override;

 private:
  ProfileBuffer& mBuf;
  uint64_t mSamplePositionInBuffer;
};

#endif