Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
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 PROFILER_PSEUDO_STACK_H_
7 #define PROFILER_PSEUDO_STACK_H_
9 #include "mozilla/ArrayUtils.h"
10 #include "mozilla/NullPtr.h"
11 #include <stdint.h>
12 #include "js/ProfilingStack.h"
13 #include <stdlib.h>
14 #include "mozilla/Atomics.h"
16 /* we duplicate this code here to avoid header dependencies
17 * which make it more difficult to include in other places */
18 #if defined(_M_X64) || defined(__x86_64__)
19 #define V8_HOST_ARCH_X64 1
20 #elif defined(_M_IX86) || defined(__i386__) || defined(__i386)
21 #define V8_HOST_ARCH_IA32 1
22 #elif defined(__ARMEL__)
23 #define V8_HOST_ARCH_ARM 1
24 #else
25 #warning Please add support for your architecture in chromium_types.h
26 #endif
28 // STORE_SEQUENCER: Because signals can interrupt our profile modification
29 // we need to make stores are not re-ordered by the compiler
30 // or hardware to make sure the profile is consistent at
31 // every point the signal can fire.
32 #ifdef V8_HOST_ARCH_ARM
33 // TODO Is there something cheaper that will prevent
34 // memory stores from being reordered
36 typedef void (*LinuxKernelMemoryBarrierFunc)(void);
37 LinuxKernelMemoryBarrierFunc pLinuxKernelMemoryBarrier __attribute__((weak)) =
38 (LinuxKernelMemoryBarrierFunc) 0xffff0fa0;
40 # define STORE_SEQUENCER() pLinuxKernelMemoryBarrier()
41 #elif defined(V8_HOST_ARCH_IA32) || defined(V8_HOST_ARCH_X64)
42 # if defined(_MSC_VER)
43 #if _MSC_VER > 1400
44 # include <intrin.h>
45 #else // _MSC_VER > 1400
46 // MSVC2005 has a name collision bug caused when both <intrin.h> and <winnt.h> are included together.
47 #ifdef _WINNT_
48 # define _interlockedbittestandreset _interlockedbittestandreset_NAME_CHANGED_TO_AVOID_MSVS2005_ERROR
49 # define _interlockedbittestandset _interlockedbittestandset_NAME_CHANGED_TO_AVOID_MSVS2005_ERROR
50 # include <intrin.h>
51 #else
52 # include <intrin.h>
53 # define _interlockedbittestandreset _interlockedbittestandreset_NAME_CHANGED_TO_AVOID_MSVS2005_ERROR
54 # define _interlockedbittestandset _interlockedbittestandset_NAME_CHANGED_TO_AVOID_MSVS2005_ERROR
55 #endif
56 // Even though MSVC2005 has the intrinsic _ReadWriteBarrier, it fails to link to it when it's
57 // not explicitly declared.
58 # pragma intrinsic(_ReadWriteBarrier)
59 #endif // _MSC_VER > 1400
60 # define STORE_SEQUENCER() _ReadWriteBarrier();
61 # elif defined(__INTEL_COMPILER)
62 # define STORE_SEQUENCER() __memory_barrier();
63 # elif __GNUC__
64 # define STORE_SEQUENCER() asm volatile("" ::: "memory");
65 # else
66 # error "Memory clobber not supported for your compiler."
67 # endif
68 #else
69 # error "Memory clobber not supported for your platform."
70 #endif
72 // We can't include <algorithm> because it causes issues on OS X, so we use
73 // our own min function.
74 static inline uint32_t sMin(uint32_t l, uint32_t r) {
75 return l < r ? l : r;
76 }
78 // A stack entry exists to allow the JS engine to inform SPS of the current
79 // backtrace, but also to instrument particular points in C++ in case stack
80 // walking is not available on the platform we are running on.
81 //
82 // Each entry has a descriptive string, a relevant stack address, and some extra
83 // information the JS engine might want to inform SPS of. This class inherits
84 // from the JS engine's version of the entry to ensure that the size and layout
85 // of the two representations are consistent.
86 class StackEntry : public js::ProfileEntry
87 {
88 public:
90 bool isCopyLabel() const volatile {
91 return !((uintptr_t)stackAddress() & 0x1);
92 }
94 void setStackAddressCopy(void *sparg, bool copy) volatile {
95 // Tagged pointer. Less significant bit used to track if mLabel needs a
96 // copy. Note that we don't need the last bit of the stack address for
97 // proper ordering. This is optimized for encoding within the JS engine's
98 // instrumentation, so we do the extra work here of encoding a bit.
99 // Last bit 1 = Don't copy, Last bit 0 = Copy.
100 if (copy) {
101 setStackAddress(reinterpret_cast<void*>(
102 reinterpret_cast<uintptr_t>(sparg) & ~NoCopyBit));
103 } else {
104 setStackAddress(reinterpret_cast<void*>(
105 reinterpret_cast<uintptr_t>(sparg) | NoCopyBit));
106 }
107 }
108 };
110 class ProfilerMarkerPayload;
111 template<typename T>
112 class ProfilerLinkedList;
113 class JSStreamWriter;
114 class JSCustomArray;
115 class ThreadProfile;
116 class ProfilerMarker {
117 friend class ProfilerLinkedList<ProfilerMarker>;
118 public:
119 ProfilerMarker(const char* aMarkerName,
120 ProfilerMarkerPayload* aPayload = nullptr,
121 float aTime = 0);
123 ~ProfilerMarker();
125 const char* GetMarkerName() const {
126 return mMarkerName;
127 }
129 void
130 StreamJSObject(JSStreamWriter& b) const;
132 void SetGeneration(int aGenID);
134 bool HasExpired(int aGenID) const {
135 return mGenID + 2 <= aGenID;
136 }
138 float GetTime();
140 private:
141 char* mMarkerName;
142 ProfilerMarkerPayload* mPayload;
143 ProfilerMarker* mNext;
144 float mTime;
145 int mGenID;
146 };
148 // Foward declaration
149 typedef struct _UnwinderThreadBuffer UnwinderThreadBuffer;
151 /**
152 * This struct is used to add a mNext field to UnwinderThreadBuffer objects for
153 * use with ProfilerLinkedList. It is done this way so that UnwinderThreadBuffer
154 * may continue to be opaque with respect to code outside of UnwinderThread2.cpp
155 */
156 struct LinkedUWTBuffer
157 {
158 LinkedUWTBuffer()
159 :mNext(nullptr)
160 {}
161 virtual ~LinkedUWTBuffer() {}
162 virtual UnwinderThreadBuffer* GetBuffer() = 0;
163 LinkedUWTBuffer* mNext;
164 };
166 template<typename T>
167 class ProfilerLinkedList {
168 public:
169 ProfilerLinkedList()
170 : mHead(nullptr)
171 , mTail(nullptr)
172 {}
174 void insert(T* elem)
175 {
176 if (!mTail) {
177 mHead = elem;
178 mTail = elem;
179 } else {
180 mTail->mNext = elem;
181 mTail = elem;
182 }
183 elem->mNext = nullptr;
184 }
186 T* popHead()
187 {
188 if (!mHead) {
189 MOZ_ASSERT(false);
190 return nullptr;
191 }
193 T* head = mHead;
195 mHead = head->mNext;
196 if (!mHead) {
197 mTail = nullptr;
198 }
200 return head;
201 }
203 const T* peek() {
204 return mHead;
205 }
207 private:
208 T* mHead;
209 T* mTail;
210 };
212 typedef ProfilerLinkedList<ProfilerMarker> ProfilerMarkerLinkedList;
213 typedef ProfilerLinkedList<LinkedUWTBuffer> UWTBufferLinkedList;
215 class PendingMarkers {
216 public:
217 PendingMarkers()
218 : mSignalLock(false)
219 {}
221 ~PendingMarkers();
223 void addMarker(ProfilerMarker *aMarker);
225 void updateGeneration(int aGenID);
227 /**
228 * Track a marker which has been inserted into the ThreadProfile.
229 * This marker can safely be deleted once the generation has
230 * expired.
231 */
232 void addStoredMarker(ProfilerMarker *aStoredMarker);
234 // called within signal. Function must be reentrant
235 ProfilerMarkerLinkedList* getPendingMarkers()
236 {
237 // if mSignalLock then the stack is inconsistent because it's being
238 // modified by the profiled thread. Post pone these markers
239 // for the next sample. The odds of a livelock are nearly impossible
240 // and would show up in a profile as many sample in 'addMarker' thus
241 // we ignore this scenario.
242 if (mSignalLock) {
243 return nullptr;
244 }
245 return &mPendingMarkers;
246 }
248 void clearMarkers()
249 {
250 while (mPendingMarkers.peek()) {
251 delete mPendingMarkers.popHead();
252 }
253 while (mStoredMarkers.peek()) {
254 delete mStoredMarkers.popHead();
255 }
256 }
258 private:
259 // Keep a list of active markers to be applied to the next sample taken
260 ProfilerMarkerLinkedList mPendingMarkers;
261 ProfilerMarkerLinkedList mStoredMarkers;
262 // If this is set then it's not safe to read mStackPointer from the signal handler
263 volatile bool mSignalLock;
264 // We don't want to modify _markers from within the signal so we allow
265 // it to queue a clear operation.
266 volatile mozilla::sig_safe_t mGenID;
267 };
269 class PendingUWTBuffers
270 {
271 public:
272 PendingUWTBuffers()
273 : mSignalLock(false)
274 {
275 }
277 void addLinkedUWTBuffer(LinkedUWTBuffer* aBuff)
278 {
279 MOZ_ASSERT(aBuff);
280 mSignalLock = true;
281 STORE_SEQUENCER();
282 mPendingUWTBuffers.insert(aBuff);
283 STORE_SEQUENCER();
284 mSignalLock = false;
285 }
287 // called within signal. Function must be reentrant
288 UWTBufferLinkedList* getLinkedUWTBuffers()
289 {
290 if (mSignalLock) {
291 return nullptr;
292 }
293 return &mPendingUWTBuffers;
294 }
296 private:
297 UWTBufferLinkedList mPendingUWTBuffers;
298 volatile bool mSignalLock;
299 };
301 // Stub eventMarker function for js-engine event generation.
302 void ProfilerJSEventMarker(const char *event);
304 // the PseudoStack members are read by signal
305 // handlers, so the mutation of them needs to be signal-safe.
306 struct PseudoStack
307 {
308 public:
309 PseudoStack()
310 : mStackPointer(0)
311 , mSleepId(0)
312 , mSleepIdObserved(0)
313 , mSleeping(false)
314 , mRuntime(nullptr)
315 , mStartJSSampling(false)
316 , mPrivacyMode(false)
317 { }
319 ~PseudoStack() {
320 if (mStackPointer != 0) {
321 // We're releasing the pseudostack while it's still in use.
322 // The label macros keep a non ref counted reference to the
323 // stack to avoid a TLS. If these are not all cleared we will
324 // get a use-after-free so better to crash now.
325 abort();
326 }
327 }
329 // This is called on every profiler restart. Put things that should happen at that time here.
330 void reinitializeOnResume() {
331 // This is needed to cause an initial sample to be taken from sleeping threads. Otherwise sleeping
332 // threads would not have any samples to copy forward while sleeping.
333 mSleepId++;
334 }
336 void addLinkedUWTBuffer(LinkedUWTBuffer* aBuff)
337 {
338 mPendingUWTBuffers.addLinkedUWTBuffer(aBuff);
339 }
341 UWTBufferLinkedList* getLinkedUWTBuffers()
342 {
343 return mPendingUWTBuffers.getLinkedUWTBuffers();
344 }
346 void addMarker(const char *aMarkerStr, ProfilerMarkerPayload *aPayload, float aTime)
347 {
348 ProfilerMarker* marker = new ProfilerMarker(aMarkerStr, aPayload, aTime);
349 mPendingMarkers.addMarker(marker);
350 }
352 void addStoredMarker(ProfilerMarker *aStoredMarker) {
353 mPendingMarkers.addStoredMarker(aStoredMarker);
354 }
356 void updateGeneration(int aGenID) {
357 mPendingMarkers.updateGeneration(aGenID);
358 }
360 // called within signal. Function must be reentrant
361 ProfilerMarkerLinkedList* getPendingMarkers()
362 {
363 return mPendingMarkers.getPendingMarkers();
364 }
366 void push(const char *aName, uint32_t line)
367 {
368 push(aName, nullptr, false, line);
369 }
371 void push(const char *aName, void *aStackAddress, bool aCopy, uint32_t line)
372 {
373 if (size_t(mStackPointer) >= mozilla::ArrayLength(mStack)) {
374 mStackPointer++;
375 return;
376 }
378 // Make sure we increment the pointer after the name has
379 // been written such that mStack is always consistent.
380 mStack[mStackPointer].setLabel(aName);
381 mStack[mStackPointer].setStackAddressCopy(aStackAddress, aCopy);
382 mStack[mStackPointer].setLine(line);
384 // Prevent the optimizer from re-ordering these instructions
385 STORE_SEQUENCER();
386 mStackPointer++;
387 }
388 void pop()
389 {
390 mStackPointer--;
391 }
392 bool isEmpty()
393 {
394 return mStackPointer == 0;
395 }
396 uint32_t stackSize() const
397 {
398 return sMin(mStackPointer, mozilla::sig_safe_t(mozilla::ArrayLength(mStack)));
399 }
401 void sampleRuntime(JSRuntime *runtime) {
402 mRuntime = runtime;
403 if (!runtime) {
404 // JS shut down
405 return;
406 }
408 static_assert(sizeof(mStack[0]) == sizeof(js::ProfileEntry),
409 "mStack must be binary compatible with js::ProfileEntry.");
410 js::SetRuntimeProfilingStack(runtime,
411 (js::ProfileEntry*) mStack,
412 (uint32_t*) &mStackPointer,
413 uint32_t(mozilla::ArrayLength(mStack)));
414 if (mStartJSSampling)
415 enableJSSampling();
416 }
417 void enableJSSampling() {
418 if (mRuntime) {
419 js::EnableRuntimeProfilingStack(mRuntime, true);
420 js::RegisterRuntimeProfilingEventMarker(mRuntime, &ProfilerJSEventMarker);
421 mStartJSSampling = false;
422 } else {
423 mStartJSSampling = true;
424 }
425 }
426 void jsOperationCallback() {
427 if (mStartJSSampling)
428 enableJSSampling();
429 }
430 void disableJSSampling() {
431 mStartJSSampling = false;
432 if (mRuntime)
433 js::EnableRuntimeProfilingStack(mRuntime, false);
434 }
436 // Keep a list of active checkpoints
437 StackEntry volatile mStack[1024];
438 private:
439 // Keep a list of pending markers that must be moved
440 // to the circular buffer
441 PendingMarkers mPendingMarkers;
442 // List of LinkedUWTBuffers that must be processed on the next tick
443 PendingUWTBuffers mPendingUWTBuffers;
444 // This may exceed the length of mStack, so instead use the stackSize() method
445 // to determine the number of valid samples in mStack
446 mozilla::sig_safe_t mStackPointer;
447 // Incremented at every sleep/wake up of the thread
448 int mSleepId;
449 // Previous id observed. If this is not the same as mSleepId, this thread is not sleeping in the same place any more
450 mozilla::Atomic<int> mSleepIdObserved;
451 // Keeps tack of whether the thread is sleeping or not (1 when sleeping 0 when awake)
452 mozilla::Atomic<int> mSleeping;
453 public:
454 // The runtime which is being sampled
455 JSRuntime *mRuntime;
456 // Start JS Profiling when possible
457 bool mStartJSSampling;
458 bool mPrivacyMode;
460 enum SleepState {NOT_SLEEPING, SLEEPING_FIRST, SLEEPING_AGAIN};
462 // The first time this is called per sleep cycle we return SLEEPING_FIRST
463 // and any other subsequent call within the same sleep cycle we return SLEEPING_AGAIN
464 SleepState observeSleeping() {
465 if (mSleeping != 0) {
466 if (mSleepIdObserved == mSleepId) {
467 return SLEEPING_AGAIN;
468 } else {
469 mSleepIdObserved = mSleepId;
470 return SLEEPING_FIRST;
471 }
472 } else {
473 return NOT_SLEEPING;
474 }
475 }
478 // Call this whenever the current thread sleeps or wakes up
479 // Calling setSleeping with the same value twice in a row is an error
480 void setSleeping(int sleeping) {
481 MOZ_ASSERT(mSleeping != sleeping);
482 mSleepId++;
483 mSleeping = sleeping;
484 }
485 };
487 #endif