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-06-02 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            5681 : AutoHashtable<EntryType>::AutoHashtable(PRUint32 initSize)
      86                 : {
      87            5681 :   this->Init(initSize);
      88            5681 : }
      89                 : 
      90                 : template<class EntryType>
      91            5679 : AutoHashtable<EntryType>::~AutoHashtable()
      92                 : {
      93            5679 :   this->Clear();
      94            5679 : }
      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            1464 : 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           32385 : HistogramGet(const char *name, PRUint32 min, PRUint32 max, PRUint32 bucketCount,
     247                 :              PRUint32 histogramType, Histogram **result)
     248                 : {
     249           32385 :   if (histogramType != nsITelemetry::HISTOGRAM_BOOLEAN
     250                 :       && histogramType != nsITelemetry::HISTOGRAM_FLAG) {
     251                 :     // Sanity checks for histogram parameters.
     252           26154 :     if (min >= max)
     253               0 :       return NS_ERROR_ILLEGAL_VALUE;
     254                 : 
     255           26154 :     if (bucketCount <= 2)
     256               2 :       return NS_ERROR_ILLEGAL_VALUE;
     257                 : 
     258           26152 :     if (min < 1)
     259               2 :       return NS_ERROR_ILLEGAL_VALUE;
     260                 :   }
     261                 : 
     262           32381 :   switch (histogramType) {
     263                 :   case nsITelemetry::HISTOGRAM_EXPONENTIAL:
     264           19785 :     *result = Histogram::FactoryGet(name, min, max, bucketCount, Histogram::kUmaTargetedHistogramFlag);
     265           19785 :     break;
     266                 :   case nsITelemetry::HISTOGRAM_LINEAR:
     267            6365 :     *result = LinearHistogram::FactoryGet(name, min, max, bucketCount, Histogram::kUmaTargetedHistogramFlag);
     268            6365 :     break;
     269                 :   case nsITelemetry::HISTOGRAM_BOOLEAN:
     270            6225 :     *result = BooleanHistogram::FactoryGet(name, Histogram::kUmaTargetedHistogramFlag);
     271            6225 :     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           32381 :   return NS_OK;
     279                 : }
     280                 : 
     281                 : // O(1) histogram lookup by numeric id
     282                 : nsresult
     283         1354137 : GetHistogramByEnumId(Telemetry::ID id, Histogram **ret)
     284                 : {
     285                 :   static Histogram* knownHistograms[Telemetry::HistogramCount] = {0};
     286         1354137 :   Histogram *h = knownHistograms[id];
     287         1354137 :   if (h) {
     288         1321836 :     *ret = h;
     289         1321836 :     return NS_OK;
     290                 :   }
     291                 : 
     292           32301 :   const TelemetryHistogram &p = gHistograms[id];
     293           32301 :   nsresult rv = HistogramGet(p.id, p.min, p.max, p.bucketCount, p.histogramType, &h);
     294           32301 :   if (NS_FAILED(rv))
     295               0 :     return rv;
     296                 : 
     297           32301 :   *ret = knownHistograms[id] = h;
     298           32301 :   return NS_OK;
     299                 : }
     300                 : 
     301                 : bool
     302             402 : FillRanges(JSContext *cx, JSObject *array, Histogram *h)
     303                 : {
     304          229987 :   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             402 :   return true;
     309                 : }
     310                 : 
     311                 : enum reflectStatus {
     312                 :   REFLECT_OK,
     313                 :   REFLECT_CORRUPT,
     314                 :   REFLECT_FAILURE
     315                 : };
     316                 : 
     317                 : enum reflectStatus
     318             402 : ReflectHistogramAndSamples(JSContext *cx, JSObject *obj, Histogram *h,
     319                 :                            const Histogram::SampleSet &ss)
     320                 : {
     321                 :   // We don't want to reflect corrupt histograms.
     322             402 :   if (h->FindCorruption(ss) != Histogram::NO_INCONSISTENCIES) {
     323               0 :     return REFLECT_CORRUPT;
     324                 :   }
     325                 : 
     326             402 :   if (!(JS_DefineProperty(cx, obj, "min", INT_TO_JSVAL(h->declared_min()), NULL, NULL, JSPROP_ENUMERATE)
     327             402 :         && JS_DefineProperty(cx, obj, "max", INT_TO_JSVAL(h->declared_max()), NULL, NULL, JSPROP_ENUMERATE)
     328             402 :         && JS_DefineProperty(cx, obj, "histogram_type", INT_TO_JSVAL(h->histogram_type()), NULL, NULL, JSPROP_ENUMERATE)
     329             804 :         && JS_DefineProperty(cx, obj, "sum", DOUBLE_TO_JSVAL(ss.sum()), NULL, NULL, JSPROP_ENUMERATE))) {
     330               0 :     return REFLECT_FAILURE;
     331                 :   }
     332                 : 
     333             402 :   const size_t count = h->bucket_count();
     334             402 :   JSObject *rarray = JS_NewArrayObject(cx, count, nsnull);
     335             402 :   if (!rarray) {
     336               0 :     return REFLECT_FAILURE;
     337                 :   }
     338             804 :   JS::AutoObjectRooter aroot(cx, rarray);
     339             402 :   if (!(FillRanges(cx, rarray, h)
     340                 :         && JS_DefineProperty(cx, obj, "ranges", OBJECT_TO_JSVAL(rarray),
     341             402 :                              NULL, NULL, JSPROP_ENUMERATE))) {
     342               0 :     return REFLECT_FAILURE;
     343                 :   }
     344                 : 
     345             402 :   JSObject *counts_array = JS_NewArrayObject(cx, count, NULL);
     346             402 :   if (!counts_array) {
     347               0 :     return REFLECT_FAILURE;
     348                 :   }
     349             804 :   JS::AutoObjectRooter croot(cx, counts_array);
     350             402 :   if (!JS_DefineProperty(cx, obj, "counts", OBJECT_TO_JSVAL(counts_array),
     351             402 :                          NULL, NULL, JSPROP_ENUMERATE)) {
     352               0 :     return REFLECT_FAILURE;
     353                 :   }
     354          229987 :   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             402 :   return REFLECT_OK;
     362                 : }
     363                 : 
     364                 : enum reflectStatus
     365             378 : ReflectHistogramSnapshot(JSContext *cx, JSObject *obj, Histogram *h)
     366                 : {
     367             756 :   Histogram::SampleSet ss;
     368             378 :   h->SnapshotSample(&ss);
     369             378 :   return ReflectHistogramAndSamples(cx, obj, h, ss);
     370                 : }
     371                 : 
     372                 : JSBool
     373             117 : JSHistogram_Add(JSContext *cx, unsigned argc, jsval *vp)
     374                 : {
     375             117 :   if (!argc) {
     376               0 :     JS_ReportError(cx, "Expected one argument");
     377               0 :     return JS_FALSE;
     378                 :   }
     379                 : 
     380             117 :   jsval v = JS_ARGV(cx, vp)[0];
     381                 :   int32 value;
     382                 : 
     383             117 :   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             117 :   if (!JS_ValueToECMAInt32(cx, v, &value)) {
     389               0 :     return JS_FALSE;
     390                 :   }
     391                 : 
     392             117 :   if (TelemetryImpl::CanRecord()) {
     393             116 :     JSObject *obj = JS_THIS_OBJECT(cx, vp);
     394             116 :     if (!obj) {
     395               0 :       return JS_FALSE;
     396                 :     }
     397                 : 
     398             116 :     Histogram *h = static_cast<Histogram*>(JS_GetPrivate(obj));
     399             116 :     if (h->histogram_type() == Histogram::BOOLEAN_HISTOGRAM)
     400               9 :       h->Add(!!value);
     401                 :     else
     402             107 :       h->Add(value);
     403                 :   }
     404             117 :   return JS_TRUE;
     405                 : }
     406                 : 
     407                 : JSBool
     408              51 : JSHistogram_Snapshot(JSContext *cx, unsigned argc, jsval *vp)
     409                 : {
     410              51 :   JSObject *obj = JS_THIS_OBJECT(cx, vp);
     411              51 :   if (!obj) {
     412               0 :     return JS_FALSE;
     413                 :   }
     414                 : 
     415              51 :   Histogram *h = static_cast<Histogram*>(JS_GetPrivate(obj));
     416              51 :   JSObject *snapshot = JS_NewObject(cx, nsnull, nsnull, nsnull);
     417              51 :   if (!snapshot)
     418               0 :     return JS_FALSE;
     419             102 :   JS::AutoObjectRooter sroot(cx, snapshot);
     420                 : 
     421              51 :   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              51 :     JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(snapshot));
     429              51 :     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            1419 : TelemetryImpl::TelemetryImpl():
     461                 : mHistogramMap(Telemetry::HistogramCount),
     462            1419 : mCanRecord(XRE_GetProcessType() == GeckoProcessType_Default),
     463            2838 : 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            1419 :   };
     473                 : 
     474            1419 :   mTrackedDBs.Init();
     475           21285 :   for (size_t i = 0; i < ArrayLength(trackedDBs); i++)
     476           19866 :     mTrackedDBs.PutEntry(nsDependentCString(trackedDBs[i]));
     477                 : 
     478                 : #ifdef DEBUG
     479                 :   // Mark immutable to prevent asserts on simultaneous access from multiple threads
     480            1419 :   mTrackedDBs.MarkImmutable();
     481                 : #endif
     482            1419 : }
     483                 : 
     484            1419 : TelemetryImpl::~TelemetryImpl() {
     485            1419 : }
     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            2816 :     for (PRUint32 i = 0; i < Telemetry::HistogramCount; i++) {
     552            2805 :       CharPtrEntryType *entry = map->PutEntry(gHistograms[i].id);
     553            2805 :       if (NS_UNLIKELY(!entry)) {
     554               0 :         map->Clear();
     555               0 :         return NS_ERROR_OUT_OF_MEMORY;
     556                 :       }
     557            2805 :       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            2048 :   for (size_t i = 0; i < Telemetry::HistogramCount; ++i) {
     780            2040 :     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            2048 :   for (size_t i = 0; i < count; ++i) {
     955            2040 :     JSString *comment = JS_InternString(cx, gHistograms[i].comment);
     956                 :     
     957            2040 :     if (!(comment
     958                 :           && JS_DefineProperty(cx, info, gHistograms[i].id,
     959                 :                                STRING_TO_JSVAL(comment), NULL, NULL,
     960            2040 :                                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              13 :     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              24 :   Histogram *h = nsnull;
    1038              24 :   nsresult rv = GetHistogramByEnumId(Telemetry::ID(entry->GetKey()), &h);
    1039              24 :   if (NS_FAILED(rv)) {
    1040               0 :     return true;
    1041                 :   }
    1042                 : 
    1043              24 :   JSObject *snapshot = JS_NewObject(cx, NULL, NULL, NULL);
    1044              24 :   if (!snapshot) {
    1045               0 :     return false;
    1046                 :   }
    1047              48 :   JS::AutoObjectRooter root(cx, snapshot);
    1048              24 :   switch (ReflectHistogramAndSamples(cx, snapshot, h, entry->mData)) {
    1049                 :   case REFLECT_OK:
    1050                 :     return JS_DefineProperty(cx, snapshots,
    1051              24 :                              h->histogram_name().c_str(),
    1052                 :                              OBJECT_TO_JSVAL(snapshot), NULL, NULL,
    1053              48 :                              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         1353989 : TelemetryImpl::CanRecord() {
    1337         1353989 :   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            1419 : TelemetryImpl::CreateTelemetryInstance()
    1352                 : {
    1353            1419 :   NS_ABORT_IF_FALSE(sTelemetry == NULL, "CreateTelemetryInstance may only be called once, via GetService()");
    1354            1419 :   sTelemetry = new TelemetryImpl(); 
    1355                 :   // AddRef for the local reference
    1356            1419 :   NS_ADDREF(sTelemetry);
    1357                 :   // AddRef for the caller
    1358            1419 :   NS_ADDREF(sTelemetry);
    1359            1419 :   return sTelemetry;
    1360                 : }
    1361                 : 
    1362                 : void
    1363            1419 : TelemetryImpl::ShutdownTelemetry()
    1364                 : {
    1365            1419 :   NS_IF_RELEASE(sTelemetry);
    1366            1419 : }
    1367                 : 
    1368                 : void
    1369              13 : TelemetryImpl::RecordSlowStatement(const nsACString &statement,
    1370                 :                                    const nsACString &dbName,
    1371                 :                                    PRUint32 delay)
    1372                 : {
    1373              13 :   MOZ_ASSERT(sTelemetry);
    1374              13 :   if (!sTelemetry->mCanRecord || !sTelemetry->mTrackedDBs.GetEntry(dbName))
    1375               7 :     return;
    1376                 : 
    1377               6 :   AutoHashtable<SlowSQLEntryType> *slowSQLMap = NULL;
    1378               6 :   if (NS_IsMainThread())
    1379               5 :     slowSQLMap = &(sTelemetry->mSlowSQLOnMainThread);
    1380                 :   else
    1381               1 :     slowSQLMap = &(sTelemetry->mSlowSQLOnOtherThread);
    1382                 : 
    1383              12 :   MutexAutoLock hashMutex(sTelemetry->mHashMutex);
    1384               6 :   SlowSQLEntryType *entry = slowSQLMap->GetEntry(statement);
    1385               6 :   if (!entry) {
    1386               6 :     entry = slowSQLMap->PutEntry(statement);
    1387               6 :     if (NS_UNLIKELY(!entry))
    1388                 :       return;
    1389               6 :     entry->mData.hitCount = 0;
    1390               6 :     entry->mData.totalTime = 0;
    1391                 :   }
    1392               6 :   entry->mData.hitCount++;
    1393               6 :   entry->mData.totalTime += delay;
    1394                 : }
    1395                 : 
    1396           17838 : NS_IMPL_THREADSAFE_ISUPPORTS1(TelemetryImpl, nsITelemetry)
    1397            1419 : 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         1353872 : Accumulate(ID aHistogram, PRUint32 aSample)
    1430                 : {
    1431         1353872 :   if (!TelemetryImpl::CanRecord()) {
    1432               0 :     return;
    1433                 :   }
    1434                 :   Histogram *h;
    1435         1353872 :   nsresult rv = GetHistogramByEnumId(aHistogram, &h);
    1436         1353871 :   if (NS_SUCCEEDED(rv))
    1437         1353871 :     h->Add(aSample);
    1438                 : }
    1439                 : 
    1440                 : void
    1441          646764 : AccumulateTimeDelta(ID aHistogram, TimeStamp start, TimeStamp end)
    1442                 : {
    1443                 :   Accumulate(aHistogram,
    1444          646764 :              static_cast<PRUint32>((end - start).ToMilliseconds()));
    1445          646764 : }
    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              13 : RecordSlowSQLStatement(const nsACString &statement,
    1463                 :                        const nsACString &dbName,
    1464                 :                        PRUint32 delay)
    1465                 : {
    1466              13 :   TelemetryImpl::RecordSlowStatement(statement, dbName, delay);
    1467              13 : }
    1468                 : 
    1469            1419 : void Init()
    1470                 : {
    1471                 :   // Make the service manager hold a long-lived reference to the service
    1472                 :   nsCOMPtr<nsITelemetry> telemetryService =
    1473            2838 :     do_GetService("@mozilla.org/base/telemetry;1");
    1474            1419 :   MOZ_ASSERT(telemetryService);
    1475            1419 : }
    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            4438 : }

Generated by: LCOV version 1.7