Bug 1882468 - Add BUG_COMPONENT for migrated monorepo files in mobile/android/moz...
[gecko.git] / mozglue / baseprofiler / core / ProfileBufferEntry.h
blobee6e401bd86f71e4cd3dccb225816d46757611f2
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef ProfileBufferEntry_h
8 #define ProfileBufferEntry_h
10 #include "BaseProfilingCategory.h"
11 #include "gtest/MozGtestFriend.h"
12 #include "mozilla/BaseProfileJSONWriter.h"
13 #include "mozilla/HashFunctions.h"
14 #include "mozilla/HashTable.h"
15 #include "mozilla/Maybe.h"
16 #include "mozilla/ProfileBufferEntryKinds.h"
17 #include "mozilla/UniquePtr.h"
18 #include "mozilla/Variant.h"
19 #include "mozilla/Vector.h"
21 #include <string>
22 #include <type_traits>
24 namespace mozilla {
25 namespace baseprofiler {
27 class ProfileBufferEntry {
28 public:
29 using KindUnderlyingType =
30 std::underlying_type_t<::mozilla::ProfileBufferEntryKind>;
31 using Kind = ::mozilla::ProfileBufferEntryKind;
33 ProfileBufferEntry();
35 static constexpr size_t kNumChars = ::mozilla::ProfileBufferEntryNumChars;
37 private:
38 // aString must be a static string.
39 ProfileBufferEntry(Kind aKind, const char* aString);
40 ProfileBufferEntry(Kind aKind, char aChars[kNumChars]);
41 ProfileBufferEntry(Kind aKind, void* aPtr);
42 ProfileBufferEntry(Kind aKind, double aDouble);
43 ProfileBufferEntry(Kind aKind, int64_t aInt64);
44 ProfileBufferEntry(Kind aKind, uint64_t aUint64);
45 ProfileBufferEntry(Kind aKind, int aInt);
46 ProfileBufferEntry(Kind aKind, BaseProfilerThreadId aThreadId);
48 public:
49 #define CTOR(KIND, TYPE, SIZE) \
50 static ProfileBufferEntry KIND(TYPE aVal) { \
51 return ProfileBufferEntry(Kind::KIND, aVal); \
53 FOR_EACH_PROFILE_BUFFER_ENTRY_KIND(CTOR)
54 #undef CTOR
56 Kind GetKind() const { return mKind; }
58 #define IS_KIND(KIND, TYPE, SIZE) \
59 bool Is##KIND() const { return mKind == Kind::KIND; }
60 FOR_EACH_PROFILE_BUFFER_ENTRY_KIND(IS_KIND)
61 #undef IS_KIND
63 private:
64 FRIEND_TEST(ThreadProfile, InsertOneEntry);
65 FRIEND_TEST(ThreadProfile, InsertOneEntryWithTinyBuffer);
66 FRIEND_TEST(ThreadProfile, InsertEntriesNoWrap);
67 FRIEND_TEST(ThreadProfile, InsertEntriesWrap);
68 FRIEND_TEST(ThreadProfile, MemoryMeasure);
69 friend class ProfileBuffer;
71 Kind mKind;
72 uint8_t mStorage[kNumChars];
74 const char* GetString() const;
75 void* GetPtr() const;
76 double GetDouble() const;
77 int GetInt() const;
78 int64_t GetInt64() const;
79 uint64_t GetUint64() const;
80 BaseProfilerThreadId GetThreadId() const;
81 void CopyCharsInto(char (&aOutArray)[kNumChars]) const;
84 // Packed layout: 1 byte for the tag + 8 bytes for the value.
85 static_assert(sizeof(ProfileBufferEntry) == 9, "bad ProfileBufferEntry size");
87 class UniqueStacks {
88 public:
89 struct FrameKey {
90 explicit FrameKey(const char* aLocation)
91 : mData(NormalFrameData{std::string(aLocation), false, 0, Nothing(),
92 Nothing()}) {}
94 FrameKey(std::string&& aLocation, bool aRelevantForJS,
95 uint64_t aInnerWindowID, const Maybe<unsigned>& aLine,
96 const Maybe<unsigned>& aColumn,
97 const Maybe<ProfilingCategoryPair>& aCategoryPair)
98 : mData(NormalFrameData{aLocation, aRelevantForJS, aInnerWindowID,
99 aLine, aColumn, aCategoryPair}) {}
101 FrameKey(const FrameKey& aToCopy) = default;
103 uint32_t Hash() const;
104 bool operator==(const FrameKey& aOther) const {
105 return mData == aOther.mData;
108 struct NormalFrameData {
109 bool operator==(const NormalFrameData& aOther) const;
111 std::string mLocation;
112 bool mRelevantForJS;
113 uint64_t mInnerWindowID;
114 Maybe<unsigned> mLine;
115 Maybe<unsigned> mColumn;
116 Maybe<ProfilingCategoryPair> mCategoryPair;
118 Variant<NormalFrameData> mData;
121 struct FrameKeyHasher {
122 using Lookup = FrameKey;
124 static HashNumber hash(const FrameKey& aLookup) {
125 HashNumber hash = 0;
126 if (aLookup.mData.is<FrameKey::NormalFrameData>()) {
127 const FrameKey::NormalFrameData& data =
128 aLookup.mData.as<FrameKey::NormalFrameData>();
129 if (!data.mLocation.empty()) {
130 hash = AddToHash(hash, HashString(data.mLocation.c_str()));
132 hash = AddToHash(hash, data.mRelevantForJS);
133 hash = mozilla::AddToHash(hash, data.mInnerWindowID);
134 if (data.mLine.isSome()) {
135 hash = AddToHash(hash, *data.mLine);
137 if (data.mColumn.isSome()) {
138 hash = AddToHash(hash, *data.mColumn);
140 if (data.mCategoryPair.isSome()) {
141 hash = AddToHash(hash, static_cast<uint32_t>(*data.mCategoryPair));
144 return hash;
147 static bool match(const FrameKey& aKey, const FrameKey& aLookup) {
148 return aKey == aLookup;
151 static void rekey(FrameKey& aKey, const FrameKey& aNewKey) {
152 aKey = aNewKey;
156 struct StackKey {
157 Maybe<uint32_t> mPrefixStackIndex;
158 uint32_t mFrameIndex;
160 explicit StackKey(uint32_t aFrame)
161 : mFrameIndex(aFrame), mHash(HashGeneric(aFrame)) {}
163 StackKey(const StackKey& aPrefix, uint32_t aPrefixStackIndex,
164 uint32_t aFrame)
165 : mPrefixStackIndex(Some(aPrefixStackIndex)),
166 mFrameIndex(aFrame),
167 mHash(AddToHash(aPrefix.mHash, aFrame)) {}
169 HashNumber Hash() const { return mHash; }
171 bool operator==(const StackKey& aOther) const {
172 return mPrefixStackIndex == aOther.mPrefixStackIndex &&
173 mFrameIndex == aOther.mFrameIndex;
176 private:
177 HashNumber mHash;
180 struct StackKeyHasher {
181 using Lookup = StackKey;
183 static HashNumber hash(const StackKey& aLookup) { return aLookup.Hash(); }
185 static bool match(const StackKey& aKey, const StackKey& aLookup) {
186 return aKey == aLookup;
189 static void rekey(StackKey& aKey, const StackKey& aNewKey) {
190 aKey = aNewKey;
194 UniqueStacks();
196 // Return a StackKey for aFrame as the stack's root frame (no prefix).
197 [[nodiscard]] StackKey BeginStack(const FrameKey& aFrame);
199 // Return a new StackKey that is obtained by appending aFrame to aStack.
200 [[nodiscard]] StackKey AppendFrame(const StackKey& aStack,
201 const FrameKey& aFrame);
203 [[nodiscard]] uint32_t GetOrAddFrameIndex(const FrameKey& aFrame);
204 [[nodiscard]] uint32_t GetOrAddStackIndex(const StackKey& aStack);
206 void SpliceFrameTableElements(SpliceableJSONWriter& aWriter);
207 void SpliceStackTableElements(SpliceableJSONWriter& aWriter);
209 UniqueJSONStrings& UniqueStrings() {
210 MOZ_RELEASE_ASSERT(mUniqueStrings.get());
211 return *mUniqueStrings;
214 private:
215 void StreamNonJITFrame(const FrameKey& aFrame);
216 void StreamStack(const StackKey& aStack);
218 UniquePtr<UniqueJSONStrings> mUniqueStrings;
220 SpliceableChunkedJSONWriter mFrameTableWriter;
221 HashMap<FrameKey, uint32_t, FrameKeyHasher> mFrameToIndexMap;
223 SpliceableChunkedJSONWriter mStackTableWriter;
224 HashMap<StackKey, uint32_t, StackKeyHasher> mStackToIndexMap;
228 // Thread profile JSON Format
229 // --------------------------
231 // The profile contains much duplicate information. The output JSON of the
232 // profile attempts to deduplicate strings, frames, and stack prefixes, to cut
233 // down on size and to increase JSON streaming speed. Deduplicated values are
234 // streamed as indices into their respective tables.
236 // Further, arrays of objects with the same set of properties (e.g., samples,
237 // frames) are output as arrays according to a schema instead of an object
238 // with property names. A property that is not present is represented in the
239 // array as null or undefined.
241 // The format of the thread profile JSON is shown by the following example
242 // with 1 sample and 1 marker:
244 // {
245 // "name": "Foo",
246 // "tid": 42,
247 // "samples":
248 // {
249 // "schema":
250 // {
251 // "stack": 0, /* index into stackTable */
252 // "time": 1, /* number */
253 // "eventDelay": 2, /* number */
254 // },
255 // "data":
256 // [
257 // [ 1, 0.0, 0.0 ] /* { stack: 1, time: 0.0, eventDelay: 0.0 } */
258 // ]
259 // },
261 // "markers":
262 // {
263 // "schema":
264 // {
265 // "name": 0, /* index into stringTable */
266 // "time": 1, /* number */
267 // "data": 2 /* arbitrary JSON */
268 // },
269 // "data":
270 // [
271 // [ 3, 0.1 ] /* { name: 'example marker', time: 0.1 } */
272 // ]
273 // },
275 // "stackTable":
276 // {
277 // "schema":
278 // {
279 // "prefix": 0, /* index into stackTable */
280 // "frame": 1 /* index into frameTable */
281 // },
282 // "data":
283 // [
284 // [ null, 0 ], /* (root) */
285 // [ 0, 1 ] /* (root) > foo.js */
286 // ]
287 // },
289 // "frameTable":
290 // {
291 // "schema":
292 // {
293 // "location": 0, /* index into stringTable */
294 // "relevantForJS": 1, /* bool */
295 // "innerWindowID": 2, /* inner window ID of global JS `window` object */
296 // "implementation": 3, /* index into stringTable */
297 // "line": 4, /* number */
298 // "column": 5, /* number */
299 // "category": 6, /* index into profile.meta.categories */
300 // "subcategory": 7 /* index into
301 // profile.meta.categories[category].subcategories */
302 // },
303 // "data":
304 // [
305 // [ 0 ], /* { location: '(root)' } */
306 // [ 1, 2 ] /* { location: 'foo.js',
307 // implementation: 'baseline' } */
308 // ]
309 // },
311 // "stringTable":
312 // [
313 // "(root)",
314 // "foo.js",
315 // "baseline",
316 // "example marker"
317 // ]
318 // }
320 // Process:
321 // {
322 // "name": "Bar",
323 // "pid": 24,
324 // "threads":
325 // [
326 // <0-N threads from above>
327 // ],
328 // "counters": /* includes the memory counter */
329 // [
330 // {
331 // "name": "qwerty",
332 // "category": "uiop",
333 // "description": "this is qwerty uiop",
334 // "sample_groups:
335 // [
336 // {
337 // "id": 42, /* number (thread id, or object identifier (tab), etc) */
338 // "samples:
339 // {
340 // "schema":
341 // {
342 // "time": 1, /* number */
343 // "number": 2, /* number (of times the counter was touched) */
344 // "count": 3 /* number (total for the counter) */
345 // },
346 // "data":
347 // [
348 // [ 0.1, 1824,
349 // 454622 ] /* { time: 0.1, number: 1824, count: 454622 } */
350 // ]
351 // },
352 // },
353 // /* more sample-group objects with different id's */
354 // ]
355 // },
356 // /* more counters */
357 // ],
358 // }
361 } // namespace baseprofiler
362 } // namespace mozilla
364 #endif /* ndef ProfileBufferEntry_h */