LCOV - code coverage report
Current view: top level - toolkit/components/telemetry - ThreadHangStats.h (source / functions) Hit Total Coverage
Test: output.info Lines: 0 93 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 35 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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/. */
       5             : 
       6             : #ifndef mozilla_BackgroundHangTelemetry_h
       7             : #define mozilla_BackgroundHangTelemetry_h
       8             : 
       9             : #include "mozilla/Array.h"
      10             : #include "mozilla/Assertions.h"
      11             : #include "mozilla/HangAnnotations.h"
      12             : #include "mozilla/Move.h"
      13             : #include "mozilla/Mutex.h"
      14             : #include "mozilla/PodOperations.h"
      15             : #include "mozilla/Vector.h"
      16             : #include "mozilla/CombinedStacks.h"
      17             : 
      18             : #include "nsString.h"
      19             : #include "prinrval.h"
      20             : #include "jsapi.h"
      21             : 
      22             : namespace mozilla {
      23             : namespace Telemetry {
      24             : 
      25             : // This variable controls the maximum number of native hang stacks which may be
      26             : // attached to a ping. This is due to how large native stacks can be. We want to
      27             : // reduce the chance of a ping being discarded due to it exceeding the maximum
      28             : // ping size.
      29             : static const uint32_t kMaximumNativeHangStacks = 300;
      30             : 
      31             : static const size_t kTimeHistogramBuckets = 8 * sizeof(PRIntervalTime);
      32             : 
      33             : /* TimeHistogram is an efficient histogram that puts time durations into
      34             :    exponential (base 2) buckets; times are accepted in PRIntervalTime and
      35             :    stored in milliseconds. */
      36             : class TimeHistogram : public mozilla::Array<uint32_t, kTimeHistogramBuckets>
      37             : {
      38             : public:
      39           0 :   TimeHistogram()
      40           0 :   {
      41           0 :     mozilla::PodArrayZero(*this);
      42           0 :   }
      43             :   // Get minimum (inclusive) range of bucket in milliseconds
      44           0 :   uint32_t GetBucketMin(size_t aBucket) const {
      45           0 :     MOZ_ASSERT(aBucket < ArrayLength(*this));
      46           0 :     return (1u << aBucket) & ~1u; // Bucket 0 starts at 0, not 1
      47             :   }
      48             :   // Get maximum (inclusive) range of bucket in milliseconds
      49           0 :   uint32_t GetBucketMax(size_t aBucket) const {
      50           0 :     MOZ_ASSERT(aBucket < ArrayLength(*this));
      51           0 :     return (1u << (aBucket + 1u)) - 1u;
      52             :   }
      53             :   void Add(PRIntervalTime aTime);
      54             : };
      55             : 
      56             : /* A native stack is a simple list of pointers, so rather than building a
      57             :    wrapper type, we typdef the type here. */
      58             : typedef std::vector<uintptr_t> NativeHangStack;
      59             : 
      60             : /* HangStack stores an array of const char pointers,
      61             :    with optional internal storage for strings. */
      62           0 : class HangStack
      63             : {
      64             : public:
      65             :   static const size_t sMaxInlineStorage = 8;
      66             : 
      67             :   // The maximum depth for the native stack frames that we might collect.
      68             :   // XXX: Consider moving this to a different object?
      69             :   static const size_t sMaxNativeFrames = 150;
      70             : 
      71             : private:
      72             :   typedef mozilla::Vector<const char*, sMaxInlineStorage> Impl;
      73             :   Impl mImpl;
      74             : 
      75             :   // Stack entries can either be a static const char*
      76             :   // or a pointer to within this buffer.
      77             :   mozilla::Vector<char, 0> mBuffer;
      78             : 
      79             : public:
      80           0 :   HangStack() {}
      81             : 
      82           0 :   HangStack(HangStack&& aOther)
      83           0 :     : mImpl(mozilla::Move(aOther.mImpl))
      84           0 :     , mBuffer(mozilla::Move(aOther.mBuffer))
      85             :   {
      86           0 :   }
      87             : 
      88             :   HangStack& operator=(HangStack&& aOther) {
      89             :     mImpl = mozilla::Move(aOther.mImpl);
      90             :     mBuffer = mozilla::Move(aOther.mBuffer);
      91             :     return *this;
      92             :   }
      93             : 
      94           0 :   bool operator==(const HangStack& aOther) const {
      95           0 :     for (size_t i = 0; i < length(); i++) {
      96           0 :       if (!IsSameAsEntry(operator[](i), aOther[i])) {
      97           0 :         return false;
      98             :       }
      99             :     }
     100           0 :     return true;
     101             :   }
     102             : 
     103             :   bool operator!=(const HangStack& aOther) const {
     104             :     return !operator==(aOther);
     105             :   }
     106             : 
     107           0 :   const char*& operator[](size_t aIndex) {
     108           0 :     return mImpl[aIndex];
     109             :   }
     110             : 
     111           0 :   const char* const& operator[](size_t aIndex) const {
     112           0 :     return mImpl[aIndex];
     113             :   }
     114             : 
     115             :   size_t capacity() const { return mImpl.capacity(); }
     116           0 :   size_t length() const { return mImpl.length(); }
     117             :   bool empty() const { return mImpl.empty(); }
     118           0 :   bool canAppendWithoutRealloc(size_t aNeeded) const {
     119           0 :     return mImpl.canAppendWithoutRealloc(aNeeded);
     120             :   }
     121           0 :   void infallibleAppend(const char* aEntry) { mImpl.infallibleAppend(aEntry); }
     122           0 :   bool reserve(size_t aRequest) { return mImpl.reserve(aRequest); }
     123           0 :   const char** begin() { return mImpl.begin(); }
     124           0 :   const char* const* begin() const { return mImpl.begin(); }
     125           0 :   const char** end() { return mImpl.end(); }
     126           0 :   const char* const* end() const { return mImpl.end(); }
     127             :   const char*& back() { return mImpl.back(); }
     128           0 :   void erase(const char** aEntry) { mImpl.erase(aEntry); }
     129           0 :   void erase(const char** aBegin, const char** aEnd) {
     130           0 :     mImpl.erase(aBegin, aEnd);
     131           0 :   }
     132             : 
     133           0 :   void clear() {
     134           0 :     mImpl.clear();
     135           0 :     mBuffer.clear();
     136           0 :   }
     137             : 
     138           0 :   bool IsInBuffer(const char* aEntry) const {
     139           0 :     return aEntry >= mBuffer.begin() && aEntry < mBuffer.end();
     140             :   }
     141             : 
     142           0 :   bool IsSameAsEntry(const char* aEntry, const char* aOther) const {
     143             :     // If the entry came from the buffer, we need to compare its content;
     144             :     // otherwise we only need to compare its pointer.
     145           0 :     return IsInBuffer(aEntry) ? !strcmp(aEntry, aOther) : (aEntry == aOther);
     146             :   }
     147             : 
     148             :   size_t AvailableBufferSize() const {
     149             :     return mBuffer.capacity() - mBuffer.length();
     150             :   }
     151             : 
     152             :   bool EnsureBufferCapacity(size_t aCapacity) {
     153             :     // aCapacity is the minimal capacity and Vector may make the actual
     154             :     // capacity larger, in which case we want to use up all the space.
     155             :     return mBuffer.reserve(aCapacity) &&
     156             :            mBuffer.reserve(mBuffer.capacity());
     157             :   }
     158             : 
     159             :   const char* InfallibleAppendViaBuffer(const char* aText, size_t aLength);
     160             :   const char* AppendViaBuffer(const char* aText, size_t aLength);
     161             : };
     162             : 
     163             : /* A hang histogram consists of a stack associated with the
     164             :    hang, along with a time histogram of the hang times. */
     165           0 : class HangHistogram : public TimeHistogram
     166             : {
     167             : public:
     168             :   // Value used for mNativeStackIndex to represent the absence of a cached
     169             :   // native stack.
     170             :   static const uint32_t NO_NATIVE_STACK_INDEX = UINT32_MAX;
     171             : 
     172             : private:
     173             :   static uint32_t GetHash(const HangStack& aStack);
     174             : 
     175             :   HangStack mStack;
     176             :   // Cached index of the native stack in the mCombinedStacks list in the owning
     177             :   // ThreadHangStats object. A default value of NO_NATIVE_STACK_INDEX means that
     178             :   // the ThreadHangStats object which owns this HangHistogram doesn't have a
     179             :   // cached CombinedStacks with this HangHistogram in it.
     180             :   uint32_t mNativeStackIndex;
     181             :   // Use a hash to speed comparisons
     182             :   const uint32_t mHash;
     183             :   // Annotations attributed to this stack
     184             :   HangMonitor::HangAnnotationsVector mAnnotations;
     185             :   // The name of the runnable on the current thread.
     186             :   nsCString mRunnableName;
     187             : 
     188             : public:
     189           0 :   explicit HangHistogram(HangStack&& aStack, const nsACString& aRunnableName)
     190           0 :     : mStack(mozilla::Move(aStack))
     191             :     , mNativeStackIndex(NO_NATIVE_STACK_INDEX)
     192           0 :     , mHash(GetHash(mStack))
     193           0 :     , mRunnableName(aRunnableName)
     194             :   {
     195           0 :   }
     196             : 
     197           0 :   HangHistogram(HangHistogram&& aOther)
     198           0 :     : TimeHistogram(mozilla::Move(aOther))
     199           0 :     , mStack(mozilla::Move(aOther.mStack))
     200           0 :     , mNativeStackIndex(mozilla::Move(aOther.mNativeStackIndex))
     201           0 :     , mHash(mozilla::Move(aOther.mHash))
     202           0 :     , mAnnotations(mozilla::Move(aOther.mAnnotations))
     203           0 :     , mRunnableName(aOther.mRunnableName)
     204             :   {
     205           0 :   }
     206             :   bool operator==(const HangHistogram& aOther) const;
     207             :   bool operator!=(const HangHistogram& aOther) const
     208             :   {
     209             :     return !operator==(aOther);
     210             :   }
     211           0 :   const HangStack& GetStack() const {
     212           0 :     return mStack;
     213             :   }
     214           0 :   uint32_t GetNativeStackIndex() const {
     215           0 :     return mNativeStackIndex;
     216             :   }
     217           0 :   void SetNativeStackIndex(uint32_t aIndex) {
     218           0 :     MOZ_ASSERT(aIndex != NO_NATIVE_STACK_INDEX);
     219           0 :     mNativeStackIndex = aIndex;
     220           0 :   }
     221           0 :   const char* GetRunnableName() const {
     222           0 :     return mRunnableName.get();
     223             :   }
     224           0 :   const HangMonitor::HangAnnotationsVector& GetAnnotations() const {
     225           0 :     return mAnnotations;
     226             :   }
     227           0 :   void Add(PRIntervalTime aTime, HangMonitor::HangAnnotationsPtr aAnnotations) {
     228           0 :     TimeHistogram::Add(aTime);
     229           0 :     if (aAnnotations) {
     230           0 :       if (!mAnnotations.append(Move(aAnnotations))) {
     231           0 :         MOZ_CRASH();
     232             :       }
     233             :     }
     234           0 :   }
     235             : };
     236             : 
     237             : /* Thread hang stats consist of
     238             :  - thread name
     239             :  - time histogram of all task run times
     240             :  - hang histograms of individual hangs
     241             :  - annotations for each hang
     242             :  - combined native stacks for all hangs
     243             : */
     244           0 : class ThreadHangStats
     245             : {
     246             : private:
     247             :   nsCString mName;
     248             : 
     249             : public:
     250             :   TimeHistogram mActivity;
     251             :   mozilla::Vector<HangHistogram, 4> mHangs;
     252             :   uint32_t mNativeStackCnt;
     253             :   CombinedStacks mCombinedStacks;
     254             : 
     255           0 :   explicit ThreadHangStats(const char* aName)
     256           0 :     : mName(aName)
     257             :     , mNativeStackCnt(0)
     258           0 :     , mCombinedStacks(Telemetry::kMaximumNativeHangStacks)
     259             :   {
     260           0 :   }
     261           0 :   ThreadHangStats(ThreadHangStats&& aOther)
     262           0 :     : mName(mozilla::Move(aOther.mName))
     263           0 :     , mActivity(mozilla::Move(aOther.mActivity))
     264           0 :     , mHangs(mozilla::Move(aOther.mHangs))
     265           0 :     , mNativeStackCnt(aOther.mNativeStackCnt)
     266           0 :     , mCombinedStacks(mozilla::Move(aOther.mCombinedStacks))
     267             :   {
     268           0 :     aOther.mNativeStackCnt = 0;
     269           0 :   }
     270           0 :   const char* GetName() const {
     271           0 :     return mName.get();
     272             :   }
     273             : };
     274             : 
     275             : /**
     276             :  * Reflects thread hang stats object as a JS object.
     277             :  *
     278             :  * @param JSContext* cx javascript context.
     279             :  * @param JSContext* cx thread hang statistics.
     280             :  *
     281             :  * @return JSObject* Javascript reflection of the statistics.
     282             :  */
     283             : JSObject*
     284             : CreateJSThreadHangStats(JSContext* cx, const Telemetry::ThreadHangStats& thread);
     285             : 
     286             : } // namespace Telemetry
     287             : } // namespace mozilla
     288             : 
     289             : #endif // mozilla_BackgroundHangTelemetry_h

Generated by: LCOV version 1.13