Bug 1128768: Part 2 - Refactor hang annotation code; r=vladan

--HG--
extra : rebase_source : f492ff9a5aa78afd2121afc8fd18c08b6831cdc3
This commit is contained in:
Aaron Klotz 2015-03-09 19:41:24 -06:00
parent d186a94983
commit 0f6fdabeee
6 changed files with 373 additions and 267 deletions

View File

@ -2553,9 +2553,9 @@ TelemetryImpl::GetChromeHangs(JSContext *cx, JS::MutableHandle<JS::Value> ret)
if (!jsAnnotation) {
return NS_ERROR_FAILURE;
}
nsAutoPtr<HangAnnotations::Enumerator> annotationsEnum;
if (!annotationInfo[iterIndex].mAnnotations->GetEnumerator(
annotationsEnum.StartAssignment())) {
UniquePtr<HangAnnotations::Enumerator> annotationsEnum =
annotationInfo[iterIndex].mAnnotations->GetEnumerator();
if (!annotationsEnum) {
return NS_ERROR_FAILURE;
}
nsAutoString key;

View File

@ -0,0 +1,262 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/. */
#include "mozilla/HangAnnotations.h"
#include <vector>
#include "MainThreadUtils.h"
#include "mozilla/DebugOnly.h"
#include "nsXULAppAPI.h"
namespace mozilla {
namespace HangMonitor {
// Chrome hang annotators. This can go away once BHR has completely replaced
// ChromeHangs.
static StaticAutoPtr<Observer::Annotators> gChromehangAnnotators;
class BrowserHangAnnotations : public HangAnnotations
{
public:
BrowserHangAnnotations();
~BrowserHangAnnotations();
void AddAnnotation(const nsAString& aName, const int32_t aData) MOZ_OVERRIDE;
void AddAnnotation(const nsAString& aName, const double aData) MOZ_OVERRIDE;
void AddAnnotation(const nsAString& aName, const nsAString& aData) MOZ_OVERRIDE;
void AddAnnotation(const nsAString& aName, const nsACString& aData) MOZ_OVERRIDE;
void AddAnnotation(const nsAString& aName, const bool aData) MOZ_OVERRIDE;
size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
bool IsEmpty() const MOZ_OVERRIDE;
UniquePtr<Enumerator> GetEnumerator() MOZ_OVERRIDE;
typedef std::pair<nsString, nsString> AnnotationType;
typedef std::vector<AnnotationType> VectorType;
typedef VectorType::const_iterator IteratorType;
private:
VectorType mAnnotations;
};
BrowserHangAnnotations::BrowserHangAnnotations()
{
MOZ_COUNT_CTOR(BrowserHangAnnotations);
}
BrowserHangAnnotations::~BrowserHangAnnotations()
{
MOZ_COUNT_DTOR(BrowserHangAnnotations);
}
void
BrowserHangAnnotations::AddAnnotation(const nsAString& aName, const int32_t aData)
{
nsString dataString;
dataString.AppendInt(aData);
AnnotationType annotation = std::make_pair(nsString(aName), dataString);
mAnnotations.push_back(annotation);
}
void
BrowserHangAnnotations::AddAnnotation(const nsAString& aName, const double aData)
{
nsString dataString;
dataString.AppendFloat(aData);
AnnotationType annotation = std::make_pair(nsString(aName), dataString);
mAnnotations.push_back(annotation);
}
void
BrowserHangAnnotations::AddAnnotation(const nsAString& aName, const nsAString& aData)
{
AnnotationType annotation = std::make_pair(nsString(aName), nsString(aData));
mAnnotations.push_back(annotation);
}
void
BrowserHangAnnotations::AddAnnotation(const nsAString& aName, const nsACString& aData)
{
nsString dataString;
AppendUTF8toUTF16(aData, dataString);
AnnotationType annotation = std::make_pair(nsString(aName), dataString);
mAnnotations.push_back(annotation);
}
void
BrowserHangAnnotations::AddAnnotation(const nsAString& aName, const bool aData)
{
nsString dataString;
dataString += aData ? NS_LITERAL_STRING("true") : NS_LITERAL_STRING("false");
AnnotationType annotation = std::make_pair(nsString(aName), dataString);
mAnnotations.push_back(annotation);
}
/**
* This class itself does not use synchronization but it (and its parent object)
* should be protected by mutual exclusion in some way. In Telemetry the chrome
* hang data is protected via TelemetryImpl::mHangReportsMutex.
*/
class ChromeHangAnnotationEnumerator : public HangAnnotations::Enumerator
{
public:
explicit ChromeHangAnnotationEnumerator(const BrowserHangAnnotations::VectorType& aAnnotations);
~ChromeHangAnnotationEnumerator();
virtual bool Next(nsAString& aOutName, nsAString& aOutValue);
private:
BrowserHangAnnotations::IteratorType mIterator;
BrowserHangAnnotations::IteratorType mEnd;
};
ChromeHangAnnotationEnumerator::ChromeHangAnnotationEnumerator(
const BrowserHangAnnotations::VectorType& aAnnotations)
: mIterator(aAnnotations.begin())
, mEnd(aAnnotations.end())
{
MOZ_COUNT_CTOR(ChromeHangAnnotationEnumerator);
}
ChromeHangAnnotationEnumerator::~ChromeHangAnnotationEnumerator()
{
MOZ_COUNT_DTOR(ChromeHangAnnotationEnumerator);
}
bool
ChromeHangAnnotationEnumerator::Next(nsAString& aOutName, nsAString& aOutValue)
{
aOutName.Truncate();
aOutValue.Truncate();
if (mIterator == mEnd) {
return false;
}
aOutName = mIterator->first;
aOutValue = mIterator->second;
++mIterator;
return true;
}
bool
BrowserHangAnnotations::IsEmpty() const
{
return mAnnotations.empty();
}
size_t
BrowserHangAnnotations::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
{
size_t result = sizeof(mAnnotations) +
mAnnotations.capacity() * sizeof(AnnotationType);
for (IteratorType i = mAnnotations.begin(), e = mAnnotations.end(); i != e;
++i) {
result += i->first.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
result += i->second.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
}
return result;
}
UniquePtr<HangAnnotations::Enumerator>
BrowserHangAnnotations::GetEnumerator()
{
if (mAnnotations.empty()) {
return nullptr;
}
return MakeUnique<ChromeHangAnnotationEnumerator>(mAnnotations);
}
namespace Observer {
Annotators::Annotators()
: mMutex("HangMonitor::Annotators::mMutex")
{
MOZ_COUNT_CTOR(Annotators);
}
Annotators::~Annotators()
{
MOZ_ASSERT(mAnnotators.empty());
MOZ_COUNT_DTOR(Annotators);
}
bool
Annotators::Register(Annotator& aAnnotator)
{
MutexAutoLock lock(mMutex);
auto result = mAnnotators.insert(&aAnnotator);
return result.second;
}
bool
Annotators::Unregister(Annotator& aAnnotator)
{
MutexAutoLock lock(mMutex);
DebugOnly<std::set<Annotator*>::size_type> numErased;
numErased = mAnnotators.erase(&aAnnotator);
MOZ_ASSERT(numErased == 1);
return mAnnotators.empty();
}
UniquePtr<HangAnnotations>
Annotators::GatherAnnotations()
{
auto annotations = MakeUnique<BrowserHangAnnotations>();
{ // Scope for lock
MutexAutoLock lock(mMutex);
for (std::set<Annotator*>::iterator i = mAnnotators.begin(),
e = mAnnotators.end();
i != e; ++i) {
(*i)->AnnotateHang(*annotations);
}
}
if (annotations->IsEmpty()) {
return nullptr;
}
return Move(annotations);
}
} // namespace Observer
void
RegisterAnnotator(Annotator& aAnnotator)
{
BackgroundHangMonitor::RegisterAnnotator(aAnnotator);
// We still register annotators for ChromeHangs
if (NS_IsMainThread() &&
GeckoProcessType_Default == XRE_GetProcessType()) {
if (!gChromehangAnnotators) {
gChromehangAnnotators = new Observer::Annotators();
}
gChromehangAnnotators->Register(aAnnotator);
}
}
void
UnregisterAnnotator(Annotator& aAnnotator)
{
BackgroundHangMonitor::UnregisterAnnotator(aAnnotator);
// We still register annotators for ChromeHangs
if (NS_IsMainThread() &&
GeckoProcessType_Default == XRE_GetProcessType()) {
if (gChromehangAnnotators->Unregister(aAnnotator)) {
gChromehangAnnotators = nullptr;
}
}
}
UniquePtr<HangAnnotations>
ChromeHangAnnotatorCallout()
{
if (!gChromehangAnnotators) {
return nullptr;
}
return gChromehangAnnotators->GatherAnnotations();
}
} // namespace HangMonitor
} // namespace mozilla

View File

@ -0,0 +1,104 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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_HangAnnotations_h
#define mozilla_HangAnnotations_h
#include <set>
#include "mozilla/MemoryReporting.h"
#include "mozilla/Mutex.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/Vector.h"
#include "nsString.h"
namespace mozilla {
namespace HangMonitor {
/**
* This class declares an abstraction for a data type that encapsulates all
* of the annotations being reported by a registered hang Annotator.
*/
class HangAnnotations
{
public:
virtual ~HangAnnotations() {}
virtual void AddAnnotation(const nsAString& aName, const int32_t aData) = 0;
virtual void AddAnnotation(const nsAString& aName, const double aData) = 0;
virtual void AddAnnotation(const nsAString& aName, const nsAString& aData) = 0;
virtual void AddAnnotation(const nsAString& aName, const nsACString& aData) = 0;
virtual void AddAnnotation(const nsAString& aName, const bool aData) = 0;
class Enumerator
{
public:
virtual ~Enumerator() {}
virtual bool Next(nsAString& aOutName, nsAString& aOutValue) = 0;
};
virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const = 0;
virtual bool IsEmpty() const = 0;
virtual UniquePtr<Enumerator> GetEnumerator() = 0;
};
typedef UniquePtr<HangAnnotations> HangAnnotationsPtr;
typedef Vector<HangAnnotationsPtr> HangAnnotationsVector;
class Annotator
{
public:
/**
* NB: This function is always called by the HangMonitor thread.
* Plan accordingly.
*/
virtual void AnnotateHang(HangAnnotations& aAnnotations) = 0;
};
/**
* Registers an Annotator to be called when a hang is detected.
* @param aAnnotator Reference to an object that implements the
* HangMonitor::Annotator interface.
*/
void RegisterAnnotator(Annotator& aAnnotator);
/**
* Registers an Annotator that was previously registered via RegisterAnnotator.
* @param aAnnotator Reference to an object that implements the
* HangMonitor::Annotator interface.
*/
void UnregisterAnnotator(Annotator& aAnnotator);
/**
* Gathers annotations. This function should be called by ChromeHangs.
* @return UniquePtr to HangAnnotations object or nullptr if none.
*/
HangAnnotationsPtr ChromeHangAnnotatorCallout();
namespace Observer {
class Annotators
{
public:
Annotators();
~Annotators();
bool Register(Annotator& aAnnotator);
bool Unregister(Annotator& aAnnotator);
HangAnnotationsPtr GatherAnnotations();
private:
Mutex mMutex;
std::set<Annotator*> mAnnotators;
};
} // namespace Observer
} // namespace HangMonitor
} // namespace mozilla
#endif // mozilla_HangAnnotations_h

View File

@ -6,8 +6,6 @@
#include "mozilla/HangMonitor.h"
#include <set>
#include "mozilla/Atomics.h"
#include "mozilla/BackgroundHangMonitor.h"
#include "mozilla/Monitor.h"
@ -74,9 +72,6 @@ static const int32_t DEFAULT_CHROME_HANG_INTERVAL = 5;
// Maximum number of PCs to gather from the stack
static const int32_t MAX_CALL_STACK_PCS = 400;
// Chrome hang annotators
static StaticAutoPtr<std::set<Annotator*>> gAnnotators;
#endif
// PrefChangedFunc
@ -122,161 +117,6 @@ Crash()
}
#ifdef REPORT_CHROME_HANGS
class ChromeHangAnnotations : public HangAnnotations
{
public:
ChromeHangAnnotations();
~ChromeHangAnnotations();
void AddAnnotation(const nsAString& aName, const int32_t aData) MOZ_OVERRIDE;
void AddAnnotation(const nsAString& aName, const double aData) MOZ_OVERRIDE;
void AddAnnotation(const nsAString& aName, const nsAString& aData) MOZ_OVERRIDE;
void AddAnnotation(const nsAString& aName, const nsACString& aData) MOZ_OVERRIDE;
void AddAnnotation(const nsAString& aName, const bool aData) MOZ_OVERRIDE;
size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
bool IsEmpty() const MOZ_OVERRIDE;
bool GetEnumerator(Enumerator** aOutEnum) MOZ_OVERRIDE;
typedef std::pair<nsString, nsString> AnnotationType;
typedef std::vector<AnnotationType> VectorType;
typedef VectorType::const_iterator IteratorType;
private:
VectorType mAnnotations;
};
ChromeHangAnnotations::ChromeHangAnnotations()
{
MOZ_COUNT_CTOR(ChromeHangAnnotations);
}
ChromeHangAnnotations::~ChromeHangAnnotations()
{
MOZ_COUNT_DTOR(ChromeHangAnnotations);
}
void
ChromeHangAnnotations::AddAnnotation(const nsAString& aName, const int32_t aData)
{
nsString dataString;
dataString.AppendInt(aData);
AnnotationType annotation = std::make_pair(nsString(aName), dataString);
mAnnotations.push_back(annotation);
}
void
ChromeHangAnnotations::AddAnnotation(const nsAString& aName, const double aData)
{
nsString dataString;
dataString.AppendFloat(aData);
AnnotationType annotation = std::make_pair(nsString(aName), dataString);
mAnnotations.push_back(annotation);
}
void
ChromeHangAnnotations::AddAnnotation(const nsAString& aName, const nsAString& aData)
{
AnnotationType annotation = std::make_pair(nsString(aName), nsString(aData));
mAnnotations.push_back(annotation);
}
void
ChromeHangAnnotations::AddAnnotation(const nsAString& aName, const nsACString& aData)
{
nsString dataString;
AppendUTF8toUTF16(aData, dataString);
AnnotationType annotation = std::make_pair(nsString(aName), dataString);
mAnnotations.push_back(annotation);
}
void
ChromeHangAnnotations::AddAnnotation(const nsAString& aName, const bool aData)
{
nsString dataString;
dataString += aData ? NS_LITERAL_STRING("true") : NS_LITERAL_STRING("false");
AnnotationType annotation = std::make_pair(nsString(aName), dataString);
mAnnotations.push_back(annotation);
}
/**
* This class itself does not use synchronization but it (and its parent object)
* should be protected by mutual exclusion in some way. In Telemetry the chrome
* hang data is protected via TelemetryImpl::mHangReportsMutex.
*/
class ChromeHangAnnotationEnumerator : public HangAnnotations::Enumerator
{
public:
ChromeHangAnnotationEnumerator(const ChromeHangAnnotations::VectorType& aAnnotations);
~ChromeHangAnnotationEnumerator();
virtual bool Next(nsAString& aOutName, nsAString& aOutValue);
private:
ChromeHangAnnotations::IteratorType mIterator;
ChromeHangAnnotations::IteratorType mEnd;
};
ChromeHangAnnotationEnumerator::ChromeHangAnnotationEnumerator(
const ChromeHangAnnotations::VectorType& aAnnotations)
: mIterator(aAnnotations.begin())
, mEnd(aAnnotations.end())
{
MOZ_COUNT_CTOR(ChromeHangAnnotationEnumerator);
}
ChromeHangAnnotationEnumerator::~ChromeHangAnnotationEnumerator()
{
MOZ_COUNT_DTOR(ChromeHangAnnotationEnumerator);
}
bool
ChromeHangAnnotationEnumerator::Next(nsAString& aOutName, nsAString& aOutValue)
{
aOutName.Truncate();
aOutValue.Truncate();
if (mIterator == mEnd) {
return false;
}
aOutName = mIterator->first;
aOutValue = mIterator->second;
++mIterator;
return true;
}
bool
ChromeHangAnnotations::IsEmpty() const
{
return mAnnotations.empty();
}
size_t
ChromeHangAnnotations::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
{
size_t result = sizeof(mAnnotations) +
mAnnotations.capacity() * sizeof(AnnotationType);
for (IteratorType i = mAnnotations.begin(), e = mAnnotations.end(); i != e;
++i) {
result += i->first.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
result += i->second.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
}
return result;
}
bool
ChromeHangAnnotations::GetEnumerator(HangAnnotations::Enumerator** aOutEnum)
{
if (!aOutEnum) {
return false;
}
*aOutEnum = nullptr;
if (mAnnotations.empty()) {
return false;
}
*aOutEnum = new ChromeHangAnnotationEnumerator(mAnnotations);
return true;
}
static void
ChromeStackWalker(uint32_t aFrameNumber, void* aPC, void* aSP, void* aClosure)
@ -329,21 +169,6 @@ GetChromeHangReport(Telemetry::ProcessedStack& aStack,
}
}
static void
ChromeHangAnnotatorCallout(ChromeHangAnnotations& aAnnotations)
{
gMonitor->AssertCurrentThreadOwns();
MOZ_ASSERT(gAnnotators);
if (!gAnnotators) {
return;
}
for (std::set<Annotator*>::iterator i = gAnnotators->begin(),
e = gAnnotators->end();
i != e; ++i) {
(*i)->AnnotateHang(aAnnotations);
}
}
#endif
void
@ -363,7 +188,7 @@ ThreadMain(void*)
Telemetry::ProcessedStack stack;
int32_t systemUptime = -1;
int32_t firefoxUptime = -1;
auto annotations = MakeUnique<ChromeHangAnnotations>();
UniquePtr<HangAnnotations> annotations;
#endif
while (true) {
@ -391,7 +216,7 @@ ThreadMain(void*)
// the minimum hang duration has been reached (not when the hang ends)
if (waitCount == 2) {
GetChromeHangReport(stack, systemUptime, firefoxUptime);
ChromeHangAnnotatorCallout(*annotations);
annotations = ChromeHangAnnotatorCallout();
}
#else
// This is the crash-on-hang feature.
@ -412,7 +237,6 @@ ThreadMain(void*)
Telemetry::RecordChromeHang(hangDuration, stack, systemUptime,
firefoxUptime, Move(annotations));
stack.Clear();
annotations = MakeUnique<ChromeHangAnnotations>();
}
#endif
lastTimestamp = timestamp;
@ -452,7 +276,6 @@ Startup()
if (!winMainThreadHandle) {
return;
}
gAnnotators = new std::set<Annotator*>();
#endif
// Don't actually start measuring hangs until we hit the main event loop.
@ -491,11 +314,6 @@ Shutdown()
delete gMonitor;
gMonitor = nullptr;
#ifdef REPORT_CHROME_HANGS
// gAnnotators is a StaticAutoPtr, so we just need to null it out.
gAnnotators = nullptr;
#endif
}
static bool
@ -584,31 +402,5 @@ Suspend()
}
}
void
RegisterAnnotator(Annotator& aAnnotator)
{
#ifdef REPORT_CHROME_HANGS
if (GeckoProcessType_Default != XRE_GetProcessType()) {
return;
}
MonitorAutoLock lock(*gMonitor);
MOZ_ASSERT(gAnnotators);
gAnnotators->insert(&aAnnotator);
#endif
}
void
UnregisterAnnotator(Annotator& aAnnotator)
{
#ifdef REPORT_CHROME_HANGS
if (GeckoProcessType_Default != XRE_GetProcessType()) {
return;
}
MonitorAutoLock lock(*gMonitor);
MOZ_ASSERT(gAnnotators);
gAnnotators->erase(&aAnnotator);
#endif
}
} // namespace HangMonitor
} // namespace mozilla

