Bug 818307: Part 1 - Add annotation support to ChromeHangs; r=vladan

This commit is contained in:
Aaron Klotz 2014-10-10 12:19:57 -06:00
Родитель 6d7ed02a07
Коммит ca4ee88bc5
4 изменённых файлов: 385 добавлений и 16 удалений

Просмотреть файл

@ -63,6 +63,7 @@
#include "mozilla/PoisonIOInterposer.h"
#include "mozilla/StartupTimeline.h"
#if defined(MOZ_ENABLE_PROFILER_SPS)
#include "mozilla/HangMonitor.h"
#include "shared-libraries.h"
#endif
@ -72,6 +73,7 @@ namespace {
using namespace base;
using namespace mozilla;
using namespace mozilla::HangMonitor;
template<class EntryType>
class AutoHashtable : public nsTHashtable<EntryType>
@ -208,14 +210,38 @@ CombinedStacks::SizeOfExcludingThis() const {
class HangReports {
public:
size_t SizeOfExcludingThis() const;
/**
* This struct encapsulates information for an individual ChromeHang annotation.
* mHangIndex is the index of the corresponding ChromeHang.
*/
struct AnnotationInfo {
AnnotationInfo(uint32_t aHangIndex,
HangAnnotations* aAnnotations)
: mHangIndex(aHangIndex)
, mAnnotations(aAnnotations)
{}
AnnotationInfo(const AnnotationInfo& aOther)
: mHangIndex(aOther.mHangIndex)
, mAnnotations(aOther.mAnnotations)
{}
~AnnotationInfo() {}
uint32_t mHangIndex;
mutable nsAutoPtr<HangAnnotations> mAnnotations;
};
size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
void AddHang(const Telemetry::ProcessedStack& aStack, uint32_t aDuration,
int32_t aSystemUptime, int32_t aFirefoxUptime);
int32_t aSystemUptime, int32_t aFirefoxUptime,
HangAnnotations* aAnnotations);
uint32_t GetDuration(unsigned aIndex) const;
int32_t GetSystemUptime(unsigned aIndex) const;
int32_t GetFirefoxUptime(unsigned aIndex) const;
const std::vector<AnnotationInfo>& GetAnnotationInfo() const;
const CombinedStacks& GetStacks() const;
private:
/**
* This struct encapsulates the data for an individual ChromeHang, excluding
* annotations.
*/
struct HangInfo {
// Hang duration (in seconds)
uint32_t mDuration;
@ -225,6 +251,7 @@ private:
int32_t mFirefoxUptime;
};
std::vector<HangInfo> mHangInfo;
std::vector<AnnotationInfo> mAnnotationInfo;
CombinedStacks mStacks;
};
@ -232,19 +259,30 @@ void
HangReports::AddHang(const Telemetry::ProcessedStack& aStack,
uint32_t aDuration,
int32_t aSystemUptime,
int32_t aFirefoxUptime) {
int32_t aFirefoxUptime,
HangAnnotations* aAnnotations) {
HangInfo info = { aDuration, aSystemUptime, aFirefoxUptime };
mHangInfo.push_back(info);
if (aAnnotations) {
AnnotationInfo ainfo(static_cast<uint32_t>(mHangInfo.size() - 1),
aAnnotations);
mAnnotationInfo.push_back(ainfo);
}
mStacks.AddStack(aStack);
}
size_t
HangReports::SizeOfExcludingThis() const {
HangReports::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const {
size_t n = 0;
n += mStacks.SizeOfExcludingThis();
// This is a crude approximation. See comment on
// CombinedStacks::SizeOfExcludingThis.
n += mHangInfo.capacity() * sizeof(HangInfo);
n += mAnnotationInfo.capacity() * sizeof(AnnotationInfo);
for (std::vector<AnnotationInfo>::const_iterator i = mAnnotationInfo.begin(),
e = mAnnotationInfo.end(); i != e; ++i) {
n += i->mAnnotations->SizeOfIncludingThis(aMallocSizeOf);
}
return n;
}
@ -268,6 +306,11 @@ HangReports::GetFirefoxUptime(unsigned aIndex) const {
return mHangInfo[aIndex].mFirefoxUptime;
}
const std::vector<HangReports::AnnotationInfo>&
HangReports::GetAnnotationInfo() const {
return mAnnotationInfo;
}
/**
* IOInterposeObserver recording statistics of main-thread I/O during execution,
* aimed at consumption by TelemetryImpl
@ -575,7 +618,8 @@ public:
static void RecordChromeHang(uint32_t aDuration,
Telemetry::ProcessedStack &aStack,
int32_t aSystemUptime,
int32_t aFirefoxUptime);
int32_t aFirefoxUptime,
HangAnnotations* aAnnotations);
#endif
static void RecordThreadHangStats(Telemetry::ThreadHangStats& aStats);
static nsresult GetHistogramEnumId(const char *name, Telemetry::ID *id);
@ -1798,7 +1842,9 @@ TelemetryImpl::GetChromeHangs(JSContext *cx, JS::MutableHandle<JS::Value> ret)
JS::Rooted<JSObject*> durationArray(cx, JS_NewArrayObject(cx, 0));
JS::Rooted<JSObject*> systemUptimeArray(cx, JS_NewArrayObject(cx, 0));
JS::Rooted<JSObject*> firefoxUptimeArray(cx, JS_NewArrayObject(cx, 0));
if (!durationArray || !systemUptimeArray || !firefoxUptimeArray) {
JS::Rooted<JSObject*> annotationsArray(cx, JS_NewArrayObject(cx, 0));
if (!durationArray || !systemUptimeArray || !firefoxUptimeArray ||
!annotationsArray) {
return NS_ERROR_FAILURE;
}
@ -1820,6 +1866,13 @@ TelemetryImpl::GetChromeHangs(JSContext *cx, JS::MutableHandle<JS::Value> ret)
return NS_ERROR_FAILURE;
}
ok = JS_DefineProperty(cx, fullReportObj, "annotations", annotationsArray,
JSPROP_ENUMERATE);
if (!ok) {
return NS_ERROR_FAILURE;
}
const size_t length = stacks.GetStackCount();
for (size_t i = 0; i < length; ++i) {
if (!JS_SetElement(cx, durationArray, i, mHangReports.GetDuration(i))) {
@ -1831,6 +1884,49 @@ TelemetryImpl::GetChromeHangs(JSContext *cx, JS::MutableHandle<JS::Value> ret)
if (!JS_SetElement(cx, firefoxUptimeArray, i, mHangReports.GetFirefoxUptime(i))) {
return NS_ERROR_FAILURE;
}
const std::vector<HangReports::AnnotationInfo>& annotationInfo =
mHangReports.GetAnnotationInfo();
uint32_t annotationsArrayIndex = 0;
for (std::vector<HangReports::AnnotationInfo>::const_iterator
ai = annotationInfo.begin(), e = annotationInfo.end(); ai != e;
++ai, ++annotationsArrayIndex) {
JS::Rooted<JSObject*> keyValueArray(cx, JS_NewArrayObject(cx, 0));
if (!keyValueArray) {
return NS_ERROR_FAILURE;
}
JS::RootedValue indexValue(cx);
indexValue.setNumber(ai->mHangIndex);
if (!JS_SetElement(cx, keyValueArray, 0, indexValue)) {
return NS_ERROR_FAILURE;
}
JS::Rooted<JSObject*> jsAnnotation(cx, JS_NewObject(cx, nullptr,
JS::NullPtr(),
JS::NullPtr()));
if (!jsAnnotation) {
return NS_ERROR_FAILURE;
}
nsAutoPtr<HangAnnotations::Enumerator> annotationsEnum;
if (!ai->mAnnotations->GetEnumerator(annotationsEnum.StartAssignment())) {
return NS_ERROR_FAILURE;
}
nsAutoString key;
nsAutoString value;
while (annotationsEnum->Next(key, value)) {
JS::RootedValue jsValue(cx);
jsValue.setString(JS_NewUCStringCopyN(cx, value.get(), value.Length()));
if (!JS_DefineUCProperty(cx, jsAnnotation, key.get(), key.Length(),
jsValue, JSPROP_ENUMERATE)) {
return NS_ERROR_FAILURE;
}
}
if (!JS_SetElement(cx, keyValueArray, 1, jsAnnotation)) {
return NS_ERROR_FAILURE;
}
if (!JS_SetElement(cx, annotationsArray, annotationsArrayIndex,
keyValueArray)) {
return NS_ERROR_FAILURE;
}
}
}
return NS_OK;
@ -2543,7 +2639,8 @@ void
TelemetryImpl::RecordChromeHang(uint32_t aDuration,
Telemetry::ProcessedStack &aStack,
int32_t aSystemUptime,
int32_t aFirefoxUptime)
int32_t aFirefoxUptime,
HangAnnotations* aAnnotations)
{
if (!sTelemetry || !sTelemetry->mCanRecord)
return;
@ -2551,7 +2648,8 @@ TelemetryImpl::RecordChromeHang(uint32_t aDuration,
MutexAutoLock hangReportMutex(sTelemetry->mHangReportsMutex);
sTelemetry->mHangReports.AddHang(aStack, aDuration,
aSystemUptime, aFirefoxUptime);
aSystemUptime, aFirefoxUptime,
aAnnotations);
}
#endif
@ -2641,7 +2739,7 @@ TelemetryImpl::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
n += mTrackedDBs.SizeOfExcludingThis(aMallocSizeOf);
{ // Scope for mHangReportsMutex lock
MutexAutoLock lock(mHangReportsMutex);
n += mHangReports.SizeOfExcludingThis();
n += mHangReports.SizeOfExcludingThis(aMallocSizeOf);
}
{ // Scope for mThreadHangStatsMutex lock
MutexAutoLock lock(mThreadHangStatsMutex);
@ -2797,10 +2895,11 @@ void Init()
void RecordChromeHang(uint32_t duration,
ProcessedStack &aStack,
int32_t aSystemUptime,
int32_t aFirefoxUptime)
int32_t aFirefoxUptime,
HangAnnotations* aAnnotations)
{
TelemetryImpl::RecordChromeHang(duration, aStack,
aSystemUptime, aFirefoxUptime);
aSystemUptime, aFirefoxUptime, aAnnotations);
}
#endif

Просмотреть файл

@ -17,6 +17,9 @@ namespace base {
}
namespace mozilla {
namespace HangMonitor {
class HangAnnotations;
}
namespace Telemetry {
#include "TelemetryHistogramEnums.h"
@ -195,12 +198,14 @@ class ProcessedStack;
* @param aStack - Array of PCs from the hung call stack
* @param aSystemUptime - System uptime at the time of the hang, in minutes
* @param aFirefoxUptime - Firefox uptime at the time of the hang, in minutes
* @param aAnnotations - Any annotations to be added to the report
*/
#if defined(MOZ_ENABLE_PROFILER_SPS)
void RecordChromeHang(uint32_t aDuration,
ProcessedStack &aStack,
int32_t aSystemUptime,
int32_t aFirefoxUptime);
int32_t aFirefoxUptime,
mozilla::HangMonitor::HangAnnotations* aAnnotations = nullptr);
#endif
class ThreadHangStats;

Просмотреть файл

@ -5,15 +5,21 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/HangMonitor.h"
#include <set>
#include "mozilla/BackgroundHangMonitor.h"
#include "mozilla/Monitor.h"
#include "mozilla/Preferences.h"
#include "mozilla/Telemetry.h"
#include "mozilla/ProcessedStack.h"
#include "mozilla/Atomics.h"
#include "nsXULAppAPI.h"
#include "nsThreadUtils.h"
#include "nsAutoPtr.h"
#include "nsReadableUtils.h"
#include "nsStackWalk.h"
#include "nsThreadUtils.h"
#include "nsXULAppAPI.h"
#ifdef MOZ_CRASHREPORTER
#include "nsExceptionHandler.h"
@ -68,6 +74,9 @@ 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
@ -113,6 +122,162 @@ 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)
{
@ -163,6 +328,22 @@ GetChromeHangReport(Telemetry::ProcessedStack& aStack,
aFirefoxUptime = -1;
}
}
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
@ -182,6 +363,7 @@ ThreadMain(void*)
Telemetry::ProcessedStack stack;
int32_t systemUptime = -1;
int32_t firefoxUptime = -1;
nsAutoPtr<ChromeHangAnnotations> annotations = new ChromeHangAnnotations();
#endif
while (true) {
@ -209,6 +391,7 @@ ThreadMain(void*)
// the minimum hang duration has been reached (not when the hang ends)
if (waitCount == 2) {
GetChromeHangReport(stack, systemUptime, firefoxUptime);
ChromeHangAnnotatorCallout(*annotations);
}
#else
// This is the crash-on-hang feature.
@ -226,9 +409,11 @@ ThreadMain(void*)
#ifdef REPORT_CHROME_HANGS
if (waitCount >= 2) {
uint32_t hangDuration = PR_IntervalToSeconds(now - lastTimestamp);
Telemetry::RecordChromeHang(hangDuration, stack,
systemUptime, firefoxUptime);
Telemetry::RecordChromeHang(hangDuration, stack, systemUptime,
firefoxUptime, annotations->IsEmpty() ?
nullptr : annotations.forget());
stack.Clear();
annotations = new ChromeHangAnnotations();
}
#endif
lastTimestamp = timestamp;
@ -268,6 +453,7 @@ Startup()
if (!winMainThreadHandle) {
return;
}
gAnnotators = new std::set<Annotator*>();
#endif
// Don't actually start measuring hangs until we hit the main event loop.
@ -306,6 +492,11 @@ 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
@ -394,5 +585,25 @@ Suspend()
}
}
void
RegisterAnnotator(Annotator& aAnnotator)
{
#ifdef REPORT_CHROME_HANGS
MonitorAutoLock lock(*gMonitor);
MOZ_ASSERT(gAnnotators);
gAnnotators->insert(&aAnnotator);
#endif
}
void
UnregisterAnnotator(Annotator& aAnnotator)
{
#ifdef REPORT_CHROME_HANGS
MonitorAutoLock lock(*gMonitor);
MOZ_ASSERT(gAnnotators);
gAnnotators->erase(&aAnnotator);
#endif
}
} // namespace HangMonitor
} // namespace mozilla

Просмотреть файл

@ -7,6 +7,9 @@
#ifndef mozilla_HangMonitor_h
#define mozilla_HangMonitor_h
#include "mozilla/MemoryReporting.h"
#include "nsString.h"
namespace mozilla {
namespace HangMonitor {
@ -38,6 +41,57 @@ 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.
*