LCOV - code coverage report
Current view: top level - tools/profiler/core - ThreadInfo.h (source / functions) Hit Total Coverage
Test: output.info Lines: 30 71 42.3 %
Date: 2017-07-14 16:53:18 Functions: 8 22 36.4 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #ifndef ThreadInfo_h
       8             : #define ThreadInfo_h
       9             : 
      10             : #include "mozilla/NotNull.h"
      11             : #include "mozilla/UniquePtrExtensions.h"
      12             : 
      13             : #include "platform.h"
      14             : #include "ProfileBuffer.h"
      15             : #include "js/ProfilingStack.h"
      16             : 
      17             : // This class contains the info for a single thread that is accessible without
      18             : // protection from gPSMutex in platform.cpp. Because there is no external
      19             : // protection against data races, it must provide internal protection. Hence
      20             : // the "Racy" prefix.
      21             : //
      22             : class RacyThreadInfo final : public PseudoStack
      23             : {
      24             : public:
      25          75 :   RacyThreadInfo()
      26          75 :     : PseudoStack()
      27          75 :     , mSleep(AWAKE)
      28             :   {
      29          75 :     MOZ_COUNT_CTOR(RacyThreadInfo);
      30          75 :   }
      31             : 
      32           1 :   ~RacyThreadInfo()
      33           1 :   {
      34           1 :     MOZ_COUNT_DTOR(RacyThreadInfo);
      35           1 :   }
      36             : 
      37           0 :   size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
      38             :   {
      39           0 :     size_t n = aMallocSizeOf(this);
      40             : 
      41             :     // Measurement of the following members may be added later if DMD finds it
      42             :     // is worthwhile:
      43             :     // - things in the PseudoStack
      44             :     // - mPendingMarkers
      45             :     //
      46             :     // If these measurements are added, the code must be careful to avoid data
      47             :     // races. (The current code doesn't have any race issues because the
      48             :     // contents of the PseudoStack object aren't accessed; |this| is used only
      49             :     // as an address for lookup by aMallocSizeof).
      50             : 
      51           0 :     return n;
      52             :   }
      53             : 
      54           0 :   void AddPendingMarker(const char* aMarkerName,
      55             :                         mozilla::UniquePtr<ProfilerMarkerPayload> aPayload,
      56             :                         double aTime)
      57             :   {
      58             :     ProfilerMarker* marker =
      59           0 :       new ProfilerMarker(aMarkerName, Move(aPayload), aTime);
      60           0 :     mPendingMarkers.insert(marker);
      61           0 :   }
      62             : 
      63             :   // Called within signal. Function must be reentrant.
      64           0 :   ProfilerMarkerLinkedList* GetPendingMarkers()
      65             :   {
      66             :     // The profiled thread is interrupted, so we can access the list safely.
      67             :     // Unless the profiled thread was in the middle of changing the list when
      68             :     // we interrupted it - in that case, accessList() will return null.
      69           0 :     return mPendingMarkers.accessList();
      70             :   }
      71             : 
      72             :   // This is called on every profiler restart. Put things that should happen at
      73             :   // that time here.
      74           0 :   void ReinitializeOnResume()
      75             :   {
      76             :     // This is needed to cause an initial sample to be taken from sleeping
      77             :     // threads that had been observed prior to the profiler stopping and
      78             :     // restarting. Otherwise sleeping threads would not have any samples to
      79             :     // copy forward while sleeping.
      80           0 :     (void)mSleep.compareExchange(SLEEPING_OBSERVED, SLEEPING_NOT_OBSERVED);
      81           0 :   }
      82             : 
      83             :   // This returns true for the second and subsequent calls in each sleep cycle.
      84           0 :   bool CanDuplicateLastSampleDueToSleep()
      85             :   {
      86           0 :     if (mSleep == AWAKE) {
      87           0 :       return false;
      88             :     }
      89             : 
      90           0 :     if (mSleep.compareExchange(SLEEPING_NOT_OBSERVED, SLEEPING_OBSERVED)) {
      91           0 :       return false;
      92             :     }
      93             : 
      94           0 :     return true;
      95             :   }
      96             : 
      97             :   // Call this whenever the current thread sleeps. Calling it twice in a row
      98             :   // without an intervening setAwake() call is an error.
      99        2657 :   void SetSleeping()
     100             :   {
     101        2657 :     MOZ_ASSERT(mSleep == AWAKE);
     102        2656 :     mSleep = SLEEPING_NOT_OBSERVED;
     103        2656 :   }
     104             : 
     105             :   // Call this whenever the current thread wakes. Calling it twice in a row
     106             :   // without an intervening setSleeping() call is an error.
     107        2652 :   void SetAwake()
     108             :   {
     109        2652 :     MOZ_ASSERT(mSleep != AWAKE);
     110        2652 :     mSleep = AWAKE;
     111        2652 :   }
     112             : 
     113           0 :   bool IsSleeping() { return mSleep != AWAKE; }
     114             : 
     115             : private:
     116             :   // A list of pending markers that must be moved to the circular buffer.
     117             :   ProfilerSignalSafeLinkedList<ProfilerMarker> mPendingMarkers;
     118             : 
     119             :   // mSleep tracks whether the thread is sleeping, and if so, whether it has
     120             :   // been previously observed. This is used for an optimization: in some cases,
     121             :   // when a thread is asleep, we duplicate the previous sample, which is
     122             :   // cheaper than taking a new sample.
     123             :   //
     124             :   // mSleep is atomic because it is accessed from multiple threads.
     125             :   //
     126             :   // - It is written only by this thread, via setSleeping() and setAwake().
     127             :   //
     128             :   // - It is read by SamplerThread::Run().
     129             :   //
     130             :   // There are two cases where racing between threads can cause an issue.
     131             :   //
     132             :   // - If CanDuplicateLastSampleDueToSleep() returns false but that result is
     133             :   //   invalidated before being acted upon, we will take a full sample
     134             :   //   unnecessarily. This is additional work but won't cause any correctness
     135             :   //   issues. (In actual fact, this case is impossible. In order to go from
     136             :   //   CanDuplicateLastSampleDueToSleep() returning false to it returning true
     137             :   //   requires an intermediate call to it in order for mSleep to go from
     138             :   //   SLEEPING_NOT_OBSERVED to SLEEPING_OBSERVED.)
     139             :   //
     140             :   // - If CanDuplicateLastSampleDueToSleep() returns true but that result is
     141             :   //   invalidated before being acted upon -- i.e. the thread wakes up before
     142             :   //   DuplicateLastSample() is called -- we will duplicate the previous
     143             :   //   sample. This is inaccurate, but only slightly... we will effectively
     144             :   //   treat the thread as having slept a tiny bit longer than it really did.
     145             :   //
     146             :   // This latter inaccuracy could be avoided by moving the
     147             :   // CanDuplicateLastSampleDueToSleep() check within the thread-freezing code,
     148             :   // e.g. the section where Tick() is called. But that would reduce the
     149             :   // effectiveness of the optimization because more code would have to be run
     150             :   // before we can tell that duplication is allowed.
     151             :   //
     152             :   static const int AWAKE = 0;
     153             :   static const int SLEEPING_NOT_OBSERVED = 1;
     154             :   static const int SLEEPING_OBSERVED = 2;
     155             :   mozilla::Atomic<int> mSleep;
     156             : };
     157             : 
     158             : // This class contains the info for a single thread.
     159             : //
     160             : // Note: A thread's ThreadInfo can be held onto after the thread itself exits,
     161             : // because we may need to output profiling information about that thread. But
     162             : // some of the fields in this class are only relevant while the thread is
     163             : // alive. It's possible that this class could be refactored so there is a
     164             : // clearer split between those fields and the fields that are still relevant
     165             : // after the thread exists.
     166             : class ThreadInfo final
     167             : {
     168             : public:
     169             :   ThreadInfo(const char* aName, int aThreadId, bool aIsMainThread,
     170             :              void* aStackTop);
     171             : 
     172             :   ~ThreadInfo();
     173             : 
     174           0 :   const char* Name() const { return mName.get(); }
     175        1040 :   int ThreadId() const { return mThreadId; }
     176             : 
     177           0 :   bool IsMainThread() const { return mIsMainThread; }
     178             : 
     179        5388 :   mozilla::NotNull<RacyThreadInfo*> RacyInfo() const { return mRacyInfo; }
     180             : 
     181             :   void StartProfiling();
     182             :   void StopProfiling();
     183           0 :   bool IsBeingProfiled() { return mIsBeingProfiled; }
     184             : 
     185             :   PlatformData* GetPlatformData() const { return mPlatformData.get(); }
     186           0 :   void* StackTop() const { return mStackTop; }
     187             : 
     188             :   size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
     189             : 
     190           0 :   ProfileBuffer::LastSample& LastSample() { return mLastSample; }
     191             : 
     192             : private:
     193             :   mozilla::UniqueFreePtr<char> mName;
     194             :   int mThreadId;
     195             :   const bool mIsMainThread;
     196             : 
     197             :   // The thread's RacyThreadInfo. This is an owning pointer. It could be an
     198             :   // inline member, but we don't do that because RacyThreadInfo is quite large
     199             :   // (due to the PseudoStack within it), and we have ThreadInfo vectors and so
     200             :   // we'd end up wasting a lot of space in those vectors for excess elements.
     201             :   mozilla::NotNull<RacyThreadInfo*> mRacyInfo;
     202             : 
     203             :   UniquePlatformData mPlatformData;
     204             :   void* mStackTop;
     205             : 
     206             :   //
     207             :   // The following code is only used for threads that are being profiled, i.e.
     208             :   // for which IsBeingProfiled() returns true.
     209             :   //
     210             : 
     211             : public:
     212             :   void StreamJSON(const ProfileBuffer& aBuffer, SpliceableJSONWriter& aWriter,
     213             :                   const mozilla::TimeStamp& aProcessStartTime,
     214             :                   double aSinceTime);
     215             : 
     216             :   // Call this method when the JS entries inside the buffer are about to
     217             :   // become invalid, i.e., just before JS shutdown.
     218             :   void FlushSamplesAndMarkers(const mozilla::TimeStamp& aProcessStartTime,
     219             :                               ProfileBuffer& aBuffer);
     220             : 
     221             :   // Returns nullptr if this is not the main thread or if this thread is not
     222             :   // being profiled.
     223           0 :   ThreadResponsiveness* GetThreadResponsiveness()
     224             :   {
     225           0 :     ThreadResponsiveness* responsiveness = mResponsiveness.ptrOr(nullptr);
     226           0 :     MOZ_ASSERT(!!responsiveness == (mIsMainThread && mIsBeingProfiled));
     227           0 :     return responsiveness;
     228             :   }
     229             : 
     230             :   // Set the JSContext of the thread to be sampled. Sampling cannot begin until
     231             :   // this has been set.
     232           4 :   void SetJSContext(JSContext* aContext)
     233             :   {
     234             :     // This function runs on-thread.
     235             : 
     236           4 :     MOZ_ASSERT(aContext && !mContext);
     237             : 
     238           4 :     mContext = aContext;
     239             : 
     240             :     // We give the JS engine a non-owning reference to the RacyInfo (just the
     241             :     // PseudoStack, really). It's important that the JS engine doesn't touch
     242             :     // this once the thread dies.
     243           4 :     js::SetContextProfilingStack(aContext, RacyInfo());
     244             : 
     245           4 :     PollJSSampling();
     246           4 :   }
     247             : 
     248             :   // Request that this thread start JS sampling. JS sampling won't actually
     249             :   // start until a subsequent PollJSSampling() call occurs *and* mContext has
     250             :   // been set.
     251           0 :   void StartJSSampling()
     252             :   {
     253             :     // This function runs on-thread or off-thread.
     254             : 
     255           0 :     MOZ_RELEASE_ASSERT(mJSSampling == INACTIVE ||
     256             :                        mJSSampling == INACTIVE_REQUESTED);
     257           0 :     mJSSampling = ACTIVE_REQUESTED;
     258           0 :   }
     259             : 
     260             :   // Request that this thread stop JS sampling. JS sampling won't actually stop
     261             :   // until a subsequent PollJSSampling() call occurs.
     262           0 :   void StopJSSampling()
     263             :   {
     264             :     // This function runs on-thread or off-thread.
     265             : 
     266           0 :     MOZ_RELEASE_ASSERT(mJSSampling == ACTIVE ||
     267             :                        mJSSampling == ACTIVE_REQUESTED);
     268           0 :     mJSSampling = INACTIVE_REQUESTED;
     269           0 :   }
     270             : 
     271             :   // Poll to see if JS sampling should be started/stopped.
     272          31 :   void PollJSSampling()
     273             :   {
     274             :     // This function runs on-thread.
     275             : 
     276             :     // We can't start/stop profiling until we have the thread's JSContext.
     277          31 :     if (mContext) {
     278             :       // It is possible for mJSSampling to go through the following sequences.
     279             :       //
     280             :       // - INACTIVE, ACTIVE_REQUESTED, INACTIVE_REQUESTED, INACTIVE
     281             :       //
     282             :       // - ACTIVE, INACTIVE_REQUESTED, ACTIVE_REQUESTED, ACTIVE
     283             :       //
     284             :       // Therefore, the if and else branches here aren't always interleaved.
     285             :       // This is ok because the JS engine can handle that.
     286             :       //
     287          31 :       if (mJSSampling == ACTIVE_REQUESTED) {
     288           0 :         mJSSampling = ACTIVE;
     289           0 :         js::EnableContextProfilingStack(mContext, true);
     290           0 :         js::RegisterContextProfilingEventMarker(mContext, profiler_add_marker);
     291             : 
     292          31 :       } else if (mJSSampling == INACTIVE_REQUESTED) {
     293           0 :         mJSSampling = INACTIVE;
     294           0 :         js::EnableContextProfilingStack(mContext, false);
     295             :       }
     296             :     }
     297          31 :   }
     298             : 
     299             : private:
     300             :   bool mIsBeingProfiled;
     301             : 
     302             :   // JS frames in the buffer may require a live JSRuntime to stream (e.g.,
     303             :   // stringifying JIT frames). In the case of JSRuntime destruction,
     304             :   // FlushSamplesAndMarkers should be called to save them. These are spliced
     305             :   // into the final stream.
     306             :   mozilla::UniquePtr<char[]> mSavedStreamedSamples;
     307             :   mozilla::UniquePtr<char[]> mSavedStreamedMarkers;
     308             :   mozilla::Maybe<UniqueStacks> mUniqueStacks;
     309             : 
     310             :   // This is only used for the main thread.
     311             :   mozilla::Maybe<ThreadResponsiveness> mResponsiveness;
     312             : 
     313             : public:
     314             :   // If this is a JS thread, this is its JSContext, which is required for any
     315             :   // JS sampling.
     316             :   JSContext* mContext;
     317             : 
     318             : private:
     319             :   // The profiler needs to start and stop JS sampling of JS threads at various
     320             :   // times. However, the JS engine can only do the required actions on the
     321             :   // JS thread itself ("on-thread"), not from another thread ("off-thread").
     322             :   // Therefore, we have the following two-step process.
     323             :   //
     324             :   // - The profiler requests (on-thread or off-thread) that the JS sampling be
     325             :   //   started/stopped, by changing mJSSampling to the appropriate REQUESTED
     326             :   //   state.
     327             :   //
     328             :   // - The relevant JS thread polls (on-thread) for changes to mJSSampling.
     329             :   //   When it sees a REQUESTED state, it performs the appropriate actions to
     330             :   //   actually start/stop JS sampling, and changes mJSSampling out of the
     331             :   //   REQUESTED state.
     332             :   //
     333             :   // The state machine is as follows.
     334             :   //
     335             :   //             INACTIVE --> ACTIVE_REQUESTED
     336             :   //                  ^       ^ |
     337             :   //                  |     _/  |
     338             :   //                  |   _/    |
     339             :   //                  |  /      |
     340             :   //                  | v       v
     341             :   //   INACTIVE_REQUESTED <-- ACTIVE
     342             :   //
     343             :   // The polling is done in the following two ways.
     344             :   //
     345             :   // - Via the interrupt callback mechanism; the JS thread must call
     346             :   //   profiler_js_interrupt_callback() from its own interrupt callback.
     347             :   //   This is how sampling must be started/stopped for threads where the
     348             :   //   request was made off-thread.
     349             :   //
     350             :   // - When {Start,Stop}JSSampling() is called on-thread, we can immediately
     351             :   //   follow it with a PollJSSampling() call to avoid the delay between the
     352             :   //   two steps. Likewise, setJSContext() calls PollJSSampling().
     353             :   //
     354             :   // One non-obvious thing about all this: these JS sampling requests are made
     355             :   // on all threads, even non-JS threads. mContext needs to also be set (via
     356             :   // setJSContext(), which can only happen for JS threads) for any JS sampling
     357             :   // to actually happen.
     358             :   //
     359             :   enum {
     360             :     INACTIVE = 0,
     361             :     ACTIVE_REQUESTED = 1,
     362             :     ACTIVE = 2,
     363             :     INACTIVE_REQUESTED = 3,
     364             :   } mJSSampling;
     365             : 
     366             :   // When sampling, this holds the generation number and offset in
     367             :   // ActivePS::mBuffer of the most recent sample for this thread.
     368             :   ProfileBuffer::LastSample mLastSample;
     369             : };
     370             : 
     371             : void
     372             : StreamSamplesAndMarkers(const char* aName, int aThreadId,
     373             :                         const ProfileBuffer& aBuffer,
     374             :                         SpliceableJSONWriter& aWriter,
     375             :                         const mozilla::TimeStamp& aProcessStartTime,
     376             :                         double aSinceTime,
     377             :                         JSContext* aContext,
     378             :                         char* aSavedStreamedSamples,
     379             :                         char* aSavedStreamedMarkers,
     380             :                         UniqueStacks& aUniqueStacks);
     381             : 
     382             : #endif  // ThreadInfo_h

Generated by: LCOV version 1.13