diff options
Diffstat (limited to 'tools/profiler/public/PseudoStack.h')
-rw-r--r-- | tools/profiler/public/PseudoStack.h | 469 |
1 files changed, 0 insertions, 469 deletions
diff --git a/tools/profiler/public/PseudoStack.h b/tools/profiler/public/PseudoStack.h deleted file mode 100644 index f9e3836ead..0000000000 --- a/tools/profiler/public/PseudoStack.h +++ /dev/null @@ -1,469 +0,0 @@ -/* -*- 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 PROFILER_PSEUDO_STACK_H_ -#define PROFILER_PSEUDO_STACK_H_ - -#include "mozilla/ArrayUtils.h" -#include <stdint.h> -#include "js/ProfilingStack.h" -#include <stdlib.h> -#include "mozilla/Atomics.h" -#ifndef SPS_STANDALONE -#include "nsISupportsImpl.h" -#endif - -/* we duplicate this code here to avoid header dependencies - * which make it more difficult to include in other places */ -#if defined(_M_X64) || defined(__x86_64__) -#define V8_HOST_ARCH_X64 1 -#elif defined(_M_IX86) || defined(__i386__) || defined(__i386) -#define V8_HOST_ARCH_IA32 1 -#elif defined(__ARMEL__) -#define V8_HOST_ARCH_ARM 1 -#else -#warning Please add support for your architecture in chromium_types.h -#endif - -// STORE_SEQUENCER: Because signals can interrupt our profile modification -// we need to make stores are not re-ordered by the compiler -// or hardware to make sure the profile is consistent at -// every point the signal can fire. -#ifdef V8_HOST_ARCH_ARM -// TODO Is there something cheaper that will prevent -// memory stores from being reordered - -typedef void (*LinuxKernelMemoryBarrierFunc)(void); -LinuxKernelMemoryBarrierFunc pLinuxKernelMemoryBarrier __attribute__((weak)) = - (LinuxKernelMemoryBarrierFunc) 0xffff0fa0; - -# define STORE_SEQUENCER() pLinuxKernelMemoryBarrier() -#elif defined(V8_HOST_ARCH_IA32) || defined(V8_HOST_ARCH_X64) -# if defined(_MSC_VER) -# include <intrin.h> -# define STORE_SEQUENCER() _ReadWriteBarrier(); -# elif defined(__INTEL_COMPILER) -# define STORE_SEQUENCER() __memory_barrier(); -# elif __GNUC__ -# define STORE_SEQUENCER() asm volatile("" ::: "memory"); -# else -# error "Memory clobber not supported for your compiler." -# endif -#else -# error "Memory clobber not supported for your platform." -#endif - -// We can't include <algorithm> because it causes issues on OS X, so we use -// our own min function. -static inline uint32_t sMin(uint32_t l, uint32_t r) { - return l < r ? l : r; -} - -// A stack entry exists to allow the JS engine to inform SPS of the current -// backtrace, but also to instrument particular points in C++ in case stack -// walking is not available on the platform we are running on. -// -// Each entry has a descriptive string, a relevant stack address, and some extra -// information the JS engine might want to inform SPS of. This class inherits -// from the JS engine's version of the entry to ensure that the size and layout -// of the two representations are consistent. -class StackEntry : public js::ProfileEntry -{ -}; - -class ProfilerMarkerPayload; -template<typename T> -class ProfilerLinkedList; -class SpliceableJSONWriter; -class UniqueStacks; - -class ProfilerMarker { - friend class ProfilerLinkedList<ProfilerMarker>; -public: - explicit ProfilerMarker(const char* aMarkerName, - ProfilerMarkerPayload* aPayload = nullptr, - double aTime = 0); - - ~ProfilerMarker(); - - const char* GetMarkerName() const { - return mMarkerName; - } - - void StreamJSON(SpliceableJSONWriter& aWriter, UniqueStacks& aUniqueStacks) const; - - void SetGeneration(uint32_t aGenID); - - bool HasExpired(uint32_t aGenID) const { - return mGenID + 2 <= aGenID; - } - - double GetTime() const; - -private: - char* mMarkerName; - ProfilerMarkerPayload* mPayload; - ProfilerMarker* mNext; - double mTime; - uint32_t mGenID; -}; - -template<typename T> -class ProfilerLinkedList { -public: - ProfilerLinkedList() - : mHead(nullptr) - , mTail(nullptr) - {} - - void insert(T* elem) - { - if (!mTail) { - mHead = elem; - mTail = elem; - } else { - mTail->mNext = elem; - mTail = elem; - } - elem->mNext = nullptr; - } - - T* popHead() - { - if (!mHead) { - MOZ_ASSERT(false); - return nullptr; - } - - T* head = mHead; - - mHead = head->mNext; - if (!mHead) { - mTail = nullptr; - } - - return head; - } - - const T* peek() { - return mHead; - } - -private: - T* mHead; - T* mTail; -}; - -typedef ProfilerLinkedList<ProfilerMarker> ProfilerMarkerLinkedList; - -template<typename T> -class ProfilerSignalSafeLinkedList { -public: - ProfilerSignalSafeLinkedList() - : mSignalLock(false) - {} - - ~ProfilerSignalSafeLinkedList() - { - if (mSignalLock) { - // Some thread is modifying the list. We should only be released on that - // thread. - abort(); - } - - while (mList.peek()) { - delete mList.popHead(); - } - } - - // Insert an item into the list. - // Must only be called from the owning thread. - // Must not be called while the list from accessList() is being accessed. - // In the profiler, we ensure that by interrupting the profiled thread - // (which is the one that owns this list and calls insert() on it) until - // we're done reading the list from the signal handler. - void insert(T* aElement) { - MOZ_ASSERT(aElement); - - mSignalLock = true; - STORE_SEQUENCER(); - - mList.insert(aElement); - - STORE_SEQUENCER(); - mSignalLock = false; - } - - // Called within signal, from any thread, possibly while insert() is in the - // middle of modifying the list (on the owning thread). Will return null if - // that is the case. - // Function must be reentrant. - ProfilerLinkedList<T>* accessList() - { - if (mSignalLock) { - return nullptr; - } - return &mList; - } - -private: - ProfilerLinkedList<T> mList; - - // If this is set, then it's not safe to read the list because its contents - // are being changed. - volatile bool mSignalLock; -}; - -// Stub eventMarker function for js-engine event generation. -void ProfilerJSEventMarker(const char *event); - -// the PseudoStack members are read by signal -// handlers, so the mutation of them needs to be signal-safe. -struct PseudoStack -{ -public: - // Create a new PseudoStack and acquire a reference to it. - static PseudoStack *create() - { - return new PseudoStack(); - } - - // This is called on every profiler restart. Put things that should happen at that time here. - void reinitializeOnResume() { - // This is needed to cause an initial sample to be taken from sleeping threads. Otherwise sleeping - // threads would not have any samples to copy forward while sleeping. - mSleepId++; - } - - void addMarker(const char* aMarkerStr, ProfilerMarkerPayload* aPayload, double aTime) - { - ProfilerMarker* marker = new ProfilerMarker(aMarkerStr, aPayload, aTime); - mPendingMarkers.insert(marker); - } - - // called within signal. Function must be reentrant - ProfilerMarkerLinkedList* getPendingMarkers() - { - // The profiled thread is interrupted, so we can access the list safely. - // Unless the profiled thread was in the middle of changing the list when - // we interrupted it - in that case, accessList() will return null. - return mPendingMarkers.accessList(); - } - - void push(const char *aName, js::ProfileEntry::Category aCategory, uint32_t line) - { - push(aName, aCategory, nullptr, false, line); - } - - void push(const char *aName, js::ProfileEntry::Category aCategory, - void *aStackAddress, bool aCopy, uint32_t line) - { - if (size_t(mStackPointer) >= mozilla::ArrayLength(mStack)) { - mStackPointer++; - return; - } - - // In order to ensure this object is kept alive while it is - // active, we acquire a reference at the outermost push. This is - // released by the corresponding pop. - if (mStackPointer == 0) { - ref(); - } - - volatile StackEntry &entry = mStack[mStackPointer]; - - // Make sure we increment the pointer after the name has - // been written such that mStack is always consistent. - entry.initCppFrame(aStackAddress, line); - entry.setLabel(aName); - MOZ_ASSERT(entry.flags() == js::ProfileEntry::IS_CPP_ENTRY); - entry.setCategory(aCategory); - - // Track if mLabel needs a copy. - if (aCopy) - entry.setFlag(js::ProfileEntry::FRAME_LABEL_COPY); - else - entry.unsetFlag(js::ProfileEntry::FRAME_LABEL_COPY); - - // Prevent the optimizer from re-ordering these instructions - STORE_SEQUENCER(); - mStackPointer++; - } - - // Pop the stack. If the stack is empty and all other references to - // this PseudoStack have been dropped, then the PseudoStack is - // deleted and "false" is returned. Otherwise "true" is returned. - bool popAndMaybeDelete() - { - mStackPointer--; - if (mStackPointer == 0) { - // Release our self-owned reference count. See 'push'. - deref(); - return false; - } else { - return true; - } - } - bool isEmpty() - { - return mStackPointer == 0; - } - uint32_t stackSize() const - { - return sMin(mStackPointer, mozilla::sig_safe_t(mozilla::ArrayLength(mStack))); - } - - void sampleContext(JSContext* context) { -#ifndef SPS_STANDALONE - if (mContext && !context) { - // On JS shut down, flush the current buffer as stringifying JIT samples - // requires a live JSContext. - flushSamplerOnJSShutdown(); - } - - mContext = context; - - if (!context) { - return; - } - - static_assert(sizeof(mStack[0]) == sizeof(js::ProfileEntry), - "mStack must be binary compatible with js::ProfileEntry."); - js::SetContextProfilingStack(context, - (js::ProfileEntry*) mStack, - (uint32_t*) &mStackPointer, - (uint32_t) mozilla::ArrayLength(mStack)); - if (mStartJSSampling) - enableJSSampling(); -#endif - } -#ifndef SPS_STANDALONE - void enableJSSampling() { - if (mContext) { - js::EnableContextProfilingStack(mContext, true); - js::RegisterContextProfilingEventMarker(mContext, &ProfilerJSEventMarker); - mStartJSSampling = false; - } else { - mStartJSSampling = true; - } - } - void jsOperationCallback() { - if (mStartJSSampling) - enableJSSampling(); - } - void disableJSSampling() { - mStartJSSampling = false; - if (mContext) - js::EnableContextProfilingStack(mContext, false); - } -#endif - - // Keep a list of active checkpoints - StackEntry volatile mStack[1024]; - private: - - // A PseudoStack can only be created via the "create" method. - PseudoStack() - : mStackPointer(0) - , mSleepId(0) - , mSleepIdObserved(0) - , mSleeping(false) - , mRefCnt(1) -#ifndef SPS_STANDALONE - , mContext(nullptr) -#endif - , mStartJSSampling(false) - , mPrivacyMode(false) - { - MOZ_COUNT_CTOR(PseudoStack); - } - - // A PseudoStack can only be deleted via deref. - ~PseudoStack() { - MOZ_COUNT_DTOR(PseudoStack); - if (mStackPointer != 0) { - // We're releasing the pseudostack while it's still in use. - // The label macros keep a non ref counted reference to the - // stack to avoid a TLS. If these are not all cleared we will - // get a use-after-free so better to crash now. - abort(); - } - } - - // No copying. - PseudoStack(const PseudoStack&) = delete; - void operator=(const PseudoStack&) = delete; - - void flushSamplerOnJSShutdown(); - - // Keep a list of pending markers that must be moved - // to the circular buffer - ProfilerSignalSafeLinkedList<ProfilerMarker> mPendingMarkers; - // This may exceed the length of mStack, so instead use the stackSize() method - // to determine the number of valid samples in mStack - mozilla::sig_safe_t mStackPointer; - // Incremented at every sleep/wake up of the thread - int mSleepId; - // Previous id observed. If this is not the same as mSleepId, this thread is not sleeping in the same place any more - mozilla::Atomic<int> mSleepIdObserved; - // Keeps tack of whether the thread is sleeping or not (1 when sleeping 0 when awake) - mozilla::Atomic<int> mSleeping; - // This class is reference counted because it must be kept alive by - // the ThreadInfo, by the reference from tlsPseudoStack, and by the - // current thread when callbacks are in progress. - mozilla::Atomic<int> mRefCnt; - - public: -#ifndef SPS_STANDALONE - // The context which is being sampled - JSContext *mContext; -#endif - // Start JS Profiling when possible - bool mStartJSSampling; - bool mPrivacyMode; - - enum SleepState {NOT_SLEEPING, SLEEPING_FIRST, SLEEPING_AGAIN}; - - // The first time this is called per sleep cycle we return SLEEPING_FIRST - // and any other subsequent call within the same sleep cycle we return SLEEPING_AGAIN - SleepState observeSleeping() { - if (mSleeping != 0) { - if (mSleepIdObserved == mSleepId) { - return SLEEPING_AGAIN; - } else { - mSleepIdObserved = mSleepId; - return SLEEPING_FIRST; - } - } else { - return NOT_SLEEPING; - } - } - - - // Call this whenever the current thread sleeps or wakes up - // Calling setSleeping with the same value twice in a row is an error - void setSleeping(int sleeping) { - MOZ_ASSERT(mSleeping != sleeping); - mSleepId++; - mSleeping = sleeping; - } - - bool isSleeping() { - return !!mSleeping; - } - - void ref() { - ++mRefCnt; - } - - void deref() { - int newValue = --mRefCnt; - if (newValue == 0) { - delete this; - } - } -}; - -#endif |