2011-04-27 18:07:02 +00:00
|
|
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/* ***** BEGIN LICENSE BLOCK *****
|
|
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
|
|
*
|
|
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
|
|
* the License. You may obtain a copy of the License at
|
|
|
|
* http://www.mozilla.org/MPL/
|
|
|
|
*
|
|
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
|
|
* for the specific language governing rights and limitations under the
|
|
|
|
* License.
|
|
|
|
*
|
|
|
|
* The Original Code is Mozilla code.
|
|
|
|
*
|
|
|
|
* The Initial Developer of the Original Code is
|
|
|
|
* Mozilla Corporation.
|
|
|
|
* Portions created by the Initial Developer are Copyright (C) 2011
|
|
|
|
* the Initial Developer. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Contributor(s):
|
|
|
|
* Taras Glek <tglek@mozilla.com>
|
2011-12-14 20:04:25 +00:00
|
|
|
* Vladan Djeric <vdjeric@mozilla.com>
|
2011-04-27 18:07:02 +00:00
|
|
|
*
|
|
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
|
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
|
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
|
|
* the provisions above, a recipient may use your version of this file under
|
|
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
|
|
*
|
|
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
|
|
|
|
#include "base/histogram.h"
|
|
|
|
#include "nsIComponentManager.h"
|
|
|
|
#include "nsIServiceManager.h"
|
|
|
|
#include "nsCOMPtr.h"
|
|
|
|
#include "mozilla/ModuleUtils.h"
|
|
|
|
#include "nsIXPConnect.h"
|
|
|
|
#include "mozilla/Services.h"
|
|
|
|
#include "jsapi.h"
|
|
|
|
#include "nsStringGlue.h"
|
|
|
|
#include "nsITelemetry.h"
|
2011-06-20 21:47:58 +00:00
|
|
|
#include "Telemetry.h"
|
2011-06-20 21:48:03 +00:00
|
|
|
#include "nsTHashtable.h"
|
|
|
|
#include "nsHashKeys.h"
|
|
|
|
#include "nsBaseHashtable.h"
|
2011-05-22 06:23:20 +00:00
|
|
|
#include "nsXULAppAPI.h"
|
2011-12-06 20:12:55 +00:00
|
|
|
#include "nsThreadUtils.h"
|
|
|
|
#include "mozilla/Mutex.h"
|
2011-04-27 18:07:02 +00:00
|
|
|
|
2011-05-20 02:33:05 +00:00
|
|
|
namespace {
|
|
|
|
|
2011-04-27 18:07:02 +00:00
|
|
|
using namespace base;
|
2011-06-20 21:47:58 +00:00
|
|
|
using namespace mozilla;
|
|
|
|
|
|
|
|
class TelemetryImpl : public nsITelemetry
|
2011-04-27 18:07:02 +00:00
|
|
|
{
|
|
|
|
NS_DECL_ISUPPORTS
|
|
|
|
NS_DECL_NSITELEMETRY
|
|
|
|
|
2011-05-20 02:33:05 +00:00
|
|
|
public:
|
2011-06-20 21:48:03 +00:00
|
|
|
TelemetryImpl();
|
|
|
|
~TelemetryImpl();
|
2011-06-28 23:57:44 +00:00
|
|
|
|
|
|
|
static bool CanRecord();
|
|
|
|
static already_AddRefed<nsITelemetry> CreateTelemetryInstance();
|
|
|
|
static void ShutdownTelemetry();
|
2011-12-06 20:12:55 +00:00
|
|
|
static void RecordSlowStatement(const nsACString &statement,
|
|
|
|
const nsACString &dbName,
|
|
|
|
PRUint32 delay);
|
2012-01-09 20:42:34 +00:00
|
|
|
static nsresult GetHistogramEnumId(const char *name, Telemetry::ID &id);
|
2011-12-06 20:12:55 +00:00
|
|
|
struct StmtStats {
|
|
|
|
PRUint32 hitCount;
|
|
|
|
PRUint32 totalTime;
|
|
|
|
};
|
|
|
|
typedef nsBaseHashtableET<nsCStringHashKey, StmtStats> SlowSQLEntryType;
|
2011-06-20 21:47:58 +00:00
|
|
|
|
|
|
|
private:
|
2011-12-14 20:04:25 +00:00
|
|
|
bool AddSQLInfo(JSContext *cx, JSObject *rootObj, bool mainThread);
|
2011-12-06 20:12:55 +00:00
|
|
|
|
2012-01-06 19:39:29 +00:00
|
|
|
// Like GetHistogramById, but returns the underlying C++ object, not the JS one.
|
|
|
|
nsresult GetHistogramByName(const nsACString &name, Histogram **ret);
|
2011-06-20 21:48:03 +00:00
|
|
|
// This is used for speedy JS string->Telemetry::ID conversions
|
|
|
|
typedef nsBaseHashtableET<nsCharPtrHashKey, Telemetry::ID> CharPtrEntryType;
|
|
|
|
typedef nsTHashtable<CharPtrEntryType> HistogramMapType;
|
|
|
|
HistogramMapType mHistogramMap;
|
2011-06-28 23:57:44 +00:00
|
|
|
bool mCanRecord;
|
|
|
|
static TelemetryImpl *sTelemetry;
|
2011-12-06 20:12:55 +00:00
|
|
|
nsTHashtable<SlowSQLEntryType> mSlowSQLOnMainThread;
|
|
|
|
nsTHashtable<SlowSQLEntryType> mSlowSQLOnOtherThread;
|
|
|
|
nsTHashtable<nsCStringHashKey> mTrackedDBs;
|
|
|
|
Mutex mHashMutex;
|
2011-04-27 18:07:02 +00:00
|
|
|
};
|
|
|
|
|
2011-06-28 23:57:44 +00:00
|
|
|
TelemetryImpl* TelemetryImpl::sTelemetry = NULL;
|
|
|
|
|
2011-05-20 02:33:05 +00:00
|
|
|
// A initializer to initialize histogram collection
|
|
|
|
StatisticsRecorder gStatisticsRecorder;
|
2011-04-27 18:07:02 +00:00
|
|
|
|
2011-06-20 21:47:58 +00:00
|
|
|
// Hardcoded probes
|
|
|
|
struct TelemetryHistogram {
|
|
|
|
const char *id;
|
|
|
|
PRUint32 min;
|
|
|
|
PRUint32 max;
|
|
|
|
PRUint32 bucketCount;
|
|
|
|
PRUint32 histogramType;
|
2012-01-06 19:40:45 +00:00
|
|
|
const char *comment;
|
2011-06-20 21:47:58 +00:00
|
|
|
};
|
|
|
|
|
2011-12-16 19:30:44 +00:00
|
|
|
// Perform the checks at the beginning of HistogramGet at compile time, so
|
|
|
|
// that if people add incorrect histogram definitions, they get compiler
|
|
|
|
// errors.
|
|
|
|
#define HISTOGRAM(id, min, max, bucket_count, histogram_type, b) \
|
|
|
|
PR_STATIC_ASSERT(nsITelemetry::HISTOGRAM_ ## histogram_type == nsITelemetry::HISTOGRAM_BOOLEAN || \
|
|
|
|
(min < max && bucket_count > 2 && min >= 1));
|
|
|
|
|
|
|
|
#include "TelemetryHistograms.h"
|
|
|
|
|
|
|
|
#undef HISTOGRAM
|
|
|
|
|
2011-06-20 21:47:58 +00:00
|
|
|
const TelemetryHistogram gHistograms[] = {
|
2012-01-06 19:40:45 +00:00
|
|
|
#define HISTOGRAM(id, min, max, bucket_count, histogram_type, comment) \
|
2012-01-09 17:47:35 +00:00
|
|
|
{ NS_STRINGIFY(id), min, max, bucket_count, \
|
2012-01-06 19:40:45 +00:00
|
|
|
nsITelemetry::HISTOGRAM_ ## histogram_type, comment },
|
2011-06-20 21:47:58 +00:00
|
|
|
|
|
|
|
#include "TelemetryHistograms.h"
|
|
|
|
|
|
|
|
#undef HISTOGRAM
|
|
|
|
};
|
|
|
|
|
2012-01-06 19:40:04 +00:00
|
|
|
bool
|
|
|
|
TelemetryHistogramType(Histogram *h, PRUint32 *result)
|
|
|
|
{
|
|
|
|
switch (h->histogram_type()) {
|
|
|
|
case Histogram::HISTOGRAM:
|
|
|
|
*result = nsITelemetry::HISTOGRAM_EXPONENTIAL;
|
|
|
|
break;
|
|
|
|
case Histogram::LINEAR_HISTOGRAM:
|
|
|
|
*result = nsITelemetry::HISTOGRAM_LINEAR;
|
|
|
|
break;
|
|
|
|
case Histogram::BOOLEAN_HISTOGRAM:
|
|
|
|
*result = nsITelemetry::HISTOGRAM_BOOLEAN;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2011-06-20 21:47:58 +00:00
|
|
|
nsresult
|
|
|
|
HistogramGet(const char *name, PRUint32 min, PRUint32 max, PRUint32 bucketCount,
|
|
|
|
PRUint32 histogramType, Histogram **result)
|
|
|
|
{
|
|
|
|
if (histogramType != nsITelemetry::HISTOGRAM_BOOLEAN) {
|
|
|
|
// Sanity checks for histogram parameters.
|
|
|
|
if (min >= max)
|
|
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
|
|
|
|
|
|
if (bucketCount <= 2)
|
|
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
|
|
|
|
|
|
if (min < 1)
|
|
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (histogramType) {
|
|
|
|
case nsITelemetry::HISTOGRAM_EXPONENTIAL:
|
2011-06-30 21:58:00 +00:00
|
|
|
*result = Histogram::FactoryGet(name, min, max, bucketCount, Histogram::kUmaTargetedHistogramFlag);
|
2011-06-20 21:47:58 +00:00
|
|
|
break;
|
|
|
|
case nsITelemetry::HISTOGRAM_LINEAR:
|
2011-06-30 21:58:00 +00:00
|
|
|
*result = LinearHistogram::FactoryGet(name, min, max, bucketCount, Histogram::kUmaTargetedHistogramFlag);
|
2011-06-20 21:47:58 +00:00
|
|
|
break;
|
|
|
|
case nsITelemetry::HISTOGRAM_BOOLEAN:
|
2011-06-30 21:58:00 +00:00
|
|
|
*result = BooleanHistogram::FactoryGet(name, Histogram::kUmaTargetedHistogramFlag);
|
2011-06-20 21:47:58 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return NS_ERROR_INVALID_ARG;
|
|
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
// O(1) histogram lookup by numeric id
|
|
|
|
nsresult
|
|
|
|
GetHistogramByEnumId(Telemetry::ID id, Histogram **ret)
|
|
|
|
{
|
|
|
|
static Histogram* knownHistograms[Telemetry::HistogramCount] = {0};
|
|
|
|
Histogram *h = knownHistograms[id];
|
|
|
|
if (h) {
|
|
|
|
*ret = h;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
const TelemetryHistogram &p = gHistograms[id];
|
2011-06-20 21:48:00 +00:00
|
|
|
nsresult rv = HistogramGet(p.id, p.min, p.max, p.bucketCount, p.histogramType, &h);
|
2011-06-20 21:47:58 +00:00
|
|
|
if (NS_FAILED(rv))
|
2011-08-05 13:53:48 +00:00
|
|
|
return rv;
|
2011-06-20 21:47:58 +00:00
|
|
|
|
|
|
|
*ret = knownHistograms[id] = h;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2011-05-20 02:33:05 +00:00
|
|
|
bool
|
2011-04-27 18:07:02 +00:00
|
|
|
FillRanges(JSContext *cx, JSObject *array, Histogram *h)
|
|
|
|
{
|
2011-06-20 21:47:58 +00:00
|
|
|
for (size_t i = 0; i < h->bucket_count(); i++) {
|
2011-04-27 18:07:02 +00:00
|
|
|
if (!JS_DefineElement(cx, array, i, INT_TO_JSVAL(h->ranges(i)), NULL, NULL, JSPROP_ENUMERATE))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2011-05-20 02:33:05 +00:00
|
|
|
JSBool
|
2011-04-27 18:07:02 +00:00
|
|
|
ReflectHistogramSnapshot(JSContext *cx, JSObject *obj, Histogram *h)
|
|
|
|
{
|
|
|
|
Histogram::SampleSet ss;
|
|
|
|
h->SnapshotSample(&ss);
|
|
|
|
JSObject *counts_array;
|
|
|
|
JSObject *rarray;
|
|
|
|
const size_t count = h->bucket_count();
|
|
|
|
if (!(JS_DefineProperty(cx, obj, "min", INT_TO_JSVAL(h->declared_min()), NULL, NULL, JSPROP_ENUMERATE)
|
|
|
|
&& JS_DefineProperty(cx, obj, "max", INT_TO_JSVAL(h->declared_max()), NULL, NULL, JSPROP_ENUMERATE)
|
|
|
|
&& JS_DefineProperty(cx, obj, "histogram_type", INT_TO_JSVAL(h->histogram_type()), NULL, NULL, JSPROP_ENUMERATE)
|
2011-08-01 20:17:47 +00:00
|
|
|
&& JS_DefineProperty(cx, obj, "sum", DOUBLE_TO_JSVAL(ss.sum()), NULL, NULL, JSPROP_ENUMERATE)
|
2011-04-27 18:07:02 +00:00
|
|
|
&& (rarray = JS_NewArrayObject(cx, count, NULL))
|
|
|
|
&& JS_DefineProperty(cx, obj, "ranges", OBJECT_TO_JSVAL(rarray), NULL, NULL, JSPROP_ENUMERATE)
|
|
|
|
&& FillRanges(cx, rarray, h)
|
|
|
|
&& (counts_array = JS_NewArrayObject(cx, count, NULL))
|
|
|
|
&& JS_DefineProperty(cx, obj, "counts", OBJECT_TO_JSVAL(counts_array), NULL, NULL, JSPROP_ENUMERATE)
|
|
|
|
)) {
|
|
|
|
return JS_FALSE;
|
|
|
|
}
|
2011-06-20 21:47:58 +00:00
|
|
|
for (size_t i = 0; i < count; i++) {
|
2011-04-27 18:07:02 +00:00
|
|
|
if (!JS_DefineElement(cx, counts_array, i, INT_TO_JSVAL(ss.counts(i)), NULL, NULL, JSPROP_ENUMERATE)) {
|
|
|
|
return JS_FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
2011-05-20 02:33:05 +00:00
|
|
|
JSBool
|
2011-04-27 18:07:02 +00:00
|
|
|
JSHistogram_Add(JSContext *cx, uintN argc, jsval *vp)
|
|
|
|
{
|
2011-06-28 23:54:33 +00:00
|
|
|
if (!argc) {
|
|
|
|
JS_ReportError(cx, "Expected one argument");
|
2011-04-27 18:07:02 +00:00
|
|
|
return JS_FALSE;
|
2011-06-28 23:54:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
jsval v = JS_ARGV(cx, vp)[0];
|
|
|
|
int32 value;
|
|
|
|
|
|
|
|
if (!(JSVAL_IS_NUMBER(v) || JSVAL_IS_BOOLEAN(v))) {
|
|
|
|
JS_ReportError(cx, "Not a number");
|
|
|
|
return JS_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!JS_ValueToECMAInt32(cx, v, &value)) {
|
2011-04-27 18:07:02 +00:00
|
|
|
return JS_FALSE;
|
2011-06-28 23:54:33 +00:00
|
|
|
}
|
|
|
|
|
2011-06-28 23:57:44 +00:00
|
|
|
if (TelemetryImpl::CanRecord()) {
|
|
|
|
JSObject *obj = JS_THIS_OBJECT(cx, vp);
|
2011-12-01 21:30:28 +00:00
|
|
|
if (!obj) {
|
|
|
|
return JS_FALSE;
|
|
|
|
}
|
|
|
|
|
2011-06-28 23:57:44 +00:00
|
|
|
Histogram *h = static_cast<Histogram*>(JS_GetPrivate(cx, obj));
|
|
|
|
if (h->histogram_type() == Histogram::BOOLEAN_HISTOGRAM)
|
|
|
|
h->Add(!!value);
|
|
|
|
else
|
|
|
|
h->Add(value);
|
|
|
|
}
|
2011-04-27 18:07:02 +00:00
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
2011-05-20 02:33:05 +00:00
|
|
|
JSBool
|
2011-04-27 18:07:02 +00:00
|
|
|
JSHistogram_Snapshot(JSContext *cx, uintN argc, jsval *vp)
|
|
|
|
{
|
|
|
|
JSObject *obj = JS_THIS_OBJECT(cx, vp);
|
2011-12-01 21:30:28 +00:00
|
|
|
if (!obj) {
|
|
|
|
return JS_FALSE;
|
|
|
|
}
|
|
|
|
|
2011-04-27 18:07:02 +00:00
|
|
|
Histogram *h = static_cast<Histogram*>(JS_GetPrivate(cx, obj));
|
|
|
|
JSObject *snapshot = JS_NewObject(cx, NULL, NULL, NULL);
|
|
|
|
if (!snapshot)
|
Bug 671185 - Incorrect return of NS_ERROR_* codes in functions returning PRBool, r=mak,ehsan,taras,biesi,pike,khuey,dholbert,josh,bjacob,bsmith
2011-07-26 04:57:58 +00:00
|
|
|
return JS_FALSE;
|
2011-04-27 18:07:02 +00:00
|
|
|
JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(snapshot));
|
|
|
|
return ReflectHistogramSnapshot(cx, snapshot, h);
|
|
|
|
}
|
|
|
|
|
2011-05-20 02:33:05 +00:00
|
|
|
nsresult
|
2011-04-27 18:07:02 +00:00
|
|
|
WrapAndReturnHistogram(Histogram *h, JSContext *cx, jsval *ret)
|
|
|
|
{
|
|
|
|
static JSClass JSHistogram_class = {
|
|
|
|
"JSHistogram", /* name */
|
|
|
|
JSCLASS_HAS_PRIVATE, /* flags */
|
|
|
|
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
|
|
|
|
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
|
|
|
|
JSCLASS_NO_OPTIONAL_MEMBERS
|
|
|
|
};
|
|
|
|
|
|
|
|
JSObject *obj = JS_NewObject(cx, &JSHistogram_class, NULL, NULL);
|
|
|
|
if (!obj)
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
*ret = OBJECT_TO_JSVAL(obj);
|
|
|
|
return (JS_SetPrivate(cx, obj, h)
|
|
|
|
&& JS_DefineFunction (cx, obj, "add", JSHistogram_Add, 1, 0)
|
|
|
|
&& JS_DefineFunction (cx, obj, "snapshot", JSHistogram_Snapshot, 1, 0)) ? NS_OK : NS_ERROR_FAILURE;
|
|
|
|
}
|
2011-06-20 21:47:58 +00:00
|
|
|
|
2011-06-28 23:57:44 +00:00
|
|
|
TelemetryImpl::TelemetryImpl():
|
2011-12-06 20:12:55 +00:00
|
|
|
mCanRecord(XRE_GetProcessType() == GeckoProcessType_Default),
|
|
|
|
mHashMutex("Telemetry::mHashMutex")
|
2011-06-28 23:57:44 +00:00
|
|
|
{
|
2011-12-06 20:12:55 +00:00
|
|
|
// A whitelist to prevent Telemetry reporting on Addon & Thunderbird DBs
|
|
|
|
const char *trackedDBs[] = {
|
|
|
|
"addons.sqlite", "chromeappsstore.sqlite", "content-prefs.sqlite",
|
|
|
|
"cookies.sqlite", "downloads.sqlite", "extensions.sqlite",
|
|
|
|
"formhistory.sqlite", "index.sqlite", "permissions.sqlite", "places.sqlite",
|
|
|
|
"search.sqlite", "signons.sqlite", "urlclassifier3.sqlite",
|
|
|
|
"webappsstore.sqlite"
|
|
|
|
};
|
|
|
|
|
|
|
|
mTrackedDBs.Init();
|
2012-01-09 18:01:52 +00:00
|
|
|
for (size_t i = 0; i < ArrayLength(trackedDBs); i++)
|
2011-12-06 20:12:55 +00:00
|
|
|
mTrackedDBs.PutEntry(nsDependentCString(trackedDBs[i]));
|
|
|
|
|
2012-01-10 20:31:34 +00:00
|
|
|
#ifdef DEBUG
|
|
|
|
// Mark immutable to prevent asserts on simultaneous access from multiple threads
|
|
|
|
mTrackedDBs.MarkImmutable();
|
|
|
|
#endif
|
|
|
|
|
2011-12-06 20:12:55 +00:00
|
|
|
mSlowSQLOnMainThread.Init();
|
|
|
|
mSlowSQLOnOtherThread.Init();
|
2011-06-20 21:48:03 +00:00
|
|
|
mHistogramMap.Init(Telemetry::HistogramCount);
|
|
|
|
}
|
|
|
|
|
|
|
|
TelemetryImpl::~TelemetryImpl() {
|
2011-12-06 20:12:55 +00:00
|
|
|
mSlowSQLOnMainThread.Clear();
|
|
|
|
mSlowSQLOnOtherThread.Clear();
|
2011-06-20 21:48:03 +00:00
|
|
|
mHistogramMap.Clear();
|
|
|
|
}
|
|
|
|
|
2011-04-27 18:07:02 +00:00
|
|
|
NS_IMETHODIMP
|
2011-06-20 21:47:58 +00:00
|
|
|
TelemetryImpl::NewHistogram(const nsACString &name, PRUint32 min, PRUint32 max, PRUint32 bucketCount, PRUint32 histogramType, JSContext *cx, jsval *ret)
|
2011-04-27 18:07:02 +00:00
|
|
|
{
|
2011-05-20 02:33:05 +00:00
|
|
|
Histogram *h;
|
2011-06-20 21:47:58 +00:00
|
|
|
nsresult rv = HistogramGet(PromiseFlatCString(name).get(), min, max, bucketCount, histogramType, &h);
|
|
|
|
if (NS_FAILED(rv))
|
|
|
|
return rv;
|
2011-06-30 21:58:00 +00:00
|
|
|
h->ClearFlags(Histogram::kUmaTargetedHistogramFlag);
|
2011-04-27 18:07:02 +00:00
|
|
|
return WrapAndReturnHistogram(h, cx, ret);
|
|
|
|
}
|
|
|
|
|
2011-12-06 20:12:55 +00:00
|
|
|
struct EnumeratorArgs {
|
|
|
|
JSContext *cx;
|
|
|
|
JSObject *statsObj;
|
|
|
|
};
|
|
|
|
|
|
|
|
PLDHashOperator
|
|
|
|
StatementEnumerator(TelemetryImpl::SlowSQLEntryType *entry, void *arg)
|
|
|
|
{
|
|
|
|
EnumeratorArgs *args = static_cast<EnumeratorArgs *>(arg);
|
2011-12-14 20:04:25 +00:00
|
|
|
const nsACString &sql = entry->GetKey();
|
2011-12-06 20:12:55 +00:00
|
|
|
jsval hitCount = UINT_TO_JSVAL(entry->mData.hitCount);
|
|
|
|
jsval totalTime = UINT_TO_JSVAL(entry->mData.totalTime);
|
|
|
|
|
2011-12-14 20:04:25 +00:00
|
|
|
JSObject *arrayObj = JS_NewArrayObject(args->cx, 2, nsnull);
|
|
|
|
if (!arrayObj ||
|
|
|
|
!JS_SetElement(args->cx, arrayObj, 0, &hitCount) ||
|
|
|
|
!JS_SetElement(args->cx, arrayObj, 1, &totalTime))
|
2011-12-06 20:12:55 +00:00
|
|
|
return PL_DHASH_STOP;
|
|
|
|
|
2011-12-14 20:04:25 +00:00
|
|
|
JSBool success = JS_DefineProperty(args->cx, args->statsObj,
|
|
|
|
sql.BeginReading(),
|
|
|
|
OBJECT_TO_JSVAL(arrayObj),
|
|
|
|
NULL, NULL, JSPROP_ENUMERATE);
|
2011-12-06 20:12:55 +00:00
|
|
|
if (!success)
|
|
|
|
return PL_DHASH_STOP;
|
|
|
|
|
|
|
|
return PL_DHASH_NEXT;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2011-12-14 20:04:25 +00:00
|
|
|
TelemetryImpl::AddSQLInfo(JSContext *cx, JSObject *rootObj, bool mainThread)
|
2011-12-06 20:12:55 +00:00
|
|
|
{
|
|
|
|
JSObject *statsObj = JS_NewObject(cx, NULL, NULL, NULL);
|
2011-12-14 20:04:25 +00:00
|
|
|
if (!statsObj)
|
2011-12-06 20:12:55 +00:00
|
|
|
return false;
|
|
|
|
|
|
|
|
JSBool ok = JS_DefineProperty(cx, rootObj,
|
2011-12-14 20:04:25 +00:00
|
|
|
mainThread ? "mainThread" : "otherThreads",
|
|
|
|
OBJECT_TO_JSVAL(statsObj),
|
2011-12-06 20:12:55 +00:00
|
|
|
NULL, NULL, JSPROP_ENUMERATE);
|
|
|
|
if (!ok)
|
|
|
|
return false;
|
|
|
|
|
2011-12-14 20:04:25 +00:00
|
|
|
EnumeratorArgs args = { cx, statsObj };
|
2011-12-06 20:12:55 +00:00
|
|
|
nsTHashtable<SlowSQLEntryType> *sqlMap;
|
|
|
|
sqlMap = (mainThread ? &mSlowSQLOnMainThread : &mSlowSQLOnOtherThread);
|
|
|
|
PRUint32 num = sqlMap->EnumerateEntries(StatementEnumerator,
|
|
|
|
static_cast<void*>(&args));
|
|
|
|
if (num != sqlMap->Count())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-01-06 19:39:29 +00:00
|
|
|
nsresult
|
2012-01-09 20:42:34 +00:00
|
|
|
TelemetryImpl::GetHistogramEnumId(const char *name, Telemetry::ID *id)
|
2012-01-06 19:39:29 +00:00
|
|
|
{
|
2012-01-09 20:42:34 +00:00
|
|
|
if (!sTelemetry) {
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
|
2012-01-06 19:39:29 +00:00
|
|
|
// Cache names
|
|
|
|
// Note the histogram names are statically allocated
|
2012-01-09 20:42:34 +00:00
|
|
|
TelemetryImpl::HistogramMapType *map = &sTelemetry->mHistogramMap;
|
|
|
|
if (!map->Count()) {
|
2012-01-06 19:39:29 +00:00
|
|
|
for (PRUint32 i = 0; i < Telemetry::HistogramCount; i++) {
|
2012-01-09 20:42:34 +00:00
|
|
|
CharPtrEntryType *entry = map->PutEntry(gHistograms[i].id);
|
2012-01-06 19:39:29 +00:00
|
|
|
if (NS_UNLIKELY(!entry)) {
|
2012-01-09 20:42:34 +00:00
|
|
|
map->Clear();
|
2012-01-06 19:39:29 +00:00
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
}
|
|
|
|
entry->mData = (Telemetry::ID) i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-01-09 20:42:34 +00:00
|
|
|
CharPtrEntryType *entry = map->GetEntry(name);
|
|
|
|
// A histogram might not be in our static list of names (e.g. secret
|
|
|
|
// histograms maintained by Histogram itself or addon histograms).
|
|
|
|
// Use a separate error code to help the caller.
|
|
|
|
if (!entry) {
|
|
|
|
return NS_ERROR_INVALID_ARG;
|
|
|
|
}
|
|
|
|
*id = entry->mData;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
TelemetryImpl::GetHistogramByName(const nsACString &name, Histogram **ret)
|
|
|
|
{
|
|
|
|
Telemetry::ID id;
|
|
|
|
nsresult rv = GetHistogramEnumId(PromiseFlatCString(name).get(), &id);
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
return rv;
|
|
|
|
}
|
2012-01-06 19:39:29 +00:00
|
|
|
|
2012-01-09 20:42:34 +00:00
|
|
|
rv = GetHistogramByEnumId(id, ret);
|
2012-01-06 19:39:29 +00:00
|
|
|
if (NS_FAILED(rv))
|
|
|
|
return rv;
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2012-01-06 19:40:04 +00:00
|
|
|
NS_IMETHODIMP
|
|
|
|
TelemetryImpl::HistogramFrom(const nsACString &name, const nsACString &existing_name,
|
|
|
|
JSContext *cx, jsval *ret)
|
|
|
|
{
|
|
|
|
Histogram *existing;
|
|
|
|
nsresult rv = GetHistogramByName(existing_name, &existing);
|
|
|
|
if (NS_FAILED(rv))
|
|
|
|
return rv;
|
|
|
|
|
|
|
|
PRUint32 histogramType;
|
|
|
|
bool success = TelemetryHistogramType(existing, &histogramType);
|
|
|
|
if (!success)
|
|
|
|
return NS_ERROR_INVALID_ARG;
|
|
|
|
|
|
|
|
Histogram *clone;
|
|
|
|
rv = HistogramGet(PromiseFlatCString(name).get(), existing->declared_min(),
|
|
|
|
existing->declared_max(), existing->bucket_count(),
|
|
|
|
histogramType, &clone);
|
|
|
|
if (NS_FAILED(rv))
|
|
|
|
return rv;
|
|
|
|
|
|
|
|
Histogram::SampleSet ss;
|
|
|
|
existing->SnapshotSample(&ss);
|
|
|
|
clone->AddSampleSet(ss);
|
|
|
|
return WrapAndReturnHistogram(clone, cx, ret);
|
|
|
|
}
|
|
|
|
|
2011-04-27 18:07:02 +00:00
|
|
|
NS_IMETHODIMP
|
2011-06-20 21:47:58 +00:00
|
|
|
TelemetryImpl::GetHistogramSnapshots(JSContext *cx, jsval *ret)
|
2011-04-27 18:07:02 +00:00
|
|
|
{
|
|
|
|
JSObject *root_obj = JS_NewObject(cx, NULL, NULL, NULL);
|
|
|
|
if (!root_obj)
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
*ret = OBJECT_TO_JSVAL(root_obj);
|
|
|
|
|
|
|
|
StatisticsRecorder::Histograms h;
|
|
|
|
StatisticsRecorder::GetHistograms(&h);
|
|
|
|
for (StatisticsRecorder::Histograms::iterator it = h.begin(); it != h.end();++it) {
|
|
|
|
Histogram *h = *it;
|
|
|
|
JSObject *hobj = JS_NewObject(cx, NULL, NULL, NULL);
|
|
|
|
if (!(hobj
|
|
|
|
&& JS_DefineProperty(cx, root_obj, h->histogram_name().c_str(),
|
|
|
|
OBJECT_TO_JSVAL(hobj), NULL, NULL, JSPROP_ENUMERATE)
|
|
|
|
&& ReflectHistogramSnapshot(cx, hobj, h))) {
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
}
|
2011-12-14 20:04:25 +00:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
TelemetryImpl::GetSlowSQL(JSContext *cx, jsval *ret)
|
|
|
|
{
|
|
|
|
JSObject *root_obj = JS_NewObject(cx, NULL, NULL, NULL);
|
|
|
|
if (!root_obj)
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
*ret = OBJECT_TO_JSVAL(root_obj);
|
2011-12-06 20:12:55 +00:00
|
|
|
|
|
|
|
MutexAutoLock hashMutex(mHashMutex);
|
|
|
|
// Add info about slow SQL queries on the main thread
|
2011-12-14 20:04:25 +00:00
|
|
|
if (!AddSQLInfo(cx, root_obj, true))
|
2011-12-06 20:12:55 +00:00
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
// Add info about slow SQL queries on other threads
|
2011-12-14 20:04:25 +00:00
|
|
|
if (!AddSQLInfo(cx, root_obj, false))
|
2011-12-06 20:12:55 +00:00
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
2011-04-27 18:07:02 +00:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2012-01-06 19:40:45 +00:00
|
|
|
NS_IMETHODIMP
|
|
|
|
TelemetryImpl::GetRegisteredHistograms(JSContext *cx, jsval *ret)
|
|
|
|
{
|
|
|
|
size_t count = ArrayLength(gHistograms);
|
|
|
|
JSObject *info = JS_NewObject(cx, NULL, NULL, NULL);
|
|
|
|
if (!info)
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
for (size_t i = 0; i < count; ++i) {
|
|
|
|
JSString *comment = JS_InternString(cx, gHistograms[i].comment);
|
|
|
|
|
|
|
|
if (!(comment
|
|
|
|
&& JS_DefineProperty(cx, info, gHistograms[i].id,
|
|
|
|
STRING_TO_JSVAL(comment), NULL, NULL,
|
|
|
|
JSPROP_ENUMERATE))) {
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
*ret = OBJECT_TO_JSVAL(info);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2011-11-16 20:33:18 +00:00
|
|
|
NS_IMETHODIMP
|
2011-12-03 09:13:14 +00:00
|
|
|
TelemetryImpl::GetHistogramById(const nsACString &name, JSContext *cx, jsval *ret)
|
2011-11-16 20:33:18 +00:00
|
|
|
{
|
2011-06-20 21:47:58 +00:00
|
|
|
Histogram *h;
|
2012-01-06 19:39:29 +00:00
|
|
|
nsresult rv = GetHistogramByName(name, &h);
|
2011-06-20 21:47:58 +00:00
|
|
|
if (NS_FAILED(rv))
|
|
|
|
return rv;
|
|
|
|
|
|
|
|
return WrapAndReturnHistogram(h, cx, ret);
|
2011-04-27 18:07:02 +00:00
|
|
|
}
|
|
|
|
|
2011-06-28 23:57:44 +00:00
|
|
|
NS_IMETHODIMP
|
2011-09-29 06:19:26 +00:00
|
|
|
TelemetryImpl::GetCanRecord(bool *ret) {
|
2011-06-28 23:57:44 +00:00
|
|
|
*ret = mCanRecord;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
2011-09-29 06:19:26 +00:00
|
|
|
TelemetryImpl::SetCanRecord(bool canRecord) {
|
2011-06-28 23:57:44 +00:00
|
|
|
mCanRecord = !!canRecord;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
TelemetryImpl::CanRecord() {
|
|
|
|
return !sTelemetry || sTelemetry->mCanRecord;
|
|
|
|
}
|
2011-06-20 21:47:58 +00:00
|
|
|
|
|
|
|
already_AddRefed<nsITelemetry>
|
2011-06-28 23:57:44 +00:00
|
|
|
TelemetryImpl::CreateTelemetryInstance()
|
|
|
|
{
|
|
|
|
NS_ABORT_IF_FALSE(sTelemetry == NULL, "CreateTelemetryInstance may only be called once, via GetService()");
|
|
|
|
sTelemetry = new TelemetryImpl();
|
|
|
|
// AddRef for the local reference
|
|
|
|
NS_ADDREF(sTelemetry);
|
|
|
|
// AddRef for the caller
|
|
|
|
NS_ADDREF(sTelemetry);
|
|
|
|
return sTelemetry;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
TelemetryImpl::ShutdownTelemetry()
|
2011-04-27 18:07:02 +00:00
|
|
|
{
|
2011-06-28 23:57:44 +00:00
|
|
|
NS_IF_RELEASE(sTelemetry);
|
2011-04-27 18:07:02 +00:00
|
|
|
}
|
|
|
|
|
2011-12-06 20:12:55 +00:00
|
|
|
void
|
|
|
|
TelemetryImpl::RecordSlowStatement(const nsACString &statement,
|
|
|
|
const nsACString &dbName,
|
|
|
|
PRUint32 delay)
|
|
|
|
{
|
2012-01-10 20:29:58 +00:00
|
|
|
MOZ_ASSERT(sTelemetry);
|
2011-12-06 20:12:55 +00:00
|
|
|
if (!sTelemetry->mCanRecord || !sTelemetry->mTrackedDBs.GetEntry(dbName))
|
|
|
|
return;
|
|
|
|
|
|
|
|
nsTHashtable<SlowSQLEntryType> *slowSQLMap = NULL;
|
|
|
|
if (NS_IsMainThread())
|
|
|
|
slowSQLMap = &(sTelemetry->mSlowSQLOnMainThread);
|
|
|
|
else
|
|
|
|
slowSQLMap = &(sTelemetry->mSlowSQLOnOtherThread);
|
|
|
|
|
|
|
|
MutexAutoLock hashMutex(sTelemetry->mHashMutex);
|
|
|
|
SlowSQLEntryType *entry = slowSQLMap->GetEntry(statement);
|
|
|
|
if (!entry) {
|
|
|
|
entry = slowSQLMap->PutEntry(statement);
|
|
|
|
if (NS_UNLIKELY(!entry))
|
|
|
|
return;
|
|
|
|
entry->mData.hitCount = 0;
|
|
|
|
entry->mData.totalTime = 0;
|
|
|
|
}
|
|
|
|
entry->mData.hitCount++;
|
|
|
|
entry->mData.totalTime += delay;
|
|
|
|
}
|
|
|
|
|
2011-06-28 23:57:44 +00:00
|
|
|
NS_IMPL_THREADSAFE_ISUPPORTS1(TelemetryImpl, nsITelemetry)
|
|
|
|
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsITelemetry, TelemetryImpl::CreateTelemetryInstance)
|
2011-04-27 18:07:02 +00:00
|
|
|
|
|
|
|
#define NS_TELEMETRY_CID \
|
2011-05-20 02:33:05 +00:00
|
|
|
{0xaea477f2, 0xb3a2, 0x469c, {0xaa, 0x29, 0x0a, 0x82, 0xd1, 0x32, 0xb8, 0x29}}
|
2011-04-27 18:07:02 +00:00
|
|
|
NS_DEFINE_NAMED_CID(NS_TELEMETRY_CID);
|
|
|
|
|
2011-06-20 21:47:58 +00:00
|
|
|
const Module::CIDEntry kTelemetryCIDs[] = {
|
|
|
|
{ &kNS_TELEMETRY_CID, false, NULL, nsITelemetryConstructor },
|
2011-04-27 18:07:02 +00:00
|
|
|
{ NULL }
|
|
|
|
};
|
|
|
|
|
2011-06-20 21:47:58 +00:00
|
|
|
const Module::ContractIDEntry kTelemetryContracts[] = {
|
2011-04-27 18:07:02 +00:00
|
|
|
{ "@mozilla.org/base/telemetry;1", &kNS_TELEMETRY_CID },
|
|
|
|
{ NULL }
|
|
|
|
};
|
|
|
|
|
2011-06-20 21:47:58 +00:00
|
|
|
const Module kTelemetryModule = {
|
|
|
|
Module::kVersion,
|
2011-04-27 18:07:02 +00:00
|
|
|
kTelemetryCIDs,
|
|
|
|
kTelemetryContracts,
|
|
|
|
NULL,
|
|
|
|
NULL,
|
|
|
|
NULL,
|
2011-06-28 23:57:44 +00:00
|
|
|
TelemetryImpl::ShutdownTelemetry
|
2011-04-27 18:07:02 +00:00
|
|
|
};
|
|
|
|
|
2011-05-20 02:33:05 +00:00
|
|
|
} // anonymous namespace
|
|
|
|
|
2011-06-20 21:47:58 +00:00
|
|
|
namespace mozilla {
|
|
|
|
namespace Telemetry {
|
|
|
|
|
|
|
|
void
|
|
|
|
Accumulate(ID aHistogram, PRUint32 aSample)
|
|
|
|
{
|
2011-06-28 23:57:44 +00:00
|
|
|
if (!TelemetryImpl::CanRecord()) {
|
|
|
|
return;
|
|
|
|
}
|
2011-06-20 21:47:58 +00:00
|
|
|
Histogram *h;
|
|
|
|
nsresult rv = GetHistogramByEnumId(aHistogram, &h);
|
|
|
|
if (NS_SUCCEEDED(rv))
|
|
|
|
h->Add(aSample);
|
|
|
|
}
|
|
|
|
|
2011-10-10 17:04:57 +00:00
|
|
|
void
|
|
|
|
AccumulateTimeDelta(ID aHistogram, TimeStamp start, TimeStamp end)
|
|
|
|
{
|
|
|
|
Accumulate(aHistogram,
|
|
|
|
static_cast<PRUint32>((end - start).ToMilliseconds()));
|
|
|
|
}
|
|
|
|
|
2011-08-05 13:53:48 +00:00
|
|
|
base::Histogram*
|
|
|
|
GetHistogramById(ID id)
|
|
|
|
{
|
|
|
|
Histogram *h = NULL;
|
|
|
|
GetHistogramByEnumId(id, &h);
|
|
|
|
return h;
|
|
|
|
}
|
|
|
|
|
2011-12-06 20:12:55 +00:00
|
|
|
void
|
|
|
|
RecordSlowSQLStatement(const nsACString &statement,
|
|
|
|
const nsACString &dbName,
|
|
|
|
PRUint32 delay)
|
|
|
|
{
|
|
|
|
TelemetryImpl::RecordSlowStatement(statement, dbName, delay);
|
|
|
|
}
|
|
|
|
|
2012-01-10 20:29:58 +00:00
|
|
|
void Init()
|
|
|
|
{
|
|
|
|
// Make the service manager hold a long-lived reference to the service
|
|
|
|
nsCOMPtr<nsITelemetry> telemetryService =
|
|
|
|
do_GetService("@mozilla.org/base/telemetry;1");
|
|
|
|
MOZ_ASSERT(telemetryService);
|
|
|
|
}
|
|
|
|
|
2011-06-20 21:47:58 +00:00
|
|
|
} // namespace Telemetry
|
|
|
|
} // namespace mozilla
|
|
|
|
|
2011-04-27 18:07:02 +00:00
|
|
|
NSMODULE_DEFN(nsTelemetryModule) = &kTelemetryModule;
|
2011-05-22 06:23:20 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* The XRE_TelemetryAdd function is to be used by embedding applications
|
|
|
|
* that can't use mozilla::Telemetry::Accumulate() directly.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
XRE_TelemetryAccumulate(int aID, PRUint32 aSample)
|
|
|
|
{
|
|
|
|
mozilla::Telemetry::Accumulate((mozilla::Telemetry::ID) aID, aSample);
|
|
|
|
}
|