diff --git a/toolkit/components/telemetry/Makefile.in b/toolkit/components/telemetry/Makefile.in index c1952ae38f5..c0a8883c18e 100644 --- a/toolkit/components/telemetry/Makefile.in +++ b/toolkit/components/telemetry/Makefile.in @@ -54,6 +54,13 @@ IS_COMPONENT = 1 LIBRARY_NAME = telemetry +EXPORTS_NAMESPACES = mozilla + +EXPORTS_mozilla = \ + Telemetry.h \ + TelemetryHistograms.h \ + $(NULL) + XPIDLSRCS = \ nsITelemetry.idl \ $(NULL) diff --git a/toolkit/components/telemetry/Telemetry.cpp b/toolkit/components/telemetry/Telemetry.cpp index eaf57d3a311..95fecbb97c5 100644 --- a/toolkit/components/telemetry/Telemetry.cpp +++ b/toolkit/components/telemetry/Telemetry.cpp @@ -36,7 +36,6 @@ * * ***** END LICENSE BLOCK ***** */ -#define XPCOM_TRANSLATE_NSGM_ENTRY_POINT #include "base/histogram.h" #include "nsIComponentManager.h" #include "nsIServiceManager.h" @@ -47,27 +46,114 @@ #include "jsapi.h" #include "nsStringGlue.h" #include "nsITelemetry.h" +#include "Telemetry.h" +#include +#include namespace { using namespace base; +using namespace mozilla; +using namespace std; -class Telemetry : public nsITelemetry +struct ltstr { + bool operator()(const char* s1, const char* s2) const { + return strcmp(s1, s2) < 0; + } +}; + +class TelemetryImpl : public nsITelemetry { NS_DECL_ISUPPORTS NS_DECL_NSITELEMETRY public: - static Telemetry* GetSingleton(); + +private: + // This is used to cache JS string->Telemetry::ID conversions + typedef map NameHistogramMap; + NameHistogramMap mHistogramMap; }; // A initializer to initialize histogram collection StatisticsRecorder gStatisticsRecorder; +// Hardcoded probes +struct TelemetryHistogram { + Histogram *histogram; + const char *id; + const char *name; + PRUint32 min; + PRUint32 max; + PRUint32 bucketCount; + PRUint32 histogramType; +}; + +const TelemetryHistogram gHistograms[] = { +#define HISTOGRAM(id, name, min, max, bucket_count, histogram_type, b) \ + { NULL, NS_STRINGIFY(id), name, min, max, bucket_count, nsITelemetry::HISTOGRAM_ ## histogram_type }, + +#include "TelemetryHistograms.h" + +#undef HISTOGRAM +}; + +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: + *result = Histogram::FactoryGet(name, min, max, bucketCount, Histogram::kNoFlags); + break; + case nsITelemetry::HISTOGRAM_LINEAR: + *result = LinearHistogram::FactoryGet(name, min, max, bucketCount, Histogram::kNoFlags); + break; + case nsITelemetry::HISTOGRAM_BOOLEAN: + *result = BooleanHistogram::FactoryGet(name, Histogram::kNoFlags); + 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]; + nsresult rv = HistogramGet(p.name, p.min, p.max, p.bucketCount, p.histogramType, &h); + if (NS_FAILED(rv)) + return NS_ERROR_FAILURE; + + *ret = knownHistograms[id] = h; + return NS_OK; +} + bool FillRanges(JSContext *cx, JSObject *array, Histogram *h) { - for (size_t i = 0;i < h->bucket_count();i++) { + 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; } @@ -94,7 +180,7 @@ ReflectHistogramSnapshot(JSContext *cx, JSObject *obj, Histogram *h) )) { return JS_FALSE; } - for (size_t i = 0;i < count;i++) { + 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; } @@ -113,7 +199,11 @@ JSHistogram_Add(JSContext *cx, uintN argc, jsval *vp) 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])); + PRUint32 value = JSVAL_TO_INT(argv[0]); + if (h->histogram_type() == Histogram::BOOLEAN_HISTOGRAM) + h->Add(!!value); + else + h->Add(value); return JS_TRUE; } @@ -148,31 +238,19 @@ WrapAndReturnHistogram(Histogram *h, JSContext *cx, jsval *ret) && 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::NewHistogram(const nsACString &name, PRUint32 min, PRUint32 max, PRUint32 bucket_count, PRUint32 histogram_type, JSContext *cx, jsval *ret) +TelemetryImpl::NewHistogram(const nsACString &name, PRUint32 min, PRUint32 max, PRUint32 bucketCount, PRUint32 histogramType, JSContext *cx, jsval *ret) { - // Sanity checks on histogram parameters. - if (min < 1) - return NS_ERROR_ILLEGAL_VALUE; - - if (min >= max) - return NS_ERROR_ILLEGAL_VALUE; - - if (bucket_count <= 2) - return NS_ERROR_ILLEGAL_VALUE; - Histogram *h; - if (histogram_type == nsITelemetry::HISTOGRAM_EXPONENTIAL) { - h = Histogram::FactoryGet(name.BeginReading(), min, max, bucket_count, Histogram::kNoFlags); - } else { - h = LinearHistogram::FactoryGet(name.BeginReading(), min, max, bucket_count, Histogram::kNoFlags); - } + nsresult rv = HistogramGet(PromiseFlatCString(name).get(), min, max, bucketCount, histogramType, &h); + if (NS_FAILED(rv)) + return rv; return WrapAndReturnHistogram(h, cx, ret); } NS_IMETHODIMP -Telemetry::GetHistogramSnapshots(JSContext *cx, jsval *ret) +TelemetryImpl::GetHistogramSnapshots(JSContext *cx, jsval *ret) { JSObject *root_obj = JS_NewObject(cx, NULL, NULL, NULL); if (!root_obj) @@ -194,51 +272,80 @@ Telemetry::GetHistogramSnapshots(JSContext *cx, jsval *ret) return NS_OK; } -NS_IMPL_THREADSAFE_ISUPPORTS1(Telemetry, nsITelemetry) -Telemetry *gJarHandler = nsnull; - -void ShutdownTelemetry() +NS_IMETHODIMP +TelemetryImpl::GetHistogramById(const nsACString &name, JSContext *cx, jsval *ret) { - NS_IF_RELEASE(gJarHandler); -} - -Telemetry* Telemetry::GetSingleton() -{ - if (!gJarHandler) { - gJarHandler = new Telemetry(); - NS_ADDREF(gJarHandler); + // Cache names for log(N) lookup + // Note the histogram names are statically allocated + if (Telemetry::HistogramCount && !mHistogramMap.size()) { + for (PRUint32 i = 0; i < Telemetry::HistogramCount; i++) { + mHistogramMap[gHistograms[i].id] = (Telemetry::ID) i; + } } - NS_ADDREF(gJarHandler); - return gJarHandler; + + NameHistogramMap::iterator it = mHistogramMap.find(PromiseFlatCString(name).get()); + if (it == mHistogramMap.end()) + return NS_ERROR_FAILURE; + + Histogram *h; + nsresult rv = GetHistogramByEnumId(it->second, &h); + if (NS_FAILED(rv)) + return rv; + + return WrapAndReturnHistogram(h, cx, ret); } -NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(Telemetry, Telemetry::GetSingleton) +NS_IMPL_THREADSAFE_ISUPPORTS1(TelemetryImpl, nsITelemetry) + +already_AddRefed +CreateTelemetryInstance() +{ + nsCOMPtr telemetry = new TelemetryImpl(); + return telemetry.forget(); +} + +NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsITelemetry, CreateTelemetryInstance) #define NS_TELEMETRY_CID \ {0xaea477f2, 0xb3a2, 0x469c, {0xaa, 0x29, 0x0a, 0x82, 0xd1, 0x32, 0xb8, 0x29}} NS_DEFINE_NAMED_CID(NS_TELEMETRY_CID); -const mozilla::Module::CIDEntry kTelemetryCIDs[] = { - { &kNS_TELEMETRY_CID, false, NULL, TelemetryConstructor }, +const Module::CIDEntry kTelemetryCIDs[] = { + { &kNS_TELEMETRY_CID, false, NULL, nsITelemetryConstructor }, { NULL } }; -const mozilla::Module::ContractIDEntry kTelemetryContracts[] = { +const Module::ContractIDEntry kTelemetryContracts[] = { { "@mozilla.org/base/telemetry;1", &kNS_TELEMETRY_CID }, { NULL } }; -const mozilla::Module kTelemetryModule = { - mozilla::Module::kVersion, +const Module kTelemetryModule = { + Module::kVersion, kTelemetryCIDs, kTelemetryContracts, NULL, NULL, NULL, - ShutdownTelemetry, + NULL, }; } // anonymous namespace +namespace mozilla { +namespace Telemetry { + +void +Accumulate(ID aHistogram, PRUint32 aSample) +{ + Histogram *h; + nsresult rv = GetHistogramByEnumId(aHistogram, &h); + if (NS_SUCCEEDED(rv)) + h->Add(aSample); +} + +} // namespace Telemetry +} // namespace mozilla + NSMODULE_DEFN(nsTelemetryModule) = &kTelemetryModule; diff --git a/toolkit/components/telemetry/Telemetry.h b/toolkit/components/telemetry/Telemetry.h new file mode 100644 index 00000000000..6087299d5a9 --- /dev/null +++ b/toolkit/components/telemetry/Telemetry.h @@ -0,0 +1,64 @@ +/* -*- 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.org code. + * + * 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 ***** */ + +#ifndef Telemetry_h__ +#define Telemetry_h__ + +namespace mozilla { +namespace Telemetry { + +enum ID { +#define HISTOGRAM(name, a, b, c, d, e, f) name, + +#include "TelemetryHistograms.h" + +#undef HISTOGRAM +HistogramCount +}; + +/** + * Adds sample to a histogram defined in TelemetryHistograms.h + * + * @param id - histogram id + * @param sample - value to record. + */ +void Accumulate(ID id, PRUint32 sample); + +} // namespace Telemetry +} // namespace mozilla +#endif // Telemetry_h__ diff --git a/toolkit/components/telemetry/TelemetryHistograms.h b/toolkit/components/telemetry/TelemetryHistograms.h new file mode 100644 index 00000000000..37c8406f26d --- /dev/null +++ b/toolkit/components/telemetry/TelemetryHistograms.h @@ -0,0 +1,50 @@ +/* -*- 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.org code. + * + * 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 ***** */ + +/** + * This file lists Telemetry histograms collected by Firefox. + * Format is HISTOGRAM(id, histogram name, minium, maximum, bucket count, + * histogram kind, human-readable description for about:telemetry) + */ + +HISTOGRAM(CYCLE_COLLECTOR, "nsCycleCollector::Collect (ms)", 1, 10000, 50, EXPONENTIAL, "Time spent on cycle collection") +HISTOGRAM(TELEMETRY_PING, "Telemetry.ping (ms)", 1, 3000, 10, EXPONENTIAL, "Telemetry submission lag") +HISTOGRAM(TELEMETRY_SUCCESS, "Telemetry.success (No, Yes)", 0, 1, 2, BOOLEAN, "Success rate of telemetry submissions") +HISTOGRAM(MEMORY_JS_GC_HEAP, "Memory::explicit/js/gc-heap (MB)", 1024, 512 * 1024, 10, EXPONENTIAL, "Memory used by the JavaScript GC") +HISTOGRAM(MEMORY_RESIDENT, "Memory::resident (MB)", 32 * 1024, 1024 * 1024, 10, EXPONENTIAL, "Resident memory reported by OS") +HISTOGRAM(MEMORY_LAYOUT_ALL, "Memory::explicit/layout/all (MB)", 1024, 64 * 1024, 10, EXPONENTIAL, "Memory reported used by layout") diff --git a/toolkit/components/telemetry/TelemetryPing.js b/toolkit/components/telemetry/TelemetryPing.js index e9f9c2273f7..3885812a89f 100644 --- a/toolkit/components/telemetry/TelemetryPing.js +++ b/toolkit/components/telemetry/TelemetryPing.js @@ -54,9 +54,9 @@ const TELEMETRY_INTERVAL = 60; const TELEMETRY_DELAY = 60000; // about:memory values to turn into histograms const MEM_HISTOGRAMS = { - "explicit/js/gc-heap": [1024, 512 * 1024, 10], - "resident": [32 * 1024, 1024 * 1024, 10], - "explicit/layout/all": [1024, 64 * 1025, 10] + "explicit/js/gc-heap": "MEMORY_JS_GC_HEAP", + "resident": "MEMORY_RESIDENT", + "explicit/layout/all": "MEMORY_LAYOUT_ALL" }; XPCOMUtils.defineLazyGetter(this, "Telemetry", function () { @@ -200,15 +200,15 @@ TelemetryPing.prototype = { while (e.hasMoreElements()) { let mr = e.getNext().QueryInterface(Ci.nsIMemoryReporter); // memReporters[mr.path] = mr.memoryUsed; - let specs = MEM_HISTOGRAMS[mr.path]; - if (!specs) { + let id = MEM_HISTOGRAMS[mr.path]; + if (!id) { continue; } let name = "Memory:" + mr.path + " (KB)"; let h = this._histograms[name]; if (!h) { - h = Telemetry.newHistogram(name, specs[0], specs[1], specs[2], Telemetry.HISTOGRAM_EXPONENTIAL); + h = Telemetry.getHistogramById(id); this._histograms[name] = h; } let v = Math.floor(mr.memoryUsed / 1024); @@ -239,8 +239,8 @@ TelemetryPing.prototype = { const TELEMETRY_PING = "telemetry.ping (ms)"; const TELEMETRY_SUCCESS = "telemetry.success (No, Yes)"; - let hping = Telemetry.newHistogram(TELEMETRY_PING, 1, 3000, 10, Telemetry.HISTOGRAM_EXPONENTIAL); - let hsuccess = Telemetry.newHistogram(TELEMETRY_SUCCESS, 1, 2, 3, Telemetry.HISTOGRAM_LINEAR); + let hping = Telemetry.getHistogramById("TELEMETRY_PING"); + let hsuccess = Telemetry.getHistogramById("TELEMETRY_SUCCESS"); let url = server + this._path; let request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"] @@ -257,8 +257,8 @@ TelemetryPing.prototype = { if (isTestPing) Services.obs.notifyObservers(null, "telemetry-test-xhr-complete", null); } - request.onerror = function(aEvent) finishRequest(1); - request.onload = function(aEvent) finishRequest(2); + request.onerror = function(aEvent) finishRequest(0); + request.onload = function(aEvent) finishRequest(1); request.send(nativeJSON.encode(payload)); }, diff --git a/toolkit/components/telemetry/nsITelemetry.idl b/toolkit/components/telemetry/nsITelemetry.idl index e9b7c0717ae..948b47ebd5d 100644 --- a/toolkit/components/telemetry/nsITelemetry.idl +++ b/toolkit/components/telemetry/nsITelemetry.idl @@ -38,16 +38,18 @@ #include "nsISupports.idl" -[scriptable, uuid(29464d3d-f838-4afb-a737-319fe0c6cc04)] +[scriptable, uuid(db854295-478d-4de9-8211-d73ed7d81cd0)] interface nsITelemetry : nsISupports { /** * Histogram types: * HISTOGRAM_EXPONENTIAL - buckets increase exponentially * HISTOGRAM_LINEAR - buckets increase linearly + * HISTOGRAM_BOOLEAN - For storing 0/1 values */ const unsigned long HISTOGRAM_EXPONENTIAL = 0; const unsigned long HISTOGRAM_LINEAR = 1; + const unsigned long HISTOGRAM_BOOLEAN = 2; /* * An object containing a snapshot from all of the currently registered histograms. @@ -65,6 +67,7 @@ interface nsITelemetry : nsISupports /** * 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 @@ -76,4 +79,12 @@ interface nsITelemetry : nsISupports */ [implicit_jscontext] jsval newHistogram(in ACString name, in PRUint32 min, in PRUint32 max, in PRUint32 bucket_count, in unsigned long histogram_type); + + /** + * Same as newHistogram above, but for histograms registered in TelemetryHistograms.h. + * + * @param id - unique identifier from TelemetryHistograms.h + */ + [implicit_jscontext] + jsval getHistogramById(in ACString id); }; diff --git a/toolkit/components/telemetry/tests/unit/test_TelemetryPing.js b/toolkit/components/telemetry/tests/unit/test_TelemetryPing.js index ccde0b410c7..bf886240cf2 100644 --- a/toolkit/components/telemetry/tests/unit/test_TelemetryPing.js +++ b/toolkit/components/telemetry/tests/unit/test_TelemetryPing.js @@ -72,16 +72,16 @@ function checkHistograms(request, response) { do_check_eq(payload.info[f], expected_info[f]); } - const TELEMETRY_PING = "telemetry.ping (ms)"; - const TELEMETRY_SUCCESS = "telemetry.success (No, Yes)"; + const TELEMETRY_PING = "Telemetry.ping (ms)"; + const TELEMETRY_SUCCESS = "Telemetry.success (No, Yes)"; do_check_true(TELEMETRY_PING in payload.histograms) // There should be one successful report from the previos telemetry ping const expected_tc = { range: [1, 2], bucket_count: 3, - histogram_type: 1, - values: {1:0, 2:1} + histogram_type: 2, + values: {0:0, 1:1, 2:0} } let tc = payload.histograms[TELEMETRY_SUCCESS] do_check_eq(uneval(tc), diff --git a/toolkit/components/telemetry/tests/unit/test_nsITelemetry.js b/toolkit/components/telemetry/tests/unit/test_nsITelemetry.js index cbcdb7ba4fc..0ab80c99b9d 100644 --- a/toolkit/components/telemetry/tests/unit/test_nsITelemetry.js +++ b/toolkit/components/telemetry/tests/unit/test_nsITelemetry.js @@ -42,6 +42,39 @@ function expect_fail(f) { do_check_true(failed); } +function test_boolean_histogram() +{ + var h = Telemetry.newHistogram("test::boolean histogram", 99,1,4, Telemetry.HISTOGRAM_BOOLEAN); + var r = h.snapshot().ranges; + // boolean histograms ignore numeric parameters + do_check_eq(uneval(r), uneval([0, 1, 2])) + var sum = 0 + for(var i=0;i #include #ifdef WIN32 @@ -167,6 +164,7 @@ #include "mozilla/Mutex.h" #include "mozilla/CondVar.h" +#include "mozilla/Telemetry.h" using namespace mozilla; @@ -2576,8 +2574,7 @@ nsCycleCollector::CleanupAfterCollection() #ifdef COLLECT_TIME_DEBUG printf("cc: CleanupAfterCollection(), total time %ums\n", interval); #endif - UMA_HISTOGRAM_TIMES("nsCycleCollector::Collect (ms)", - base::TimeDelta::FromMilliseconds(interval)); + Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR, interval); #ifdef DEBUG_CC ExplainLiveExpectedGarbage();