1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/tools/profiler/PseudoStack.h Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,488 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#ifndef PROFILER_PSEUDO_STACK_H_ 1.10 +#define PROFILER_PSEUDO_STACK_H_ 1.11 + 1.12 +#include "mozilla/ArrayUtils.h" 1.13 +#include "mozilla/NullPtr.h" 1.14 +#include <stdint.h> 1.15 +#include "js/ProfilingStack.h" 1.16 +#include <stdlib.h> 1.17 +#include "mozilla/Atomics.h" 1.18 + 1.19 +/* we duplicate this code here to avoid header dependencies 1.20 + * which make it more difficult to include in other places */ 1.21 +#if defined(_M_X64) || defined(__x86_64__) 1.22 +#define V8_HOST_ARCH_X64 1 1.23 +#elif defined(_M_IX86) || defined(__i386__) || defined(__i386) 1.24 +#define V8_HOST_ARCH_IA32 1 1.25 +#elif defined(__ARMEL__) 1.26 +#define V8_HOST_ARCH_ARM 1 1.27 +#else 1.28 +#warning Please add support for your architecture in chromium_types.h 1.29 +#endif 1.30 + 1.31 +// STORE_SEQUENCER: Because signals can interrupt our profile modification 1.32 +// we need to make stores are not re-ordered by the compiler 1.33 +// or hardware to make sure the profile is consistent at 1.34 +// every point the signal can fire. 1.35 +#ifdef V8_HOST_ARCH_ARM 1.36 +// TODO Is there something cheaper that will prevent 1.37 +// memory stores from being reordered 1.38 + 1.39 +typedef void (*LinuxKernelMemoryBarrierFunc)(void); 1.40 +LinuxKernelMemoryBarrierFunc pLinuxKernelMemoryBarrier __attribute__((weak)) = 1.41 + (LinuxKernelMemoryBarrierFunc) 0xffff0fa0; 1.42 + 1.43 +# define STORE_SEQUENCER() pLinuxKernelMemoryBarrier() 1.44 +#elif defined(V8_HOST_ARCH_IA32) || defined(V8_HOST_ARCH_X64) 1.45 +# if defined(_MSC_VER) 1.46 +#if _MSC_VER > 1400 1.47 +# include <intrin.h> 1.48 +#else // _MSC_VER > 1400 1.49 + // MSVC2005 has a name collision bug caused when both <intrin.h> and <winnt.h> are included together. 1.50 +#ifdef _WINNT_ 1.51 +# define _interlockedbittestandreset _interlockedbittestandreset_NAME_CHANGED_TO_AVOID_MSVS2005_ERROR 1.52 +# define _interlockedbittestandset _interlockedbittestandset_NAME_CHANGED_TO_AVOID_MSVS2005_ERROR 1.53 +# include <intrin.h> 1.54 +#else 1.55 +# include <intrin.h> 1.56 +# define _interlockedbittestandreset _interlockedbittestandreset_NAME_CHANGED_TO_AVOID_MSVS2005_ERROR 1.57 +# define _interlockedbittestandset _interlockedbittestandset_NAME_CHANGED_TO_AVOID_MSVS2005_ERROR 1.58 +#endif 1.59 + // Even though MSVC2005 has the intrinsic _ReadWriteBarrier, it fails to link to it when it's 1.60 + // not explicitly declared. 1.61 +# pragma intrinsic(_ReadWriteBarrier) 1.62 +#endif // _MSC_VER > 1400 1.63 +# define STORE_SEQUENCER() _ReadWriteBarrier(); 1.64 +# elif defined(__INTEL_COMPILER) 1.65 +# define STORE_SEQUENCER() __memory_barrier(); 1.66 +# elif __GNUC__ 1.67 +# define STORE_SEQUENCER() asm volatile("" ::: "memory"); 1.68 +# else 1.69 +# error "Memory clobber not supported for your compiler." 1.70 +# endif 1.71 +#else 1.72 +# error "Memory clobber not supported for your platform." 1.73 +#endif 1.74 + 1.75 +// We can't include <algorithm> because it causes issues on OS X, so we use 1.76 +// our own min function. 1.77 +static inline uint32_t sMin(uint32_t l, uint32_t r) { 1.78 + return l < r ? l : r; 1.79 +} 1.80 + 1.81 +// A stack entry exists to allow the JS engine to inform SPS of the current 1.82 +// backtrace, but also to instrument particular points in C++ in case stack 1.83 +// walking is not available on the platform we are running on. 1.84 +// 1.85 +// Each entry has a descriptive string, a relevant stack address, and some extra 1.86 +// information the JS engine might want to inform SPS of. This class inherits 1.87 +// from the JS engine's version of the entry to ensure that the size and layout 1.88 +// of the two representations are consistent. 1.89 +class StackEntry : public js::ProfileEntry 1.90 +{ 1.91 +public: 1.92 + 1.93 + bool isCopyLabel() const volatile { 1.94 + return !((uintptr_t)stackAddress() & 0x1); 1.95 + } 1.96 + 1.97 + void setStackAddressCopy(void *sparg, bool copy) volatile { 1.98 + // Tagged pointer. Less significant bit used to track if mLabel needs a 1.99 + // copy. Note that we don't need the last bit of the stack address for 1.100 + // proper ordering. This is optimized for encoding within the JS engine's 1.101 + // instrumentation, so we do the extra work here of encoding a bit. 1.102 + // Last bit 1 = Don't copy, Last bit 0 = Copy. 1.103 + if (copy) { 1.104 + setStackAddress(reinterpret_cast<void*>( 1.105 + reinterpret_cast<uintptr_t>(sparg) & ~NoCopyBit)); 1.106 + } else { 1.107 + setStackAddress(reinterpret_cast<void*>( 1.108 + reinterpret_cast<uintptr_t>(sparg) | NoCopyBit)); 1.109 + } 1.110 + } 1.111 +}; 1.112 + 1.113 +class ProfilerMarkerPayload; 1.114 +template<typename T> 1.115 +class ProfilerLinkedList; 1.116 +class JSStreamWriter; 1.117 +class JSCustomArray; 1.118 +class ThreadProfile; 1.119 +class ProfilerMarker { 1.120 + friend class ProfilerLinkedList<ProfilerMarker>; 1.121 +public: 1.122 + ProfilerMarker(const char* aMarkerName, 1.123 + ProfilerMarkerPayload* aPayload = nullptr, 1.124 + float aTime = 0); 1.125 + 1.126 + ~ProfilerMarker(); 1.127 + 1.128 + const char* GetMarkerName() const { 1.129 + return mMarkerName; 1.130 + } 1.131 + 1.132 + void 1.133 + StreamJSObject(JSStreamWriter& b) const; 1.134 + 1.135 + void SetGeneration(int aGenID); 1.136 + 1.137 + bool HasExpired(int aGenID) const { 1.138 + return mGenID + 2 <= aGenID; 1.139 + } 1.140 + 1.141 + float GetTime(); 1.142 + 1.143 +private: 1.144 + char* mMarkerName; 1.145 + ProfilerMarkerPayload* mPayload; 1.146 + ProfilerMarker* mNext; 1.147 + float mTime; 1.148 + int mGenID; 1.149 +}; 1.150 + 1.151 +// Foward declaration 1.152 +typedef struct _UnwinderThreadBuffer UnwinderThreadBuffer; 1.153 + 1.154 +/** 1.155 + * This struct is used to add a mNext field to UnwinderThreadBuffer objects for 1.156 + * use with ProfilerLinkedList. It is done this way so that UnwinderThreadBuffer 1.157 + * may continue to be opaque with respect to code outside of UnwinderThread2.cpp 1.158 + */ 1.159 +struct LinkedUWTBuffer 1.160 +{ 1.161 + LinkedUWTBuffer() 1.162 + :mNext(nullptr) 1.163 + {} 1.164 + virtual ~LinkedUWTBuffer() {} 1.165 + virtual UnwinderThreadBuffer* GetBuffer() = 0; 1.166 + LinkedUWTBuffer* mNext; 1.167 +}; 1.168 + 1.169 +template<typename T> 1.170 +class ProfilerLinkedList { 1.171 +public: 1.172 + ProfilerLinkedList() 1.173 + : mHead(nullptr) 1.174 + , mTail(nullptr) 1.175 + {} 1.176 + 1.177 + void insert(T* elem) 1.178 + { 1.179 + if (!mTail) { 1.180 + mHead = elem; 1.181 + mTail = elem; 1.182 + } else { 1.183 + mTail->mNext = elem; 1.184 + mTail = elem; 1.185 + } 1.186 + elem->mNext = nullptr; 1.187 + } 1.188 + 1.189 + T* popHead() 1.190 + { 1.191 + if (!mHead) { 1.192 + MOZ_ASSERT(false); 1.193 + return nullptr; 1.194 + } 1.195 + 1.196 + T* head = mHead; 1.197 + 1.198 + mHead = head->mNext; 1.199 + if (!mHead) { 1.200 + mTail = nullptr; 1.201 + } 1.202 + 1.203 + return head; 1.204 + } 1.205 + 1.206 + const T* peek() { 1.207 + return mHead; 1.208 + } 1.209 + 1.210 +private: 1.211 + T* mHead; 1.212 + T* mTail; 1.213 +}; 1.214 + 1.215 +typedef ProfilerLinkedList<ProfilerMarker> ProfilerMarkerLinkedList; 1.216 +typedef ProfilerLinkedList<LinkedUWTBuffer> UWTBufferLinkedList; 1.217 + 1.218 +class PendingMarkers { 1.219 +public: 1.220 + PendingMarkers() 1.221 + : mSignalLock(false) 1.222 + {} 1.223 + 1.224 + ~PendingMarkers(); 1.225 + 1.226 + void addMarker(ProfilerMarker *aMarker); 1.227 + 1.228 + void updateGeneration(int aGenID); 1.229 + 1.230 + /** 1.231 + * Track a marker which has been inserted into the ThreadProfile. 1.232 + * This marker can safely be deleted once the generation has 1.233 + * expired. 1.234 + */ 1.235 + void addStoredMarker(ProfilerMarker *aStoredMarker); 1.236 + 1.237 + // called within signal. Function must be reentrant 1.238 + ProfilerMarkerLinkedList* getPendingMarkers() 1.239 + { 1.240 + // if mSignalLock then the stack is inconsistent because it's being 1.241 + // modified by the profiled thread. Post pone these markers 1.242 + // for the next sample. The odds of a livelock are nearly impossible 1.243 + // and would show up in a profile as many sample in 'addMarker' thus 1.244 + // we ignore this scenario. 1.245 + if (mSignalLock) { 1.246 + return nullptr; 1.247 + } 1.248 + return &mPendingMarkers; 1.249 + } 1.250 + 1.251 + void clearMarkers() 1.252 + { 1.253 + while (mPendingMarkers.peek()) { 1.254 + delete mPendingMarkers.popHead(); 1.255 + } 1.256 + while (mStoredMarkers.peek()) { 1.257 + delete mStoredMarkers.popHead(); 1.258 + } 1.259 + } 1.260 + 1.261 +private: 1.262 + // Keep a list of active markers to be applied to the next sample taken 1.263 + ProfilerMarkerLinkedList mPendingMarkers; 1.264 + ProfilerMarkerLinkedList mStoredMarkers; 1.265 + // If this is set then it's not safe to read mStackPointer from the signal handler 1.266 + volatile bool mSignalLock; 1.267 + // We don't want to modify _markers from within the signal so we allow 1.268 + // it to queue a clear operation. 1.269 + volatile mozilla::sig_safe_t mGenID; 1.270 +}; 1.271 + 1.272 +class PendingUWTBuffers 1.273 +{ 1.274 +public: 1.275 + PendingUWTBuffers() 1.276 + : mSignalLock(false) 1.277 + { 1.278 + } 1.279 + 1.280 + void addLinkedUWTBuffer(LinkedUWTBuffer* aBuff) 1.281 + { 1.282 + MOZ_ASSERT(aBuff); 1.283 + mSignalLock = true; 1.284 + STORE_SEQUENCER(); 1.285 + mPendingUWTBuffers.insert(aBuff); 1.286 + STORE_SEQUENCER(); 1.287 + mSignalLock = false; 1.288 + } 1.289 + 1.290 + // called within signal. Function must be reentrant 1.291 + UWTBufferLinkedList* getLinkedUWTBuffers() 1.292 + { 1.293 + if (mSignalLock) { 1.294 + return nullptr; 1.295 + } 1.296 + return &mPendingUWTBuffers; 1.297 + } 1.298 + 1.299 +private: 1.300 + UWTBufferLinkedList mPendingUWTBuffers; 1.301 + volatile bool mSignalLock; 1.302 +}; 1.303 + 1.304 +// Stub eventMarker function for js-engine event generation. 1.305 +void ProfilerJSEventMarker(const char *event); 1.306 + 1.307 +// the PseudoStack members are read by signal 1.308 +// handlers, so the mutation of them needs to be signal-safe. 1.309 +struct PseudoStack 1.310 +{ 1.311 +public: 1.312 + PseudoStack() 1.313 + : mStackPointer(0) 1.314 + , mSleepId(0) 1.315 + , mSleepIdObserved(0) 1.316 + , mSleeping(false) 1.317 + , mRuntime(nullptr) 1.318 + , mStartJSSampling(false) 1.319 + , mPrivacyMode(false) 1.320 + { } 1.321 + 1.322 + ~PseudoStack() { 1.323 + if (mStackPointer != 0) { 1.324 + // We're releasing the pseudostack while it's still in use. 1.325 + // The label macros keep a non ref counted reference to the 1.326 + // stack to avoid a TLS. If these are not all cleared we will 1.327 + // get a use-after-free so better to crash now. 1.328 + abort(); 1.329 + } 1.330 + } 1.331 + 1.332 + // This is called on every profiler restart. Put things that should happen at that time here. 1.333 + void reinitializeOnResume() { 1.334 + // This is needed to cause an initial sample to be taken from sleeping threads. Otherwise sleeping 1.335 + // threads would not have any samples to copy forward while sleeping. 1.336 + mSleepId++; 1.337 + } 1.338 + 1.339 + void addLinkedUWTBuffer(LinkedUWTBuffer* aBuff) 1.340 + { 1.341 + mPendingUWTBuffers.addLinkedUWTBuffer(aBuff); 1.342 + } 1.343 + 1.344 + UWTBufferLinkedList* getLinkedUWTBuffers() 1.345 + { 1.346 + return mPendingUWTBuffers.getLinkedUWTBuffers(); 1.347 + } 1.348 + 1.349 + void addMarker(const char *aMarkerStr, ProfilerMarkerPayload *aPayload, float aTime) 1.350 + { 1.351 + ProfilerMarker* marker = new ProfilerMarker(aMarkerStr, aPayload, aTime); 1.352 + mPendingMarkers.addMarker(marker); 1.353 + } 1.354 + 1.355 + void addStoredMarker(ProfilerMarker *aStoredMarker) { 1.356 + mPendingMarkers.addStoredMarker(aStoredMarker); 1.357 + } 1.358 + 1.359 + void updateGeneration(int aGenID) { 1.360 + mPendingMarkers.updateGeneration(aGenID); 1.361 + } 1.362 + 1.363 + // called within signal. Function must be reentrant 1.364 + ProfilerMarkerLinkedList* getPendingMarkers() 1.365 + { 1.366 + return mPendingMarkers.getPendingMarkers(); 1.367 + } 1.368 + 1.369 + void push(const char *aName, uint32_t line) 1.370 + { 1.371 + push(aName, nullptr, false, line); 1.372 + } 1.373 + 1.374 + void push(const char *aName, void *aStackAddress, bool aCopy, uint32_t line) 1.375 + { 1.376 + if (size_t(mStackPointer) >= mozilla::ArrayLength(mStack)) { 1.377 + mStackPointer++; 1.378 + return; 1.379 + } 1.380 + 1.381 + // Make sure we increment the pointer after the name has 1.382 + // been written such that mStack is always consistent. 1.383 + mStack[mStackPointer].setLabel(aName); 1.384 + mStack[mStackPointer].setStackAddressCopy(aStackAddress, aCopy); 1.385 + mStack[mStackPointer].setLine(line); 1.386 + 1.387 + // Prevent the optimizer from re-ordering these instructions 1.388 + STORE_SEQUENCER(); 1.389 + mStackPointer++; 1.390 + } 1.391 + void pop() 1.392 + { 1.393 + mStackPointer--; 1.394 + } 1.395 + bool isEmpty() 1.396 + { 1.397 + return mStackPointer == 0; 1.398 + } 1.399 + uint32_t stackSize() const 1.400 + { 1.401 + return sMin(mStackPointer, mozilla::sig_safe_t(mozilla::ArrayLength(mStack))); 1.402 + } 1.403 + 1.404 + void sampleRuntime(JSRuntime *runtime) { 1.405 + mRuntime = runtime; 1.406 + if (!runtime) { 1.407 + // JS shut down 1.408 + return; 1.409 + } 1.410 + 1.411 + static_assert(sizeof(mStack[0]) == sizeof(js::ProfileEntry), 1.412 + "mStack must be binary compatible with js::ProfileEntry."); 1.413 + js::SetRuntimeProfilingStack(runtime, 1.414 + (js::ProfileEntry*) mStack, 1.415 + (uint32_t*) &mStackPointer, 1.416 + uint32_t(mozilla::ArrayLength(mStack))); 1.417 + if (mStartJSSampling) 1.418 + enableJSSampling(); 1.419 + } 1.420 + void enableJSSampling() { 1.421 + if (mRuntime) { 1.422 + js::EnableRuntimeProfilingStack(mRuntime, true); 1.423 + js::RegisterRuntimeProfilingEventMarker(mRuntime, &ProfilerJSEventMarker); 1.424 + mStartJSSampling = false; 1.425 + } else { 1.426 + mStartJSSampling = true; 1.427 + } 1.428 + } 1.429 + void jsOperationCallback() { 1.430 + if (mStartJSSampling) 1.431 + enableJSSampling(); 1.432 + } 1.433 + void disableJSSampling() { 1.434 + mStartJSSampling = false; 1.435 + if (mRuntime) 1.436 + js::EnableRuntimeProfilingStack(mRuntime, false); 1.437 + } 1.438 + 1.439 + // Keep a list of active checkpoints 1.440 + StackEntry volatile mStack[1024]; 1.441 + private: 1.442 + // Keep a list of pending markers that must be moved 1.443 + // to the circular buffer 1.444 + PendingMarkers mPendingMarkers; 1.445 + // List of LinkedUWTBuffers that must be processed on the next tick 1.446 + PendingUWTBuffers mPendingUWTBuffers; 1.447 + // This may exceed the length of mStack, so instead use the stackSize() method 1.448 + // to determine the number of valid samples in mStack 1.449 + mozilla::sig_safe_t mStackPointer; 1.450 + // Incremented at every sleep/wake up of the thread 1.451 + int mSleepId; 1.452 + // Previous id observed. If this is not the same as mSleepId, this thread is not sleeping in the same place any more 1.453 + mozilla::Atomic<int> mSleepIdObserved; 1.454 + // Keeps tack of whether the thread is sleeping or not (1 when sleeping 0 when awake) 1.455 + mozilla::Atomic<int> mSleeping; 1.456 + public: 1.457 + // The runtime which is being sampled 1.458 + JSRuntime *mRuntime; 1.459 + // Start JS Profiling when possible 1.460 + bool mStartJSSampling; 1.461 + bool mPrivacyMode; 1.462 + 1.463 + enum SleepState {NOT_SLEEPING, SLEEPING_FIRST, SLEEPING_AGAIN}; 1.464 + 1.465 + // The first time this is called per sleep cycle we return SLEEPING_FIRST 1.466 + // and any other subsequent call within the same sleep cycle we return SLEEPING_AGAIN 1.467 + SleepState observeSleeping() { 1.468 + if (mSleeping != 0) { 1.469 + if (mSleepIdObserved == mSleepId) { 1.470 + return SLEEPING_AGAIN; 1.471 + } else { 1.472 + mSleepIdObserved = mSleepId; 1.473 + return SLEEPING_FIRST; 1.474 + } 1.475 + } else { 1.476 + return NOT_SLEEPING; 1.477 + } 1.478 + } 1.479 + 1.480 + 1.481 + // Call this whenever the current thread sleeps or wakes up 1.482 + // Calling setSleeping with the same value twice in a row is an error 1.483 + void setSleeping(int sleeping) { 1.484 + MOZ_ASSERT(mSleeping != sleeping); 1.485 + mSleepId++; 1.486 + mSleeping = sleeping; 1.487 + } 1.488 +}; 1.489 + 1.490 +#endif 1.491 +