tools/profiler/PseudoStack.h

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     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

mercurial