tools/profiler/PseudoStack.h

changeset 0
6474c204b198
     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 +

mercurial