View File

@ -7,9 +7,6 @@
#ifndef mozilla_HangMonitor_h
#define mozilla_HangMonitor_h
#include "mozilla/MemoryReporting.h"
#include "nsString.h"
namespace mozilla {
namespace HangMonitor {
@ -41,57 +38,6 @@ void Startup();
*/
void Shutdown();
/**
* This class declares an abstraction for a data type that encapsulates all
* of the annotations being reported by a registered hang Annotator.
*/
class HangAnnotations
{
public:
virtual ~HangAnnotations() {}
virtual void AddAnnotation(const nsAString& aName, const int32_t aData) = 0;
virtual void AddAnnotation(const nsAString& aName, const double aData) = 0;
virtual void AddAnnotation(const nsAString& aName, const nsAString& aData) = 0;
virtual void AddAnnotation(const nsAString& aName, const nsACString& aData) = 0;
virtual void AddAnnotation(const nsAString& aName, const bool aData) = 0;
class Enumerator
{
public:
virtual ~Enumerator() {}
virtual bool Next(nsAString& aOutName, nsAString& aOutValue) = 0;
};
virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const = 0;
virtual bool IsEmpty() const = 0;
virtual bool GetEnumerator(Enumerator **aOutEnum) = 0;
};
class Annotator
{
public:
/**
* NB: This function is always called by the HangMonitor thread.
* Plan accordingly.
*/
virtual void AnnotateHang(HangAnnotations& aAnnotations) = 0;
};
/**
* Registers an Annotator to be called when a hang is detected.
* @param aAnnotator Reference to an object that implements the
* HangMonitor::Annotator interface.
*/
void RegisterAnnotator(Annotator& aAnnotator);
/**
* Registers an Annotator that was previously registered via RegisterAnnotator.
* @param aAnnotator Reference to an object that implements the
* HangMonitor::Annotator interface.
*/
void UnregisterAnnotator(Annotator& aAnnotator);
/**
* Notify the hang monitor of activity which will reset its internal timer.
*

View File

@ -29,6 +29,7 @@ EXPORTS += [
EXPORTS.mozilla += [
'BackgroundHangMonitor.h',
'HangAnnotations.h',
'HangMonitor.h',
'LazyIdleThread.h',
'SyncRunnable.h',
@ -36,6 +37,7 @@ EXPORTS.mozilla += [
UNIFIED_SOURCES += [
'BackgroundHangMonitor.cpp',
'HangAnnotations.cpp',
'HangMonitor.cpp',
'LazyIdleThread.cpp',
'nsEnvironment.cpp',