LCOV - app.info - toolkit/components/telemetry/Telemetry.cpp

LCOV - code coverage report
| Current view: | directory - toolkit/components/telemetry - Telemetry.cpp (source / functions) |  | Found | Hit | Coverage |
| Test: | app.info | Lines: | 618 | 500 | 80.9 %
| Date: | 2012-04-21 | Functions: | 92 | 87 | 94.6 %

           1                 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
           2                 : /* ***** BEGIN LICENSE BLOCK *****
           3                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
           4                 :  *
           5                 :  * The contents of this file are subject to the Mozilla Public License Version
           6                 :  * 1.1 (the "License"); you may not use this file except in compliance with
           7                 :  * the License. You may obtain a copy of the License at
           8                 :  * http://www.mozilla.org/MPL/
           9                 :  *
          10                 :  * Software distributed under the License is distributed on an "AS IS" basis,
          11                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
          12                 :  * for the specific language governing rights and limitations under the
          13                 :  * License.
          14                 :  *
          15                 :  * The Original Code is Mozilla code.
          16                 :  *
          17                 :  * The Initial Developer of the Original Code is
          18                 :  * Mozilla Corporation.
          19                 :  * Portions created by the Initial Developer are Copyright (C) 2011
          20                 :  * the Initial Developer. All Rights Reserved.
          21                 :  *
          22                 :  * Contributor(s):
          23                 :  *   Taras Glek <tglek@mozilla.com>
          24                 :  *   Vladan Djeric <vdjeric@mozilla.com>
          25                 :  *
          26                 :  * Alternatively, the contents of this file may be used under the terms of
          27                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
          28                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
          29                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
          30                 :  * of those above. If you wish to allow use of your version of this file only
          31                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
          32                 :  * use your version of this file under the terms of the MPL, indicate your
          33                 :  * decision by deleting the provisions above and replace them with the notice
          34                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
          35                 :  * the provisions above, a recipient may use your version of this file under
          36                 :  * the terms of any one of the MPL, the GPL or the LGPL.
          37                 :  *
          38                 :  * ***** END LICENSE BLOCK ***** */
          39                 :
          40                 : #include "base/histogram.h"
          41                 : #include "base/pickle.h"
          42                 : #include "nsIComponentManager.h"
          43                 : #include "nsIServiceManager.h"
          44                 : #include "nsCOMPtr.h"
          45                 : #include "mozilla/ModuleUtils.h"
          46                 : #include "nsIXPConnect.h"
          47                 : #include "mozilla/Services.h"
          48                 : #include "jsapi.h"
          49                 : #include "nsStringGlue.h"
          50                 : #include "nsITelemetry.h"
          51                 : #include "nsIFile.h"
          52                 : #include "nsILocalFile.h"
          53                 : #include "Telemetry.h"
          54                 : #include "nsTHashtable.h"
          55                 : #include "nsHashKeys.h"
          56                 : #include "nsBaseHashtable.h"
          57                 : #include "nsXULAppAPI.h"
          58                 : #include "nsThreadUtils.h"
          59                 : #include "mozilla/Mutex.h"
          60                 : #include "mozilla/FileUtils.h"
          61                 :
          62                 : namespace {
          63                 :
          64                 : using namespace base;
          65                 : using namespace mozilla;
          66                 :
          67                 : template<class EntryType>
          68                 : class AutoHashtable : public nsTHashtable<EntryType>
          69                 : {
          70                 : public:
          71                 :   AutoHashtable(PRUint32 initSize = PL_DHASH_MIN_SIZE);
          72                 :   ~AutoHashtable();
          73                 :   typedef bool (*ReflectEntryFunc)(EntryType *entry, JSContext *cx, JSObject *obj);
          74                 :   bool ReflectHashtable(ReflectEntryFunc entryFunc, JSContext *cx, JSObject *obj);
          75                 : private:
          76                 :   struct EnumeratorArgs {
          77                 :     JSContext *cx;
          78                 :     JSObject *obj;
          79                 :     ReflectEntryFunc entryFunc;
          80                 :   };
          81                 :   static PLDHashOperator ReflectEntryStub(EntryType *entry, void *arg);
          82                 : };
          83                 :
          84                 : template<class EntryType>
          85            5465 : AutoHashtable<EntryType>::AutoHashtable(PRUint32 initSize)
          86                 : {
          87            5465 :   this->Init(initSize);
          88            5465 : }
          89                 :
          90                 : template<class EntryType>
          91            5463 : AutoHashtable<EntryType>::~AutoHashtable()
          92                 : {
          93            5463 :   this->Clear();
          94            5463 : }
          95                 :
          96                 : template<typename EntryType>
          97                 : PLDHashOperator
          98              50 : AutoHashtable<EntryType>::ReflectEntryStub(EntryType *entry, void *arg)
          99                 : {
         100              50 :   EnumeratorArgs *args = static_cast<EnumeratorArgs *>(arg);
         101              50 :   if (!args->entryFunc(entry, args->cx, args->obj)) {
         102               0 :     return PL_DHASH_STOP;
         103                 :   }
         104              50 :   return PL_DHASH_NEXT;
         105                 : }
         106                 :
         107                 : /**
         108                 :  * Reflect the individual entries of table into JS, usually by defining
         109                 :  * some property and value of obj.  entryFunc is called for each entry.
         110                 :  */
         111                 : template<typename EntryType>
         112                 : bool
         113              25 : AutoHashtable<EntryType>::ReflectHashtable(ReflectEntryFunc entryFunc,
         114                 :                                            JSContext *cx, JSObject *obj)
         115                 : {
         116              25 :   EnumeratorArgs args = { cx, obj, entryFunc };
         117              25 :   PRUint32 num = this->EnumerateEntries(ReflectEntryStub, static_cast<void*>(&args));
         118              25 :   return num == this->Count();
         119                 : }
         120                 :
         121                 : class TelemetryImpl : public nsITelemetry
         122                 : {
         123                 :   NS_DECL_ISUPPORTS
         124                 :   NS_DECL_NSITELEMETRY
         125                 :
         126                 : public:
         127                 :   TelemetryImpl();
         128                 :   ~TelemetryImpl();
         129                 :
         130                 :   static bool CanRecord();
         131                 :   static already_AddRefed<nsITelemetry> CreateTelemetryInstance();
         132                 :   static void ShutdownTelemetry();
         133                 :   static void RecordSlowStatement(const nsACString &statement,
         134                 :                                   const nsACString &dbName,
         135                 :                                   PRUint32 delay);
         136                 :   static nsresult GetHistogramEnumId(const char *name, Telemetry::ID *id);
         137                 :   struct StmtStats {
         138                 :     PRUint32 hitCount;
         139                 :     PRUint32 totalTime;
         140                 :   };
         141                 :   typedef nsBaseHashtableET<nsCStringHashKey, StmtStats> SlowSQLEntryType;
         142                 :
         143                 : private:
         144                 :   static bool StatementReflector(SlowSQLEntryType *entry, JSContext *cx,
         145                 :                                  JSObject *obj);
         146                 :   bool AddSQLInfo(JSContext *cx, JSObject *rootObj, bool mainThread);
         147                 :
         148                 :   // Like GetHistogramById, but returns the underlying C++ object, not the JS one.
         149                 :   nsresult GetHistogramByName(const nsACString &name, Histogram **ret);
         150                 :   bool ShouldReflectHistogram(Histogram *h);
         151                 :   void IdentifyCorruptHistograms(StatisticsRecorder::Histograms &hs);
         152                 :   typedef StatisticsRecorder::Histograms::iterator HistogramIterator;
         153                 :
         154                 :   struct AddonHistogramInfo {
         155                 :     PRUint32 min;
         156                 :     PRUint32 max;
         157                 :     PRUint32 bucketCount;
         158                 :     PRUint32 histogramType;
         159                 :     Histogram *h;
         160                 :   };
         161                 :   typedef nsBaseHashtableET<nsCStringHashKey, AddonHistogramInfo> AddonHistogramEntryType;
         162                 :   typedef AutoHashtable<AddonHistogramEntryType> AddonHistogramMapType;
         163                 :   typedef nsBaseHashtableET<nsCStringHashKey, AddonHistogramMapType *> AddonEntryType;
         164                 :   typedef AutoHashtable<AddonEntryType> AddonMapType;
         165                 :   static bool AddonHistogramReflector(AddonHistogramEntryType *entry,
         166                 :                                       JSContext *cx, JSObject *obj);
         167                 :   static bool AddonReflector(AddonEntryType *entry, JSContext *cx, JSObject *obj);
         168                 :   static bool CreateHistogramForAddon(const nsACString &name,
         169                 :                                       AddonHistogramInfo &info);
         170                 :   AddonMapType mAddonMap;
         171                 :
         172                 :   // This is used for speedy string->Telemetry::ID conversions
         173                 :   typedef nsBaseHashtableET<nsCharPtrHashKey, Telemetry::ID> CharPtrEntryType;
         174                 :   typedef AutoHashtable<CharPtrEntryType> HistogramMapType;
         175                 :   HistogramMapType mHistogramMap;
         176                 :   bool mCanRecord;
         177                 :   static TelemetryImpl *sTelemetry;
         178                 :   AutoHashtable<SlowSQLEntryType> mSlowSQLOnMainThread;
         179                 :   AutoHashtable<SlowSQLEntryType> mSlowSQLOnOtherThread;
         180                 :   // This gets marked immutable in debug builds, so we can't use
         181                 :   // AutoHashtable here.
         182                 :   nsTHashtable<nsCStringHashKey> mTrackedDBs;
         183                 :   Mutex mHashMutex;
         184                 : };
         185                 :
         186                 : TelemetryImpl*  TelemetryImpl::sTelemetry = NULL;
         187                 :
         188                 : // A initializer to initialize histogram collection
         189            1396 : StatisticsRecorder gStatisticsRecorder;
         190                 :
         191                 : // Hardcoded probes
         192                 : struct TelemetryHistogram {
         193                 :   const char *id;
         194                 :   PRUint32 min;
         195                 :   PRUint32 max;
         196                 :   PRUint32 bucketCount;
         197                 :   PRUint32 histogramType;
         198                 :   const char *comment;
         199                 : };
         200                 :
         201                 : // Perform the checks at the beginning of HistogramGet at compile time, so
         202                 : // that if people add incorrect histogram definitions, they get compiler
         203                 : // errors.
         204                 : #define HISTOGRAM(id, min, max, bucket_count, histogram_type, b) \
         205                 :   PR_STATIC_ASSERT(nsITelemetry::HISTOGRAM_ ## histogram_type == nsITelemetry::HISTOGRAM_BOOLEAN || \
         206                 :                    nsITelemetry::HISTOGRAM_ ## histogram_type == nsITelemetry::HISTOGRAM_FLAG || \
         207                 :                    (min < max && bucket_count > 2 && min >= 1));
         208                 :
         209                 : #include "TelemetryHistograms.h"
         210                 :
         211                 : #undef HISTOGRAM
         212                 :
         213                 : const TelemetryHistogram gHistograms[] = {
         214                 : #define HISTOGRAM(id, min, max, bucket_count, histogram_type, comment) \
         215                 :   { NS_STRINGIFY(id), min, max, bucket_count, \
         216                 :     nsITelemetry::HISTOGRAM_ ## histogram_type, comment },
         217                 :
         218                 : #include "TelemetryHistograms.h"
         219                 :
         220                 : #undef HISTOGRAM
         221                 : };
         222                 : bool gCorruptHistograms[Telemetry::HistogramCount];
         223                 :
         224                 : bool
         225              72 : TelemetryHistogramType(Histogram *h, PRUint32 *result)
         226                 : {
         227              72 :   switch (h->histogram_type()) {
         228                 :   case Histogram::HISTOGRAM:
         229              42 :     *result = nsITelemetry::HISTOGRAM_EXPONENTIAL;
         230              42 :     break;
         231                 :   case Histogram::LINEAR_HISTOGRAM:
         232              28 :     *result = nsITelemetry::HISTOGRAM_LINEAR;
         233              28 :     break;
         234                 :   case Histogram::BOOLEAN_HISTOGRAM:
         235               2 :     *result = nsITelemetry::HISTOGRAM_BOOLEAN;
         236               2 :     break;
         237                 :   case Histogram::FLAG_HISTOGRAM:
         238               0 :     *result = nsITelemetry::HISTOGRAM_FLAG;
         239                 :   default:
         240               0 :     return false;
         241                 :   }
         242              72 :   return true;
         243                 : }
         244                 :
         245                 : nsresult
         246           31762 : HistogramGet(const char *name, PRUint32 min, PRUint32 max, PRUint32 bucketCount,
         247                 :              PRUint32 histogramType, Histogram **result)
         248                 : {
         249           31762 :   if (histogramType != nsITelemetry::HISTOGRAM_BOOLEAN
         250                 :       && histogramType != nsITelemetry::HISTOGRAM_FLAG) {
         251                 :     // Sanity checks for histogram parameters.
         252           25668 :     if (min >= max)
         253               0 :       return NS_ERROR_ILLEGAL_VALUE;
         254                 :
         255           25668 :     if (bucketCount <= 2)
         256               2 :       return NS_ERROR_ILLEGAL_VALUE;
         257                 :
         258           25666 :     if (min < 1)
         259               2 :       return NS_ERROR_ILLEGAL_VALUE;
         260                 :   }
         261                 :
         262           31758 :   switch (histogramType) {
         263                 :   case nsITelemetry::HISTOGRAM_EXPONENTIAL:
         264           19391 :     *result = Histogram::FactoryGet(name, min, max, bucketCount, Histogram::kUmaTargetedHistogramFlag);
         265           19391 :     break;
         266                 :   case nsITelemetry::HISTOGRAM_LINEAR:
         267            6273 :     *result = LinearHistogram::FactoryGet(name, min, max, bucketCount, Histogram::kUmaTargetedHistogramFlag);
         268            6273 :     break;
         269                 :   case nsITelemetry::HISTOGRAM_BOOLEAN:
         270            6088 :     *result = BooleanHistogram::FactoryGet(name, Histogram::kUmaTargetedHistogramFlag);
         271            6088 :     break;
         272                 :   case nsITelemetry::HISTOGRAM_FLAG:
         273               6 :     *result = FlagHistogram::FactoryGet(name, Histogram::kUmaTargetedHistogramFlag);
         274               6 :     break;
         275                 :   default:
         276               0 :     return NS_ERROR_INVALID_ARG;
         277                 :   }
         278           31758 :   return NS_OK;
         279                 : }
         280                 :
         281                 : // O(1) histogram lookup by numeric id
         282                 : nsresult
         283         1278618 : GetHistogramByEnumId(Telemetry::ID id, Histogram **ret)
         284                 : {
         285                 :   static Histogram* knownHistograms[Telemetry::HistogramCount] = {0};
         286         1278618 :   Histogram *h = knownHistograms[id];
         287         1278618 :   if (h) {
         288         1246940 :     *ret = h;
         289         1246940 :     return NS_OK;
         290                 :   }
         291                 :
         292           31678 :   const TelemetryHistogram &p = gHistograms[id];
         293           31678 :   nsresult rv = HistogramGet(p.id, p.min, p.max, p.bucketCount, p.histogramType, &h);
         294           31678 :   if (NS_FAILED(rv))
         295               0 :     return rv;
         296                 :
         297           31678 :   *ret = knownHistograms[id] = h;
         298           31678 :   return NS_OK;
         299                 : }
         300                 :
         301                 : bool
         302             404 : FillRanges(JSContext *cx, JSObject *array, Histogram *h)
         303                 : {
         304          229989 :   for (size_t i = 0; i < h->bucket_count(); i++) {
         305          229585 :     if (!JS_DefineElement(cx, array, i, INT_TO_JSVAL(h->ranges(i)), NULL, NULL, JSPROP_ENUMERATE))
         306               0 :       return false;
         307                 :   }
         308             404 :   return true;
         309                 : }
         310                 :
         311                 : enum reflectStatus {
         312                 :   REFLECT_OK,
         313                 :   REFLECT_CORRUPT,
         314                 :   REFLECT_FAILURE
         315                 : };
         316                 :
         317                 : enum reflectStatus
         318             404 : ReflectHistogramAndSamples(JSContext *cx, JSObject *obj, Histogram *h,
         319                 :                            const Histogram::SampleSet &ss)
         320                 : {
         321                 :   // We don't want to reflect corrupt histograms.
         322             404 :   if (h->FindCorruption(ss) != Histogram::NO_INCONSISTENCIES) {
         323               0 :     return REFLECT_CORRUPT;
         324                 :   }
         325                 :
         326             404 :   if (!(JS_DefineProperty(cx, obj, "min", INT_TO_JSVAL(h->declared_min()), NULL, NULL, JSPROP_ENUMERATE)
         327             404 :         && JS_DefineProperty(cx, obj, "max", INT_TO_JSVAL(h->declared_max()), NULL, NULL, JSPROP_ENUMERATE)
         328             404 :         && JS_DefineProperty(cx, obj, "histogram_type", INT_TO_JSVAL(h->histogram_type()), NULL, NULL, JSPROP_ENUMERATE)
         329             808 :         && JS_DefineProperty(cx, obj, "sum", DOUBLE_TO_JSVAL(ss.sum()), NULL, NULL, JSPROP_ENUMERATE))) {
         330               0 :     return REFLECT_FAILURE;
         331                 :   }
         332                 :
         333             404 :   const size_t count = h->bucket_count();
         334             404 :   JSObject *rarray = JS_NewArrayObject(cx, count, nsnull);
         335             404 :   if (!rarray) {
         336               0 :     return REFLECT_FAILURE;
         337                 :   }
         338             808 :   JS::AutoObjectRooter aroot(cx, rarray);
         339             404 :   if (!(FillRanges(cx, rarray, h)
         340                 :         && JS_DefineProperty(cx, obj, "ranges", OBJECT_TO_JSVAL(rarray),
         341             404 :                              NULL, NULL, JSPROP_ENUMERATE))) {
         342               0 :     return REFLECT_FAILURE;
         343                 :   }
         344                 :
         345             404 :   JSObject *counts_array = JS_NewArrayObject(cx, count, NULL);
         346             404 :   if (!counts_array) {
         347               0 :     return REFLECT_FAILURE;
         348                 :   }
         349             808 :   JS::AutoObjectRooter croot(cx, counts_array);
         350             404 :   if (!JS_DefineProperty(cx, obj, "counts", OBJECT_TO_JSVAL(counts_array),
         351             404 :                          NULL, NULL, JSPROP_ENUMERATE)) {
         352               0 :     return REFLECT_FAILURE;
         353                 :   }
         354          229989 :   for (size_t i = 0; i < count; i++) {
         355          459170 :     if (!JS_DefineElement(cx, counts_array, i, INT_TO_JSVAL(ss.counts(i)),
         356          229585 :                           NULL, NULL, JSPROP_ENUMERATE)) {
         357               0 :       return REFLECT_FAILURE;
         358                 :     }
         359                 :   }
         360                 :
         361             404 :   return REFLECT_OK;
         362                 : }
         363                 :
         364                 : enum reflectStatus
         365             376 : ReflectHistogramSnapshot(JSContext *cx, JSObject *obj, Histogram *h)
         366                 : {
         367             752 :   Histogram::SampleSet ss;
         368             376 :   h->SnapshotSample(&ss);
         369             376 :   return ReflectHistogramAndSamples(cx, obj, h, ss);
         370                 : }
         371                 :
         372                 : JSBool
         373             119 : JSHistogram_Add(JSContext *cx, unsigned argc, jsval *vp)
         374                 : {
         375             119 :   if (!argc) {
         376               0 :     JS_ReportError(cx, "Expected one argument");
         377               0 :     return JS_FALSE;
         378                 :   }
         379                 :
         380             119 :   jsval v = JS_ARGV(cx, vp)[0];
         381                 :   int32 value;
         382                 :
         383             119 :   if (!(JSVAL_IS_NUMBER(v) || JSVAL_IS_BOOLEAN(v))) {
         384               0 :     JS_ReportError(cx, "Not a number");
         385               0 :     return JS_FALSE;
         386                 :   }
         387                 :
         388             119 :   if (!JS_ValueToECMAInt32(cx, v, &value)) {
         389               0 :     return JS_FALSE;
         390                 :   }
         391                 :
         392             119 :   if (TelemetryImpl::CanRecord()) {
         393             118 :     JSObject *obj = JS_THIS_OBJECT(cx, vp);
         394             118 :     if (!obj) {
         395               0 :       return JS_FALSE;
         396                 :     }
         397                 :
         398             118 :     Histogram *h = static_cast<Histogram*>(JS_GetPrivate(obj));
         399             118 :     if (h->histogram_type() == Histogram::BOOLEAN_HISTOGRAM)
         400               9 :       h->Add(!!value);
         401                 :     else
         402             109 :       h->Add(value);
         403                 :   }
         404             119 :   return JS_TRUE;
         405                 : }
         406                 :
         407                 : JSBool
         408              49 : JSHistogram_Snapshot(JSContext *cx, unsigned argc, jsval *vp)
         409                 : {
         410              49 :   JSObject *obj = JS_THIS_OBJECT(cx, vp);
         411              49 :   if (!obj) {
         412               0 :     return JS_FALSE;
         413                 :   }
         414                 :
         415              49 :   Histogram *h = static_cast<Histogram*>(JS_GetPrivate(obj));
         416              49 :   JSObject *snapshot = JS_NewObject(cx, nsnull, nsnull, nsnull);
         417              49 :   if (!snapshot)
         418               0 :     return JS_FALSE;
         419              98 :   JS::AutoObjectRooter sroot(cx, snapshot);
         420                 :
         421              49 :   switch (ReflectHistogramSnapshot(cx, snapshot, h)) {
         422                 :   case REFLECT_FAILURE:
         423               0 :     return JS_FALSE;
         424                 :   case REFLECT_CORRUPT:
         425               0 :     JS_ReportError(cx, "Histogram is corrupt");
         426               0 :     return JS_FALSE;
         427                 :   case REFLECT_OK:
         428              49 :     JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(snapshot));
         429              49 :     return JS_TRUE;
         430                 :   default:
         431               0 :     MOZ_NOT_REACHED("unhandled reflection status");
         432                 :     return JS_FALSE;
         433                 :   }
         434                 : }
         435                 :
         436                 : nsresult
         437             177 : WrapAndReturnHistogram(Histogram *h, JSContext *cx, jsval *ret)
         438                 : {
         439                 :   static JSClass JSHistogram_class = {
         440                 :     "JSHistogram",  /* name */
         441                 :     JSCLASS_HAS_PRIVATE, /* flags */
         442                 :     JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
         443                 :     JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
         444                 :     JSCLASS_NO_OPTIONAL_MEMBERS
         445                 :   };
         446                 :
         447             177 :   JSObject *obj = JS_NewObject(cx, &JSHistogram_class, NULL, NULL);
         448             177 :   if (!obj)
         449               0 :     return NS_ERROR_FAILURE;
         450             354 :   JS::AutoObjectRooter root(cx, obj);
         451             177 :   if (!(JS_DefineFunction (cx, obj, "add", JSHistogram_Add, 1, 0)
         452             177 :         && JS_DefineFunction (cx, obj, "snapshot", JSHistogram_Snapshot, 1, 0))) {
         453               0 :     return NS_ERROR_FAILURE;
         454                 :   }
         455             177 :   *ret = OBJECT_TO_JSVAL(obj);
         456             177 :   JS_SetPrivate(obj, h);
         457             177 :   return NS_OK;
         458                 : }
         459                 :
         460            1365 : TelemetryImpl::TelemetryImpl():
         461                 : mHistogramMap(Telemetry::HistogramCount),
         462            1365 : mCanRecord(XRE_GetProcessType() == GeckoProcessType_Default),
         463            2730 : mHashMutex("Telemetry::mHashMutex")
         464                 : {
         465                 :   // A whitelist to prevent Telemetry reporting on Addon & Thunderbird DBs
         466                 :   const char *trackedDBs[] = {
         467                 :     "addons.sqlite", "chromeappsstore.sqlite", "content-prefs.sqlite",
         468                 :     "cookies.sqlite", "downloads.sqlite", "extensions.sqlite",
         469                 :     "formhistory.sqlite", "index.sqlite", "permissions.sqlite", "places.sqlite",
         470                 :     "search.sqlite", "signons.sqlite", "urlclassifier3.sqlite",
         471                 :     "webappsstore.sqlite"
         472            1365 :   };
         473                 :
         474            1365 :   mTrackedDBs.Init();
         475           20475 :   for (size_t i = 0; i < ArrayLength(trackedDBs); i++)
         476           19110 :     mTrackedDBs.PutEntry(nsDependentCString(trackedDBs[i]));
         477                 :
         478                 : #ifdef DEBUG
         479                 :   // Mark immutable to prevent asserts on simultaneous access from multiple threads
         480            1365 :   mTrackedDBs.MarkImmutable();
         481                 : #endif
         482            1365 : }
         483                 :
         484            1365 : TelemetryImpl::~TelemetryImpl() {
         485            1365 : }
         486                 :
         487                 : NS_IMETHODIMP
         488               9 : TelemetryImpl::NewHistogram(const nsACString &name, PRUint32 min, PRUint32 max, PRUint32 bucketCount, PRUint32 histogramType, JSContext *cx, jsval *ret)
         489                 : {
         490                 :   Histogram *h;
         491               9 :   nsresult rv = HistogramGet(PromiseFlatCString(name).get(), min, max, bucketCount, histogramType, &h);
         492               9 :   if (NS_FAILED(rv))
         493               4 :     return rv;
         494               5 :   h->ClearFlags(Histogram::kUmaTargetedHistogramFlag);
         495               5 :   return WrapAndReturnHistogram(h, cx, ret);
         496                 : }
         497                 :
         498                 : bool
         499               0 : TelemetryImpl::StatementReflector(SlowSQLEntryType *entry, JSContext *cx,
         500                 :                                   JSObject *obj)
         501                 : {
         502               0 :   const nsACString &sql = entry->GetKey();
         503               0 :   jsval hitCount = UINT_TO_JSVAL(entry->mData.hitCount);
         504               0 :   jsval totalTime = UINT_TO_JSVAL(entry->mData.totalTime);
         505                 :
         506               0 :   JSObject *arrayObj = JS_NewArrayObject(cx, 2, nsnull);
         507               0 :   if (!arrayObj) {
         508               0 :     return false;
         509                 :   }
         510               0 :   JS::AutoObjectRooter root(cx, arrayObj);
         511               0 :   return (JS_SetElement(cx, arrayObj, 0, &hitCount)
         512               0 :           && JS_SetElement(cx, arrayObj, 1, &totalTime)
         513                 :           && JS_DefineProperty(cx, obj,
         514                 :                                sql.BeginReading(),
         515                 :                                OBJECT_TO_JSVAL(arrayObj),
         516               0 :                                NULL, NULL, JSPROP_ENUMERATE));
         517                 : }
         518                 :
         519                 : bool
         520              14 : TelemetryImpl::AddSQLInfo(JSContext *cx, JSObject *rootObj, bool mainThread)
         521                 : {
         522              14 :   JSObject *statsObj = JS_NewObject(cx, NULL, NULL, NULL);
         523              14 :   if (!statsObj)
         524               0 :     return false;
         525              28 :   JS::AutoObjectRooter root(cx, statsObj);
         526                 :
         527                 :   AutoHashtable<SlowSQLEntryType> &sqlMap = (mainThread
         528                 :                                              ? mSlowSQLOnMainThread
         529              14 :                                              : mSlowSQLOnOtherThread);
         530              14 :   if (!sqlMap.ReflectHashtable(StatementReflector, cx, statsObj)) {
         531               0 :     return false;
         532                 :   }
         533                 :
         534                 :   return JS_DefineProperty(cx, rootObj,
         535                 :                            mainThread ? "mainThread" : "otherThreads",
         536                 :                            OBJECT_TO_JSVAL(statsObj),
         537              14 :                            NULL, NULL, JSPROP_ENUMERATE);
         538                 : }
         539                 :
         540                 : nsresult
         541             893 : TelemetryImpl::GetHistogramEnumId(const char *name, Telemetry::ID *id)
         542                 : {
         543             893 :   if (!sTelemetry) {
         544               0 :     return NS_ERROR_FAILURE;
         545                 :   }
         546                 :
         547                 :   // Cache names
         548                 :   // Note the histogram names are statically allocated
         549             893 :   TelemetryImpl::HistogramMapType *map = &sTelemetry->mHistogramMap;
         550             893 :   if (!map->Count()) {
         551            3024 :     for (PRUint32 i = 0; i < Telemetry::HistogramCount; i++) {
         552            3012 :       CharPtrEntryType *entry = map->PutEntry(gHistograms[i].id);
         553            3012 :       if (NS_UNLIKELY(!entry)) {
         554               0 :         map->Clear();
         555               0 :         return NS_ERROR_OUT_OF_MEMORY;
         556                 :       }
         557            3012 :       entry->mData = (Telemetry::ID) i;
         558                 :     }
         559                 :   }
         560                 :
         561             893 :   CharPtrEntryType *entry = map->GetEntry(name);
         562             893 :   if (!entry) {
         563             283 :     return NS_ERROR_INVALID_ARG;
         564                 :   }
         565             610 :   *id = entry->mData;
         566             610 :   return NS_OK;
         567                 : }
         568                 :
         569                 : nsresult
         570             173 : TelemetryImpl::GetHistogramByName(const nsACString &name, Histogram **ret)
         571                 : {
         572                 :   Telemetry::ID id;
         573             173 :   nsresult rv = GetHistogramEnumId(PromiseFlatCString(name).get(), &id);
         574             173 :   if (NS_FAILED(rv)) {
         575               3 :     return rv;
         576                 :   }
         577                 :
         578             170 :   rv = GetHistogramByEnumId(id, ret);
         579             170 :   if (NS_FAILED(rv))
         580               0 :     return rv;
         581                 :
         582             170 :   return NS_OK;
         583                 : }
         584                 :
         585                 : NS_IMETHODIMP
         586              72 : TelemetryImpl::HistogramFrom(const nsACString &name, const nsACString &existing_name,
         587                 :                              JSContext *cx, jsval *ret)
         588                 : {
         589                 :   Histogram *existing;
         590              72 :   nsresult rv = GetHistogramByName(existing_name, &existing);
         591              72 :   if (NS_FAILED(rv))
         592               0 :     return rv;
         593                 :
         594                 :   PRUint32 histogramType;
         595              72 :   bool success = TelemetryHistogramType(existing, &histogramType);
         596              72 :   if (!success)
         597               0 :     return NS_ERROR_INVALID_ARG;
         598                 :
         599                 :   Histogram *clone;
         600             144 :   rv = HistogramGet(PromiseFlatCString(name).get(), existing->declared_min(),
         601             144 :                     existing->declared_max(), existing->bucket_count(),
         602             288 :                     histogramType, &clone);
         603              72 :   if (NS_FAILED(rv))
         604               0 :     return rv;
         605                 :
         606             144 :   Histogram::SampleSet ss;
         607              72 :   existing->SnapshotSample(&ss);
         608              72 :   clone->AddSampleSet(ss);
         609              72 :   return WrapAndReturnHistogram(clone, cx, ret);
         610                 : }
         611                 :
         612                 : void
         613               8 : TelemetryImpl::IdentifyCorruptHistograms(StatisticsRecorder::Histograms &hs)
         614                 : {
         615             331 :   for (HistogramIterator it = hs.begin(); it != hs.end(); ++it) {
         616             323 :     Histogram *h = *it;
         617                 :
         618                 :     Telemetry::ID id;
         619             323 :     nsresult rv = GetHistogramEnumId(h->histogram_name().c_str(), &id);
         620                 :     // This histogram isn't a static histogram, just ignore it.
         621             323 :     if (NS_FAILED(rv)) {
         622             123 :       continue;
         623                 :     }
         624                 :
         625             200 :     if (gCorruptHistograms[id]) {
         626               0 :       continue;
         627                 :     }
         628                 :
         629             400 :     Histogram::SampleSet ss;
         630             200 :     h->SnapshotSample(&ss);
         631             200 :     Histogram::Inconsistencies check = h->FindCorruption(ss);
         632             200 :     bool corrupt = (check != Histogram::NO_INCONSISTENCIES);
         633                 :
         634             200 :     if (corrupt) {
         635               0 :       Telemetry::ID corruptID = Telemetry::HistogramCount;
         636               0 :       if (check & Histogram::RANGE_CHECKSUM_ERROR) {
         637               0 :         corruptID = Telemetry::RANGE_CHECKSUM_ERRORS;
         638               0 :       } else if (check & Histogram::BUCKET_ORDER_ERROR) {
         639               0 :         corruptID = Telemetry::BUCKET_ORDER_ERRORS;
         640               0 :       } else if (check & Histogram::COUNT_HIGH_ERROR) {
         641               0 :         corruptID = Telemetry::TOTAL_COUNT_HIGH_ERRORS;
         642               0 :       } else if (check & Histogram::COUNT_LOW_ERROR) {
         643               0 :         corruptID = Telemetry::TOTAL_COUNT_LOW_ERRORS;
         644                 :       }
         645               0 :       Telemetry::Accumulate(corruptID, 1);
         646                 :     }
         647                 :
         648             200 :     gCorruptHistograms[id] = corrupt;
         649                 :   }
         650               8 : }
         651                 :
         652                 : bool
         653             323 : TelemetryImpl::ShouldReflectHistogram(Histogram *h)
         654                 : {
         655             323 :   const char *name = h->histogram_name().c_str();
         656                 :   Telemetry::ID id;
         657             323 :   nsresult rv = GetHistogramEnumId(name, &id);
         658             323 :   if (NS_FAILED(rv)) {
         659                 :     // GetHistogramEnumId generally should not fail.  But a lookup
         660                 :     // failure shouldn't prevent us from reflecting histograms into JS.
         661                 :     //
         662                 :     // However, these two histograms are created by Histogram itself for
         663                 :     // tracking corruption.  We have our own histograms for that, so
         664                 :     // ignore these two.
         665             246 :     if (strcmp(name, "Histogram.InconsistentCountHigh") == 0
         666             123 :         || strcmp(name, "Histogram.InconsistentCountLow") == 0) {
         667               0 :       return false;
         668                 :     }
         669             123 :     return true;
         670                 :   } else {
         671             200 :     return !gCorruptHistograms[id];
         672                 :   }
         673                 : }
         674                 :
         675                 : // Compute the name to pass into Histogram for the addon histogram
         676                 : // 'name' from the addon 'id'.  We can't use 'name' directly because it
         677                 : // might conflict with other histograms in other addons or even with our
         678                 : // own.
         679                 : void
         680               2 : AddonHistogramName(const nsACString &id, const nsACString &name,
         681                 :                    nsACString &ret)
         682                 : {
         683               2 :   ret.Append(id);
         684               2 :   ret.Append(NS_LITERAL_CSTRING(":"));
         685               2 :   ret.Append(name);
         686               2 : }
         687                 :
         688                 : NS_IMETHODIMP
         689               6 : TelemetryImpl::RegisterAddonHistogram(const nsACString &id,
         690                 :                                       const nsACString &name,
         691                 :                                       PRUint32 min, PRUint32 max,
         692                 :                                       PRUint32 bucketCount,
         693                 :                                       PRUint32 histogramType)
         694                 : {
         695               6 :   AddonEntryType *addonEntry = mAddonMap.GetEntry(id);
         696               6 :   if (!addonEntry) {
         697               3 :     addonEntry = mAddonMap.PutEntry(id);
         698               3 :     if (NS_UNLIKELY(!addonEntry)) {
         699               0 :       return NS_ERROR_OUT_OF_MEMORY;
         700                 :     }
         701               3 :     addonEntry->mData = new AddonHistogramMapType();
         702                 :   }
         703                 :
         704               6 :   AddonHistogramMapType *histogramMap = addonEntry->mData;
         705               6 :   AddonHistogramEntryType *histogramEntry = histogramMap->GetEntry(name);
         706                 :   // Can't re-register the same histogram.
         707               6 :   if (histogramEntry) {
         708               1 :     return NS_ERROR_FAILURE;
         709                 :   }
         710                 :
         711               5 :   histogramEntry = histogramMap->PutEntry(name);
         712               5 :   if (NS_UNLIKELY(!histogramEntry)) {
         713               0 :     return NS_ERROR_OUT_OF_MEMORY;
         714                 :   }
         715                 :
         716               5 :   AddonHistogramInfo &info = histogramEntry->mData;
         717               5 :   info.min = min;
         718               5 :   info.max = max;
         719               5 :   info.bucketCount = bucketCount;
         720               5 :   info.histogramType = histogramType;
         721                 :
         722               5 :   return NS_OK;
         723                 : }
         724                 :
         725                 : NS_IMETHODIMP
         726               3 : TelemetryImpl::GetAddonHistogram(const nsACString &id, const nsACString &name,
         727                 :                                  JSContext *cx, jsval *ret)
         728                 : {
         729               3 :   AddonEntryType *addonEntry = mAddonMap.GetEntry(id);
         730                 :   // The given id has not been registered.
         731               3 :   if (!addonEntry) {
         732               1 :     return NS_ERROR_INVALID_ARG;
         733                 :   }
         734                 :
         735               2 :   AddonHistogramMapType *histogramMap = addonEntry->mData;
         736               2 :   AddonHistogramEntryType *histogramEntry = histogramMap->GetEntry(name);
         737                 :   // The given histogram name has not been registered.
         738               2 :   if (!histogramEntry) {
         739               0 :     return NS_ERROR_INVALID_ARG;
         740                 :   }
         741                 :
         742               2 :   AddonHistogramInfo &info = histogramEntry->mData;
         743               2 :   if (!info.h) {
         744               4 :     nsCAutoString actualName;
         745               2 :     AddonHistogramName(id, name, actualName);
         746               2 :     if (!CreateHistogramForAddon(actualName, info)) {
         747               0 :       return NS_ERROR_FAILURE;
         748                 :     }
         749                 :   }
         750               2 :   return WrapAndReturnHistogram(info.h, cx, ret);
         751                 : }
         752                 :
         753                 : NS_IMETHODIMP
         754               1 : TelemetryImpl::UnregisterAddonHistograms(const nsACString &id)
         755                 : {
         756               1 :   AddonEntryType *addonEntry = mAddonMap.GetEntry(id);
         757               1 :   if (addonEntry) {
         758                 :     // Histogram's destructor is private, so this is the best we can do.
         759                 :     // The histograms the addon created *will* stick around, but they
         760                 :     // will be deleted if and when the addon registers histograms with
         761                 :     // the same names.
         762               1 :     delete addonEntry->mData;
         763               1 :     mAddonMap.RemoveEntry(id);
         764                 :   }
         765                 :
         766               1 :   return NS_OK;
         767                 : }
         768                 :
         769                 : NS_IMETHODIMP
         770               8 : TelemetryImpl::GetHistogramSnapshots(JSContext *cx, jsval *ret)
         771                 : {
         772               8 :   JSObject *root_obj = JS_NewObject(cx, NULL, NULL, NULL);
         773               8 :   if (!root_obj)
         774               0 :     return NS_ERROR_FAILURE;
         775               8 :   *ret = OBJECT_TO_JSVAL(root_obj);
         776                 :
         777                 :   // Ensure that all the HISTOGRAM_FLAG histograms have been created, so
         778                 :   // that their values are snapshotted.
         779            2016 :   for (size_t i = 0; i < Telemetry::HistogramCount; ++i) {
         780            2008 :     if (gHistograms[i].histogramType == nsITelemetry::HISTOGRAM_FLAG) {
         781                 :       Histogram *h;
         782              32 :       DebugOnly<nsresult> rv = GetHistogramByEnumId(Telemetry::ID(i), &h);
         783              16 :       MOZ_ASSERT(NS_SUCCEEDED(rv));
         784                 :     }
         785                 :   };
         786                 :
         787              16 :   StatisticsRecorder::Histograms hs;
         788               8 :   StatisticsRecorder::GetHistograms(&hs);
         789                 :
         790                 :   // We identify corrupt histograms first, rather than interspersing it
         791                 :   // in the loop below, to ensure that our corruption statistics don't
         792                 :   // depend on histogram enumeration order.
         793                 :   //
         794                 :   // Of course, we hope that all of these corruption-statistics
         795                 :   // histograms are not themselves corrupt...
         796               8 :   IdentifyCorruptHistograms(hs);
         797                 :
         798                 :   // OK, now we can actually reflect things.
         799             331 :   for (HistogramIterator it = hs.begin(); it != hs.end(); ++it) {
         800             323 :     Histogram *h = *it;
         801             323 :     if (!ShouldReflectHistogram(h)) {
         802               0 :       continue;
         803                 :     }
         804                 :
         805             323 :     JSObject *hobj = JS_NewObject(cx, NULL, NULL, NULL);
         806             323 :     if (!hobj) {
         807               0 :       return NS_ERROR_FAILURE;
         808                 :     }
         809             646 :     JS::AutoObjectRooter root(cx, hobj);
         810             323 :     switch (ReflectHistogramSnapshot(cx, hobj, h)) {
         811                 :     case REFLECT_CORRUPT:
         812                 :       // We can still hit this case even if ShouldReflectHistograms
         813                 :       // returns true.  The histogram lies outside of our control
         814                 :       // somehow; just skip it.
         815               0 :       continue;
         816                 :     case REFLECT_FAILURE:
         817               0 :       return NS_ERROR_FAILURE;
         818                 :     case REFLECT_OK:
         819             646 :       if (!JS_DefineProperty(cx, root_obj, h->histogram_name().c_str(),
         820             646 :                              OBJECT_TO_JSVAL(hobj), NULL, NULL, JSPROP_ENUMERATE)) {
         821               0 :         return NS_ERROR_FAILURE;
         822                 :       }
         823                 :     }
         824                 :   }
         825               8 :   return NS_OK;
         826                 : }
         827                 :
         828                 : bool
         829               3 : TelemetryImpl::CreateHistogramForAddon(const nsACString &name,
         830                 :                                        AddonHistogramInfo &info)
         831                 : {
         832                 :   Histogram *h;
         833               3 :   nsresult rv = HistogramGet(PromiseFlatCString(name).get(),
         834                 :                              info.min, info.max, info.bucketCount,
         835               3 :                              info.histogramType, &h);
         836               3 :   if (NS_FAILED(rv)) {
         837               0 :     return false;
         838                 :   }
         839                 :   // Don't let this histogram be reported via the normal means
         840                 :   // (e.g. Telemetry.registeredHistograms); we'll make it available in
         841                 :   // other ways.
         842               3 :   h->ClearFlags(Histogram::kUmaTargetedHistogramFlag);
         843               3 :   info.h = h;
         844               3 :   return true;
         845                 : }
         846                 :
         847                 : bool
         848               8 : TelemetryImpl::AddonHistogramReflector(AddonHistogramEntryType *entry,
         849                 :                                        JSContext *cx, JSObject *obj)
         850                 : {
         851               8 :   AddonHistogramInfo &info = entry->mData;
         852                 :
         853                 :   // Never even accessed the histogram.
         854               8 :   if (!info.h) {
         855                 :     // Have to force creation of HISTOGRAM_FLAG histograms.
         856               5 :     if (info.histogramType != nsITelemetry::HISTOGRAM_FLAG)
         857               4 :       return true;
         858                 :
         859               1 :     if (!CreateHistogramForAddon(entry->GetKey(), info)) {
         860               0 :       return false;
         861                 :     }
         862                 :   }
         863                 :
         864               4 :   JSObject *snapshot = JS_NewObject(cx, NULL, NULL, NULL);
         865               4 :   if (!snapshot) {
         866                 :     // Just consider this to be skippable.
         867               0 :     return true;
         868                 :   }
         869               8 :   JS::AutoObjectRooter r(cx, snapshot);
         870               4 :   switch (ReflectHistogramSnapshot(cx, snapshot, info.h)) {
         871                 :   case REFLECT_FAILURE:
         872                 :   case REFLECT_CORRUPT:
         873               0 :     return false;
         874                 :   case REFLECT_OK:
         875               4 :     const nsACString &histogramName = entry->GetKey();
         876               8 :     if (!JS_DefineProperty(cx, obj,
         877               4 :                            PromiseFlatCString(histogramName).get(),
         878                 :                            OBJECT_TO_JSVAL(snapshot), NULL, NULL,
         879               4 :                            JSPROP_ENUMERATE)) {
         880               0 :       return false;
         881                 :     }
         882               4 :     break;
         883                 :   }
         884               4 :   return true;
         885                 : }
         886                 :
         887                 : bool
         888               5 : TelemetryImpl::AddonReflector(AddonEntryType *entry,
         889                 :                               JSContext *cx, JSObject *obj)
         890                 : {
         891               5 :   const nsACString &addonId = entry->GetKey();
         892               5 :   JSObject *subobj = JS_NewObject(cx, NULL, NULL, NULL);
         893               5 :   if (!subobj) {
         894               0 :     return false;
         895                 :   }
         896              10 :   JS::AutoObjectRooter r(cx, subobj);
         897                 :
         898               5 :   AddonHistogramMapType *map = entry->mData;
         899              10 :   if (!(map->ReflectHashtable(AddonHistogramReflector, cx, subobj)
         900                 :         && JS_DefineProperty(cx, obj,
         901               5 :                              PromiseFlatCString(addonId).get(),
         902                 :                              OBJECT_TO_JSVAL(subobj), NULL, NULL,
         903              10 :                              JSPROP_ENUMERATE))) {
         904               0 :     return false;
         905                 :   }
         906               5 :   return true;
         907                 : }
         908                 :
         909                 : NS_IMETHODIMP
         910               5 : TelemetryImpl::GetAddonHistogramSnapshots(JSContext *cx, jsval *ret)
         911                 : {
         912               5 :   *ret = JSVAL_VOID;
         913               5 :   JSObject *obj = JS_NewObject(cx, NULL, NULL, NULL);
         914               5 :   if (!obj) {
         915               0 :     return NS_ERROR_FAILURE;
         916                 :   }
         917              10 :   JS::AutoObjectRooter r(cx, obj);
         918                 :
         919               5 :   if (!mAddonMap.ReflectHashtable(AddonReflector, cx, obj)) {
         920               0 :     return NS_ERROR_FAILURE;
         921                 :   }
         922               5 :   *ret = OBJECT_TO_JSVAL(obj);
         923               5 :   return NS_OK;
         924                 : }
         925                 :
         926                 : NS_IMETHODIMP
         927               7 : TelemetryImpl::GetSlowSQL(JSContext *cx, jsval *ret)
         928                 : {
         929               7 :   JSObject *root_obj = JS_NewObject(cx, NULL, NULL, NULL);
         930               7 :   if (!root_obj)
         931               0 :     return NS_ERROR_FAILURE;
         932               7 :   *ret = OBJECT_TO_JSVAL(root_obj);
         933                 :
         934              14 :   MutexAutoLock hashMutex(mHashMutex);
         935                 :   // Add info about slow SQL queries on the main thread
         936               7 :   if (!AddSQLInfo(cx, root_obj, true))
         937               0 :     return NS_ERROR_FAILURE;
         938                 :   // Add info about slow SQL queries on other threads
         939               7 :   if (!AddSQLInfo(cx, root_obj, false))
         940               0 :     return NS_ERROR_FAILURE;
         941                 :
         942               7 :   return NS_OK;
         943                 : }
         944                 :
         945                 : NS_IMETHODIMP
         946               8 : TelemetryImpl::GetRegisteredHistograms(JSContext *cx, jsval *ret)
         947                 : {
         948               8 :   size_t count = ArrayLength(gHistograms);
         949               8 :   JSObject *info = JS_NewObject(cx, NULL, NULL, NULL);
         950               8 :   if (!info)
         951               0 :     return NS_ERROR_FAILURE;
         952              16 :   JS::AutoObjectRooter root(cx, info);
         953                 :
         954            2016 :   for (size_t i = 0; i < count; ++i) {
         955            2008 :     JSString *comment = JS_InternString(cx, gHistograms[i].comment);
         956                 :
         957            2008 :     if (!(comment
         958                 :           && JS_DefineProperty(cx, info, gHistograms[i].id,
         959                 :                                STRING_TO_JSVAL(comment), NULL, NULL,
         960            2008 :                                JSPROP_ENUMERATE))) {
         961               0 :       return NS_ERROR_FAILURE;
         962                 :     }
         963                 :   }
         964                 :
         965               8 :   *ret = OBJECT_TO_JSVAL(info);
         966               8 :   return NS_OK;
         967                 : }
         968                 :
         969                 : NS_IMETHODIMP
         970             101 : TelemetryImpl::GetHistogramById(const nsACString &name, JSContext *cx, jsval *ret)
         971                 : {
         972                 :   Histogram *h;
         973             101 :   nsresult rv = GetHistogramByName(name, &h);
         974             101 :   if (NS_FAILED(rv))
         975               3 :     return rv;
         976                 :
         977              98 :   return WrapAndReturnHistogram(h, cx, ret);
         978                 : }
         979                 :
         980                 : class TelemetrySessionData : public nsITelemetrySessionData
         981                 : {
         982                 :   NS_DECL_ISUPPORTS
         983                 :   NS_DECL_NSITELEMETRYSESSIONDATA
         984                 :
         985                 : public:
         986                 :   static nsresult LoadFromDisk(nsIFile *, TelemetrySessionData **ptr);
         987                 :   static nsresult SaveToDisk(nsIFile *, const nsACString &uuid);
         988                 :
         989                 :   TelemetrySessionData(const char *uuid);
         990                 :   ~TelemetrySessionData();
         991                 :
         992                 : private:
         993                 :   typedef nsBaseHashtableET<nsUint32HashKey, Histogram::SampleSet> EntryType;
         994                 :   typedef AutoHashtable<EntryType> SessionMapType;
         995                 :   static bool SampleReflector(EntryType *entry, JSContext *cx, JSObject *obj);
         996                 :   SessionMapType mSampleSetMap;
         997                 :   nsCString mUUID;
         998                 :
         999                 :   bool DeserializeHistogramData(Pickle &pickle, void **iter);
        1000                 :   static bool SerializeHistogramData(Pickle &pickle);
        1001                 :
        1002                 :   // The file format version.  Should be incremented whenever we change
        1003                 :   // how individual SampleSets are stored in the file.
        1004                 :   static const unsigned int sVersion = 1;
        1005                 : };
        1006                 :
        1007              36 : NS_IMPL_THREADSAFE_ISUPPORTS1(TelemetrySessionData, nsITelemetrySessionData)
        1008                 :
        1009               2 : TelemetrySessionData::TelemetrySessionData(const char *uuid)
        1010               2 :   : mUUID(uuid)
        1011                 : {
        1012               2 : }
        1013                 :
        1014               2 : TelemetrySessionData::~TelemetrySessionData()
        1015                 : {
        1016               2 : }
        1017                 :
        1018                 : NS_IMETHODIMP
        1019               1 : TelemetrySessionData::GetUuid(nsACString &uuid)
        1020                 : {
        1021               1 :   uuid = mUUID;
        1022               1 :   return NS_OK;
        1023                 : }
        1024                 :
        1025                 : bool
        1026              37 : TelemetrySessionData::SampleReflector(EntryType *entry, JSContext *cx,
        1027                 :                                       JSObject *snapshots)
        1028                 : {
        1029                 :   // Don't reflect histograms with no data associated with them.
        1030              37 :   if (entry->mData.sum() == 0) {
        1031               9 :     return true;
        1032                 :   }
        1033                 :
        1034                 :   // This has the undesirable effect of creating a histogram for the
        1035                 :   // current session with the given ID.  But there's no good way to
        1036                 :   // compute the ranges and buckets from scratch.
        1037              28 :   Histogram *h = nsnull;
        1038              28 :   nsresult rv = GetHistogramByEnumId(Telemetry::ID(entry->GetKey()), &h);
        1039              28 :   if (NS_FAILED(rv)) {
        1040               0 :     return true;
        1041                 :   }
        1042                 :
        1043              28 :   JSObject *snapshot = JS_NewObject(cx, NULL, NULL, NULL);
        1044              28 :   if (!snapshot) {
        1045               0 :     return false;
        1046                 :   }
        1047              56 :   JS::AutoObjectRooter root(cx, snapshot);
        1048              28 :   switch (ReflectHistogramAndSamples(cx, snapshot, h, entry->mData)) {
        1049                 :   case REFLECT_OK:
        1050                 :     return JS_DefineProperty(cx, snapshots,
        1051              28 :                              h->histogram_name().c_str(),
        1052                 :                              OBJECT_TO_JSVAL(snapshot), NULL, NULL,
        1053              56 :                              JSPROP_ENUMERATE);
        1054                 :   case REFLECT_CORRUPT:
        1055                 :     // Just ignore this one.
        1056               0 :     return true;
        1057                 :   case REFLECT_FAILURE:
        1058               0 :     return false;
        1059                 :   default:
        1060               0 :     MOZ_NOT_REACHED("unhandled reflection status");
        1061                 :     return false;
        1062                 :   }
        1063                 : }
        1064                 :
        1065                 : NS_IMETHODIMP
        1066               1 : TelemetrySessionData::GetSnapshots(JSContext *cx, jsval *ret)
        1067                 : {
        1068               1 :   JSObject *snapshots = JS_NewObject(cx, NULL, NULL, NULL);
        1069               1 :   if (!snapshots) {
        1070               0 :     return NS_ERROR_FAILURE;
        1071                 :   }
        1072               2 :   JS::AutoObjectRooter root(cx, snapshots);
        1073                 :
        1074               1 :   if (!mSampleSetMap.ReflectHashtable(SampleReflector, cx, snapshots)) {
        1075               0 :     return NS_ERROR_FAILURE;
        1076                 :   }
        1077                 :
        1078               1 :   *ret = OBJECT_TO_JSVAL(snapshots);
        1079               1 :   return NS_OK;
        1080                 : }
        1081                 :
        1082                 : bool
        1083               2 : TelemetrySessionData::DeserializeHistogramData(Pickle &pickle, void **iter)
        1084                 : {
        1085               2 :   PRUint32 count = 0;
        1086               2 :   if (!pickle.ReadUInt32(iter, &count)) {
        1087               0 :     return false;
        1088                 :   }
        1089                 :
        1090              76 :   for (size_t i = 0; i < count; ++i) {
        1091                 :     int stored_length;
        1092                 :     const char *name;
        1093              74 :     if (!pickle.ReadData(iter, &name, &stored_length)) {
        1094               0 :       return false;
        1095                 :     }
        1096                 :
        1097                 :     Telemetry::ID id;
        1098              74 :     nsresult rv = TelemetryImpl::GetHistogramEnumId(name, &id);
        1099              74 :     if (NS_FAILED(rv)) {
        1100                 :       // We serialized a non-static histogram or we serialized a
        1101                 :       // histogram that is no longer defined in TelemetryHistograms.h.
        1102                 :       // Just drop its data on the floor.  If we can't deserialize the
        1103                 :       // data, though, we're in trouble.
        1104              68 :       Histogram::SampleSet ss;
        1105              34 :       if (!ss.Deserialize(iter, pickle)) {
        1106               0 :         return false;
        1107                 :       }
        1108                 :     } else {
        1109              40 :       EntryType *entry = mSampleSetMap.GetEntry(id);
        1110              40 :       if (!entry) {
        1111              40 :         entry = mSampleSetMap.PutEntry(id);
        1112              40 :         if (NS_UNLIKELY(!entry)) {
        1113               0 :           return false;
        1114                 :         }
        1115              40 :         if (!entry->mData.Deserialize(iter, pickle)) {
        1116               0 :           return false;
        1117                 :         }
        1118                 :       }
        1119                 :     }
        1120                 :   }
        1121                 :
        1122               2 :   return true;
        1123                 : }
        1124                 :
        1125                 : nsresult
        1126               2 : TelemetrySessionData::LoadFromDisk(nsIFile *file, TelemetrySessionData **ptr)
        1127                 : {
        1128               2 :   *ptr = nsnull;
        1129                 :   nsresult rv;
        1130               4 :   nsCOMPtr<nsILocalFile> f(do_QueryInterface(file, &rv));
        1131               2 :   if (NS_FAILED(rv)) {
        1132               0 :     return rv;
        1133                 :   }
        1134                 :
        1135               4 :   AutoFDClose fd;
        1136               2 :   rv = f->OpenNSPRFileDesc(PR_RDONLY, 0, &fd);
        1137               2 :   if (NS_FAILED(rv)) {
        1138               0 :     return NS_ERROR_FAILURE;
        1139                 :   }
        1140                 :
        1141                 :   // If there's not even enough data to read the header for the pickle,
        1142                 :   // don't bother.  Conveniently, this handles the error case as well.
        1143               2 :   PRInt32 size = PR_Available(fd);
        1144               2 :   if (size < static_cast<PRInt32>(sizeof(Pickle::Header))) {
        1145               0 :     return NS_ERROR_FAILURE;
        1146                 :   }
        1147                 :
        1148               6 :   nsAutoArrayPtr<char> data(new char[size]);
        1149               2 :   PRInt32 amount = PR_Read(fd, data, size);
        1150               2 :   if (amount != size) {
        1151               0 :     return NS_ERROR_FAILURE;
        1152                 :   }
        1153                 :
        1154               4 :   Pickle pickle(data, size);
        1155               2 :   void *iter = NULL;
        1156                 :
        1157                 :   // Make sure that how much data the pickle thinks it has corresponds
        1158                 :   // with how much data we actually read.
        1159               2 :   const Pickle::Header *header = pickle.headerT<Pickle::Header>();
        1160               2 :   if (header->payload_size != static_cast<PRUint32>(amount) - sizeof(*header)) {
        1161               0 :     return NS_ERROR_FAILURE;
        1162                 :   }
        1163                 :
        1164                 :   unsigned int storedVersion;
        1165               2 :   if (!(pickle.ReadUInt32(&iter, &storedVersion)
        1166               2 :         && storedVersion == sVersion)) {
        1167               0 :     return NS_ERROR_FAILURE;
        1168                 :   }
        1169                 :
        1170                 :   const char *uuid;
        1171                 :   int uuidLength;
        1172               2 :   if (!pickle.ReadData(&iter, &uuid, &uuidLength)) {
        1173               0 :     return NS_ERROR_FAILURE;
        1174                 :   }
        1175                 :
        1176               4 :   nsAutoPtr<TelemetrySessionData> sessionData(new TelemetrySessionData(uuid));
        1177               2 :   if (!sessionData->DeserializeHistogramData(pickle, &iter)) {
        1178               0 :     return NS_ERROR_FAILURE;
        1179                 :   }
        1180                 :
        1181               2 :   *ptr = sessionData.forget();
        1182               2 :   return NS_OK;
        1183                 : }
        1184                 :
        1185                 : bool
        1186               2 : TelemetrySessionData::SerializeHistogramData(Pickle &pickle)
        1187                 : {
        1188               4 :   StatisticsRecorder::Histograms hs;
        1189               2 :   StatisticsRecorder::GetHistograms(&hs);
        1190                 :
        1191               2 :   if (!pickle.WriteUInt32(hs.size())) {
        1192               0 :     return false;
        1193                 :   }
        1194                 :
        1195             152 :   for (StatisticsRecorder::Histograms::const_iterator it = hs.begin();
        1196              76 :        it != hs.end();
        1197                 :        ++it) {
        1198              74 :     const Histogram *h = *it;
        1199              74 :     const char *name = h->histogram_name().c_str();
        1200                 :
        1201             148 :     Histogram::SampleSet ss;
        1202              74 :     h->SnapshotSample(&ss);
        1203                 :
        1204              74 :     if (!(pickle.WriteData(name, strlen(name)+1)
        1205              74 :           && ss.Serialize(&pickle))) {
        1206               0 :       return false;
        1207                 :     }
        1208                 :   }
        1209                 :
        1210               2 :   return true;
        1211                 : }
        1212                 :
        1213                 : nsresult
        1214               2 : TelemetrySessionData::SaveToDisk(nsIFile *file, const nsACString &uuid)
        1215                 : {
        1216                 :   nsresult rv;
        1217               4 :   nsCOMPtr<nsILocalFile> f(do_QueryInterface(file, &rv));
        1218               2 :   if (NS_FAILED(rv)) {
        1219               0 :     return rv;
        1220                 :   }
        1221                 :
        1222               4 :   AutoFDClose fd;
        1223               2 :   rv = f->OpenNSPRFileDesc(PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, 0600, &fd);
        1224               2 :   if (NS_FAILED(rv)) {
        1225               0 :     return rv;
        1226                 :   }
        1227                 :
        1228               4 :   Pickle pickle;
        1229               2 :   if (!pickle.WriteUInt32(sVersion)) {
        1230               0 :     return NS_ERROR_FAILURE;
        1231                 :   }
        1232                 :
        1233                 :   // Include the trailing NULL for the UUID to make reading easier.
        1234                 :   const char *data;
        1235               2 :   size_t length = uuid.GetData(&data);
        1236               2 :   if (!(pickle.WriteData(data, length+1)
        1237               2 :         && SerializeHistogramData(pickle))) {
        1238               0 :     return NS_ERROR_FAILURE;
        1239                 :   }
        1240                 :
        1241               2 :   PRInt32 amount = PR_Write(fd, static_cast<const char*>(pickle.data()),
        1242               4 :                             pickle.size());
        1243               2 :   if (amount != pickle.size()) {
        1244               0 :     return NS_ERROR_FAILURE;
        1245                 :   }
        1246                 :
        1247               2 :   return NS_OK;
        1248                 : }
        1249                 :
        1250                 : class SaveHistogramEvent : public nsRunnable
        1251               8 : {
        1252                 : public:
        1253               2 :   SaveHistogramEvent(nsIFile *file, const nsACString &uuid,
        1254                 :                      nsITelemetrySaveSessionDataCallback *callback)
        1255               2 :     : mFile(file), mUUID(uuid), mCallback(callback)
        1256               2 :   {}
        1257                 :
        1258               2 :   NS_IMETHOD Run()
        1259                 :   {
        1260               2 :     nsresult rv = TelemetrySessionData::SaveToDisk(mFile, mUUID);
        1261               2 :     mCallback->Handle(!!NS_SUCCEEDED(rv));
        1262               2 :     return rv;
        1263                 :   }
        1264                 :
        1265                 : private:
        1266                 :   nsCOMPtr<nsIFile> mFile;
        1267                 :   nsCString mUUID;
        1268                 :   nsCOMPtr<nsITelemetrySaveSessionDataCallback> mCallback;
        1269                 : };
        1270                 :
        1271                 : NS_IMETHODIMP
        1272               2 : TelemetryImpl::SaveHistograms(nsIFile *file, const nsACString &uuid,
        1273                 :                               nsITelemetrySaveSessionDataCallback *callback,
        1274                 :                               bool isSynchronous)
        1275                 : {
        1276               4 :   nsCOMPtr<nsIRunnable> event = new SaveHistogramEvent(file, uuid, callback);
        1277               2 :   if (isSynchronous) {
        1278               1 :     return event ? event->Run() : NS_ERROR_FAILURE;
        1279                 :   } else {
        1280               1 :     return NS_DispatchToCurrentThread(event);
        1281                 :   }
        1282                 : }
        1283                 :
        1284                 : class LoadHistogramEvent : public nsRunnable
        1285               8 : {
        1286                 : public:
        1287               2 :   LoadHistogramEvent(nsIFile *file,
        1288                 :                      nsITelemetryLoadSessionDataCallback *callback)
        1289               2 :     : mFile(file), mCallback(callback)
        1290               2 :   {}
        1291                 :
        1292               2 :   NS_IMETHOD Run()
        1293                 :   {
        1294               2 :     TelemetrySessionData *sessionData = nsnull;
        1295               2 :     nsresult rv = TelemetrySessionData::LoadFromDisk(mFile, &sessionData);
        1296               2 :     if (NS_FAILED(rv)) {
        1297               0 :       mCallback->Handle(nsnull);
        1298                 :     } else {
        1299               4 :       nsCOMPtr<nsITelemetrySessionData> data(sessionData);
        1300               2 :       mCallback->Handle(data);
        1301                 :     }
        1302               2 :     return rv;
        1303                 :   }
        1304                 :
        1305                 : private:
        1306                 :   nsCOMPtr<nsIFile> mFile;
        1307                 :   nsCOMPtr<nsITelemetryLoadSessionDataCallback> mCallback;
        1308                 : };
        1309                 :
        1310                 : NS_IMETHODIMP
        1311               2 : TelemetryImpl::LoadHistograms(nsIFile *file,
        1312                 :                               nsITelemetryLoadSessionDataCallback *callback,
        1313                 :                               bool isSynchronous)
        1314                 : {
        1315               4 :   nsCOMPtr<nsIRunnable> event = new LoadHistogramEvent(file, callback);
        1316               2 :   if (isSynchronous) {
        1317               1 :     return event ? event->Run() : NS_ERROR_FAILURE;
        1318                 :   } else {
        1319               1 :     return NS_DispatchToCurrentThread(event);
        1320                 :   }
        1321                 : }
        1322                 :
        1323                 : NS_IMETHODIMP
        1324               0 : TelemetryImpl::GetCanRecord(bool *ret) {
        1325               0 :   *ret = mCanRecord;
        1326               0 :   return NS_OK;
        1327                 : }
        1328                 :
        1329                 : NS_IMETHODIMP
        1330               2 : TelemetryImpl::SetCanRecord(bool canRecord) {
        1331               2 :   mCanRecord = !!canRecord;
        1332               2 :   return NS_OK;
        1333                 : }
        1334                 :
        1335                 : bool
        1336         1278467 : TelemetryImpl::CanRecord() {
        1337         1278467 :   return !sTelemetry || sTelemetry->mCanRecord;
        1338                 : }
        1339                 :
        1340                 : NS_IMETHODIMP
        1341               3 : TelemetryImpl::GetCanSend(bool *ret) {
        1342                 : #if defined(MOZILLA_OFFICIAL) && defined(MOZ_TELEMETRY_REPORTING)
        1343                 :   *ret = true;
        1344                 : #else
        1345               3 :   *ret = false;
        1346                 : #endif
        1347               3 :   return NS_OK;
        1348                 : }
        1349                 :
        1350                 : already_AddRefed<nsITelemetry>
        1351            1365 : TelemetryImpl::CreateTelemetryInstance()
        1352                 : {
        1353            1365 :   NS_ABORT_IF_FALSE(sTelemetry == NULL, "CreateTelemetryInstance may only be called once, via GetService()");
        1354            1365 :   sTelemetry = new TelemetryImpl();
        1355                 :   // AddRef for the local reference
        1356            1365 :   NS_ADDREF(sTelemetry);
        1357                 :   // AddRef for the caller
        1358            1365 :   NS_ADDREF(sTelemetry);
        1359            1365 :   return sTelemetry;
        1360                 : }
        1361                 :
        1362                 : void
        1363            1365 : TelemetryImpl::ShutdownTelemetry()
        1364                 : {
        1365            1365 :   NS_IF_RELEASE(sTelemetry);
        1366            1365 : }
        1367                 :
        1368                 : void
        1369             558 : TelemetryImpl::RecordSlowStatement(const nsACString &statement,
        1370                 :                                    const nsACString &dbName,
        1371                 :                                    PRUint32 delay)
        1372                 : {
        1373             558 :   MOZ_ASSERT(sTelemetry);
        1374             558 :   if (!sTelemetry->mCanRecord || !sTelemetry->mTrackedDBs.GetEntry(dbName))
        1375              10 :     return;
        1376                 :
        1377             548 :   AutoHashtable<SlowSQLEntryType> *slowSQLMap = NULL;
        1378             548 :   if (NS_IsMainThread())
        1379             128 :     slowSQLMap = &(sTelemetry->mSlowSQLOnMainThread);
        1380                 :   else
        1381             420 :     slowSQLMap = &(sTelemetry->mSlowSQLOnOtherThread);
        1382                 :
        1383            1096 :   MutexAutoLock hashMutex(sTelemetry->mHashMutex);
        1384             548 :   SlowSQLEntryType *entry = slowSQLMap->GetEntry(statement);
        1385             548 :   if (!entry) {
        1386             110 :     entry = slowSQLMap->PutEntry(statement);
        1387             110 :     if (NS_UNLIKELY(!entry))
        1388                 :       return;
        1389             110 :     entry->mData.hitCount = 0;
        1390             110 :     entry->mData.totalTime = 0;
        1391                 :   }
        1392             548 :   entry->mData.hitCount++;
        1393             548 :   entry->mData.totalTime += delay;
        1394                 : }
        1395                 :
        1396           17214 : NS_IMPL_THREADSAFE_ISUPPORTS1(TelemetryImpl, nsITelemetry)
        1397            1365 : NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsITelemetry, TelemetryImpl::CreateTelemetryInstance)
        1398                 :
        1399                 : #define NS_TELEMETRY_CID \
        1400                 :   {0xaea477f2, 0xb3a2, 0x469c, {0xaa, 0x29, 0x0a, 0x82, 0xd1, 0x32, 0xb8, 0x29}}
        1401                 : NS_DEFINE_NAMED_CID(NS_TELEMETRY_CID);
        1402                 :
        1403                 : const Module::CIDEntry kTelemetryCIDs[] = {
        1404                 :   { &kNS_TELEMETRY_CID, false, NULL, nsITelemetryConstructor },
        1405                 :   { NULL }
        1406                 : };
        1407                 :
        1408                 : const Module::ContractIDEntry kTelemetryContracts[] = {
        1409                 :   { "@mozilla.org/base/telemetry;1", &kNS_TELEMETRY_CID },
        1410                 :   { NULL }
        1411                 : };
        1412                 :
        1413                 : const Module kTelemetryModule = {
        1414                 :   Module::kVersion,
        1415                 :   kTelemetryCIDs,
        1416                 :   kTelemetryContracts,
        1417                 :   NULL,
        1418                 :   NULL,
        1419                 :   NULL,
        1420                 :   TelemetryImpl::ShutdownTelemetry
        1421                 : };
        1422                 :
        1423                 : } // anonymous namespace
        1424                 :
        1425                 : namespace mozilla {
        1426                 : namespace Telemetry {
        1427                 :
        1428                 : void
        1429         1278348 : Accumulate(ID aHistogram, PRUint32 aSample)
        1430                 : {
        1431         1278348 :   if (!TelemetryImpl::CanRecord()) {
        1432               0 :     return;
        1433                 :   }
        1434                 :   Histogram *h;
        1435         1278348 :   nsresult rv = GetHistogramByEnumId(aHistogram, &h);
        1436         1278348 :   if (NS_SUCCEEDED(rv))
        1437         1278348 :     h->Add(aSample);
        1438                 : }
        1439                 :
        1440                 : void
        1441          607827 : AccumulateTimeDelta(ID aHistogram, TimeStamp start, TimeStamp end)
        1442                 : {
        1443                 :   Accumulate(aHistogram,
        1444          607827 :              static_cast<PRUint32>((end - start).ToMilliseconds()));
        1445          607827 : }
        1446                 :
        1447                 : bool
        1448               0 : CanRecord()
        1449                 : {
        1450               0 :   return TelemetryImpl::CanRecord();
        1451                 : }
        1452                 :
        1453                 : base::Histogram*
        1454              56 : GetHistogramById(ID id)
        1455                 : {
        1456              56 :   Histogram *h = NULL;
        1457              56 :   GetHistogramByEnumId(id, &h);
        1458              56 :   return h;
        1459                 : }
        1460                 :
        1461                 : void
        1462             558 : RecordSlowSQLStatement(const nsACString &statement,
        1463                 :                        const nsACString &dbName,
        1464                 :                        PRUint32 delay)
        1465                 : {
        1466             558 :   TelemetryImpl::RecordSlowStatement(statement, dbName, delay);
        1467             558 : }
        1468                 :
        1469            1365 : void Init()
        1470                 : {
        1471                 :   // Make the service manager hold a long-lived reference to the service
        1472                 :   nsCOMPtr<nsITelemetry> telemetryService =
        1473            2730 :     do_GetService("@mozilla.org/base/telemetry;1");
        1474            1365 :   MOZ_ASSERT(telemetryService);
        1475            1365 : }
        1476                 :
        1477                 : } // namespace Telemetry
        1478                 : } // namespace mozilla
        1479                 :
        1480                 : NSMODULE_DEFN(nsTelemetryModule) = &kTelemetryModule;
        1481                 :
        1482                 : /**
        1483                 :  * The XRE_TelemetryAdd function is to be used by embedding applications
        1484                 :  * that can't use mozilla::Telemetry::Accumulate() directly.
        1485                 :  */
        1486                 : void
        1487              46 : XRE_TelemetryAccumulate(int aID, PRUint32 aSample)
        1488                 : {
        1489              46 :   mozilla::Telemetry::Accumulate((mozilla::Telemetry::ID) aID, aSample);
        1490            4234 : }

Generated by: LCOV version 1.7