Line data Source code
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/. */
6 :
7 : #ifndef ProfileBufferEntry_h
8 : #define ProfileBufferEntry_h
9 :
10 : #include <ostream>
11 : #include "GeckoProfiler.h"
12 : #include "platform.h"
13 : #include "ProfileJSONWriter.h"
14 : #include "ProfilerBacktrace.h"
15 : #include "mozilla/RefPtr.h"
16 : #include <string>
17 : #include <map>
18 : #include "js/ProfilingFrameIterator.h"
19 : #include "js/TrackedOptimizationInfo.h"
20 : #include "nsHashKeys.h"
21 : #include "nsDataHashtable.h"
22 : #include "mozilla/Maybe.h"
23 : #include "mozilla/Vector.h"
24 : #include "gtest/MozGtestFriend.h"
25 : #include "mozilla/HashFunctions.h"
26 : #include "mozilla/UniquePtr.h"
27 :
28 : class ProfilerMarker;
29 :
30 : #define FOR_EACH_PROFILE_BUFFER_ENTRY_KIND(macro) \
31 : macro(Category, int) \
32 : macro(Label, const char*) \
33 : macro(DynamicStringFragment, char*) /* char[kNumChars], really */ \
34 : macro(JitReturnAddr, void*) \
35 : macro(LineNumber, int) \
36 : macro(NativeLeafAddr, void*) \
37 : macro(Marker, ProfilerMarker*) \
38 : macro(ResidentMemory, double) \
39 : macro(Responsiveness, double) \
40 : macro(ThreadId, int) \
41 : macro(Time, double) \
42 : macro(UnsharedMemory, double)
43 :
44 : // NB: Packing this structure has been shown to cause SIGBUS issues on ARM.
45 : #if !defined(GP_ARCH_arm)
46 : #pragma pack(push, 1)
47 : #endif
48 :
49 : class ProfileBufferEntry
50 : {
51 : public:
52 : enum class Kind : uint8_t {
53 : INVALID = 0,
54 : #define KIND(k, t) k,
55 : FOR_EACH_PROFILE_BUFFER_ENTRY_KIND(KIND)
56 : #undef KIND
57 : LIMIT
58 : };
59 :
60 : ProfileBufferEntry();
61 :
62 : // This is equal to sizeof(double), which is the largest non-char variant in
63 : // |u|.
64 : static const size_t kNumChars = 8;
65 :
66 : private:
67 : // aString must be a static string.
68 : ProfileBufferEntry(Kind aKind, const char *aString);
69 : ProfileBufferEntry(Kind aKind, char aChars[kNumChars]);
70 : ProfileBufferEntry(Kind aKind, void *aPtr);
71 : ProfileBufferEntry(Kind aKind, ProfilerMarker *aMarker);
72 : ProfileBufferEntry(Kind aKind, double aDouble);
73 : ProfileBufferEntry(Kind aKind, int aInt);
74 :
75 : public:
76 : #define CTOR(k, t) \
77 : static ProfileBufferEntry k(t aVal) { \
78 : return ProfileBufferEntry(Kind::k, aVal); \
79 : }
80 0 : FOR_EACH_PROFILE_BUFFER_ENTRY_KIND(CTOR)
81 : #undef CTOR
82 :
83 0 : Kind GetKind() const { return mKind; }
84 :
85 : #define IS_KIND(k, t) bool Is##k() const { return mKind == Kind::k; }
86 0 : FOR_EACH_PROFILE_BUFFER_ENTRY_KIND(IS_KIND)
87 : #undef IS_KIND
88 :
89 : private:
90 : FRIEND_TEST(ThreadProfile, InsertOneEntry);
91 : FRIEND_TEST(ThreadProfile, InsertOneEntryWithTinyBuffer);
92 : FRIEND_TEST(ThreadProfile, InsertEntriesNoWrap);
93 : FRIEND_TEST(ThreadProfile, InsertEntriesWrap);
94 : FRIEND_TEST(ThreadProfile, MemoryMeasure);
95 : friend class ProfileBuffer;
96 :
97 : Kind mKind;
98 : union {
99 : const char* mString;
100 : char mChars[kNumChars];
101 : void* mPtr;
102 : ProfilerMarker* mMarker;
103 : double mDouble;
104 : int mInt;
105 : } u;
106 : };
107 :
108 : #if !defined(GP_ARCH_arm)
109 : // Packed layout: 1 byte for the tag + 8 bytes for the value.
110 : static_assert(sizeof(ProfileBufferEntry) == 9, "bad ProfileBufferEntry size");
111 : #pragma pack(pop)
112 : #endif
113 :
114 0 : class UniqueJSONStrings
115 : {
116 : public:
117 0 : UniqueJSONStrings() {
118 0 : mStringTableWriter.StartBareList();
119 0 : }
120 :
121 0 : void SpliceStringTableElements(SpliceableJSONWriter& aWriter) {
122 0 : aWriter.TakeAndSplice(mStringTableWriter.WriteFunc());
123 0 : }
124 :
125 0 : void WriteProperty(mozilla::JSONWriter& aWriter, const char* aName, const char* aStr) {
126 0 : aWriter.IntProperty(aName, GetOrAddIndex(aStr));
127 0 : }
128 :
129 0 : void WriteElement(mozilla::JSONWriter& aWriter, const char* aStr) {
130 0 : aWriter.IntElement(GetOrAddIndex(aStr));
131 0 : }
132 :
133 : uint32_t GetOrAddIndex(const char* aStr);
134 :
135 : struct StringKey {
136 :
137 0 : explicit StringKey(const char* aStr)
138 0 : : mStr(strdup(aStr))
139 : {
140 0 : mHash = mozilla::HashString(mStr);
141 0 : }
142 :
143 0 : StringKey(const StringKey& aOther)
144 0 : : mStr(strdup(aOther.mStr))
145 : {
146 0 : mHash = aOther.mHash;
147 0 : }
148 :
149 0 : ~StringKey() {
150 0 : free(mStr);
151 0 : }
152 :
153 : uint32_t Hash() const;
154 : bool operator==(const StringKey& aOther) const {
155 : return strcmp(mStr, aOther.mStr) == 0;
156 : }
157 0 : bool operator<(const StringKey& aOther) const {
158 0 : return mHash < aOther.mHash;
159 : }
160 :
161 : private:
162 : uint32_t mHash;
163 : char* mStr;
164 : };
165 : private:
166 : SpliceableChunkedJSONWriter mStringTableWriter;
167 : std::map<StringKey, uint32_t> mStringToIndexMap;
168 : };
169 :
170 0 : class UniqueStacks
171 : {
172 : public:
173 0 : struct FrameKey {
174 : // This cannot be a std::string, as it is not memmove compatible, which
175 : // is used by nsHashTable
176 : nsCString mLocation;
177 : mozilla::Maybe<unsigned> mLine;
178 : mozilla::Maybe<unsigned> mCategory;
179 : mozilla::Maybe<void*> mJITAddress;
180 : mozilla::Maybe<uint32_t> mJITDepth;
181 :
182 0 : explicit FrameKey(const char* aLocation)
183 0 : : mLocation(aLocation)
184 : {
185 0 : mHash = Hash();
186 0 : }
187 :
188 0 : FrameKey(const FrameKey& aToCopy)
189 0 : : mLocation(aToCopy.mLocation)
190 : , mLine(aToCopy.mLine)
191 : , mCategory(aToCopy.mCategory)
192 : , mJITAddress(aToCopy.mJITAddress)
193 0 : , mJITDepth(aToCopy.mJITDepth)
194 : {
195 0 : mHash = Hash();
196 0 : }
197 :
198 0 : FrameKey(void* aJITAddress, uint32_t aJITDepth)
199 0 : : mJITAddress(mozilla::Some(aJITAddress))
200 0 : , mJITDepth(mozilla::Some(aJITDepth))
201 : {
202 0 : mHash = Hash();
203 0 : }
204 :
205 : uint32_t Hash() const;
206 : bool operator==(const FrameKey& aOther) const;
207 : bool operator<(const FrameKey& aOther) const {
208 : return mHash < aOther.mHash;
209 : }
210 :
211 : private:
212 : uint32_t mHash;
213 : };
214 :
215 : // A FrameKey that holds a scoped reference to a JIT FrameHandle.
216 0 : struct MOZ_STACK_CLASS OnStackFrameKey : public FrameKey {
217 0 : explicit OnStackFrameKey(const char* aLocation)
218 0 : : FrameKey(aLocation)
219 0 : , mJITFrameHandle(nullptr)
220 0 : { }
221 :
222 : OnStackFrameKey(const OnStackFrameKey& aToCopy)
223 : : FrameKey(aToCopy)
224 : , mJITFrameHandle(aToCopy.mJITFrameHandle)
225 : { }
226 :
227 : const JS::ForEachProfiledFrameOp::FrameHandle* mJITFrameHandle;
228 :
229 0 : OnStackFrameKey(void* aJITAddress, unsigned aJITDepth)
230 0 : : FrameKey(aJITAddress, aJITDepth)
231 0 : , mJITFrameHandle(nullptr)
232 0 : { }
233 :
234 0 : OnStackFrameKey(void* aJITAddress, unsigned aJITDepth,
235 : const JS::ForEachProfiledFrameOp::FrameHandle& aJITFrameHandle)
236 0 : : FrameKey(aJITAddress, aJITDepth)
237 0 : , mJITFrameHandle(&aJITFrameHandle)
238 0 : { }
239 : };
240 :
241 0 : struct StackKey {
242 : mozilla::Maybe<uint32_t> mPrefixHash;
243 : mozilla::Maybe<uint32_t> mPrefix;
244 : uint32_t mFrame;
245 :
246 0 : explicit StackKey(uint32_t aFrame)
247 0 : : mFrame(aFrame)
248 : {
249 0 : mHash = Hash();
250 0 : }
251 :
252 : uint32_t Hash() const;
253 : bool operator==(const StackKey& aOther) const;
254 : bool operator<(const StackKey& aOther) const {
255 : return mHash < aOther.mHash;
256 : }
257 :
258 0 : void UpdateHash(uint32_t aPrefixHash, uint32_t aPrefix, uint32_t aFrame) {
259 0 : mPrefixHash = mozilla::Some(aPrefixHash);
260 0 : mPrefix = mozilla::Some(aPrefix);
261 0 : mFrame = aFrame;
262 0 : mHash = Hash();
263 0 : }
264 :
265 : private:
266 : uint32_t mHash;
267 : };
268 :
269 0 : class Stack {
270 : public:
271 : Stack(UniqueStacks& aUniqueStacks, const OnStackFrameKey& aRoot);
272 :
273 : void AppendFrame(const OnStackFrameKey& aFrame);
274 : uint32_t GetOrAddIndex() const;
275 :
276 : private:
277 : UniqueStacks& mUniqueStacks;
278 : StackKey mStack;
279 : };
280 :
281 : explicit UniqueStacks(JSContext* aContext);
282 :
283 : Stack BeginStack(const OnStackFrameKey& aRoot);
284 : uint32_t LookupJITFrameDepth(void* aAddr);
285 : void AddJITFrameDepth(void* aAddr, unsigned depth);
286 : void SpliceFrameTableElements(SpliceableJSONWriter& aWriter);
287 : void SpliceStackTableElements(SpliceableJSONWriter& aWriter);
288 :
289 : private:
290 : uint32_t GetOrAddFrameIndex(const OnStackFrameKey& aFrame);
291 : uint32_t GetOrAddStackIndex(const StackKey& aStack);
292 : void StreamFrame(const OnStackFrameKey& aFrame);
293 : void StreamStack(const StackKey& aStack);
294 :
295 : public:
296 : UniqueJSONStrings mUniqueStrings;
297 :
298 : private:
299 : JSContext* mContext;
300 :
301 : // To avoid incurring JitcodeGlobalTable lookup costs for every JIT frame,
302 : // we cache the depth of frames keyed by JIT code address. If an address a
303 : // maps to a depth d, then frames keyed by a for depths 0 to d are
304 : // guaranteed to be in mFrameToIndexMap.
305 : std::map<void*, uint32_t> mJITFrameDepthMap;
306 :
307 : uint32_t mFrameCount;
308 : SpliceableChunkedJSONWriter mFrameTableWriter;
309 : nsDataHashtable<nsGenericHashKey<FrameKey>, uint32_t> mFrameToIndexMap;
310 :
311 : SpliceableChunkedJSONWriter mStackTableWriter;
312 :
313 : nsDataHashtable<nsGenericHashKey<StackKey>, uint32_t> mStackToIndexMap;
314 : };
315 :
316 : //
317 : // Thread profile JSON Format
318 : // --------------------------
319 : //
320 : // The profile contains much duplicate information. The output JSON of the
321 : // profile attempts to deduplicate strings, frames, and stack prefixes, to cut
322 : // down on size and to increase JSON streaming speed. Deduplicated values are
323 : // streamed as indices into their respective tables.
324 : //
325 : // Further, arrays of objects with the same set of properties (e.g., samples,
326 : // frames) are output as arrays according to a schema instead of an object
327 : // with property names. A property that is not present is represented in the
328 : // array as null or undefined.
329 : //
330 : // The format of the thread profile JSON is shown by the following example
331 : // with 1 sample and 1 marker:
332 : //
333 : // {
334 : // "name": "Foo",
335 : // "tid": 42,
336 : // "samples":
337 : // {
338 : // "schema":
339 : // {
340 : // "stack": 0, /* index into stackTable */
341 : // "time": 1, /* number */
342 : // "responsiveness": 2, /* number */
343 : // "rss": 3, /* number */
344 : // "uss": 4 /* number */
345 : // },
346 : // "data":
347 : // [
348 : // [ 1, 0.0, 0.0 ] /* { stack: 1, time: 0.0, responsiveness: 0.0 } */
349 : // ]
350 : // },
351 : //
352 : // "markers":
353 : // {
354 : // "schema":
355 : // {
356 : // "name": 0, /* index into stringTable */
357 : // "time": 1, /* number */
358 : // "data": 2 /* arbitrary JSON */
359 : // },
360 : // "data":
361 : // [
362 : // [ 3, 0.1 ] /* { name: 'example marker', time: 0.1 } */
363 : // ]
364 : // },
365 : //
366 : // "stackTable":
367 : // {
368 : // "schema":
369 : // {
370 : // "prefix": 0, /* index into stackTable */
371 : // "frame": 1 /* index into frameTable */
372 : // },
373 : // "data":
374 : // [
375 : // [ null, 0 ], /* (root) */
376 : // [ 0, 1 ] /* (root) > foo.js */
377 : // ]
378 : // },
379 : //
380 : // "frameTable":
381 : // {
382 : // "schema":
383 : // {
384 : // "location": 0, /* index into stringTable */
385 : // "implementation": 1, /* index into stringTable */
386 : // "optimizations": 2, /* arbitrary JSON */
387 : // "line": 3, /* number */
388 : // "category": 4 /* number */
389 : // },
390 : // "data":
391 : // [
392 : // [ 0 ], /* { location: '(root)' } */
393 : // [ 1, 2 ] /* { location: 'foo.js', implementation: 'baseline' } */
394 : // ]
395 : // },
396 : //
397 : // "stringTable":
398 : // [
399 : // "(root)",
400 : // "foo.js",
401 : // "baseline",
402 : // "example marker"
403 : // ]
404 : // }
405 : //
406 :
407 : #endif /* ndef ProfileBufferEntry_h */
|