diff --git a/toolkit/components/telemetry/Telemetry.cpp b/toolkit/components/telemetry/Telemetry.cpp index 3f85d88a18f..f54a379e9e3 100644 --- a/toolkit/components/telemetry/Telemetry.cpp +++ b/toolkit/components/telemetry/Telemetry.cpp @@ -60,6 +60,60 @@ namespace { using namespace base; using namespace mozilla; +template +class AutoHashtable : public nsTHashtable +{ +public: + AutoHashtable(PRUint32 initSize = PL_DHASH_MIN_SIZE); + ~AutoHashtable(); + typedef bool (*ReflectEntryFunc)(EntryType *entry, JSContext *cx, JSObject *obj); + bool ReflectHashtable(ReflectEntryFunc entryFunc, JSContext *cx, JSObject *obj); +private: + struct EnumeratorArgs { + JSContext *cx; + JSObject *obj; + ReflectEntryFunc entryFunc; + }; + static PLDHashOperator ReflectEntryStub(EntryType *entry, void *arg); +}; + +template +AutoHashtable::AutoHashtable(PRUint32 initSize) +{ + this->Init(initSize); +} + +template +AutoHashtable::~AutoHashtable() +{ + this->Clear(); +} + +template +PLDHashOperator +AutoHashtable::ReflectEntryStub(EntryType *entry, void *arg) +{ + EnumeratorArgs *args = static_cast(arg); + if (!args->entryFunc(entry, args->cx, args->obj)) { + return PL_DHASH_STOP; + } + return PL_DHASH_NEXT; +} + +/** + * Reflect the individual entries of table into JS, usually by defining + * some property and value of obj. entryFunc is called for each entry. + */ +template +bool +AutoHashtable::ReflectHashtable(ReflectEntryFunc entryFunc, + JSContext *cx, JSObject *obj) +{ + EnumeratorArgs args = { cx, obj, entryFunc }; + PRUint32 num = EnumerateEntries(ReflectEntryStub, static_cast(&args)); + return num == this->Count(); +} + class TelemetryImpl : public nsITelemetry { NS_DECL_ISUPPORTS @@ -83,6 +137,8 @@ public: typedef nsBaseHashtableET SlowSQLEntryType; private: + static bool StatementReflector(SlowSQLEntryType *entry, JSContext *cx, + JSObject *obj); bool AddSQLInfo(JSContext *cx, JSObject *rootObj, bool mainThread); // Like GetHistogramById, but returns the underlying C++ object, not the JS one. @@ -92,12 +148,14 @@ private: typedef StatisticsRecorder::Histograms::iterator HistogramIterator; // This is used for speedy string->Telemetry::ID conversions typedef nsBaseHashtableET CharPtrEntryType; - typedef nsTHashtable HistogramMapType; + typedef AutoHashtable HistogramMapType; HistogramMapType mHistogramMap; bool mCanRecord; static TelemetryImpl *sTelemetry; - nsTHashtable mSlowSQLOnMainThread; - nsTHashtable mSlowSQLOnOtherThread; + AutoHashtable mSlowSQLOnMainThread; + AutoHashtable mSlowSQLOnOtherThread; + // This gets marked immutable in debug builds, so we can't use + // AutoHashtable here. nsTHashtable mTrackedDBs; Mutex mHashMutex; }; @@ -344,6 +402,7 @@ WrapAndReturnHistogram(Histogram *h, JSContext *cx, jsval *ret) } TelemetryImpl::TelemetryImpl(): +mHistogramMap(Telemetry::HistogramCount), mCanRecord(XRE_GetProcessType() == GeckoProcessType_Default), mHashMutex("Telemetry::mHashMutex") { @@ -364,16 +423,9 @@ mHashMutex("Telemetry::mHashMutex") // Mark immutable to prevent asserts on simultaneous access from multiple threads mTrackedDBs.MarkImmutable(); #endif - - mSlowSQLOnMainThread.Init(); - mSlowSQLOnOtherThread.Init(); - mHistogramMap.Init(Telemetry::HistogramCount); } TelemetryImpl::~TelemetryImpl() { - mSlowSQLOnMainThread.Clear(); - mSlowSQLOnOtherThread.Clear(); - mHistogramMap.Clear(); } NS_IMETHODIMP @@ -387,33 +439,22 @@ TelemetryImpl::NewHistogram(const nsACString &name, PRUint32 min, PRUint32 max, return WrapAndReturnHistogram(h, cx, ret); } -struct EnumeratorArgs { - JSContext *cx; - JSObject *statsObj; -}; - -PLDHashOperator -StatementEnumerator(TelemetryImpl::SlowSQLEntryType *entry, void *arg) +bool +TelemetryImpl::StatementReflector(SlowSQLEntryType *entry, JSContext *cx, + JSObject *obj) { - EnumeratorArgs *args = static_cast(arg); const nsACString &sql = entry->GetKey(); jsval hitCount = UINT_TO_JSVAL(entry->mData.hitCount); jsval totalTime = UINT_TO_JSVAL(entry->mData.totalTime); - JSObject *arrayObj = JS_NewArrayObject(args->cx, 2, nsnull); - if (!arrayObj || - !JS_SetElement(args->cx, arrayObj, 0, &hitCount) || - !JS_SetElement(args->cx, arrayObj, 1, &totalTime)) - return PL_DHASH_STOP; - - JSBool success = JS_DefineProperty(args->cx, args->statsObj, - sql.BeginReading(), - OBJECT_TO_JSVAL(arrayObj), - NULL, NULL, JSPROP_ENUMERATE); - if (!success) - return PL_DHASH_STOP; - - return PL_DHASH_NEXT; + JSObject *arrayObj = JS_NewArrayObject(cx, 2, nsnull); + return (arrayObj + && JS_SetElement(cx, arrayObj, 0, &hitCount) + && JS_SetElement(cx, arrayObj, 1, &totalTime) + && JS_DefineProperty(cx, obj, + sql.BeginReading(), + OBJECT_TO_JSVAL(arrayObj), + NULL, NULL, JSPROP_ENUMERATE)); } bool @@ -430,15 +471,10 @@ TelemetryImpl::AddSQLInfo(JSContext *cx, JSObject *rootObj, bool mainThread) if (!ok) return false; - EnumeratorArgs args = { cx, statsObj }; - nsTHashtable *sqlMap; - sqlMap = (mainThread ? &mSlowSQLOnMainThread : &mSlowSQLOnOtherThread); - PRUint32 num = sqlMap->EnumerateEntries(StatementEnumerator, - static_cast(&args)); - if (num != sqlMap->Count()) - return false; - - return true; + AutoHashtable &sqlMap = (mainThread + ? mSlowSQLOnMainThread + : mSlowSQLOnOtherThread); + return sqlMap.ReflectHashtable(StatementReflector, cx, statsObj); } nsresult @@ -731,7 +767,7 @@ TelemetryImpl::RecordSlowStatement(const nsACString &statement, if (!sTelemetry->mCanRecord || !sTelemetry->mTrackedDBs.GetEntry(dbName)) return; - nsTHashtable *slowSQLMap = NULL; + AutoHashtable *slowSQLMap = NULL; if (NS_IsMainThread()) slowSQLMap = &(sTelemetry->mSlowSQLOnMainThread); else