diff options
Diffstat (limited to 'toolkit/components/telemetry/ThreadHangStats.h')
-rw-r--r-- | toolkit/components/telemetry/ThreadHangStats.h | 230 |
1 files changed, 230 insertions, 0 deletions
diff --git a/toolkit/components/telemetry/ThreadHangStats.h b/toolkit/components/telemetry/ThreadHangStats.h new file mode 100644 index 000000000..60aa680c8 --- /dev/null +++ b/toolkit/components/telemetry/ThreadHangStats.h @@ -0,0 +1,230 @@ +/* -*- 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 mozilla_BackgroundHangTelemetry_h +#define mozilla_BackgroundHangTelemetry_h + +#include "mozilla/Array.h" +#include "mozilla/Assertions.h" +#include "mozilla/HangAnnotations.h" +#include "mozilla/Move.h" +#include "mozilla/Mutex.h" +#include "mozilla/PodOperations.h" +#include "mozilla/Vector.h" + +#include "nsString.h" +#include "prinrval.h" + +namespace mozilla { +namespace Telemetry { + +static const size_t kTimeHistogramBuckets = 8 * sizeof(PRIntervalTime); + +/* TimeHistogram is an efficient histogram that puts time durations into + exponential (base 2) buckets; times are accepted in PRIntervalTime and + stored in milliseconds. */ +class TimeHistogram : public mozilla::Array<uint32_t, kTimeHistogramBuckets> +{ +public: + TimeHistogram() + { + mozilla::PodArrayZero(*this); + } + // Get minimum (inclusive) range of bucket in milliseconds + uint32_t GetBucketMin(size_t aBucket) const { + MOZ_ASSERT(aBucket < ArrayLength(*this)); + return (1u << aBucket) & ~1u; // Bucket 0 starts at 0, not 1 + } + // Get maximum (inclusive) range of bucket in milliseconds + uint32_t GetBucketMax(size_t aBucket) const { + MOZ_ASSERT(aBucket < ArrayLength(*this)); + return (1u << (aBucket + 1u)) - 1u; + } + void Add(PRIntervalTime aTime); +}; + +/* HangStack stores an array of const char pointers, + with optional internal storage for strings. */ +class HangStack +{ +public: + static const size_t sMaxInlineStorage = 8; + +private: + typedef mozilla::Vector<const char*, sMaxInlineStorage> Impl; + Impl mImpl; + + // Stack entries can either be a static const char* + // or a pointer to within this buffer. + mozilla::Vector<char, 0> mBuffer; + +public: + HangStack() { } + + HangStack(HangStack&& aOther) + : mImpl(mozilla::Move(aOther.mImpl)) + , mBuffer(mozilla::Move(aOther.mBuffer)) + { + } + + bool operator==(const HangStack& aOther) const { + for (size_t i = 0; i < length(); i++) { + if (!IsSameAsEntry(operator[](i), aOther[i])) { + return false; + } + } + return true; + } + + bool operator!=(const HangStack& aOther) const { + return !operator==(aOther); + } + + const char*& operator[](size_t aIndex) { + return mImpl[aIndex]; + } + + const char* const& operator[](size_t aIndex) const { + return mImpl[aIndex]; + } + + size_t capacity() const { return mImpl.capacity(); } + size_t length() const { return mImpl.length(); } + bool empty() const { return mImpl.empty(); } + bool canAppendWithoutRealloc(size_t aNeeded) const { + return mImpl.canAppendWithoutRealloc(aNeeded); + } + void infallibleAppend(const char* aEntry) { mImpl.infallibleAppend(aEntry); } + bool reserve(size_t aRequest) { return mImpl.reserve(aRequest); } + const char** begin() { return mImpl.begin(); } + const char* const* begin() const { return mImpl.begin(); } + const char** end() { return mImpl.end(); } + const char* const* end() const { return mImpl.end(); } + const char*& back() { return mImpl.back(); } + void erase(const char** aEntry) { mImpl.erase(aEntry); } + void erase(const char** aBegin, const char** aEnd) { + mImpl.erase(aBegin, aEnd); + } + + void clear() { + mImpl.clear(); + mBuffer.clear(); + } + + bool IsInBuffer(const char* aEntry) const { + return aEntry >= mBuffer.begin() && aEntry < mBuffer.end(); + } + + bool IsSameAsEntry(const char* aEntry, const char* aOther) const { + // If the entry came from the buffer, we need to compare its content; + // otherwise we only need to compare its pointer. + return IsInBuffer(aEntry) ? !strcmp(aEntry, aOther) : (aEntry == aOther); + } + + size_t AvailableBufferSize() const { + return mBuffer.capacity() - mBuffer.length(); + } + + bool EnsureBufferCapacity(size_t aCapacity) { + // aCapacity is the minimal capacity and Vector may make the actual + // capacity larger, in which case we want to use up all the space. + return mBuffer.reserve(aCapacity) && + mBuffer.reserve(mBuffer.capacity()); + } + + const char* InfallibleAppendViaBuffer(const char* aText, size_t aLength); + const char* AppendViaBuffer(const char* aText, size_t aLength); +}; + +/* A hang histogram consists of a stack associated with the + hang, along with a time histogram of the hang times. */ +class HangHistogram : public TimeHistogram +{ +private: + static uint32_t GetHash(const HangStack& aStack); + + HangStack mStack; + // Native stack that corresponds to the pseudostack in mStack + HangStack mNativeStack; + // Use a hash to speed comparisons + const uint32_t mHash; + // Annotations attributed to this stack + HangMonitor::HangAnnotationsVector mAnnotations; + +public: + explicit HangHistogram(HangStack&& aStack) + : mStack(mozilla::Move(aStack)) + , mHash(GetHash(mStack)) + { + } + HangHistogram(HangHistogram&& aOther) + : TimeHistogram(mozilla::Move(aOther)) + , mStack(mozilla::Move(aOther.mStack)) + , mNativeStack(mozilla::Move(aOther.mNativeStack)) + , mHash(mozilla::Move(aOther.mHash)) + , mAnnotations(mozilla::Move(aOther.mAnnotations)) + { + } + bool operator==(const HangHistogram& aOther) const; + bool operator!=(const HangHistogram& aOther) const + { + return !operator==(aOther); + } + const HangStack& GetStack() const { + return mStack; + } + HangStack& GetNativeStack() { + return mNativeStack; + } + const HangStack& GetNativeStack() const { + return mNativeStack; + } + const HangMonitor::HangAnnotationsVector& GetAnnotations() const { + return mAnnotations; + } + void Add(PRIntervalTime aTime, HangMonitor::HangAnnotationsPtr aAnnotations) { + TimeHistogram::Add(aTime); + if (aAnnotations) { + if (!mAnnotations.append(Move(aAnnotations))) { + MOZ_CRASH(); + } + } + } +}; + +/* Thread hang stats consist of + - thread name + - time histogram of all task run times + - hang histograms of individual hangs + - annotations for each hang +*/ +class ThreadHangStats +{ +private: + nsCString mName; + +public: + TimeHistogram mActivity; + mozilla::Vector<HangHistogram, 4> mHangs; + + explicit ThreadHangStats(const char* aName) + : mName(aName) + { + } + ThreadHangStats(ThreadHangStats&& aOther) + : mName(mozilla::Move(aOther.mName)) + , mActivity(mozilla::Move(aOther.mActivity)) + , mHangs(mozilla::Move(aOther.mHangs)) + { + } + const char* GetName() const { + return mName.get(); + } +}; + +} // namespace Telemetry +} // namespace mozilla + +#endif // mozilla_BackgroundHangTelemetry_h |