diff --git a/toolkit/library/nsStaticXULComponents.cpp b/toolkit/library/nsStaticXULComponents.cpp index 13f61f62facf..4719bd5a51c2 100644 --- a/toolkit/library/nsStaticXULComponents.cpp +++ b/toolkit/library/nsStaticXULComponents.cpp @@ -283,6 +283,7 @@ MODULE(jsperf) \ SERVICES_CRYPTO_MODULE \ MOZ_APP_COMPONENT_MODULES \ + MODULE(nsTelemetryModule) \ /* end of list */ #define MODULE(_name) \ diff --git a/xpcom/base/Makefile.in b/xpcom/base/Makefile.in index 9409b4503ace..4e3ca15c9691 100644 --- a/xpcom/base/Makefile.in +++ b/xpcom/base/Makefile.in @@ -70,6 +70,7 @@ CPPSRCS = \ nsStackWalk.cpp \ nsMemoryReporterManager.cpp \ FunctionTimer.cpp \ + nsTelemetry.cpp \ $(NULL) ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT)) @@ -140,6 +141,7 @@ XPIDLSRCS = \ nsIUUIDGenerator.idl \ nsIMutable.idl \ nsIMemoryReporter.idl \ + nsITelemetry.idl \ $(NULL) ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT)) @@ -152,6 +154,8 @@ FORCE_STATIC_LIB = 1 # Force use of PIC FORCE_USE_PIC = 1 +include $(topsrcdir)/config/config.mk +include $(topsrcdir)/ipc/chromium/chromium-config.mk include $(topsrcdir)/config/rules.mk DEFINES += -D_IMPL_NS_COM diff --git a/xpcom/base/nsITelemetry.idl b/xpcom/base/nsITelemetry.idl new file mode 100644 index 000000000000..2c6bb7614412 --- /dev/null +++ b/xpcom/base/nsITelemetry.idl @@ -0,0 +1,76 @@ +/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */ +/* ***** 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 Firefox. + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation . + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Taras Glek + * + * 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 "nsISupports.idl" + +[scriptable, uuid(5c9afdb5-0532-47f3-be31-79e13a6db642)] +interface nsITelemetry : nsISupports +{ + /* + * An object containing a snapshot from all of the currently registered histograms. + * { name1: {data1}, name2:{data2}...} + * where data is consists of the following properties: + * min - Minimal bucket size + * max - Maximum bucket size + * histogram_type - 0:Exponential, 1:Linear + * counts - array representing contents of the buckets in the histogram + * ranges - an array with calculated bucket sizes + * sum - sum of the bucket contents + */ + [implicit_jscontext] + readonly attribute jsval histogramSnapshots; + + /** + * Create and return a histogram where bucket sizes increase exponentially. Parameters: + * @param name Unique histogram name + * @param min - Minimal bucket size + * @param max - Maximum bucket size + * @param bucket_count - number of buckets in the histogram. + * The returned object has the following functions: + * add(int) - Adds an int value to the appropriate bucket + * snapshot() - Returns a snapshot of the histogram with the same data fields as in histogramSnapshots() + */ + [implicit_jscontext] + jsval newExponentialHistogram(in ACString name, in PRInt32 min, in PRInt32 max, in PRUint32 bucket_count); + + /* + Same as newExponentialHistogram, but for linear histograms + */ + [implicit_jscontext] + jsval newLinearHistogram(in ACString name, in PRInt32 min, in PRInt32 max, in PRUint32 bucket_count); +}; diff --git a/xpcom/base/nsTelemetry.cpp b/xpcom/base/nsTelemetry.cpp new file mode 100644 index 000000000000..d65977a7cdc6 --- /dev/null +++ b/xpcom/base/nsTelemetry.cpp @@ -0,0 +1,233 @@ +/* -*- 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 + * + * 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 ***** */ + +#define XPCOM_TRANSLATE_NSGM_ENTRY_POINT +#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" + +using namespace base; +using namespace mozilla; + +class Telemetry : public nsITelemetry +{ + NS_DECL_ISUPPORTS + NS_DECL_NSITELEMETRY + + public: + static Telemetry* GetSingleton(); +}; + +// A static initializer to initialize histogram collection +static StatisticsRecorder gStatisticsRecorder; + +static bool +FillRanges(JSContext *cx, JSObject *array, Histogram *h) +{ + for (size_t i = 0;i < h->bucket_count();i++) { + if (!JS_DefineElement(cx, array, i, INT_TO_JSVAL(h->ranges(i)), NULL, NULL, JSPROP_ENUMERATE)) + return false; + } + return true; +} + +static JSBool +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) + && JS_DefineProperty(cx, obj, "sum", INT_TO_JSVAL(ss.sum()), NULL, NULL, JSPROP_ENUMERATE) + && (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; + } + for (size_t i = 0;i < count;i++) { + if (!JS_DefineElement(cx, counts_array, i, INT_TO_JSVAL(ss.counts(i)), NULL, NULL, JSPROP_ENUMERATE)) { + return JS_FALSE; + } + } + return JS_TRUE; +} + +static JSBool +JSHistogram_Add(JSContext *cx, uintN argc, jsval *vp) +{ + jsval *argv = JS_ARGV(cx, vp); + JSString *str; + if (!JS_ConvertArguments(cx, argc, argv, "i", &str)) + return JS_FALSE; + if (!JSVAL_IS_INT(argv[0])) + return JS_FALSE; + JSObject *obj = JS_THIS_OBJECT(cx, vp); + Histogram *h = static_cast(JS_GetPrivate(cx, obj)); + h->Add(JSVAL_TO_INT(argv[0])); + return JS_TRUE; +} + +static JSBool +JSHistogram_Snapshot(JSContext *cx, uintN argc, jsval *vp) +{ + JSObject *obj = JS_THIS_OBJECT(cx, vp); + Histogram *h = static_cast(JS_GetPrivate(cx, obj)); + JSObject *snapshot = JS_NewObject(cx, NULL, NULL, NULL); + if (!snapshot) + return NS_ERROR_FAILURE; + JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(snapshot)); + return ReflectHistogramSnapshot(cx, snapshot, h); +} + +static nsresult +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; +} + +NS_IMETHODIMP +Telemetry::NewExponentialHistogram(const nsACString &name, PRInt32 min, PRInt32 max, PRUint32 size, JSContext *cx, jsval *ret) +{ + Histogram *h = base::Histogram::FactoryGet(name.BeginReading(), min, max, size, base::Histogram::kNoFlags); + return WrapAndReturnHistogram(h, cx, ret); +} + +NS_IMETHODIMP +Telemetry::NewLinearHistogram(const nsACString &name, PRInt32 min, PRInt32 max, PRUint32 size, JSContext *cx, jsval *ret) +{ + Histogram *h = base::LinearHistogram::FactoryGet(name.BeginReading(), min, max, size, base::Histogram::kNoFlags); + return WrapAndReturnHistogram(h, cx, ret); +} + +NS_IMETHODIMP +Telemetry::GetHistogramSnapshots(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); + + 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; + } + } + return NS_OK; +} + +NS_IMPL_THREADSAFE_ISUPPORTS1(Telemetry, nsITelemetry) + +static Telemetry *gJarHandler = nsnull; + +static void ShutdownTelemetry() +{ + NS_IF_RELEASE(gJarHandler); +} + +Telemetry* Telemetry::GetSingleton() +{ + if (!gJarHandler) { + gJarHandler = new Telemetry(); + NS_ADDREF(gJarHandler); + } + NS_ADDREF(gJarHandler); + return gJarHandler; +} + +NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(Telemetry, Telemetry::GetSingleton) + +#define NS_TELEMETRY_CID \ + {0xf880b792, 0xe6cd, 0x46e7, {0x9c, 0x22, 0x3e, 0x12, 0xc3, 0x8b, 0xc6, 0xca}} +NS_DEFINE_NAMED_CID(NS_TELEMETRY_CID); + +static const mozilla::Module::CIDEntry kTelemetryCIDs[] = { + { &kNS_TELEMETRY_CID, false, NULL, TelemetryConstructor }, + { NULL } +}; + +static const mozilla::Module::ContractIDEntry kTelemetryContracts[] = { + { "@mozilla.org/base/telemetry;1", &kNS_TELEMETRY_CID }, + { NULL } +}; + +static const mozilla::Module kTelemetryModule = { + mozilla::Module::kVersion, + kTelemetryCIDs, + kTelemetryContracts, + NULL, + NULL, + NULL, + ShutdownTelemetry, +}; + +NSMODULE_DEFN(nsTelemetryModule) = &kTelemetryModule; diff --git a/xpcom/tests/unit/test_nsITelemetry.js b/xpcom/tests/unit/test_nsITelemetry.js new file mode 100644 index 000000000000..8de4edaf7be6 --- /dev/null +++ b/xpcom/tests/unit/test_nsITelemetry.js @@ -0,0 +1,38 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +const Cc = Components.classes; +const Ci = Components.interfaces; + +const Telemetry = Cc["@mozilla.org/base/telemetry;1"].getService(Ci.nsITelemetry); + +function test_histogram(histogram_constructor, name, min, max, bucket_count) { + var h = histogram_constructor(name, min, max, bucket_count); + + var r = h.snapshot().ranges; + var sum = 0; + for(var i=0;i