toolkit/components/telemetry/Telemetry.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/toolkit/components/telemetry/Telemetry.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,3071 @@
     1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* vim: set ts=8 sts=2 et sw=2 tw=80: */
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#include <algorithm>
    1.11 +
    1.12 +#include <fstream>
    1.13 +
    1.14 +#include <prio.h>
    1.15 +
    1.16 +#include "mozilla/Attributes.h"
    1.17 +#include "mozilla/DebugOnly.h"
    1.18 +#include "mozilla/Likely.h"
    1.19 +#include "mozilla/MathAlgorithms.h"
    1.20 +
    1.21 +#include "base/histogram.h"
    1.22 +#include "base/pickle.h"
    1.23 +#include "nsIComponentManager.h"
    1.24 +#include "nsIServiceManager.h"
    1.25 +#include "nsThreadManager.h"
    1.26 +#include "nsCOMArray.h"
    1.27 +#include "nsCOMPtr.h"
    1.28 +#include "nsXPCOMPrivate.h"
    1.29 +#include "nsIXULAppInfo.h"
    1.30 +#include "nsVersionComparator.h"
    1.31 +#include "mozilla/MemoryReporting.h"
    1.32 +#include "mozilla/ModuleUtils.h"
    1.33 +#include "nsIXPConnect.h"
    1.34 +#include "mozilla/Services.h"
    1.35 +#include "jsapi.h"
    1.36 +#include "jsfriendapi.h"
    1.37 +#include "js/GCAPI.h"
    1.38 +#include "nsString.h"
    1.39 +#include "nsITelemetry.h"
    1.40 +#include "nsIFile.h"
    1.41 +#include "nsIFileStreams.h"
    1.42 +#include "nsIMemoryReporter.h"
    1.43 +#include "nsISeekableStream.h"
    1.44 +#include "Telemetry.h"
    1.45 +#include "nsTHashtable.h"
    1.46 +#include "nsHashKeys.h"
    1.47 +#include "nsBaseHashtable.h"
    1.48 +#include "nsXULAppAPI.h"
    1.49 +#include "nsReadableUtils.h"
    1.50 +#include "nsThreadUtils.h"
    1.51 +#if defined(XP_WIN)
    1.52 +#include "nsUnicharUtils.h"
    1.53 +#endif
    1.54 +#include "nsNetCID.h"
    1.55 +#include "nsNetUtil.h"
    1.56 +#include "plstr.h"
    1.57 +#include "nsAppDirectoryServiceDefs.h"
    1.58 +#include "mozilla/BackgroundHangMonitor.h"
    1.59 +#include "mozilla/ThreadHangStats.h"
    1.60 +#include "mozilla/ProcessedStack.h"
    1.61 +#include "mozilla/Mutex.h"
    1.62 +#include "mozilla/FileUtils.h"
    1.63 +#include "mozilla/Preferences.h"
    1.64 +#include "mozilla/StaticPtr.h"
    1.65 +#include "mozilla/IOInterposer.h"
    1.66 +#include "mozilla/PoisonIOInterposer.h"
    1.67 +#include "mozilla/StartupTimeline.h"
    1.68 +#if defined(MOZ_ENABLE_PROFILER_SPS)
    1.69 +#include "shared-libraries.h"
    1.70 +#endif
    1.71 +
    1.72 +#define EXPIRED_ID "__expired__"
    1.73 +
    1.74 +namespace {
    1.75 +
    1.76 +using namespace base;
    1.77 +using namespace mozilla;
    1.78 +
    1.79 +template<class EntryType>
    1.80 +class AutoHashtable : public nsTHashtable<EntryType>
    1.81 +{
    1.82 +public:
    1.83 +  AutoHashtable(uint32_t initSize = PL_DHASH_MIN_SIZE);
    1.84 +  typedef bool (*ReflectEntryFunc)(EntryType *entry, JSContext *cx, JS::Handle<JSObject*> obj);
    1.85 +  bool ReflectIntoJS(ReflectEntryFunc entryFunc, JSContext *cx, JS::Handle<JSObject*> obj);
    1.86 +private:
    1.87 +  struct EnumeratorArgs {
    1.88 +    JSContext *cx;
    1.89 +    JS::Handle<JSObject*> obj;
    1.90 +    ReflectEntryFunc entryFunc;
    1.91 +  };
    1.92 +  static PLDHashOperator ReflectEntryStub(EntryType *entry, void *arg);
    1.93 +};
    1.94 +
    1.95 +template<class EntryType>
    1.96 +AutoHashtable<EntryType>::AutoHashtable(uint32_t initSize)
    1.97 +  : nsTHashtable<EntryType>(initSize)
    1.98 +{
    1.99 +}
   1.100 +
   1.101 +template<typename EntryType>
   1.102 +PLDHashOperator
   1.103 +AutoHashtable<EntryType>::ReflectEntryStub(EntryType *entry, void *arg)
   1.104 +{
   1.105 +  EnumeratorArgs *args = static_cast<EnumeratorArgs *>(arg);
   1.106 +  if (!args->entryFunc(entry, args->cx, args->obj)) {
   1.107 +    return PL_DHASH_STOP;
   1.108 +  }
   1.109 +  return PL_DHASH_NEXT;
   1.110 +}
   1.111 +
   1.112 +/**
   1.113 + * Reflect the individual entries of table into JS, usually by defining
   1.114 + * some property and value of obj.  entryFunc is called for each entry.
   1.115 + */
   1.116 +template<typename EntryType>
   1.117 +bool
   1.118 +AutoHashtable<EntryType>::ReflectIntoJS(ReflectEntryFunc entryFunc,
   1.119 +                                        JSContext *cx, JS::Handle<JSObject*> obj)
   1.120 +{
   1.121 +  EnumeratorArgs args = { cx, obj, entryFunc };
   1.122 +  uint32_t num = this->EnumerateEntries(ReflectEntryStub, static_cast<void*>(&args));
   1.123 +  return num == this->Count();
   1.124 +}
   1.125 +
   1.126 +// This class is conceptually a list of ProcessedStack objects, but it represents them
   1.127 +// more efficiently by keeping a single global list of modules.
   1.128 +class CombinedStacks {
   1.129 +public:
   1.130 +  typedef std::vector<Telemetry::ProcessedStack::Frame> Stack;
   1.131 +  const Telemetry::ProcessedStack::Module& GetModule(unsigned aIndex) const;
   1.132 +  size_t GetModuleCount() const;
   1.133 +  const Stack& GetStack(unsigned aIndex) const;
   1.134 +  void AddStack(const Telemetry::ProcessedStack& aStack);
   1.135 +  size_t GetStackCount() const;
   1.136 +  size_t SizeOfExcludingThis() const;
   1.137 +private:
   1.138 +  std::vector<Telemetry::ProcessedStack::Module> mModules;
   1.139 +  std::vector<Stack> mStacks;
   1.140 +};
   1.141 +
   1.142 +static JSObject *
   1.143 +CreateJSStackObject(JSContext *cx, const CombinedStacks &stacks);
   1.144 +
   1.145 +size_t
   1.146 +CombinedStacks::GetModuleCount() const {
   1.147 +  return mModules.size();
   1.148 +}
   1.149 +
   1.150 +const Telemetry::ProcessedStack::Module&
   1.151 +CombinedStacks::GetModule(unsigned aIndex) const {
   1.152 +  return mModules[aIndex];
   1.153 +}
   1.154 +
   1.155 +void
   1.156 +CombinedStacks::AddStack(const Telemetry::ProcessedStack& aStack) {
   1.157 +  mStacks.resize(mStacks.size() + 1);
   1.158 +  CombinedStacks::Stack& adjustedStack = mStacks.back();
   1.159 +
   1.160 +  size_t stackSize = aStack.GetStackSize();
   1.161 +  for (size_t i = 0; i < stackSize; ++i) {
   1.162 +    const Telemetry::ProcessedStack::Frame& frame = aStack.GetFrame(i);
   1.163 +    uint16_t modIndex;
   1.164 +    if (frame.mModIndex == std::numeric_limits<uint16_t>::max()) {
   1.165 +      modIndex = frame.mModIndex;
   1.166 +    } else {
   1.167 +      const Telemetry::ProcessedStack::Module& module =
   1.168 +        aStack.GetModule(frame.mModIndex);
   1.169 +      std::vector<Telemetry::ProcessedStack::Module>::iterator modIterator =
   1.170 +        std::find(mModules.begin(), mModules.end(), module);
   1.171 +      if (modIterator == mModules.end()) {
   1.172 +        mModules.push_back(module);
   1.173 +        modIndex = mModules.size() - 1;
   1.174 +      } else {
   1.175 +        modIndex = modIterator - mModules.begin();
   1.176 +      }
   1.177 +    }
   1.178 +    Telemetry::ProcessedStack::Frame adjustedFrame = { frame.mOffset, modIndex };
   1.179 +    adjustedStack.push_back(adjustedFrame);
   1.180 +  }
   1.181 +}
   1.182 +
   1.183 +const CombinedStacks::Stack&
   1.184 +CombinedStacks::GetStack(unsigned aIndex) const {
   1.185 +  return mStacks[aIndex];
   1.186 +}
   1.187 +
   1.188 +size_t
   1.189 +CombinedStacks::GetStackCount() const {
   1.190 +  return mStacks.size();
   1.191 +}
   1.192 +
   1.193 +size_t
   1.194 +CombinedStacks::SizeOfExcludingThis() const {
   1.195 +  // This is a crude approximation. We would like to do something like
   1.196 +  // aMallocSizeOf(&mModules[0]), but on linux aMallocSizeOf will call
   1.197 +  // malloc_usable_size which is only safe on the pointers returned by malloc.
   1.198 +  // While it works on current libstdc++, it is better to be safe and not assume
   1.199 +  // that &vec[0] points to one. We could use a custom allocator, but
   1.200 +  // it doesn't seem worth it.
   1.201 +  size_t n = 0;
   1.202 +  n += mModules.capacity() * sizeof(Telemetry::ProcessedStack::Module);
   1.203 +  n += mStacks.capacity() * sizeof(Stack);
   1.204 +  for (std::vector<Stack>::const_iterator i = mStacks.begin(),
   1.205 +         e = mStacks.end(); i != e; ++i) {
   1.206 +    const Stack& s = *i;
   1.207 +    n += s.capacity() * sizeof(Telemetry::ProcessedStack::Frame);
   1.208 +  }
   1.209 +  return n;
   1.210 +}
   1.211 +
   1.212 +class HangReports {
   1.213 +public:
   1.214 +  size_t SizeOfExcludingThis() const;
   1.215 +  void AddHang(const Telemetry::ProcessedStack& aStack, uint32_t aDuration,
   1.216 +               int32_t aSystemUptime, int32_t aFirefoxUptime);
   1.217 +  uint32_t GetDuration(unsigned aIndex) const;
   1.218 +  int32_t GetSystemUptime(unsigned aIndex) const;
   1.219 +  int32_t GetFirefoxUptime(unsigned aIndex) const;
   1.220 +  const CombinedStacks& GetStacks() const;
   1.221 +private:
   1.222 +  struct HangInfo {
   1.223 +    // Hang duration (in seconds)
   1.224 +    uint32_t mDuration;
   1.225 +    // System uptime (in minutes) at the time of the hang
   1.226 +    int32_t mSystemUptime;
   1.227 +    // Firefox uptime (in minutes) at the time of the hang
   1.228 +    int32_t mFirefoxUptime;
   1.229 +  };
   1.230 +  std::vector<HangInfo> mHangInfo;
   1.231 +  CombinedStacks mStacks;
   1.232 +};
   1.233 +
   1.234 +void
   1.235 +HangReports::AddHang(const Telemetry::ProcessedStack& aStack,
   1.236 +                     uint32_t aDuration,
   1.237 +                     int32_t aSystemUptime,
   1.238 +                     int32_t aFirefoxUptime) {
   1.239 +  HangInfo info = { aDuration, aSystemUptime, aFirefoxUptime };
   1.240 +  mHangInfo.push_back(info);
   1.241 +  mStacks.AddStack(aStack);
   1.242 +}
   1.243 +
   1.244 +size_t
   1.245 +HangReports::SizeOfExcludingThis() const {
   1.246 +  size_t n = 0;
   1.247 +  n += mStacks.SizeOfExcludingThis();
   1.248 +  // This is a crude approximation. See comment on
   1.249 +  // CombinedStacks::SizeOfExcludingThis.
   1.250 +  n += mHangInfo.capacity() * sizeof(HangInfo);
   1.251 +  return n;
   1.252 +}
   1.253 +
   1.254 +const CombinedStacks&
   1.255 +HangReports::GetStacks() const {
   1.256 +  return mStacks;
   1.257 +}
   1.258 +
   1.259 +uint32_t
   1.260 +HangReports::GetDuration(unsigned aIndex) const {
   1.261 +  return mHangInfo[aIndex].mDuration;
   1.262 +}
   1.263 +
   1.264 +int32_t
   1.265 +HangReports::GetSystemUptime(unsigned aIndex) const {
   1.266 +  return mHangInfo[aIndex].mSystemUptime;
   1.267 +}
   1.268 +
   1.269 +int32_t
   1.270 +HangReports::GetFirefoxUptime(unsigned aIndex) const {
   1.271 +  return mHangInfo[aIndex].mFirefoxUptime;
   1.272 +}
   1.273 +
   1.274 +/**
   1.275 + * IOInterposeObserver recording statistics of main-thread I/O during execution,
   1.276 + * aimed at consumption by TelemetryImpl
   1.277 + */
   1.278 +class TelemetryIOInterposeObserver : public IOInterposeObserver
   1.279 +{
   1.280 +  /** File-level statistics structure */
   1.281 +  struct FileStats {
   1.282 +    FileStats()
   1.283 +      : creates(0)
   1.284 +      , reads(0)
   1.285 +      , writes(0)
   1.286 +      , fsyncs(0)
   1.287 +      , stats(0)
   1.288 +      , totalTime(0)
   1.289 +    {}
   1.290 +    uint32_t  creates;      /** Number of create/open operations */
   1.291 +    uint32_t  reads;        /** Number of read operations */
   1.292 +    uint32_t  writes;       /** Number of write operations */
   1.293 +    uint32_t  fsyncs;       /** Number of fsync operations */
   1.294 +    uint32_t  stats;        /** Number of stat operations */
   1.295 +    double    totalTime;    /** Accumulated duration of all operations */
   1.296 +  };
   1.297 +
   1.298 +  struct SafeDir {
   1.299 +    SafeDir(const nsAString& aPath, const nsAString& aSubstName)
   1.300 +      : mPath(aPath)
   1.301 +      , mSubstName(aSubstName)
   1.302 +    {}
   1.303 +    size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const {
   1.304 +      return mPath.SizeOfExcludingThisIfUnshared(aMallocSizeOf) +
   1.305 +             mSubstName.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
   1.306 +    }
   1.307 +    nsString  mPath;        /** Path to the directory */
   1.308 +    nsString  mSubstName;   /** Name to substitute with */
   1.309 +  };
   1.310 +
   1.311 +public:
   1.312 +  TelemetryIOInterposeObserver(nsIFile* aXreDir);
   1.313 +
   1.314 +  /**
   1.315 +   * An implementation of Observe that records statistics of all
   1.316 +   * file IO operations.
   1.317 +   */
   1.318 +  void Observe(Observation& aOb);
   1.319 +
   1.320 +  /**
   1.321 +   * Reflect recorded file IO statistics into Javascript
   1.322 +   */
   1.323 +  bool ReflectIntoJS(JSContext *cx, JS::Handle<JSObject*> rootObj);
   1.324 +
   1.325 +  /**
   1.326 +   * Adds a path for inclusion in main thread I/O report.
   1.327 +   * @param aPath Directory path
   1.328 +   * @param aSubstName Name to substitute for aPath for privacy reasons
   1.329 +   */
   1.330 +  void AddPath(const nsAString& aPath, const nsAString& aSubstName);
   1.331 +
   1.332 +  /**
   1.333 +   * Get size of hash table with file stats
   1.334 +   */
   1.335 +  size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const {
   1.336 +    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
   1.337 +  }
   1.338 +
   1.339 +  size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const {
   1.340 +    size_t size;
   1.341 +    size = mFileStats.SizeOfExcludingThis(SizeOfFileIOEntryTypeExcludingThis,
   1.342 +                                          aMallocSizeOf) +
   1.343 +           mSafeDirs.SizeOfExcludingThis(aMallocSizeOf);
   1.344 +    uint32_t safeDirsLen = mSafeDirs.Length();
   1.345 +    for (uint32_t i = 0; i < safeDirsLen; ++i) {
   1.346 +      size += mSafeDirs[i].SizeOfExcludingThis(aMallocSizeOf);
   1.347 +    }
   1.348 +    return size;
   1.349 +  }
   1.350 +
   1.351 +private:
   1.352 +  enum Stage
   1.353 +  {
   1.354 +    STAGE_STARTUP = 0,
   1.355 +    STAGE_NORMAL,
   1.356 +    STAGE_SHUTDOWN,
   1.357 +    NUM_STAGES
   1.358 +  };
   1.359 +  static inline Stage NextStage(Stage aStage)
   1.360 +  {
   1.361 +    switch (aStage) {
   1.362 +      case STAGE_STARTUP:
   1.363 +        return STAGE_NORMAL;
   1.364 +      case STAGE_NORMAL:
   1.365 +        return STAGE_SHUTDOWN;
   1.366 +      case STAGE_SHUTDOWN:
   1.367 +        return STAGE_SHUTDOWN;
   1.368 +      default:
   1.369 +        return NUM_STAGES;
   1.370 +    }
   1.371 +  }
   1.372 +
   1.373 +  struct FileStatsByStage
   1.374 +  {
   1.375 +    FileStats mStats[NUM_STAGES];
   1.376 +  };
   1.377 +  typedef nsBaseHashtableET<nsStringHashKey, FileStatsByStage> FileIOEntryType;
   1.378 +
   1.379 +  // Statistics for each filename
   1.380 +  AutoHashtable<FileIOEntryType> mFileStats;
   1.381 +  // Container for whitelisted directories
   1.382 +  nsTArray<SafeDir> mSafeDirs;
   1.383 +  Stage             mCurStage;
   1.384 +
   1.385 +  /**
   1.386 +   * Reflect a FileIOEntryType object to a Javascript property on obj with
   1.387 +   * filename as key containing array:
   1.388 +   * [totalTime, creates, reads, writes, fsyncs, stats]
   1.389 +   */
   1.390 +  static bool ReflectFileStats(FileIOEntryType* entry, JSContext *cx,
   1.391 +                               JS::Handle<JSObject*> obj);
   1.392 +
   1.393 +  static size_t SizeOfFileIOEntryTypeExcludingThis(FileIOEntryType* aEntry,
   1.394 +                                                   mozilla::MallocSizeOf mallocSizeOf,
   1.395 +                                                   void*)
   1.396 +  {
   1.397 +    return aEntry->GetKey().SizeOfExcludingThisIfUnshared(mallocSizeOf);
   1.398 +  }
   1.399 +};
   1.400 +
   1.401 +TelemetryIOInterposeObserver::TelemetryIOInterposeObserver(nsIFile* aXreDir)
   1.402 +  : mCurStage(STAGE_STARTUP)
   1.403 +{
   1.404 +  nsAutoString xreDirPath;
   1.405 +  nsresult rv = aXreDir->GetPath(xreDirPath);
   1.406 +  if (NS_SUCCEEDED(rv)) {
   1.407 +    AddPath(xreDirPath, NS_LITERAL_STRING("{xre}"));
   1.408 +  }
   1.409 +}
   1.410 +
   1.411 +void TelemetryIOInterposeObserver::AddPath(const nsAString& aPath,
   1.412 +                                           const nsAString& aSubstName)
   1.413 +{
   1.414 +  mSafeDirs.AppendElement(SafeDir(aPath, aSubstName));
   1.415 +}
   1.416 + 
   1.417 +void TelemetryIOInterposeObserver::Observe(Observation& aOb)
   1.418 +{
   1.419 +  // We only report main-thread I/O
   1.420 +  if (!IsMainThread()) {
   1.421 +    return;
   1.422 +  }
   1.423 +
   1.424 +  if (aOb.ObservedOperation() == OpNextStage) {
   1.425 +    mCurStage = NextStage(mCurStage);
   1.426 +    MOZ_ASSERT(mCurStage < NUM_STAGES);
   1.427 +    return;
   1.428 +  }
   1.429 +
   1.430 +  // Get the filename
   1.431 +  const char16_t* filename = aOb.Filename();
   1.432 + 
   1.433 +  // Discard observations without filename
   1.434 +  if (!filename) {
   1.435 +    return;
   1.436 +  }
   1.437 +
   1.438 +#if defined(XP_WIN)
   1.439 +  nsCaseInsensitiveStringComparator comparator;
   1.440 +#else
   1.441 +  nsDefaultStringComparator comparator;
   1.442 +#endif
   1.443 +  nsAutoString      processedName;
   1.444 +  nsDependentString filenameStr(filename);
   1.445 +  uint32_t safeDirsLen = mSafeDirs.Length();
   1.446 +  for (uint32_t i = 0; i < safeDirsLen; ++i) {
   1.447 +    if (StringBeginsWith(filenameStr, mSafeDirs[i].mPath, comparator)) {
   1.448 +      processedName = mSafeDirs[i].mSubstName;
   1.449 +      processedName += Substring(filenameStr, mSafeDirs[i].mPath.Length());
   1.450 +      break;
   1.451 +    }
   1.452 +  }
   1.453 +
   1.454 +  if (processedName.IsEmpty()) {
   1.455 +    return;
   1.456 +  }
   1.457 +
   1.458 +  // Create a new entry or retrieve the existing one
   1.459 +  FileIOEntryType* entry = mFileStats.PutEntry(processedName);
   1.460 +  if (entry) {
   1.461 +    FileStats& stats = entry->mData.mStats[mCurStage];
   1.462 +    // Update the statistics
   1.463 +    stats.totalTime += (double) aOb.Duration().ToMilliseconds();
   1.464 +    switch (aOb.ObservedOperation()) {
   1.465 +      case OpCreateOrOpen:
   1.466 +        stats.creates++;
   1.467 +        break;
   1.468 +      case OpRead:
   1.469 +        stats.reads++;
   1.470 +        break;
   1.471 +      case OpWrite:
   1.472 +        stats.writes++;
   1.473 +        break;
   1.474 +      case OpFSync:
   1.475 +        stats.fsyncs++;
   1.476 +        break;
   1.477 +      case OpStat:
   1.478 +        stats.stats++;
   1.479 +        break;
   1.480 +      default:
   1.481 +        break;
   1.482 +    }
   1.483 +  }
   1.484 +}
   1.485 +
   1.486 +bool TelemetryIOInterposeObserver::ReflectFileStats(FileIOEntryType* entry,
   1.487 +                                                    JSContext *cx,
   1.488 +                                                    JS::Handle<JSObject*> obj)
   1.489 +{
   1.490 +  JS::AutoValueArray<NUM_STAGES> stages(cx);
   1.491 +
   1.492 +  FileStatsByStage& statsByStage = entry->mData;
   1.493 +  for (int s = STAGE_STARTUP; s < NUM_STAGES; ++s) {
   1.494 +    FileStats& fileStats = statsByStage.mStats[s];
   1.495 +
   1.496 +    if (fileStats.totalTime == 0 && fileStats.creates == 0 &&
   1.497 +        fileStats.reads == 0 && fileStats.writes == 0 &&
   1.498 +        fileStats.fsyncs == 0 && fileStats.stats == 0) {
   1.499 +      // Don't add an array that contains no information
   1.500 +      stages[s].setNull();
   1.501 +      continue;
   1.502 +    }
   1.503 +
   1.504 +    // Array we want to report
   1.505 +    JS::AutoValueArray<6> stats(cx);
   1.506 +    stats[0].setNumber(fileStats.totalTime);
   1.507 +    stats[1].setNumber(fileStats.creates);
   1.508 +    stats[2].setNumber(fileStats.reads);
   1.509 +    stats[3].setNumber(fileStats.writes);
   1.510 +    stats[4].setNumber(fileStats.fsyncs);
   1.511 +    stats[5].setNumber(fileStats.stats);
   1.512 +
   1.513 +    // Create jsStats as array of elements above
   1.514 +    JS::RootedObject jsStats(cx, JS_NewArrayObject(cx, stats));
   1.515 +    if (!jsStats) {
   1.516 +      continue;
   1.517 +    }
   1.518 +
   1.519 +    stages[s].setObject(*jsStats);
   1.520 +  }
   1.521 +
   1.522 +  JS::RootedObject jsEntry(cx, JS_NewArrayObject(cx, stages));
   1.523 +  if (!jsEntry) {
   1.524 +    return false;
   1.525 +  }
   1.526 +
   1.527 +  // Add jsEntry to top-level dictionary
   1.528 +  const nsAString& key = entry->GetKey();
   1.529 +  return JS_DefineUCProperty(cx, obj, key.Data(), key.Length(),
   1.530 +                             OBJECT_TO_JSVAL(jsEntry), nullptr, nullptr,
   1.531 +                             JSPROP_ENUMERATE | JSPROP_READONLY);
   1.532 +}
   1.533 +
   1.534 +bool TelemetryIOInterposeObserver::ReflectIntoJS(JSContext *cx,
   1.535 +                                                 JS::Handle<JSObject*> rootObj)
   1.536 +{
   1.537 +  return mFileStats.ReflectIntoJS(ReflectFileStats, cx, rootObj);
   1.538 +}
   1.539 +
   1.540 +// This is not a member of TelemetryImpl because we want to record I/O during
   1.541 +// startup.
   1.542 +StaticAutoPtr<TelemetryIOInterposeObserver> sTelemetryIOObserver;
   1.543 +
   1.544 +void
   1.545 +ClearIOReporting()
   1.546 +{
   1.547 +  if (!sTelemetryIOObserver) {
   1.548 +    return;
   1.549 +  }
   1.550 +  IOInterposer::Unregister(IOInterposeObserver::OpAllWithStaging,
   1.551 +                           sTelemetryIOObserver);
   1.552 +  sTelemetryIOObserver = nullptr;
   1.553 +}
   1.554 +
   1.555 +class TelemetryImpl MOZ_FINAL
   1.556 +  : public nsITelemetry
   1.557 +  , public nsIMemoryReporter
   1.558 +{
   1.559 +  NS_DECL_THREADSAFE_ISUPPORTS
   1.560 +  NS_DECL_NSITELEMETRY
   1.561 +  NS_DECL_NSIMEMORYREPORTER
   1.562 +
   1.563 +public:
   1.564 +  ~TelemetryImpl();
   1.565 +
   1.566 +  void InitMemoryReporter();
   1.567 +
   1.568 +  static bool CanRecord();
   1.569 +  static already_AddRefed<nsITelemetry> CreateTelemetryInstance();
   1.570 +  static void ShutdownTelemetry();
   1.571 +  static void RecordSlowStatement(const nsACString &sql, const nsACString &dbName,
   1.572 +                                  uint32_t delay);
   1.573 +#if defined(MOZ_ENABLE_PROFILER_SPS)
   1.574 +  static void RecordChromeHang(uint32_t aDuration,
   1.575 +                               Telemetry::ProcessedStack &aStack,
   1.576 +                               int32_t aSystemUptime,
   1.577 +                               int32_t aFirefoxUptime);
   1.578 +#endif
   1.579 +  static void RecordThreadHangStats(Telemetry::ThreadHangStats& aStats);
   1.580 +  static nsresult GetHistogramEnumId(const char *name, Telemetry::ID *id);
   1.581 +  size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf);
   1.582 +  struct Stat {
   1.583 +    uint32_t hitCount;
   1.584 +    uint32_t totalTime;
   1.585 +  };
   1.586 +  struct StmtStats {
   1.587 +    struct Stat mainThread;
   1.588 +    struct Stat otherThreads;
   1.589 +  };
   1.590 +  typedef nsBaseHashtableET<nsCStringHashKey, StmtStats> SlowSQLEntryType;
   1.591 +
   1.592 +private:
   1.593 +  TelemetryImpl();
   1.594 +
   1.595 +  static nsCString SanitizeSQL(const nsACString& sql);
   1.596 +
   1.597 +  enum SanitizedState { Sanitized, Unsanitized };
   1.598 +
   1.599 +  static void StoreSlowSQL(const nsACString &offender, uint32_t delay,
   1.600 +                           SanitizedState state);
   1.601 +
   1.602 +  static bool ReflectMainThreadSQL(SlowSQLEntryType *entry, JSContext *cx,
   1.603 +                                   JS::Handle<JSObject*> obj);
   1.604 +  static bool ReflectOtherThreadsSQL(SlowSQLEntryType *entry, JSContext *cx,
   1.605 +                                     JS::Handle<JSObject*> obj);
   1.606 +  static bool ReflectSQL(const SlowSQLEntryType *entry, const Stat *stat,
   1.607 +                         JSContext *cx, JS::Handle<JSObject*> obj);
   1.608 +
   1.609 +  bool AddSQLInfo(JSContext *cx, JS::Handle<JSObject*> rootObj, bool mainThread,
   1.610 +                  bool privateSQL);
   1.611 +  bool GetSQLStats(JSContext *cx, JS::MutableHandle<JS::Value> ret,
   1.612 +                   bool includePrivateSql);
   1.613 +
   1.614 +  // Like GetHistogramById, but returns the underlying C++ object, not the JS one.
   1.615 +  nsresult GetHistogramByName(const nsACString &name, Histogram **ret);
   1.616 +  bool ShouldReflectHistogram(Histogram *h);
   1.617 +  void IdentifyCorruptHistograms(StatisticsRecorder::Histograms &hs);
   1.618 +  typedef StatisticsRecorder::Histograms::iterator HistogramIterator;
   1.619 +
   1.620 +  struct AddonHistogramInfo {
   1.621 +    uint32_t min;
   1.622 +    uint32_t max;
   1.623 +    uint32_t bucketCount;
   1.624 +    uint32_t histogramType;
   1.625 +    Histogram *h;
   1.626 +  };
   1.627 +  typedef nsBaseHashtableET<nsCStringHashKey, AddonHistogramInfo> AddonHistogramEntryType;
   1.628 +  typedef AutoHashtable<AddonHistogramEntryType> AddonHistogramMapType;
   1.629 +  typedef nsBaseHashtableET<nsCStringHashKey, AddonHistogramMapType *> AddonEntryType;
   1.630 +  typedef AutoHashtable<AddonEntryType> AddonMapType;
   1.631 +  static bool AddonHistogramReflector(AddonHistogramEntryType *entry,
   1.632 +                                      JSContext *cx, JS::Handle<JSObject*> obj);
   1.633 +  static bool AddonReflector(AddonEntryType *entry, JSContext *cx, JS::Handle<JSObject*> obj);
   1.634 +  static bool CreateHistogramForAddon(const nsACString &name,
   1.635 +                                      AddonHistogramInfo &info);
   1.636 +  void ReadLateWritesStacks(nsIFile* aProfileDir);
   1.637 +  AddonMapType mAddonMap;
   1.638 +
   1.639 +  // This is used for speedy string->Telemetry::ID conversions
   1.640 +  typedef nsBaseHashtableET<nsCharPtrHashKey, Telemetry::ID> CharPtrEntryType;
   1.641 +  typedef AutoHashtable<CharPtrEntryType> HistogramMapType;
   1.642 +  HistogramMapType mHistogramMap;
   1.643 +  bool mCanRecord;
   1.644 +  static TelemetryImpl *sTelemetry;
   1.645 +  AutoHashtable<SlowSQLEntryType> mPrivateSQL;
   1.646 +  AutoHashtable<SlowSQLEntryType> mSanitizedSQL;
   1.647 +  // This gets marked immutable in debug builds, so we can't use
   1.648 +  // AutoHashtable here.
   1.649 +  nsTHashtable<nsCStringHashKey> mTrackedDBs;
   1.650 +  Mutex mHashMutex;
   1.651 +  HangReports mHangReports;
   1.652 +  Mutex mHangReportsMutex;
   1.653 +  // mThreadHangStats stores recorded, inactive thread hang stats
   1.654 +  Vector<Telemetry::ThreadHangStats> mThreadHangStats;
   1.655 +  Mutex mThreadHangStatsMutex;
   1.656 +
   1.657 +  CombinedStacks mLateWritesStacks; // This is collected out of the main thread.
   1.658 +  bool mCachedTelemetryData;
   1.659 +  uint32_t mLastShutdownTime;
   1.660 +  uint32_t mFailedLockCount;
   1.661 +  nsCOMArray<nsIFetchTelemetryDataCallback> mCallbacks;
   1.662 +  friend class nsFetchTelemetryData;
   1.663 +};
   1.664 +
   1.665 +TelemetryImpl*  TelemetryImpl::sTelemetry = nullptr;
   1.666 +
   1.667 +MOZ_DEFINE_MALLOC_SIZE_OF(TelemetryMallocSizeOf)
   1.668 +
   1.669 +NS_IMETHODIMP
   1.670 +TelemetryImpl::CollectReports(nsIHandleReportCallback* aHandleReport,
   1.671 +                              nsISupports* aData)
   1.672 +{
   1.673 +  return MOZ_COLLECT_REPORT(
   1.674 +    "explicit/telemetry", KIND_HEAP, UNITS_BYTES,
   1.675 +    SizeOfIncludingThis(TelemetryMallocSizeOf),
   1.676 +    "Memory used by the telemetry system.");
   1.677 +}
   1.678 +
   1.679 +// A initializer to initialize histogram collection
   1.680 +StatisticsRecorder gStatisticsRecorder;
   1.681 +
   1.682 +// Hardcoded probes
   1.683 +struct TelemetryHistogram {
   1.684 +  uint32_t min;
   1.685 +  uint32_t max;
   1.686 +  uint32_t bucketCount;
   1.687 +  uint32_t histogramType;
   1.688 +  uint32_t id_offset;
   1.689 +  uint32_t expiration_offset;
   1.690 +  bool extendedStatisticsOK;
   1.691 +
   1.692 +  const char *id() const;
   1.693 +  const char *expiration() const;
   1.694 +};
   1.695 +
   1.696 +#include "TelemetryHistogramData.inc"
   1.697 +bool gCorruptHistograms[Telemetry::HistogramCount];
   1.698 +
   1.699 +const char *
   1.700 +TelemetryHistogram::id() const
   1.701 +{
   1.702 +  return &gHistogramStringTable[this->id_offset];
   1.703 +}
   1.704 +
   1.705 +const char *
   1.706 +TelemetryHistogram::expiration() const
   1.707 +{
   1.708 +  return &gHistogramStringTable[this->expiration_offset];
   1.709 +}
   1.710 +
   1.711 +bool
   1.712 +IsExpired(const char *expiration){
   1.713 +  static Version current_version = Version(MOZ_APP_VERSION);
   1.714 +  MOZ_ASSERT(expiration);
   1.715 +  return strcmp(expiration, "never") && (mozilla::Version(expiration) <= current_version);
   1.716 +}
   1.717 +
   1.718 +bool
   1.719 +IsExpired(const Histogram *histogram){
   1.720 +  return histogram->histogram_name() == EXPIRED_ID;
   1.721 +}
   1.722 +
   1.723 +nsresult
   1.724 +HistogramGet(const char *name, const char *expiration, uint32_t min, uint32_t max,
   1.725 +             uint32_t bucketCount, uint32_t histogramType, Histogram **result)
   1.726 +{
   1.727 +  if (histogramType != nsITelemetry::HISTOGRAM_BOOLEAN
   1.728 +      && histogramType != nsITelemetry::HISTOGRAM_FLAG) {
   1.729 +    // Sanity checks for histogram parameters.
   1.730 +    if (min >= max)
   1.731 +      return NS_ERROR_ILLEGAL_VALUE;
   1.732 +
   1.733 +    if (bucketCount <= 2)
   1.734 +      return NS_ERROR_ILLEGAL_VALUE;
   1.735 +
   1.736 +    if (min < 1)
   1.737 +      return NS_ERROR_ILLEGAL_VALUE;
   1.738 +  }
   1.739 +
   1.740 +  if (IsExpired(expiration)) {
   1.741 +    name = EXPIRED_ID;
   1.742 +    min = 1;
   1.743 +    max = 2;
   1.744 +    bucketCount = 3;
   1.745 +    histogramType = nsITelemetry::HISTOGRAM_LINEAR;
   1.746 +  }
   1.747 +
   1.748 +  switch (histogramType) {
   1.749 +  case nsITelemetry::HISTOGRAM_EXPONENTIAL:
   1.750 +    *result = Histogram::FactoryGet(name, min, max, bucketCount, Histogram::kUmaTargetedHistogramFlag);
   1.751 +    break;
   1.752 +  case nsITelemetry::HISTOGRAM_LINEAR:
   1.753 +    *result = LinearHistogram::FactoryGet(name, min, max, bucketCount, Histogram::kUmaTargetedHistogramFlag);
   1.754 +    break;
   1.755 +  case nsITelemetry::HISTOGRAM_BOOLEAN:
   1.756 +    *result = BooleanHistogram::FactoryGet(name, Histogram::kUmaTargetedHistogramFlag);
   1.757 +    break;
   1.758 +  case nsITelemetry::HISTOGRAM_FLAG:
   1.759 +    *result = FlagHistogram::FactoryGet(name, Histogram::kUmaTargetedHistogramFlag);
   1.760 +    break;
   1.761 +  default:
   1.762 +    return NS_ERROR_INVALID_ARG;
   1.763 +  }
   1.764 +  return NS_OK;
   1.765 +}
   1.766 +
   1.767 +// O(1) histogram lookup by numeric id
   1.768 +nsresult
   1.769 +GetHistogramByEnumId(Telemetry::ID id, Histogram **ret)
   1.770 +{
   1.771 +  static Histogram* knownHistograms[Telemetry::HistogramCount] = {0};
   1.772 +  Histogram *h = knownHistograms[id];
   1.773 +  if (h) {
   1.774 +    *ret = h;
   1.775 +    return NS_OK;
   1.776 +  }
   1.777 +
   1.778 +  const TelemetryHistogram &p = gHistograms[id];
   1.779 +  nsresult rv = HistogramGet(p.id(), p.expiration(), p.min, p.max, p.bucketCount, p.histogramType, &h);
   1.780 +  if (NS_FAILED(rv))
   1.781 +    return rv;
   1.782 +
   1.783 +#ifdef DEBUG
   1.784 +  // Check that the C++ Histogram code computes the same ranges as the
   1.785 +  // Python histogram code.
   1.786 +  if (!IsExpired(p.expiration())) {
   1.787 +    const struct bounds &b = gBucketLowerBoundIndex[id];
   1.788 +    if (b.length != 0) {
   1.789 +      MOZ_ASSERT(size_t(b.length) == h->bucket_count(),
   1.790 +                 "C++/Python bucket # mismatch");
   1.791 +      for (int i = 0; i < b.length; ++i) {
   1.792 +        MOZ_ASSERT(gBucketLowerBounds[b.offset + i] == h->ranges(i),
   1.793 +                   "C++/Python bucket mismatch");
   1.794 +      }
   1.795 +    }
   1.796 +  }
   1.797 +#endif
   1.798 +
   1.799 +  if (p.extendedStatisticsOK) {
   1.800 +    h->SetFlags(Histogram::kExtendedStatisticsFlag);
   1.801 +  }
   1.802 +  *ret = knownHistograms[id] = h;
   1.803 +  return NS_OK;
   1.804 +}
   1.805 +
   1.806 +bool
   1.807 +FillRanges(JSContext *cx, JS::Handle<JSObject*> array, Histogram *h)
   1.808 +{
   1.809 +  JS::Rooted<JS::Value> range(cx);
   1.810 +  for (size_t i = 0; i < h->bucket_count(); i++) {
   1.811 +    range = INT_TO_JSVAL(h->ranges(i));
   1.812 +    if (!JS_DefineElement(cx, array, i, range, nullptr, nullptr, JSPROP_ENUMERATE))
   1.813 +      return false;
   1.814 +  }
   1.815 +  return true;
   1.816 +}
   1.817 +
   1.818 +enum reflectStatus {
   1.819 +  REFLECT_OK,
   1.820 +  REFLECT_CORRUPT,
   1.821 +  REFLECT_FAILURE
   1.822 +};
   1.823 +
   1.824 +enum reflectStatus
   1.825 +ReflectHistogramAndSamples(JSContext *cx, JS::Handle<JSObject*> obj, Histogram *h,
   1.826 +                           const Histogram::SampleSet &ss)
   1.827 +{
   1.828 +  // We don't want to reflect corrupt histograms.
   1.829 +  if (h->FindCorruption(ss) != Histogram::NO_INCONSISTENCIES) {
   1.830 +    return REFLECT_CORRUPT;
   1.831 +  }
   1.832 +
   1.833 +  if (!(JS_DefineProperty(cx, obj, "min", h->declared_min(), JSPROP_ENUMERATE)
   1.834 +        && JS_DefineProperty(cx, obj, "max", h->declared_max(), JSPROP_ENUMERATE)
   1.835 +        && JS_DefineProperty(cx, obj, "histogram_type", h->histogram_type(), JSPROP_ENUMERATE)
   1.836 +        && JS_DefineProperty(cx, obj, "sum", double(ss.sum()), JSPROP_ENUMERATE))) {
   1.837 +    return REFLECT_FAILURE;
   1.838 +  }
   1.839 +
   1.840 +  if (h->histogram_type() == Histogram::HISTOGRAM) {
   1.841 +    if (!(JS_DefineProperty(cx, obj, "log_sum", ss.log_sum(), JSPROP_ENUMERATE)
   1.842 +          && JS_DefineProperty(cx, obj, "log_sum_squares", ss.log_sum_squares(), JSPROP_ENUMERATE))) {
   1.843 +      return REFLECT_FAILURE;
   1.844 +    }
   1.845 +  } else {
   1.846 +    // Export |sum_squares| as two separate 32-bit properties so that we
   1.847 +    // can accurately reconstruct it on the analysis side.
   1.848 +    uint64_t sum_squares = ss.sum_squares();
   1.849 +    // Cast to avoid implicit truncation warnings.
   1.850 +    uint32_t lo = static_cast<uint32_t>(sum_squares);
   1.851 +    uint32_t hi = static_cast<uint32_t>(sum_squares >> 32);
   1.852 +    if (!(JS_DefineProperty(cx, obj, "sum_squares_lo", lo, JSPROP_ENUMERATE)
   1.853 +          && JS_DefineProperty(cx, obj, "sum_squares_hi", hi, JSPROP_ENUMERATE))) {
   1.854 +      return REFLECT_FAILURE;
   1.855 +    }
   1.856 +  }
   1.857 +
   1.858 +  const size_t count = h->bucket_count();
   1.859 +  JS::Rooted<JSObject*> rarray(cx, JS_NewArrayObject(cx, count));
   1.860 +  if (!rarray) {
   1.861 +    return REFLECT_FAILURE;
   1.862 +  }
   1.863 +  if (!(FillRanges(cx, rarray, h)
   1.864 +        && JS_DefineProperty(cx, obj, "ranges", rarray, JSPROP_ENUMERATE))) {
   1.865 +    return REFLECT_FAILURE;
   1.866 +  }
   1.867 +
   1.868 +  JS::Rooted<JSObject*> counts_array(cx, JS_NewArrayObject(cx, count));
   1.869 +  if (!counts_array) {
   1.870 +    return REFLECT_FAILURE;
   1.871 +  }
   1.872 +  if (!JS_DefineProperty(cx, obj, "counts", counts_array, JSPROP_ENUMERATE)) {
   1.873 +    return REFLECT_FAILURE;
   1.874 +  }
   1.875 +  for (size_t i = 0; i < count; i++) {
   1.876 +    if (!JS_DefineElement(cx, counts_array, i, INT_TO_JSVAL(ss.counts(i)),
   1.877 +                          nullptr, nullptr, JSPROP_ENUMERATE)) {
   1.878 +      return REFLECT_FAILURE;
   1.879 +    }
   1.880 +  }
   1.881 +
   1.882 +  return REFLECT_OK;
   1.883 +}
   1.884 +
   1.885 +enum reflectStatus
   1.886 +ReflectHistogramSnapshot(JSContext *cx, JS::Handle<JSObject*> obj, Histogram *h)
   1.887 +{
   1.888 +  Histogram::SampleSet ss;
   1.889 +  h->SnapshotSample(&ss);
   1.890 +  return ReflectHistogramAndSamples(cx, obj, h, ss);
   1.891 +}
   1.892 +
   1.893 +bool
   1.894 +IsEmpty(const Histogram *h)
   1.895 +{
   1.896 +  Histogram::SampleSet ss;
   1.897 +  h->SnapshotSample(&ss);
   1.898 +
   1.899 +  return ss.counts(0) == 0 && ss.sum() == 0;
   1.900 +}
   1.901 +
   1.902 +bool
   1.903 +JSHistogram_Add(JSContext *cx, unsigned argc, JS::Value *vp)
   1.904 +{
   1.905 +  JS::CallArgs args = CallArgsFromVp(argc, vp);
   1.906 +  if (!args.length()) {
   1.907 +    JS_ReportError(cx, "Expected one argument");
   1.908 +    return false;
   1.909 +  }
   1.910 +
   1.911 +  if (!(args[0].isNumber() || args[0].isBoolean())) {
   1.912 +    JS_ReportError(cx, "Not a number");
   1.913 +    return false;
   1.914 +  }
   1.915 +
   1.916 +  int32_t value;
   1.917 +  if (!JS::ToInt32(cx, args[0], &value)) {
   1.918 +    return false;
   1.919 +  }
   1.920 +
   1.921 +  if (TelemetryImpl::CanRecord()) {
   1.922 +    JSObject *obj = JS_THIS_OBJECT(cx, vp);
   1.923 +    if (!obj) {
   1.924 +      return false;
   1.925 +    }
   1.926 +
   1.927 +    Histogram *h = static_cast<Histogram*>(JS_GetPrivate(obj));
   1.928 +    h->Add(value);
   1.929 +  }
   1.930 +  return true;
   1.931 +
   1.932 +}
   1.933 +
   1.934 +bool
   1.935 +JSHistogram_Snapshot(JSContext *cx, unsigned argc, JS::Value *vp)
   1.936 +{
   1.937 +  JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
   1.938 +  JSObject *obj = JS_THIS_OBJECT(cx, vp);
   1.939 +  if (!obj) {
   1.940 +    return false;
   1.941 +  }
   1.942 +
   1.943 +  Histogram *h = static_cast<Histogram*>(JS_GetPrivate(obj));
   1.944 +  JS::Rooted<JSObject*> snapshot(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr()));
   1.945 +  if (!snapshot)
   1.946 +    return false;
   1.947 +
   1.948 +  switch (ReflectHistogramSnapshot(cx, snapshot, h)) {
   1.949 +  case REFLECT_FAILURE:
   1.950 +    return false;
   1.951 +  case REFLECT_CORRUPT:
   1.952 +    JS_ReportError(cx, "Histogram is corrupt");
   1.953 +    return false;
   1.954 +  case REFLECT_OK:
   1.955 +    args.rval().setObject(*snapshot);
   1.956 +    return true;
   1.957 +  default:
   1.958 +    MOZ_CRASH("unhandled reflection status");
   1.959 +  }
   1.960 +}
   1.961 +
   1.962 +bool
   1.963 +JSHistogram_Clear(JSContext *cx, unsigned argc, JS::Value *vp)
   1.964 +{
   1.965 +  JSObject *obj = JS_THIS_OBJECT(cx, vp);
   1.966 +  if (!obj) {
   1.967 +    return false;
   1.968 +  }
   1.969 +
   1.970 +  Histogram *h = static_cast<Histogram*>(JS_GetPrivate(obj));
   1.971 +  h->Clear();
   1.972 +  return true;
   1.973 +}
   1.974 +
   1.975 +nsresult
   1.976 +WrapAndReturnHistogram(Histogram *h, JSContext *cx, JS::MutableHandle<JS::Value> ret)
   1.977 +{
   1.978 +  static const JSClass JSHistogram_class = {
   1.979 +    "JSHistogram",  /* name */
   1.980 +    JSCLASS_HAS_PRIVATE, /* flags */
   1.981 +    JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
   1.982 +    JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub
   1.983 +  };
   1.984 +
   1.985 +  JS::Rooted<JSObject*> obj(cx, JS_NewObject(cx, &JSHistogram_class, JS::NullPtr(), JS::NullPtr()));
   1.986 +  if (!obj)
   1.987 +    return NS_ERROR_FAILURE;
   1.988 +  if (!(JS_DefineFunction(cx, obj, "add", JSHistogram_Add, 1, 0)
   1.989 +        && JS_DefineFunction(cx, obj, "snapshot", JSHistogram_Snapshot, 0, 0)
   1.990 +        && JS_DefineFunction(cx, obj, "clear", JSHistogram_Clear, 0, 0))) {
   1.991 +    return NS_ERROR_FAILURE;
   1.992 +  }
   1.993 +  JS_SetPrivate(obj, h);
   1.994 +  ret.setObject(*obj);
   1.995 +  return NS_OK;
   1.996 +}
   1.997 +
   1.998 +static uint32_t
   1.999 +ReadLastShutdownDuration(const char *filename) {
  1.1000 +  FILE *f = fopen(filename, "r");
  1.1001 +  if (!f) {
  1.1002 +    return 0;
  1.1003 +  }
  1.1004 +
  1.1005 +  int shutdownTime;
  1.1006 +  int r = fscanf(f, "%d\n", &shutdownTime);
  1.1007 +  fclose(f);
  1.1008 +  if (r != 1) {
  1.1009 +    return 0;
  1.1010 +  }
  1.1011 +
  1.1012 +  return shutdownTime;
  1.1013 +}
  1.1014 +
  1.1015 +const int32_t kMaxFailedProfileLockFileSize = 10;
  1.1016 +
  1.1017 +bool
  1.1018 +GetFailedLockCount(nsIInputStream* inStream, uint32_t aCount,
  1.1019 +                   unsigned int& result)
  1.1020 +{
  1.1021 +  nsAutoCString bufStr;
  1.1022 +  nsresult rv;
  1.1023 +  rv = NS_ReadInputStreamToString(inStream, bufStr, aCount);
  1.1024 +  NS_ENSURE_SUCCESS(rv, false);
  1.1025 +  result = bufStr.ToInteger(&rv);
  1.1026 +  return NS_SUCCEEDED(rv) && result > 0;
  1.1027 +}
  1.1028 +
  1.1029 +nsresult
  1.1030 +GetFailedProfileLockFile(nsIFile* *aFile, nsIFile* aProfileDir)
  1.1031 +{
  1.1032 +  NS_ENSURE_ARG_POINTER(aProfileDir);
  1.1033 +
  1.1034 +  nsresult rv = aProfileDir->Clone(aFile);
  1.1035 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1036 +
  1.1037 +  (*aFile)->AppendNative(NS_LITERAL_CSTRING("Telemetry.FailedProfileLocks.txt"));
  1.1038 +  return NS_OK;
  1.1039 +}
  1.1040 +
  1.1041 +class nsFetchTelemetryData : public nsRunnable
  1.1042 +{
  1.1043 +public:
  1.1044 +  nsFetchTelemetryData(const char* aShutdownTimeFilename,
  1.1045 +                       nsIFile* aFailedProfileLockFile,
  1.1046 +                       nsIFile* aProfileDir)
  1.1047 +    : mShutdownTimeFilename(aShutdownTimeFilename),
  1.1048 +      mFailedProfileLockFile(aFailedProfileLockFile),
  1.1049 +      mTelemetry(TelemetryImpl::sTelemetry),
  1.1050 +      mProfileDir(aProfileDir)
  1.1051 +  {
  1.1052 +  }
  1.1053 +
  1.1054 +private:
  1.1055 +  const char* mShutdownTimeFilename;
  1.1056 +  nsCOMPtr<nsIFile> mFailedProfileLockFile;
  1.1057 +  nsRefPtr<TelemetryImpl> mTelemetry;
  1.1058 +  nsCOMPtr<nsIFile> mProfileDir;
  1.1059 +
  1.1060 +public:
  1.1061 +  void MainThread() {
  1.1062 +    mTelemetry->mCachedTelemetryData = true;
  1.1063 +    for (unsigned int i = 0, n = mTelemetry->mCallbacks.Count(); i < n; ++i) {
  1.1064 +      mTelemetry->mCallbacks[i]->Complete();
  1.1065 +    }
  1.1066 +    mTelemetry->mCallbacks.Clear();
  1.1067 +  }
  1.1068 +
  1.1069 +  NS_IMETHOD Run() {
  1.1070 +    LoadFailedLockCount(mTelemetry->mFailedLockCount);
  1.1071 +    mTelemetry->mLastShutdownTime = 
  1.1072 +      ReadLastShutdownDuration(mShutdownTimeFilename);
  1.1073 +    mTelemetry->ReadLateWritesStacks(mProfileDir);
  1.1074 +    nsCOMPtr<nsIRunnable> e =
  1.1075 +      NS_NewRunnableMethod(this, &nsFetchTelemetryData::MainThread);
  1.1076 +    NS_ENSURE_STATE(e);
  1.1077 +    NS_DispatchToMainThread(e, NS_DISPATCH_NORMAL);
  1.1078 +    return NS_OK;
  1.1079 +  }
  1.1080 +
  1.1081 +private:
  1.1082 +  nsresult
  1.1083 +  LoadFailedLockCount(uint32_t& failedLockCount)
  1.1084 +  {
  1.1085 +    failedLockCount = 0;
  1.1086 +    int64_t fileSize = 0;
  1.1087 +    nsresult rv = mFailedProfileLockFile->GetFileSize(&fileSize);
  1.1088 +    if (NS_FAILED(rv)) {
  1.1089 +      return rv;
  1.1090 +    }
  1.1091 +    NS_ENSURE_TRUE(fileSize <= kMaxFailedProfileLockFileSize,
  1.1092 +                   NS_ERROR_UNEXPECTED);
  1.1093 +    nsCOMPtr<nsIInputStream> inStream;
  1.1094 +    rv = NS_NewLocalFileInputStream(getter_AddRefs(inStream),
  1.1095 +                                    mFailedProfileLockFile,
  1.1096 +                                    PR_RDONLY);
  1.1097 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1098 +    NS_ENSURE_TRUE(GetFailedLockCount(inStream, fileSize, failedLockCount),
  1.1099 +                   NS_ERROR_UNEXPECTED);
  1.1100 +    inStream->Close();
  1.1101 +
  1.1102 +    mFailedProfileLockFile->Remove(false);
  1.1103 +    return NS_OK;
  1.1104 +  }
  1.1105 +};
  1.1106 +
  1.1107 +static TimeStamp gRecordedShutdownStartTime;
  1.1108 +static bool gAlreadyFreedShutdownTimeFileName = false;
  1.1109 +static char *gRecordedShutdownTimeFileName = nullptr;
  1.1110 +
  1.1111 +static char *
  1.1112 +GetShutdownTimeFileName()
  1.1113 +{
  1.1114 +  if (gAlreadyFreedShutdownTimeFileName) {
  1.1115 +    return nullptr;
  1.1116 +  }
  1.1117 +
  1.1118 +  if (!gRecordedShutdownTimeFileName) {
  1.1119 +    nsCOMPtr<nsIFile> mozFile;
  1.1120 +    NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(mozFile));
  1.1121 +    if (!mozFile)
  1.1122 +      return nullptr;
  1.1123 +
  1.1124 +    mozFile->AppendNative(NS_LITERAL_CSTRING("Telemetry.ShutdownTime.txt"));
  1.1125 +    nsAutoCString nativePath;
  1.1126 +    nsresult rv = mozFile->GetNativePath(nativePath);
  1.1127 +    if (!NS_SUCCEEDED(rv))
  1.1128 +      return nullptr;
  1.1129 +
  1.1130 +    gRecordedShutdownTimeFileName = PL_strdup(nativePath.get());
  1.1131 +  }
  1.1132 +
  1.1133 +  return gRecordedShutdownTimeFileName;
  1.1134 +}
  1.1135 +
  1.1136 +NS_IMETHODIMP
  1.1137 +TelemetryImpl::GetLastShutdownDuration(uint32_t *aResult)
  1.1138 +{
  1.1139 +  // The user must call AsyncFetchTelemetryData first. We return zero instead of
  1.1140 +  // reporting a failure so that the rest of telemetry can uniformly handle
  1.1141 +  // the read not being available yet.
  1.1142 +  if (!mCachedTelemetryData) {
  1.1143 +    *aResult = 0;
  1.1144 +    return NS_OK;
  1.1145 +  }
  1.1146 +
  1.1147 +  *aResult = mLastShutdownTime;
  1.1148 +  return NS_OK;
  1.1149 +}
  1.1150 +
  1.1151 +NS_IMETHODIMP
  1.1152 +TelemetryImpl::GetFailedProfileLockCount(uint32_t* aResult)
  1.1153 +{
  1.1154 +  // The user must call AsyncFetchTelemetryData first. We return zero instead of
  1.1155 +  // reporting a failure so that the rest of telemetry can uniformly handle
  1.1156 +  // the read not being available yet.
  1.1157 +  if (!mCachedTelemetryData) {
  1.1158 +    *aResult = 0;
  1.1159 +    return NS_OK;
  1.1160 +  }
  1.1161 +
  1.1162 +  *aResult = mFailedLockCount;
  1.1163 +  return NS_OK;
  1.1164 +}
  1.1165 +
  1.1166 +NS_IMETHODIMP
  1.1167 +TelemetryImpl::AsyncFetchTelemetryData(nsIFetchTelemetryDataCallback *aCallback)
  1.1168 +{
  1.1169 +  // We have finished reading the data already, just call the callback.
  1.1170 +  if (mCachedTelemetryData) {
  1.1171 +    aCallback->Complete();
  1.1172 +    return NS_OK;
  1.1173 +  }
  1.1174 +
  1.1175 +  // We already have a read request running, just remember the callback.
  1.1176 +  if (mCallbacks.Count() != 0) {
  1.1177 +    mCallbacks.AppendObject(aCallback);
  1.1178 +    return NS_OK;
  1.1179 +  }
  1.1180 +
  1.1181 +  // We make this check so that GetShutdownTimeFileName() doesn't get
  1.1182 +  // called; calling that function without telemetry enabled violates
  1.1183 +  // assumptions that the write-the-shutdown-timestamp machinery makes.
  1.1184 +  if (!Telemetry::CanRecord()) {
  1.1185 +    mCachedTelemetryData = true;
  1.1186 +    aCallback->Complete();
  1.1187 +    return NS_OK;
  1.1188 +  }
  1.1189 +
  1.1190 +  // Send the read to a background thread provided by the stream transport
  1.1191 +  // service to avoid a read in the main thread.
  1.1192 +  nsCOMPtr<nsIEventTarget> targetThread =
  1.1193 +    do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
  1.1194 +  if (!targetThread) {
  1.1195 +    mCachedTelemetryData = true;
  1.1196 +    aCallback->Complete();
  1.1197 +    return NS_OK;
  1.1198 +  }
  1.1199 +
  1.1200 +  // We have to get the filename from the main thread.
  1.1201 +  const char *shutdownTimeFilename = GetShutdownTimeFileName();
  1.1202 +  if (!shutdownTimeFilename) {
  1.1203 +    mCachedTelemetryData = true;
  1.1204 +    aCallback->Complete();
  1.1205 +    return NS_OK;
  1.1206 +  }
  1.1207 +
  1.1208 +  nsCOMPtr<nsIFile> profileDir;
  1.1209 +  nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
  1.1210 +                                       getter_AddRefs(profileDir));
  1.1211 +  if (NS_FAILED(rv)) {
  1.1212 +    mCachedTelemetryData = true;
  1.1213 +    aCallback->Complete();
  1.1214 +    return NS_OK;
  1.1215 +  }
  1.1216 +
  1.1217 +  nsCOMPtr<nsIFile> failedProfileLockFile;
  1.1218 +  rv = GetFailedProfileLockFile(getter_AddRefs(failedProfileLockFile),
  1.1219 +                                profileDir);
  1.1220 +  if (NS_FAILED(rv)) {
  1.1221 +    mCachedTelemetryData = true;
  1.1222 +    aCallback->Complete();
  1.1223 +    return NS_OK;
  1.1224 +  }
  1.1225 +
  1.1226 +  mCallbacks.AppendObject(aCallback);
  1.1227 +
  1.1228 +  nsCOMPtr<nsIRunnable> event = new nsFetchTelemetryData(shutdownTimeFilename,
  1.1229 +                                                         failedProfileLockFile,
  1.1230 +                                                         profileDir);
  1.1231 +
  1.1232 +  targetThread->Dispatch(event, NS_DISPATCH_NORMAL);
  1.1233 +  return NS_OK;
  1.1234 +}
  1.1235 +
  1.1236 +TelemetryImpl::TelemetryImpl():
  1.1237 +mHistogramMap(Telemetry::HistogramCount),
  1.1238 +mCanRecord(XRE_GetProcessType() == GeckoProcessType_Default),
  1.1239 +mHashMutex("Telemetry::mHashMutex"),
  1.1240 +mHangReportsMutex("Telemetry::mHangReportsMutex"),
  1.1241 +mThreadHangStatsMutex("Telemetry::mThreadHangStatsMutex"),
  1.1242 +mCachedTelemetryData(false),
  1.1243 +mLastShutdownTime(0),
  1.1244 +mFailedLockCount(0)
  1.1245 +{
  1.1246 +  // A whitelist to prevent Telemetry reporting on Addon & Thunderbird DBs
  1.1247 +  const char *trackedDBs[] = {
  1.1248 +    "addons.sqlite", "content-prefs.sqlite", "cookies.sqlite",
  1.1249 +    "downloads.sqlite", "extensions.sqlite", "formhistory.sqlite",
  1.1250 +    "index.sqlite", "healthreport.sqlite", "netpredictions.sqlite",
  1.1251 +    "permissions.sqlite", "places.sqlite", "search.sqlite", "signons.sqlite",
  1.1252 +    "urlclassifier3.sqlite", "webappsstore.sqlite"
  1.1253 +  };
  1.1254 +
  1.1255 +  for (size_t i = 0; i < ArrayLength(trackedDBs); i++)
  1.1256 +    mTrackedDBs.PutEntry(nsDependentCString(trackedDBs[i]));
  1.1257 +
  1.1258 +#ifdef DEBUG
  1.1259 +  // Mark immutable to prevent asserts on simultaneous access from multiple threads
  1.1260 +  mTrackedDBs.MarkImmutable();
  1.1261 +#endif
  1.1262 +}
  1.1263 +
  1.1264 +TelemetryImpl::~TelemetryImpl() {
  1.1265 +  UnregisterWeakMemoryReporter(this);
  1.1266 +}
  1.1267 +
  1.1268 +void
  1.1269 +TelemetryImpl::InitMemoryReporter() {
  1.1270 +  RegisterWeakMemoryReporter(this);
  1.1271 +}
  1.1272 +
  1.1273 +NS_IMETHODIMP
  1.1274 +TelemetryImpl::NewHistogram(const nsACString &name, const nsACString &expiration, uint32_t min,
  1.1275 +                            uint32_t max, uint32_t bucketCount, uint32_t histogramType,
  1.1276 +                            JSContext *cx, JS::MutableHandle<JS::Value> ret)
  1.1277 +{
  1.1278 +  Histogram *h;
  1.1279 +  nsresult rv = HistogramGet(PromiseFlatCString(name).get(), PromiseFlatCString(expiration).get(),
  1.1280 +                             min, max, bucketCount, histogramType, &h);
  1.1281 +  if (NS_FAILED(rv))
  1.1282 +    return rv;
  1.1283 +  h->ClearFlags(Histogram::kUmaTargetedHistogramFlag);
  1.1284 +  h->SetFlags(Histogram::kExtendedStatisticsFlag);
  1.1285 +  return WrapAndReturnHistogram(h, cx, ret);
  1.1286 +}
  1.1287 +
  1.1288 +bool
  1.1289 +TelemetryImpl::ReflectSQL(const SlowSQLEntryType *entry,
  1.1290 +                          const Stat *stat,
  1.1291 +                          JSContext *cx,
  1.1292 +                          JS::Handle<JSObject*> obj)
  1.1293 +{
  1.1294 +  if (stat->hitCount == 0)
  1.1295 +    return true;
  1.1296 +
  1.1297 +  const nsACString &sql = entry->GetKey();
  1.1298 +
  1.1299 +  JS::Rooted<JSObject*> arrayObj(cx, JS_NewArrayObject(cx, 0));
  1.1300 +  if (!arrayObj) {
  1.1301 +    return false;
  1.1302 +  }
  1.1303 +  return (JS_SetElement(cx, arrayObj, 0, stat->hitCount)
  1.1304 +          && JS_SetElement(cx, arrayObj, 1, stat->totalTime)
  1.1305 +          && JS_DefineProperty(cx, obj, sql.BeginReading(), arrayObj,
  1.1306 +                               JSPROP_ENUMERATE));
  1.1307 +}
  1.1308 +
  1.1309 +bool
  1.1310 +TelemetryImpl::ReflectMainThreadSQL(SlowSQLEntryType *entry, JSContext *cx,
  1.1311 +                                    JS::Handle<JSObject*> obj)
  1.1312 +{
  1.1313 +  return ReflectSQL(entry, &entry->mData.mainThread, cx, obj);
  1.1314 +}
  1.1315 +
  1.1316 +bool
  1.1317 +TelemetryImpl::ReflectOtherThreadsSQL(SlowSQLEntryType *entry, JSContext *cx,
  1.1318 +                                      JS::Handle<JSObject*> obj)
  1.1319 +{
  1.1320 +  return ReflectSQL(entry, &entry->mData.otherThreads, cx, obj);
  1.1321 +}
  1.1322 +
  1.1323 +bool
  1.1324 +TelemetryImpl::AddSQLInfo(JSContext *cx, JS::Handle<JSObject*> rootObj, bool mainThread,
  1.1325 +                          bool privateSQL)
  1.1326 +{
  1.1327 +  JS::Rooted<JSObject*> statsObj(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr()));
  1.1328 +  if (!statsObj)
  1.1329 +    return false;
  1.1330 +
  1.1331 +  AutoHashtable<SlowSQLEntryType> &sqlMap =
  1.1332 +    (privateSQL ? mPrivateSQL : mSanitizedSQL);
  1.1333 +  AutoHashtable<SlowSQLEntryType>::ReflectEntryFunc reflectFunction =
  1.1334 +    (mainThread ? ReflectMainThreadSQL : ReflectOtherThreadsSQL);
  1.1335 +  if (!sqlMap.ReflectIntoJS(reflectFunction, cx, statsObj)) {
  1.1336 +    return false;
  1.1337 +  }
  1.1338 +
  1.1339 +  return JS_DefineProperty(cx, rootObj,
  1.1340 +                           mainThread ? "mainThread" : "otherThreads",
  1.1341 +                           statsObj, JSPROP_ENUMERATE);
  1.1342 +}
  1.1343 +
  1.1344 +nsresult
  1.1345 +TelemetryImpl::GetHistogramEnumId(const char *name, Telemetry::ID *id)
  1.1346 +{
  1.1347 +  if (!sTelemetry) {
  1.1348 +    return NS_ERROR_FAILURE;
  1.1349 +  }
  1.1350 +
  1.1351 +  // Cache names
  1.1352 +  // Note the histogram names are statically allocated
  1.1353 +  TelemetryImpl::HistogramMapType *map = &sTelemetry->mHistogramMap;
  1.1354 +  if (!map->Count()) {
  1.1355 +    for (uint32_t i = 0; i < Telemetry::HistogramCount; i++) {
  1.1356 +      CharPtrEntryType *entry = map->PutEntry(gHistograms[i].id());
  1.1357 +      if (MOZ_UNLIKELY(!entry)) {
  1.1358 +        map->Clear();
  1.1359 +        return NS_ERROR_OUT_OF_MEMORY;
  1.1360 +      }
  1.1361 +      entry->mData = (Telemetry::ID) i;
  1.1362 +    }
  1.1363 +  }
  1.1364 +
  1.1365 +  CharPtrEntryType *entry = map->GetEntry(name);
  1.1366 +  if (!entry) {
  1.1367 +    return NS_ERROR_INVALID_ARG;
  1.1368 +  }
  1.1369 +  *id = entry->mData;
  1.1370 +  return NS_OK;
  1.1371 +}
  1.1372 +
  1.1373 +nsresult
  1.1374 +TelemetryImpl::GetHistogramByName(const nsACString &name, Histogram **ret)
  1.1375 +{
  1.1376 +  Telemetry::ID id;
  1.1377 +  nsresult rv = GetHistogramEnumId(PromiseFlatCString(name).get(), &id);
  1.1378 +  if (NS_FAILED(rv)) {
  1.1379 +    return rv;
  1.1380 +  }
  1.1381 +
  1.1382 +  rv = GetHistogramByEnumId(id, ret);
  1.1383 +  if (NS_FAILED(rv))
  1.1384 +    return rv;
  1.1385 +
  1.1386 +  return NS_OK;
  1.1387 +}
  1.1388 +
  1.1389 +NS_IMETHODIMP
  1.1390 +TelemetryImpl::HistogramFrom(const nsACString &name, const nsACString &existing_name,
  1.1391 +                             JSContext *cx, JS::MutableHandle<JS::Value> ret)
  1.1392 +{
  1.1393 +  Telemetry::ID id;
  1.1394 +  nsresult rv = GetHistogramEnumId(PromiseFlatCString(existing_name).get(), &id);
  1.1395 +  if (NS_FAILED(rv)) {
  1.1396 +    return rv;
  1.1397 +  }
  1.1398 +  const TelemetryHistogram &p = gHistograms[id];
  1.1399 +
  1.1400 +  Histogram *existing;
  1.1401 +  rv = GetHistogramByEnumId(id, &existing);
  1.1402 +  if (NS_FAILED(rv)) {
  1.1403 +    return rv;
  1.1404 +  }
  1.1405 +
  1.1406 +  Histogram *clone;
  1.1407 +  rv = HistogramGet(PromiseFlatCString(name).get(), p.expiration(),
  1.1408 +                    existing->declared_min(), existing->declared_max(),
  1.1409 +                    existing->bucket_count(), p.histogramType, &clone);
  1.1410 +  if (NS_FAILED(rv))
  1.1411 +    return rv;
  1.1412 +
  1.1413 +  Histogram::SampleSet ss;
  1.1414 +  existing->SnapshotSample(&ss);
  1.1415 +  clone->AddSampleSet(ss);
  1.1416 +  return WrapAndReturnHistogram(clone, cx, ret);
  1.1417 +}
  1.1418 +
  1.1419 +void
  1.1420 +TelemetryImpl::IdentifyCorruptHistograms(StatisticsRecorder::Histograms &hs)
  1.1421 +{
  1.1422 +  for (HistogramIterator it = hs.begin(); it != hs.end(); ++it) {
  1.1423 +    Histogram *h = *it;
  1.1424 +
  1.1425 +    Telemetry::ID id;
  1.1426 +    nsresult rv = GetHistogramEnumId(h->histogram_name().c_str(), &id);
  1.1427 +    // This histogram isn't a static histogram, just ignore it.
  1.1428 +    if (NS_FAILED(rv)) {
  1.1429 +      continue;
  1.1430 +    }
  1.1431 +
  1.1432 +    if (gCorruptHistograms[id]) {
  1.1433 +      continue;
  1.1434 +    }
  1.1435 +
  1.1436 +    Histogram::SampleSet ss;
  1.1437 +    h->SnapshotSample(&ss);
  1.1438 +    Histogram::Inconsistencies check = h->FindCorruption(ss);
  1.1439 +    bool corrupt = (check != Histogram::NO_INCONSISTENCIES);
  1.1440 +
  1.1441 +    if (corrupt) {
  1.1442 +      Telemetry::ID corruptID = Telemetry::HistogramCount;
  1.1443 +      if (check & Histogram::RANGE_CHECKSUM_ERROR) {
  1.1444 +        corruptID = Telemetry::RANGE_CHECKSUM_ERRORS;
  1.1445 +      } else if (check & Histogram::BUCKET_ORDER_ERROR) {
  1.1446 +        corruptID = Telemetry::BUCKET_ORDER_ERRORS;
  1.1447 +      } else if (check & Histogram::COUNT_HIGH_ERROR) {
  1.1448 +        corruptID = Telemetry::TOTAL_COUNT_HIGH_ERRORS;
  1.1449 +      } else if (check & Histogram::COUNT_LOW_ERROR) {
  1.1450 +        corruptID = Telemetry::TOTAL_COUNT_LOW_ERRORS;
  1.1451 +      }
  1.1452 +      Telemetry::Accumulate(corruptID, 1);
  1.1453 +    }
  1.1454 +
  1.1455 +    gCorruptHistograms[id] = corrupt;
  1.1456 +  }
  1.1457 +}
  1.1458 +
  1.1459 +bool
  1.1460 +TelemetryImpl::ShouldReflectHistogram(Histogram *h)
  1.1461 +{
  1.1462 +  const char *name = h->histogram_name().c_str();
  1.1463 +  Telemetry::ID id;
  1.1464 +  nsresult rv = GetHistogramEnumId(name, &id);
  1.1465 +  if (NS_FAILED(rv)) {
  1.1466 +    // GetHistogramEnumId generally should not fail.  But a lookup
  1.1467 +    // failure shouldn't prevent us from reflecting histograms into JS.
  1.1468 +    //
  1.1469 +    // However, these two histograms are created by Histogram itself for
  1.1470 +    // tracking corruption.  We have our own histograms for that, so
  1.1471 +    // ignore these two.
  1.1472 +    if (strcmp(name, "Histogram.InconsistentCountHigh") == 0
  1.1473 +        || strcmp(name, "Histogram.InconsistentCountLow") == 0) {
  1.1474 +      return false;
  1.1475 +    }
  1.1476 +    return true;
  1.1477 +  } else {
  1.1478 +    return !gCorruptHistograms[id];
  1.1479 +  }
  1.1480 +}
  1.1481 +
  1.1482 +// Compute the name to pass into Histogram for the addon histogram
  1.1483 +// 'name' from the addon 'id'.  We can't use 'name' directly because it
  1.1484 +// might conflict with other histograms in other addons or even with our
  1.1485 +// own.
  1.1486 +void
  1.1487 +AddonHistogramName(const nsACString &id, const nsACString &name,
  1.1488 +                   nsACString &ret)
  1.1489 +{
  1.1490 +  ret.Append(id);
  1.1491 +  ret.Append(NS_LITERAL_CSTRING(":"));
  1.1492 +  ret.Append(name);
  1.1493 +}
  1.1494 +
  1.1495 +NS_IMETHODIMP
  1.1496 +TelemetryImpl::RegisterAddonHistogram(const nsACString &id,
  1.1497 +                                      const nsACString &name,
  1.1498 +                                      uint32_t min, uint32_t max,
  1.1499 +                                      uint32_t bucketCount,
  1.1500 +                                      uint32_t histogramType)
  1.1501 +{
  1.1502 +  AddonEntryType *addonEntry = mAddonMap.GetEntry(id);
  1.1503 +  if (!addonEntry) {
  1.1504 +    addonEntry = mAddonMap.PutEntry(id);
  1.1505 +    if (MOZ_UNLIKELY(!addonEntry)) {
  1.1506 +      return NS_ERROR_OUT_OF_MEMORY;
  1.1507 +    }
  1.1508 +    addonEntry->mData = new AddonHistogramMapType();
  1.1509 +  }
  1.1510 +
  1.1511 +  AddonHistogramMapType *histogramMap = addonEntry->mData;
  1.1512 +  AddonHistogramEntryType *histogramEntry = histogramMap->GetEntry(name);
  1.1513 +  // Can't re-register the same histogram.
  1.1514 +  if (histogramEntry) {
  1.1515 +    return NS_ERROR_FAILURE;
  1.1516 +  }
  1.1517 +
  1.1518 +  histogramEntry = histogramMap->PutEntry(name);
  1.1519 +  if (MOZ_UNLIKELY(!histogramEntry)) {
  1.1520 +    return NS_ERROR_OUT_OF_MEMORY;
  1.1521 +  }
  1.1522 +
  1.1523 +  AddonHistogramInfo &info = histogramEntry->mData;
  1.1524 +  info.min = min;
  1.1525 +  info.max = max;
  1.1526 +  info.bucketCount = bucketCount;
  1.1527 +  info.histogramType = histogramType;
  1.1528 +
  1.1529 +  return NS_OK;
  1.1530 +}
  1.1531 +
  1.1532 +NS_IMETHODIMP
  1.1533 +TelemetryImpl::GetAddonHistogram(const nsACString &id, const nsACString &name,
  1.1534 +                                 JSContext *cx, JS::MutableHandle<JS::Value> ret)
  1.1535 +{
  1.1536 +  AddonEntryType *addonEntry = mAddonMap.GetEntry(id);
  1.1537 +  // The given id has not been registered.
  1.1538 +  if (!addonEntry) {
  1.1539 +    return NS_ERROR_INVALID_ARG;
  1.1540 +  }
  1.1541 +
  1.1542 +  AddonHistogramMapType *histogramMap = addonEntry->mData;
  1.1543 +  AddonHistogramEntryType *histogramEntry = histogramMap->GetEntry(name);
  1.1544 +  // The given histogram name has not been registered.
  1.1545 +  if (!histogramEntry) {
  1.1546 +    return NS_ERROR_INVALID_ARG;
  1.1547 +  }
  1.1548 +
  1.1549 +  AddonHistogramInfo &info = histogramEntry->mData;
  1.1550 +  if (!info.h) {
  1.1551 +    nsAutoCString actualName;
  1.1552 +    AddonHistogramName(id, name, actualName);
  1.1553 +    if (!CreateHistogramForAddon(actualName, info)) {
  1.1554 +      return NS_ERROR_FAILURE;
  1.1555 +    }
  1.1556 +  }
  1.1557 +  return WrapAndReturnHistogram(info.h, cx, ret);
  1.1558 +}
  1.1559 +
  1.1560 +NS_IMETHODIMP
  1.1561 +TelemetryImpl::UnregisterAddonHistograms(const nsACString &id)
  1.1562 +{
  1.1563 +  AddonEntryType *addonEntry = mAddonMap.GetEntry(id);
  1.1564 +  if (addonEntry) {
  1.1565 +    // Histogram's destructor is private, so this is the best we can do.
  1.1566 +    // The histograms the addon created *will* stick around, but they
  1.1567 +    // will be deleted if and when the addon registers histograms with
  1.1568 +    // the same names.
  1.1569 +    delete addonEntry->mData;
  1.1570 +    mAddonMap.RemoveEntry(id);
  1.1571 +  }
  1.1572 +
  1.1573 +  return NS_OK;
  1.1574 +}
  1.1575 +
  1.1576 +NS_IMETHODIMP
  1.1577 +TelemetryImpl::GetHistogramSnapshots(JSContext *cx, JS::MutableHandle<JS::Value> ret)
  1.1578 +{
  1.1579 +  JS::Rooted<JSObject*> root_obj(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr()));
  1.1580 +  if (!root_obj)
  1.1581 +    return NS_ERROR_FAILURE;
  1.1582 +  ret.setObject(*root_obj);
  1.1583 +
  1.1584 +  // Ensure that all the HISTOGRAM_FLAG histograms have been created, so
  1.1585 +  // that their values are snapshotted.
  1.1586 +  for (size_t i = 0; i < Telemetry::HistogramCount; ++i) {
  1.1587 +    if (gHistograms[i].histogramType == nsITelemetry::HISTOGRAM_FLAG) {
  1.1588 +      Histogram *h;
  1.1589 +      DebugOnly<nsresult> rv = GetHistogramByEnumId(Telemetry::ID(i), &h);
  1.1590 +      MOZ_ASSERT(NS_SUCCEEDED(rv));
  1.1591 +    }
  1.1592 +  };
  1.1593 +
  1.1594 +  StatisticsRecorder::Histograms hs;
  1.1595 +  StatisticsRecorder::GetHistograms(&hs);
  1.1596 +
  1.1597 +  // We identify corrupt histograms first, rather than interspersing it
  1.1598 +  // in the loop below, to ensure that our corruption statistics don't
  1.1599 +  // depend on histogram enumeration order.
  1.1600 +  //
  1.1601 +  // Of course, we hope that all of these corruption-statistics
  1.1602 +  // histograms are not themselves corrupt...
  1.1603 +  IdentifyCorruptHistograms(hs);
  1.1604 +
  1.1605 +  // OK, now we can actually reflect things.
  1.1606 +  JS::Rooted<JSObject*> hobj(cx);
  1.1607 +  for (HistogramIterator it = hs.begin(); it != hs.end(); ++it) {
  1.1608 +    Histogram *h = *it;
  1.1609 +    if (!ShouldReflectHistogram(h) || IsEmpty(h) || IsExpired(h)) {
  1.1610 +      continue;
  1.1611 +    }
  1.1612 +
  1.1613 +    hobj = JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr());
  1.1614 +    if (!hobj) {
  1.1615 +      return NS_ERROR_FAILURE;
  1.1616 +    }
  1.1617 +    switch (ReflectHistogramSnapshot(cx, hobj, h)) {
  1.1618 +    case REFLECT_CORRUPT:
  1.1619 +      // We can still hit this case even if ShouldReflectHistograms
  1.1620 +      // returns true.  The histogram lies outside of our control
  1.1621 +      // somehow; just skip it.
  1.1622 +      continue;
  1.1623 +    case REFLECT_FAILURE:
  1.1624 +      return NS_ERROR_FAILURE;
  1.1625 +    case REFLECT_OK:
  1.1626 +      if (!JS_DefineProperty(cx, root_obj, h->histogram_name().c_str(), hobj,
  1.1627 +                             JSPROP_ENUMERATE)) {
  1.1628 +        return NS_ERROR_FAILURE;
  1.1629 +      }
  1.1630 +    }
  1.1631 +  }
  1.1632 +  return NS_OK;
  1.1633 +}
  1.1634 +
  1.1635 +bool
  1.1636 +TelemetryImpl::CreateHistogramForAddon(const nsACString &name,
  1.1637 +                                       AddonHistogramInfo &info)
  1.1638 +{
  1.1639 +  Histogram *h;
  1.1640 +  nsresult rv = HistogramGet(PromiseFlatCString(name).get(), "never",
  1.1641 +                             info.min, info.max, info.bucketCount,
  1.1642 +                             info.histogramType, &h);
  1.1643 +  if (NS_FAILED(rv)) {
  1.1644 +    return false;
  1.1645 +  }
  1.1646 +  // Don't let this histogram be reported via the normal means
  1.1647 +  // (e.g. Telemetry.registeredHistograms); we'll make it available in
  1.1648 +  // other ways.
  1.1649 +  h->ClearFlags(Histogram::kUmaTargetedHistogramFlag);
  1.1650 +  info.h = h;
  1.1651 +  return true;
  1.1652 +}
  1.1653 +
  1.1654 +bool
  1.1655 +TelemetryImpl::AddonHistogramReflector(AddonHistogramEntryType *entry,
  1.1656 +                                       JSContext *cx, JS::Handle<JSObject*> obj)
  1.1657 +{
  1.1658 +  AddonHistogramInfo &info = entry->mData;
  1.1659 +
  1.1660 +  // Never even accessed the histogram.
  1.1661 +  if (!info.h) {
  1.1662 +    // Have to force creation of HISTOGRAM_FLAG histograms.
  1.1663 +    if (info.histogramType != nsITelemetry::HISTOGRAM_FLAG) 
  1.1664 +      return true;
  1.1665 +
  1.1666 +    if (!CreateHistogramForAddon(entry->GetKey(), info)) {
  1.1667 +      return false;
  1.1668 +    }
  1.1669 +  }
  1.1670 +
  1.1671 +  if (IsEmpty(info.h)) {
  1.1672 +    return true;
  1.1673 +  }
  1.1674 +
  1.1675 +  JS::Rooted<JSObject*> snapshot(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr()));
  1.1676 +  if (!snapshot) {
  1.1677 +    // Just consider this to be skippable.
  1.1678 +    return true;
  1.1679 +  }
  1.1680 +  switch (ReflectHistogramSnapshot(cx, snapshot, info.h)) {
  1.1681 +  case REFLECT_FAILURE:
  1.1682 +  case REFLECT_CORRUPT:
  1.1683 +    return false;
  1.1684 +  case REFLECT_OK:
  1.1685 +    const nsACString &histogramName = entry->GetKey();
  1.1686 +    if (!JS_DefineProperty(cx, obj, PromiseFlatCString(histogramName).get(),
  1.1687 +                           snapshot, JSPROP_ENUMERATE)) {
  1.1688 +      return false;
  1.1689 +    }
  1.1690 +    break;
  1.1691 +  }
  1.1692 +  return true;
  1.1693 +}
  1.1694 +
  1.1695 +bool
  1.1696 +TelemetryImpl::AddonReflector(AddonEntryType *entry,
  1.1697 +                              JSContext *cx, JS::Handle<JSObject*> obj)
  1.1698 +{
  1.1699 +  const nsACString &addonId = entry->GetKey();
  1.1700 +  JS::Rooted<JSObject*> subobj(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr()));
  1.1701 +  if (!subobj) {
  1.1702 +    return false;
  1.1703 +  }
  1.1704 +
  1.1705 +  AddonHistogramMapType *map = entry->mData;
  1.1706 +  if (!(map->ReflectIntoJS(AddonHistogramReflector, cx, subobj)
  1.1707 +        && JS_DefineProperty(cx, obj, PromiseFlatCString(addonId).get(),
  1.1708 +                             subobj, JSPROP_ENUMERATE))) {
  1.1709 +    return false;
  1.1710 +  }
  1.1711 +  return true;
  1.1712 +}
  1.1713 +
  1.1714 +NS_IMETHODIMP
  1.1715 +TelemetryImpl::GetAddonHistogramSnapshots(JSContext *cx, JS::MutableHandle<JS::Value> ret)
  1.1716 +{
  1.1717 +  JS::Rooted<JSObject*> obj(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr()));
  1.1718 +  if (!obj) {
  1.1719 +    return NS_ERROR_FAILURE;
  1.1720 +  }
  1.1721 +
  1.1722 +  if (!mAddonMap.ReflectIntoJS(AddonReflector, cx, obj)) {
  1.1723 +    return NS_ERROR_FAILURE;
  1.1724 +  }
  1.1725 +  ret.setObject(*obj);
  1.1726 +  return NS_OK;
  1.1727 +}
  1.1728 +
  1.1729 +bool
  1.1730 +TelemetryImpl::GetSQLStats(JSContext *cx, JS::MutableHandle<JS::Value> ret, bool includePrivateSql)
  1.1731 +{
  1.1732 +  JS::Rooted<JSObject*> root_obj(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr()));
  1.1733 +  if (!root_obj)
  1.1734 +    return false;
  1.1735 +  ret.setObject(*root_obj);
  1.1736 +
  1.1737 +  MutexAutoLock hashMutex(mHashMutex);
  1.1738 +  // Add info about slow SQL queries on the main thread
  1.1739 +  if (!AddSQLInfo(cx, root_obj, true, includePrivateSql))
  1.1740 +    return false;
  1.1741 +  // Add info about slow SQL queries on other threads
  1.1742 +  if (!AddSQLInfo(cx, root_obj, false, includePrivateSql))
  1.1743 +    return false;
  1.1744 +
  1.1745 +  return true;
  1.1746 +}
  1.1747 +
  1.1748 +NS_IMETHODIMP
  1.1749 +TelemetryImpl::GetSlowSQL(JSContext *cx, JS::MutableHandle<JS::Value> ret)
  1.1750 +{
  1.1751 +  if (GetSQLStats(cx, ret, false))
  1.1752 +    return NS_OK;
  1.1753 +  return NS_ERROR_FAILURE;
  1.1754 +}
  1.1755 +
  1.1756 +NS_IMETHODIMP
  1.1757 +TelemetryImpl::GetDebugSlowSQL(JSContext *cx, JS::MutableHandle<JS::Value> ret)
  1.1758 +{
  1.1759 +  bool revealPrivateSql =
  1.1760 +    Preferences::GetBool("toolkit.telemetry.debugSlowSql", false);
  1.1761 +  if (GetSQLStats(cx, ret, revealPrivateSql))
  1.1762 +    return NS_OK;
  1.1763 +  return NS_ERROR_FAILURE;
  1.1764 +}
  1.1765 +
  1.1766 +NS_IMETHODIMP
  1.1767 +TelemetryImpl::GetMaximalNumberOfConcurrentThreads(uint32_t *ret)
  1.1768 +{
  1.1769 +  *ret = nsThreadManager::get()->GetHighestNumberOfThreads();
  1.1770 +  return NS_OK;
  1.1771 +}
  1.1772 +
  1.1773 +NS_IMETHODIMP
  1.1774 +TelemetryImpl::GetChromeHangs(JSContext *cx, JS::MutableHandle<JS::Value> ret)
  1.1775 +{
  1.1776 +  MutexAutoLock hangReportMutex(mHangReportsMutex);
  1.1777 +
  1.1778 +  const CombinedStacks& stacks = mHangReports.GetStacks();
  1.1779 +  JS::Rooted<JSObject*> fullReportObj(cx, CreateJSStackObject(cx, stacks));
  1.1780 +  if (!fullReportObj) {
  1.1781 +    return NS_ERROR_FAILURE;
  1.1782 +  }
  1.1783 +
  1.1784 +  ret.setObject(*fullReportObj);
  1.1785 +
  1.1786 +  JS::Rooted<JSObject*> durationArray(cx, JS_NewArrayObject(cx, 0));
  1.1787 +  JS::Rooted<JSObject*> systemUptimeArray(cx, JS_NewArrayObject(cx, 0));
  1.1788 +  JS::Rooted<JSObject*> firefoxUptimeArray(cx, JS_NewArrayObject(cx, 0));
  1.1789 +  if (!durationArray || !systemUptimeArray || !firefoxUptimeArray) {
  1.1790 +    return NS_ERROR_FAILURE;
  1.1791 +  }
  1.1792 +
  1.1793 +  bool ok = JS_DefineProperty(cx, fullReportObj, "durations",
  1.1794 +                              durationArray, JSPROP_ENUMERATE);
  1.1795 +  if (!ok) {
  1.1796 +    return NS_ERROR_FAILURE;
  1.1797 +  }
  1.1798 +
  1.1799 +  ok = JS_DefineProperty(cx, fullReportObj, "systemUptime",
  1.1800 +                         systemUptimeArray, JSPROP_ENUMERATE);
  1.1801 +  if (!ok) {
  1.1802 +    return NS_ERROR_FAILURE;
  1.1803 +  }
  1.1804 +
  1.1805 +  ok = JS_DefineProperty(cx, fullReportObj, "firefoxUptime",
  1.1806 +                         firefoxUptimeArray, JSPROP_ENUMERATE);
  1.1807 +  if (!ok) {
  1.1808 +    return NS_ERROR_FAILURE;
  1.1809 +  }
  1.1810 +
  1.1811 +  const size_t length = stacks.GetStackCount();
  1.1812 +  for (size_t i = 0; i < length; ++i) {
  1.1813 +    if (!JS_SetElement(cx, durationArray, i, mHangReports.GetDuration(i))) {
  1.1814 +      return NS_ERROR_FAILURE;
  1.1815 +    }
  1.1816 +    if (!JS_SetElement(cx, systemUptimeArray, i, mHangReports.GetSystemUptime(i))) {
  1.1817 +      return NS_ERROR_FAILURE;
  1.1818 +    }
  1.1819 +    if (!JS_SetElement(cx, firefoxUptimeArray, i, mHangReports.GetFirefoxUptime(i))) {
  1.1820 +      return NS_ERROR_FAILURE;
  1.1821 +    }
  1.1822 +  }
  1.1823 +
  1.1824 +  return NS_OK;
  1.1825 +}
  1.1826 +
  1.1827 +static JSObject *
  1.1828 +CreateJSStackObject(JSContext *cx, const CombinedStacks &stacks) {
  1.1829 +  JS::Rooted<JSObject*> ret(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr()));
  1.1830 +  if (!ret) {
  1.1831 +    return nullptr;
  1.1832 +  }
  1.1833 +
  1.1834 +  JS::Rooted<JSObject*> moduleArray(cx, JS_NewArrayObject(cx, 0));
  1.1835 +  if (!moduleArray) {
  1.1836 +    return nullptr;
  1.1837 +  }
  1.1838 +  bool ok = JS_DefineProperty(cx, ret, "memoryMap", moduleArray,
  1.1839 +                              JSPROP_ENUMERATE);
  1.1840 +  if (!ok) {
  1.1841 +    return nullptr;
  1.1842 +  }
  1.1843 +
  1.1844 +  const size_t moduleCount = stacks.GetModuleCount();
  1.1845 +  for (size_t moduleIndex = 0; moduleIndex < moduleCount; ++moduleIndex) {
  1.1846 +    // Current module
  1.1847 +    const Telemetry::ProcessedStack::Module& module =
  1.1848 +      stacks.GetModule(moduleIndex);
  1.1849 +
  1.1850 +    JS::Rooted<JSObject*> moduleInfoArray(cx, JS_NewArrayObject(cx, 0));
  1.1851 +    if (!moduleInfoArray) {
  1.1852 +      return nullptr;
  1.1853 +    }
  1.1854 +    if (!JS_SetElement(cx, moduleArray, moduleIndex, moduleInfoArray)) {
  1.1855 +      return nullptr;
  1.1856 +    }
  1.1857 +
  1.1858 +    unsigned index = 0;
  1.1859 +
  1.1860 +    // Module name
  1.1861 +    JS::Rooted<JSString*> str(cx, JS_NewStringCopyZ(cx, module.mName.c_str()));
  1.1862 +    if (!str) {
  1.1863 +      return nullptr;
  1.1864 +    }
  1.1865 +    if (!JS_SetElement(cx, moduleInfoArray, index++, str)) {
  1.1866 +      return nullptr;
  1.1867 +    }
  1.1868 +
  1.1869 +    // Module breakpad identifier
  1.1870 +    JS::Rooted<JSString*> id(cx, JS_NewStringCopyZ(cx, module.mBreakpadId.c_str()));
  1.1871 +    if (!id) {
  1.1872 +      return nullptr;
  1.1873 +    }
  1.1874 +    if (!JS_SetElement(cx, moduleInfoArray, index++, id)) {
  1.1875 +      return nullptr;
  1.1876 +    }
  1.1877 +  }
  1.1878 +
  1.1879 +  JS::Rooted<JSObject*> reportArray(cx, JS_NewArrayObject(cx, 0));
  1.1880 +  if (!reportArray) {
  1.1881 +    return nullptr;
  1.1882 +  }
  1.1883 +  ok = JS_DefineProperty(cx, ret, "stacks", reportArray, JSPROP_ENUMERATE);
  1.1884 +  if (!ok) {
  1.1885 +    return nullptr;
  1.1886 +  }
  1.1887 +
  1.1888 +  const size_t length = stacks.GetStackCount();
  1.1889 +  for (size_t i = 0; i < length; ++i) {
  1.1890 +    // Represent call stack PCs as (module index, offset) pairs.
  1.1891 +    JS::Rooted<JSObject*> pcArray(cx, JS_NewArrayObject(cx, 0));
  1.1892 +    if (!pcArray) {
  1.1893 +      return nullptr;
  1.1894 +    }
  1.1895 +
  1.1896 +    if (!JS_SetElement(cx, reportArray, i, pcArray)) {
  1.1897 +      return nullptr;
  1.1898 +    }
  1.1899 +
  1.1900 +    const CombinedStacks::Stack& stack = stacks.GetStack(i);
  1.1901 +    const uint32_t pcCount = stack.size();
  1.1902 +    for (size_t pcIndex = 0; pcIndex < pcCount; ++pcIndex) {
  1.1903 +      const Telemetry::ProcessedStack::Frame& frame = stack[pcIndex];
  1.1904 +      JS::Rooted<JSObject*> framePair(cx, JS_NewArrayObject(cx, 0));
  1.1905 +      if (!framePair) {
  1.1906 +        return nullptr;
  1.1907 +      }
  1.1908 +      int modIndex = (std::numeric_limits<uint16_t>::max() == frame.mModIndex) ?
  1.1909 +        -1 : frame.mModIndex;
  1.1910 +      if (!JS_SetElement(cx, framePair, 0, modIndex)) {
  1.1911 +        return nullptr;
  1.1912 +      }
  1.1913 +      if (!JS_SetElement(cx, framePair, 1, static_cast<double>(frame.mOffset))) {
  1.1914 +        return nullptr;
  1.1915 +      }
  1.1916 +      if (!JS_SetElement(cx, pcArray, pcIndex, framePair)) {
  1.1917 +        return nullptr;
  1.1918 +      }
  1.1919 +    }
  1.1920 +  }
  1.1921 +
  1.1922 +  return ret;
  1.1923 +}
  1.1924 +
  1.1925 +static bool
  1.1926 +IsValidBreakpadId(const std::string &breakpadId) {
  1.1927 +  if (breakpadId.size() < 33) {
  1.1928 +    return false;
  1.1929 +  }
  1.1930 +  for (unsigned i = 0, n = breakpadId.size(); i < n; ++i) {
  1.1931 +    char c = breakpadId[i];
  1.1932 +    if ((c < '0' || c > '9') && (c < 'A' || c > 'F')) {
  1.1933 +      return false;
  1.1934 +    }
  1.1935 +  }
  1.1936 +  return true;
  1.1937 +}
  1.1938 +
  1.1939 +// Read a stack from the given file name. In case of any error, aStack is
  1.1940 +// unchanged.
  1.1941 +static void
  1.1942 +ReadStack(const char *aFileName, Telemetry::ProcessedStack &aStack)
  1.1943 +{
  1.1944 +  std::ifstream file(aFileName);
  1.1945 +
  1.1946 +  size_t numModules;
  1.1947 +  file >> numModules;
  1.1948 +  if (file.fail()) {
  1.1949 +    return;
  1.1950 +  }
  1.1951 +
  1.1952 +  char newline = file.get();
  1.1953 +  if (file.fail() || newline != '\n') {
  1.1954 +    return;
  1.1955 +  }
  1.1956 +
  1.1957 +  Telemetry::ProcessedStack stack;
  1.1958 +  for (size_t i = 0; i < numModules; ++i) {
  1.1959 +    std::string breakpadId;
  1.1960 +    file >> breakpadId;
  1.1961 +    if (file.fail() || !IsValidBreakpadId(breakpadId)) {
  1.1962 +      return;
  1.1963 +    }
  1.1964 +
  1.1965 +    char space = file.get();
  1.1966 +    if (file.fail() || space != ' ') {
  1.1967 +      return;
  1.1968 +    }
  1.1969 +
  1.1970 +    std::string moduleName;
  1.1971 +    getline(file, moduleName);
  1.1972 +    if (file.fail() || moduleName[0] == ' ') {
  1.1973 +      return;
  1.1974 +    }
  1.1975 +
  1.1976 +    Telemetry::ProcessedStack::Module module = {
  1.1977 +      moduleName,
  1.1978 +      breakpadId
  1.1979 +    };
  1.1980 +    stack.AddModule(module);
  1.1981 +  }
  1.1982 +
  1.1983 +  size_t numFrames;
  1.1984 +  file >> numFrames;
  1.1985 +  if (file.fail()) {
  1.1986 +    return;
  1.1987 +  }
  1.1988 +
  1.1989 +  newline = file.get();
  1.1990 +  if (file.fail() || newline != '\n') {
  1.1991 +    return;
  1.1992 +  }
  1.1993 +
  1.1994 +  for (size_t i = 0; i < numFrames; ++i) {
  1.1995 +    uint16_t index;
  1.1996 +    file >> index;
  1.1997 +    uintptr_t offset;
  1.1998 +    file >> std::hex >> offset >> std::dec;
  1.1999 +    if (file.fail()) {
  1.2000 +      return;
  1.2001 +    }
  1.2002 +
  1.2003 +    Telemetry::ProcessedStack::Frame frame = {
  1.2004 +      offset,
  1.2005 +      index
  1.2006 +    };
  1.2007 +    stack.AddFrame(frame);
  1.2008 +  }
  1.2009 +
  1.2010 +  aStack = stack;
  1.2011 +}
  1.2012 +
  1.2013 +static JSObject*
  1.2014 +CreateJSTimeHistogram(JSContext* cx, const Telemetry::TimeHistogram& time)
  1.2015 +{
  1.2016 +  /* Create JS representation of TimeHistogram,
  1.2017 +     in the format of Chromium-style histograms. */
  1.2018 +  JS::RootedObject ret(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr()));
  1.2019 +  if (!ret) {
  1.2020 +    return nullptr;
  1.2021 +  }
  1.2022 +
  1.2023 +  if (!JS_DefineProperty(cx, ret, "min", time.GetBucketMin(0),
  1.2024 +                         JSPROP_ENUMERATE) ||
  1.2025 +      !JS_DefineProperty(cx, ret, "max",
  1.2026 +                         time.GetBucketMax(ArrayLength(time) - 1),
  1.2027 +                         JSPROP_ENUMERATE) ||
  1.2028 +      !JS_DefineProperty(cx, ret, "histogram_type",
  1.2029 +                         nsITelemetry::HISTOGRAM_EXPONENTIAL,
  1.2030 +                         JSPROP_ENUMERATE)) {
  1.2031 +    return nullptr;
  1.2032 +  }
  1.2033 +  // TODO: calculate "sum", "log_sum", and "log_sum_squares"
  1.2034 +  if (!JS_DefineProperty(cx, ret, "sum", 0, JSPROP_ENUMERATE) ||
  1.2035 +      !JS_DefineProperty(cx, ret, "log_sum", 0.0, JSPROP_ENUMERATE) ||
  1.2036 +      !JS_DefineProperty(cx, ret, "log_sum_squares", 0.0, JSPROP_ENUMERATE)) {
  1.2037 +    return nullptr;
  1.2038 +  }
  1.2039 +
  1.2040 +  JS::RootedObject ranges(
  1.2041 +    cx, JS_NewArrayObject(cx, ArrayLength(time) + 1));
  1.2042 +  JS::RootedObject counts(
  1.2043 +    cx, JS_NewArrayObject(cx, ArrayLength(time) + 1));
  1.2044 +  if (!ranges || !counts) {
  1.2045 +    return nullptr;
  1.2046 +  }
  1.2047 +  /* In a Chromium-style histogram, the first bucket is an "under" bucket
  1.2048 +     that represents all values below the histogram's range. */
  1.2049 +  if (!JS_SetElement(cx, ranges, 0, time.GetBucketMin(0)) ||
  1.2050 +      !JS_SetElement(cx, counts, 0, 0)) {
  1.2051 +    return nullptr;
  1.2052 +  }
  1.2053 +  for (size_t i = 0; i < ArrayLength(time); i++) {
  1.2054 +    if (!JS_SetElement(cx, ranges, i + 1, time.GetBucketMax(i)) ||
  1.2055 +        !JS_SetElement(cx, counts, i + 1, time[i])) {
  1.2056 +      return nullptr;
  1.2057 +    }
  1.2058 +  }
  1.2059 +  if (!JS_DefineProperty(cx, ret, "ranges", ranges, JSPROP_ENUMERATE) ||
  1.2060 +      !JS_DefineProperty(cx, ret, "counts", counts, JSPROP_ENUMERATE)) {
  1.2061 +    return nullptr;
  1.2062 +  }
  1.2063 +  return ret;
  1.2064 +}
  1.2065 +
  1.2066 +static JSObject*
  1.2067 +CreateJSHangHistogram(JSContext* cx, const Telemetry::HangHistogram& hang)
  1.2068 +{
  1.2069 +  JS::RootedObject ret(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr()));
  1.2070 +  if (!ret) {
  1.2071 +    return nullptr;
  1.2072 +  }
  1.2073 +
  1.2074 +  const Telemetry::HangHistogram::Stack& hangStack = hang.GetStack();
  1.2075 +  JS::RootedObject stack(cx,
  1.2076 +    JS_NewArrayObject(cx, hangStack.length()));
  1.2077 +  if (!ret) {
  1.2078 +    return nullptr;
  1.2079 +  }
  1.2080 +  for (size_t i = 0; i < hangStack.length(); i++) {
  1.2081 +    JS::RootedString string(cx, JS_NewStringCopyZ(cx, hangStack[i]));
  1.2082 +    if (!JS_SetElement(cx, stack, i, string)) {
  1.2083 +      return nullptr;
  1.2084 +    }
  1.2085 +  }
  1.2086 +
  1.2087 +  JS::RootedObject time(cx, CreateJSTimeHistogram(cx, hang));
  1.2088 +  if (!time ||
  1.2089 +      !JS_DefineProperty(cx, ret, "stack", stack, JSPROP_ENUMERATE) ||
  1.2090 +      !JS_DefineProperty(cx, ret, "histogram", time, JSPROP_ENUMERATE)) {
  1.2091 +    return nullptr;
  1.2092 +  }
  1.2093 +  return ret;
  1.2094 +}
  1.2095 +
  1.2096 +static JSObject*
  1.2097 +CreateJSThreadHangStats(JSContext* cx, const Telemetry::ThreadHangStats& thread)
  1.2098 +{
  1.2099 +  JS::RootedObject ret(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr()));
  1.2100 +  if (!ret) {
  1.2101 +    return nullptr;
  1.2102 +  }
  1.2103 +  JS::RootedString name(cx, JS_NewStringCopyZ(cx, thread.GetName()));
  1.2104 +  if (!name ||
  1.2105 +      !JS_DefineProperty(cx, ret, "name", name, JSPROP_ENUMERATE)) {
  1.2106 +    return nullptr;
  1.2107 +  }
  1.2108 +
  1.2109 +  JS::RootedObject activity(cx, CreateJSTimeHistogram(cx, thread.mActivity));
  1.2110 +  if (!activity ||
  1.2111 +      !JS_DefineProperty(cx, ret, "activity", activity, JSPROP_ENUMERATE)) {
  1.2112 +    return nullptr;
  1.2113 +  }
  1.2114 +
  1.2115 +  JS::RootedObject hangs(cx, JS_NewArrayObject(cx, 0));
  1.2116 +  if (!hangs) {
  1.2117 +    return nullptr;
  1.2118 +  }
  1.2119 +  for (size_t i = 0; i < thread.mHangs.length(); i++) {
  1.2120 +    JS::RootedObject obj(cx, CreateJSHangHistogram(cx, thread.mHangs[i]));
  1.2121 +    if (!JS_SetElement(cx, hangs, i, obj)) {
  1.2122 +      return nullptr;
  1.2123 +    }
  1.2124 +  }
  1.2125 +  if (!JS_DefineProperty(cx, ret, "hangs", hangs, JSPROP_ENUMERATE)) {
  1.2126 +    return nullptr;
  1.2127 +  }
  1.2128 +  return ret;
  1.2129 +}
  1.2130 +
  1.2131 +NS_IMETHODIMP
  1.2132 +TelemetryImpl::GetThreadHangStats(JSContext* cx, JS::MutableHandle<JS::Value> ret)
  1.2133 +{
  1.2134 +  JS::RootedObject retObj(cx, JS_NewArrayObject(cx, 0));
  1.2135 +  if (!retObj) {
  1.2136 +    return NS_ERROR_FAILURE;
  1.2137 +  }
  1.2138 +  size_t threadIndex = 0;
  1.2139 +
  1.2140 +#ifdef MOZ_ENABLE_BACKGROUND_HANG_MONITOR
  1.2141 +  /* First add active threads; we need to hold |iter| (and its lock)
  1.2142 +     throughout this method to avoid a race condition where a thread can
  1.2143 +     be recorded twice if the thread is destroyed while this method is
  1.2144 +     running */
  1.2145 +  BackgroundHangMonitor::ThreadHangStatsIterator iter;
  1.2146 +  for (Telemetry::ThreadHangStats* histogram = iter.GetNext();
  1.2147 +       histogram; histogram = iter.GetNext()) {
  1.2148 +    JS::RootedObject obj(cx,
  1.2149 +      CreateJSThreadHangStats(cx, *histogram));
  1.2150 +    if (!JS_SetElement(cx, retObj, threadIndex++, obj)) {
  1.2151 +      return NS_ERROR_FAILURE;
  1.2152 +    }
  1.2153 +  }
  1.2154 +#endif
  1.2155 +
  1.2156 +  // Add saved threads next
  1.2157 +  MutexAutoLock autoLock(mThreadHangStatsMutex);
  1.2158 +  for (size_t i = 0; i < mThreadHangStats.length(); i++) {
  1.2159 +    JS::RootedObject obj(cx,
  1.2160 +      CreateJSThreadHangStats(cx, mThreadHangStats[i]));
  1.2161 +    if (!JS_SetElement(cx, retObj, threadIndex++, obj)) {
  1.2162 +      return NS_ERROR_FAILURE;
  1.2163 +    }
  1.2164 +  }
  1.2165 +  ret.setObject(*retObj);
  1.2166 +  return NS_OK;
  1.2167 +}
  1.2168 +
  1.2169 +void
  1.2170 +TelemetryImpl::ReadLateWritesStacks(nsIFile* aProfileDir)
  1.2171 +{
  1.2172 +  nsAutoCString nativePath;
  1.2173 +  nsresult rv = aProfileDir->GetNativePath(nativePath);
  1.2174 +  if (NS_FAILED(rv)) {
  1.2175 +    return;
  1.2176 +  }
  1.2177 +
  1.2178 +  const char *name = nativePath.get();
  1.2179 +  PRDir *dir = PR_OpenDir(name);
  1.2180 +  if (!dir) {
  1.2181 +    return;
  1.2182 +  }
  1.2183 +
  1.2184 +  PRDirEntry *ent;
  1.2185 +  const char *prefix = "Telemetry.LateWriteFinal-";
  1.2186 +  unsigned int prefixLen = strlen(prefix);
  1.2187 +  while ((ent = PR_ReadDir(dir, PR_SKIP_NONE))) {
  1.2188 +    if (strncmp(prefix, ent->name, prefixLen) != 0) {
  1.2189 +      continue;
  1.2190 +    }
  1.2191 +
  1.2192 +    nsAutoCString stackNativePath = nativePath;
  1.2193 +    stackNativePath += XPCOM_FILE_PATH_SEPARATOR;
  1.2194 +    stackNativePath += nsDependentCString(ent->name);
  1.2195 +
  1.2196 +    Telemetry::ProcessedStack stack;
  1.2197 +    ReadStack(stackNativePath.get(), stack);
  1.2198 +    if (stack.GetStackSize() != 0) {
  1.2199 +      mLateWritesStacks.AddStack(stack);
  1.2200 +    }
  1.2201 +    // Delete the file so that we don't report it again on the next run.
  1.2202 +    PR_Delete(stackNativePath.get());
  1.2203 +  }
  1.2204 +  PR_CloseDir(dir);
  1.2205 +}
  1.2206 +
  1.2207 +NS_IMETHODIMP
  1.2208 +TelemetryImpl::GetLateWrites(JSContext *cx, JS::MutableHandle<JS::Value> ret)
  1.2209 +{
  1.2210 +  // The user must call AsyncReadTelemetryData first. We return an empty list
  1.2211 +  // instead of reporting a failure so that the rest of telemetry can uniformly
  1.2212 +  // handle the read not being available yet.
  1.2213 +
  1.2214 +  // FIXME: we allocate the js object again and again in the getter. We should
  1.2215 +  // figure out a way to cache it. In order to do that we have to call
  1.2216 +  // JS_AddNamedObjectRoot. A natural place to do so is in the TelemetryImpl
  1.2217 +  // constructor, but it is not clear how to get a JSContext in there.
  1.2218 +  // Another option would be to call it in here when we first call
  1.2219 +  // CreateJSStackObject, but we would still need to figure out where to call
  1.2220 +  // JS_RemoveObjectRoot. Would it be ok to never call JS_RemoveObjectRoot
  1.2221 +  // and just set the pointer to nullptr is the telemetry destructor?
  1.2222 +
  1.2223 +  JSObject *report;
  1.2224 +  if (!mCachedTelemetryData) {
  1.2225 +    CombinedStacks empty;
  1.2226 +    report = CreateJSStackObject(cx, empty);
  1.2227 +  } else {
  1.2228 +    report = CreateJSStackObject(cx, mLateWritesStacks);
  1.2229 +  }
  1.2230 +
  1.2231 +  if (report == nullptr) {
  1.2232 +    return NS_ERROR_FAILURE;
  1.2233 +  }
  1.2234 +
  1.2235 +  ret.setObject(*report);
  1.2236 +  return NS_OK;
  1.2237 +}
  1.2238 +
  1.2239 +NS_IMETHODIMP
  1.2240 +TelemetryImpl::RegisteredHistograms(uint32_t *aCount, char*** aHistograms)
  1.2241 +{
  1.2242 +  size_t count = ArrayLength(gHistograms);
  1.2243 +  size_t offset = 0;
  1.2244 +  char** histograms = static_cast<char**>(nsMemory::Alloc(count * sizeof(char*)));
  1.2245 +
  1.2246 +  for (size_t i = 0; i < count; ++i) {
  1.2247 +    if (IsExpired(gHistograms[i].expiration())) {
  1.2248 +      offset++;
  1.2249 +      continue;
  1.2250 +    }
  1.2251 +
  1.2252 +    const char* h = gHistograms[i].id();
  1.2253 +    size_t len = strlen(h);
  1.2254 +    histograms[i - offset] = static_cast<char*>(nsMemory::Clone(h, len+1));
  1.2255 +  }
  1.2256 +
  1.2257 +  *aCount = count - offset;
  1.2258 +  *aHistograms = histograms;
  1.2259 +  return NS_OK;
  1.2260 +}
  1.2261 +
  1.2262 +NS_IMETHODIMP
  1.2263 +TelemetryImpl::GetHistogramById(const nsACString &name, JSContext *cx,
  1.2264 +                                JS::MutableHandle<JS::Value> ret)
  1.2265 +{
  1.2266 +  Histogram *h;
  1.2267 +  nsresult rv = GetHistogramByName(name, &h);
  1.2268 +  if (NS_FAILED(rv))
  1.2269 +    return rv;
  1.2270 +
  1.2271 +  return WrapAndReturnHistogram(h, cx, ret);
  1.2272 +}
  1.2273 +
  1.2274 +NS_IMETHODIMP
  1.2275 +TelemetryImpl::GetCanRecord(bool *ret) {
  1.2276 +  *ret = mCanRecord;
  1.2277 +  return NS_OK;
  1.2278 +}
  1.2279 +
  1.2280 +NS_IMETHODIMP
  1.2281 +TelemetryImpl::SetCanRecord(bool canRecord) {
  1.2282 +  mCanRecord = !!canRecord;
  1.2283 +  return NS_OK;
  1.2284 +}
  1.2285 +
  1.2286 +bool
  1.2287 +TelemetryImpl::CanRecord() {
  1.2288 +  return !sTelemetry || sTelemetry->mCanRecord;
  1.2289 +}
  1.2290 +
  1.2291 +NS_IMETHODIMP
  1.2292 +TelemetryImpl::GetCanSend(bool *ret) {
  1.2293 +#if defined(MOZILLA_OFFICIAL) && defined(MOZ_TELEMETRY_REPORTING)
  1.2294 +  *ret = true;
  1.2295 +#else
  1.2296 +  *ret = false;
  1.2297 +#endif
  1.2298 +  return NS_OK;
  1.2299 +}
  1.2300 +
  1.2301 +already_AddRefed<nsITelemetry>
  1.2302 +TelemetryImpl::CreateTelemetryInstance()
  1.2303 +{
  1.2304 +  NS_ABORT_IF_FALSE(sTelemetry == nullptr, "CreateTelemetryInstance may only be called once, via GetService()");
  1.2305 +  sTelemetry = new TelemetryImpl();
  1.2306 +  // AddRef for the local reference
  1.2307 +  NS_ADDREF(sTelemetry);
  1.2308 +  // AddRef for the caller
  1.2309 +  nsCOMPtr<nsITelemetry> ret = sTelemetry;
  1.2310 +
  1.2311 +  sTelemetry->InitMemoryReporter();
  1.2312 +
  1.2313 +  return ret.forget();
  1.2314 +}
  1.2315 +
  1.2316 +void
  1.2317 +TelemetryImpl::ShutdownTelemetry()
  1.2318 +{
  1.2319 +  // No point in collecting IO beyond this point
  1.2320 +  ClearIOReporting();
  1.2321 +  NS_IF_RELEASE(sTelemetry);
  1.2322 +}
  1.2323 +
  1.2324 +void
  1.2325 +TelemetryImpl::StoreSlowSQL(const nsACString &sql, uint32_t delay,
  1.2326 +                            SanitizedState state)
  1.2327 +{
  1.2328 +  AutoHashtable<SlowSQLEntryType> *slowSQLMap = nullptr;
  1.2329 +  if (state == Sanitized)
  1.2330 +    slowSQLMap = &(sTelemetry->mSanitizedSQL);
  1.2331 +  else
  1.2332 +    slowSQLMap = &(sTelemetry->mPrivateSQL);
  1.2333 +
  1.2334 +  MutexAutoLock hashMutex(sTelemetry->mHashMutex);
  1.2335 +
  1.2336 +  SlowSQLEntryType *entry = slowSQLMap->GetEntry(sql);
  1.2337 +  if (!entry) {
  1.2338 +    entry = slowSQLMap->PutEntry(sql);
  1.2339 +    if (MOZ_UNLIKELY(!entry))
  1.2340 +      return;
  1.2341 +    entry->mData.mainThread.hitCount = 0;
  1.2342 +    entry->mData.mainThread.totalTime = 0;
  1.2343 +    entry->mData.otherThreads.hitCount = 0;
  1.2344 +    entry->mData.otherThreads.totalTime = 0;
  1.2345 +  }
  1.2346 +
  1.2347 +  if (NS_IsMainThread()) {
  1.2348 +    entry->mData.mainThread.hitCount++;
  1.2349 +    entry->mData.mainThread.totalTime += delay;
  1.2350 +  } else {
  1.2351 +    entry->mData.otherThreads.hitCount++;
  1.2352 +    entry->mData.otherThreads.totalTime += delay;
  1.2353 +  }
  1.2354 +}
  1.2355 +
  1.2356 +/**
  1.2357 + * This method replaces string literals in SQL strings with the word :private
  1.2358 + *
  1.2359 + * States used in this state machine:
  1.2360 + *
  1.2361 + * NORMAL:
  1.2362 + *  - This is the active state when not iterating over a string literal or
  1.2363 + *  comment
  1.2364 + *
  1.2365 + * SINGLE_QUOTE:
  1.2366 + *  - Defined here: http://www.sqlite.org/lang_expr.html
  1.2367 + *  - This state represents iterating over a string literal opened with
  1.2368 + *  a single quote.
  1.2369 + *  - A single quote within the string can be encoded by putting 2 single quotes
  1.2370 + *  in a row, e.g. 'This literal contains an escaped quote '''
  1.2371 + *  - Any double quotes found within a single-quoted literal are ignored
  1.2372 + *  - This state covers BLOB literals, e.g. X'ABC123'
  1.2373 + *  - The string literal and the enclosing quotes will be replaced with
  1.2374 + *  the text :private
  1.2375 + *
  1.2376 + * DOUBLE_QUOTE:
  1.2377 + *  - Same rules as the SINGLE_QUOTE state.
  1.2378 + *  - According to http://www.sqlite.org/lang_keywords.html,
  1.2379 + *  SQLite interprets text in double quotes as an identifier unless it's used in
  1.2380 + *  a context where it cannot be resolved to an identifier and a string literal
  1.2381 + *  is allowed. This method removes text in double-quotes for safety.
  1.2382 + *
  1.2383 + *  DASH_COMMENT:
  1.2384 + *  - http://www.sqlite.org/lang_comment.html
  1.2385 + *  - A dash comment starts with two dashes in a row,
  1.2386 + *  e.g. DROP TABLE foo -- a comment
  1.2387 + *  - Any text following two dashes in a row is interpreted as a comment until
  1.2388 + *  end of input or a newline character
  1.2389 + *  - Any quotes found within the comment are ignored and no replacements made
  1.2390 + *
  1.2391 + *  C_STYLE_COMMENT:
  1.2392 + *  - http://www.sqlite.org/lang_comment.html
  1.2393 + *  - A C-style comment starts with a forward slash and an asterisk, and ends
  1.2394 + *  with an asterisk and a forward slash
  1.2395 + *  - Any text following comment start is interpreted as a comment up to end of
  1.2396 + *  input or comment end
  1.2397 + *  - Any quotes found within the comment are ignored and no replacements made
  1.2398 + */
  1.2399 +nsCString
  1.2400 +TelemetryImpl::SanitizeSQL(const nsACString &sql) {
  1.2401 +  nsCString output;
  1.2402 +  int length = sql.Length();
  1.2403 +
  1.2404 +  typedef enum {
  1.2405 +    NORMAL,
  1.2406 +    SINGLE_QUOTE,
  1.2407 +    DOUBLE_QUOTE,
  1.2408 +    DASH_COMMENT,
  1.2409 +    C_STYLE_COMMENT,
  1.2410 +  } State;
  1.2411 +
  1.2412 +  State state = NORMAL;
  1.2413 +  int fragmentStart = 0;
  1.2414 +  for (int i = 0; i < length; i++) {
  1.2415 +    char character = sql[i];
  1.2416 +    char nextCharacter = (i + 1 < length) ? sql[i + 1] : '\0';
  1.2417 +
  1.2418 +    switch (character) {
  1.2419 +      case '\'':
  1.2420 +      case '"':
  1.2421 +        if (state == NORMAL) {
  1.2422 +          state = (character == '\'') ? SINGLE_QUOTE : DOUBLE_QUOTE;
  1.2423 +          output += nsDependentCSubstring(sql, fragmentStart, i - fragmentStart);
  1.2424 +          output += ":private";
  1.2425 +          fragmentStart = -1;
  1.2426 +        } else if ((state == SINGLE_QUOTE && character == '\'') ||
  1.2427 +                   (state == DOUBLE_QUOTE && character == '"')) {
  1.2428 +          if (nextCharacter == character) {
  1.2429 +            // Two consecutive quotes within a string literal are a single escaped quote
  1.2430 +            i++;
  1.2431 +          } else {
  1.2432 +            state = NORMAL;
  1.2433 +            fragmentStart = i + 1;
  1.2434 +          }
  1.2435 +        }
  1.2436 +        break;
  1.2437 +      case '-':
  1.2438 +        if (state == NORMAL) {
  1.2439 +          if (nextCharacter == '-') {
  1.2440 +            state = DASH_COMMENT;
  1.2441 +            i++;
  1.2442 +          }
  1.2443 +        }
  1.2444 +        break;
  1.2445 +      case '\n':
  1.2446 +        if (state == DASH_COMMENT) {
  1.2447 +          state = NORMAL;
  1.2448 +        }
  1.2449 +        break;
  1.2450 +      case '/':
  1.2451 +        if (state == NORMAL) {
  1.2452 +          if (nextCharacter == '*') {
  1.2453 +            state = C_STYLE_COMMENT;
  1.2454 +            i++;
  1.2455 +          }
  1.2456 +        }
  1.2457 +        break;
  1.2458 +      case '*':
  1.2459 +        if (state == C_STYLE_COMMENT) {
  1.2460 +          if (nextCharacter == '/') {
  1.2461 +            state = NORMAL;
  1.2462 +          }
  1.2463 +        }
  1.2464 +        break;
  1.2465 +      default:
  1.2466 +        continue;
  1.2467 +    }
  1.2468 +  }
  1.2469 +
  1.2470 +  if ((fragmentStart >= 0) && fragmentStart < length)
  1.2471 +    output += nsDependentCSubstring(sql, fragmentStart, length - fragmentStart);
  1.2472 +
  1.2473 +  return output;
  1.2474 +}
  1.2475 +
  1.2476 +// Slow SQL statements will be automatically
  1.2477 +// trimmed to kMaxSlowStatementLength characters.
  1.2478 +// This limit doesn't include the ellipsis and DB name,
  1.2479 +// that are appended at the end of the stored statement.
  1.2480 +const uint32_t kMaxSlowStatementLength = 1000;
  1.2481 +
  1.2482 +void
  1.2483 +TelemetryImpl::RecordSlowStatement(const nsACString &sql,
  1.2484 +                                   const nsACString &dbName,
  1.2485 +                                   uint32_t delay)
  1.2486 +{
  1.2487 +  if (!sTelemetry || !sTelemetry->mCanRecord)
  1.2488 +    return;
  1.2489 +
  1.2490 +  bool isFirefoxDB = sTelemetry->mTrackedDBs.Contains(dbName);
  1.2491 +  if (isFirefoxDB) {
  1.2492 +    nsAutoCString sanitizedSQL(SanitizeSQL(sql));
  1.2493 +    if (sanitizedSQL.Length() > kMaxSlowStatementLength) {
  1.2494 +      sanitizedSQL.SetLength(kMaxSlowStatementLength);
  1.2495 +      sanitizedSQL += "...";
  1.2496 +    }
  1.2497 +    sanitizedSQL.AppendPrintf(" /* %s */", nsPromiseFlatCString(dbName).get());
  1.2498 +    StoreSlowSQL(sanitizedSQL, delay, Sanitized);
  1.2499 +  } else {
  1.2500 +    // Report aggregate DB-level statistics for addon DBs
  1.2501 +    nsAutoCString aggregate;
  1.2502 +    aggregate.AppendPrintf("Untracked SQL for %s",
  1.2503 +                           nsPromiseFlatCString(dbName).get());
  1.2504 +    StoreSlowSQL(aggregate, delay, Sanitized);
  1.2505 +  }
  1.2506 +
  1.2507 +  nsAutoCString fullSQL;
  1.2508 +  fullSQL.AppendPrintf("%s /* %s */",
  1.2509 +                       nsPromiseFlatCString(sql).get(),
  1.2510 +                       nsPromiseFlatCString(dbName).get());
  1.2511 +  StoreSlowSQL(fullSQL, delay, Unsanitized);
  1.2512 +}
  1.2513 +
  1.2514 +#if defined(MOZ_ENABLE_PROFILER_SPS)
  1.2515 +void
  1.2516 +TelemetryImpl::RecordChromeHang(uint32_t aDuration,
  1.2517 +                                Telemetry::ProcessedStack &aStack,
  1.2518 +                                int32_t aSystemUptime,
  1.2519 +                                int32_t aFirefoxUptime)
  1.2520 +{
  1.2521 +  if (!sTelemetry || !sTelemetry->mCanRecord)
  1.2522 +    return;
  1.2523 +
  1.2524 +  MutexAutoLock hangReportMutex(sTelemetry->mHangReportsMutex);
  1.2525 +
  1.2526 +  sTelemetry->mHangReports.AddHang(aStack, aDuration,
  1.2527 +                                   aSystemUptime, aFirefoxUptime);
  1.2528 +}
  1.2529 +#endif
  1.2530 +
  1.2531 +void
  1.2532 +TelemetryImpl::RecordThreadHangStats(Telemetry::ThreadHangStats& aStats)
  1.2533 +{
  1.2534 +  if (!sTelemetry || !sTelemetry->mCanRecord)
  1.2535 +    return;
  1.2536 +
  1.2537 +  MutexAutoLock autoLock(sTelemetry->mThreadHangStatsMutex);
  1.2538 +
  1.2539 +  sTelemetry->mThreadHangStats.append(Move(aStats));
  1.2540 +}
  1.2541 +
  1.2542 +NS_IMPL_ISUPPORTS(TelemetryImpl, nsITelemetry, nsIMemoryReporter)
  1.2543 +NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsITelemetry, TelemetryImpl::CreateTelemetryInstance)
  1.2544 +
  1.2545 +#define NS_TELEMETRY_CID \
  1.2546 +  {0xaea477f2, 0xb3a2, 0x469c, {0xaa, 0x29, 0x0a, 0x82, 0xd1, 0x32, 0xb8, 0x29}}
  1.2547 +NS_DEFINE_NAMED_CID(NS_TELEMETRY_CID);
  1.2548 +
  1.2549 +const Module::CIDEntry kTelemetryCIDs[] = {
  1.2550 +  { &kNS_TELEMETRY_CID, false, nullptr, nsITelemetryConstructor },
  1.2551 +  { nullptr }
  1.2552 +};
  1.2553 +
  1.2554 +const Module::ContractIDEntry kTelemetryContracts[] = {
  1.2555 +  { "@mozilla.org/base/telemetry;1", &kNS_TELEMETRY_CID },
  1.2556 +  { nullptr }
  1.2557 +};
  1.2558 +
  1.2559 +const Module kTelemetryModule = {
  1.2560 +  Module::kVersion,
  1.2561 +  kTelemetryCIDs,
  1.2562 +  kTelemetryContracts,
  1.2563 +  nullptr,
  1.2564 +  nullptr,
  1.2565 +  nullptr,
  1.2566 +  TelemetryImpl::ShutdownTelemetry
  1.2567 +};
  1.2568 +
  1.2569 +NS_IMETHODIMP
  1.2570 +TelemetryImpl::GetFileIOReports(JSContext *cx, JS::MutableHandleValue ret)
  1.2571 +{
  1.2572 +  if (sTelemetryIOObserver) {
  1.2573 +    JS::Rooted<JSObject*> obj(cx, JS_NewObject(cx, nullptr, JS::NullPtr(),
  1.2574 +                                               JS::NullPtr()));
  1.2575 +    if (!obj) {
  1.2576 +      return NS_ERROR_FAILURE;
  1.2577 +    }
  1.2578 +
  1.2579 +    if (!sTelemetryIOObserver->ReflectIntoJS(cx, obj)) {
  1.2580 +      return NS_ERROR_FAILURE;
  1.2581 +    }
  1.2582 +    ret.setObject(*obj);
  1.2583 +    return NS_OK;
  1.2584 +  }
  1.2585 +  ret.setNull();
  1.2586 +  return NS_OK;
  1.2587 +}
  1.2588 +
  1.2589 +NS_IMETHODIMP
  1.2590 +TelemetryImpl::MsSinceProcessStart(double* aResult)
  1.2591 +{
  1.2592 +  bool error;
  1.2593 +  *aResult = (TimeStamp::NowLoRes() -
  1.2594 +              TimeStamp::ProcessCreation(error)).ToMilliseconds();
  1.2595 +  if (error) {
  1.2596 +    return NS_ERROR_NOT_AVAILABLE;
  1.2597 +  }
  1.2598 +  return NS_OK;
  1.2599 +}
  1.2600 +
  1.2601 +size_t
  1.2602 +TelemetryImpl::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
  1.2603 +{
  1.2604 +  size_t n = aMallocSizeOf(this);
  1.2605 +  // Ignore the hashtables in mAddonMap; they are not significant.
  1.2606 +  n += mAddonMap.SizeOfExcludingThis(nullptr, aMallocSizeOf);
  1.2607 +  n += mHistogramMap.SizeOfExcludingThis(nullptr, aMallocSizeOf);
  1.2608 +  { // Scope for mHashMutex lock
  1.2609 +    MutexAutoLock lock(mHashMutex);
  1.2610 +    n += mPrivateSQL.SizeOfExcludingThis(nullptr, aMallocSizeOf);
  1.2611 +    n += mSanitizedSQL.SizeOfExcludingThis(nullptr, aMallocSizeOf);
  1.2612 +  }
  1.2613 +  n += mTrackedDBs.SizeOfExcludingThis(nullptr, aMallocSizeOf);
  1.2614 +  { // Scope for mHangReportsMutex lock
  1.2615 +    MutexAutoLock lock(mHangReportsMutex);
  1.2616 +    n += mHangReports.SizeOfExcludingThis();
  1.2617 +  }
  1.2618 +  { // Scope for mThreadHangStatsMutex lock
  1.2619 +    MutexAutoLock lock(mThreadHangStatsMutex);
  1.2620 +    n += mThreadHangStats.sizeOfExcludingThis(aMallocSizeOf);
  1.2621 +  }
  1.2622 +
  1.2623 +  // It's a bit gross that we measure this other stuff that lives outside of
  1.2624 +  // TelemetryImpl... oh well.
  1.2625 +  if (sTelemetryIOObserver) {
  1.2626 +    n += sTelemetryIOObserver->SizeOfIncludingThis(aMallocSizeOf);
  1.2627 +  }
  1.2628 +  StatisticsRecorder::Histograms hs;
  1.2629 +  StatisticsRecorder::GetHistograms(&hs);
  1.2630 +  for (HistogramIterator it = hs.begin(); it != hs.end(); ++it) {
  1.2631 +    Histogram *h = *it;
  1.2632 +    n += h->SizeOfIncludingThis(aMallocSizeOf);
  1.2633 +  }
  1.2634 +
  1.2635 +  return n;
  1.2636 +}
  1.2637 +
  1.2638 +} // anonymous namespace
  1.2639 +
  1.2640 +namespace mozilla {
  1.2641 +void
  1.2642 +RecordShutdownStartTimeStamp() {
  1.2643 +#ifdef DEBUG
  1.2644 +  // FIXME: this function should only be called once, since it should be called
  1.2645 +  // at the earliest point we *know* we are shutting down. Unfortunately
  1.2646 +  // this assert has been firing. Given that if we are called multiple times
  1.2647 +  // we just keep the last timestamp, the assert is commented for now.
  1.2648 +  static bool recorded = false;
  1.2649 +  //  MOZ_ASSERT(!recorded);
  1.2650 +  (void)recorded; // Silence unused-var warnings (remove when assert re-enabled)
  1.2651 +  recorded = true;
  1.2652 +#endif
  1.2653 +
  1.2654 +  if (!Telemetry::CanRecord())
  1.2655 +    return;
  1.2656 +
  1.2657 +  gRecordedShutdownStartTime = TimeStamp::Now();
  1.2658 +
  1.2659 +  GetShutdownTimeFileName();
  1.2660 +}
  1.2661 +
  1.2662 +void
  1.2663 +RecordShutdownEndTimeStamp() {
  1.2664 +  if (!gRecordedShutdownTimeFileName || gAlreadyFreedShutdownTimeFileName)
  1.2665 +    return;
  1.2666 +
  1.2667 +  nsCString name(gRecordedShutdownTimeFileName);
  1.2668 +  PL_strfree(gRecordedShutdownTimeFileName);
  1.2669 +  gRecordedShutdownTimeFileName = nullptr;
  1.2670 +  gAlreadyFreedShutdownTimeFileName = true;
  1.2671 +
  1.2672 +  nsCString tmpName = name;
  1.2673 +  tmpName += ".tmp";
  1.2674 +  FILE *f = fopen(tmpName.get(), "w");
  1.2675 +  if (!f)
  1.2676 +    return;
  1.2677 +  // On a normal release build this should be called just before
  1.2678 +  // calling _exit, but on a debug build or when the user forces a full
  1.2679 +  // shutdown this is called as late as possible, so we have to
  1.2680 +  // white list this write as write poisoning will be enabled.
  1.2681 +  MozillaRegisterDebugFILE(f);
  1.2682 +
  1.2683 +  TimeStamp now = TimeStamp::Now();
  1.2684 +  MOZ_ASSERT(now >= gRecordedShutdownStartTime);
  1.2685 +  TimeDuration diff = now - gRecordedShutdownStartTime;
  1.2686 +  uint32_t diff2 = diff.ToMilliseconds();
  1.2687 +  int written = fprintf(f, "%d\n", diff2);
  1.2688 +  MozillaUnRegisterDebugFILE(f);
  1.2689 +  int rv = fclose(f);
  1.2690 +  if (written < 0 || rv != 0) {
  1.2691 +    PR_Delete(tmpName.get());
  1.2692 +    return;
  1.2693 +  }
  1.2694 +  PR_Delete(name.get());
  1.2695 +  PR_Rename(tmpName.get(), name.get());
  1.2696 +}
  1.2697 +
  1.2698 +namespace Telemetry {
  1.2699 +
  1.2700 +void
  1.2701 +Accumulate(ID aHistogram, uint32_t aSample)
  1.2702 +{
  1.2703 +  if (!TelemetryImpl::CanRecord()) {
  1.2704 +    return;
  1.2705 +  }
  1.2706 +  Histogram *h;
  1.2707 +  nsresult rv = GetHistogramByEnumId(aHistogram, &h);
  1.2708 +  if (NS_SUCCEEDED(rv))
  1.2709 +    h->Add(aSample);
  1.2710 +}
  1.2711 +
  1.2712 +void
  1.2713 +Accumulate(const char* name, uint32_t sample)
  1.2714 +{
  1.2715 +  if (!TelemetryImpl::CanRecord()) {
  1.2716 +    return;
  1.2717 +  }
  1.2718 +  ID id;
  1.2719 +  nsresult rv = TelemetryImpl::GetHistogramEnumId(name, &id);
  1.2720 +  if (NS_FAILED(rv)) {
  1.2721 +    return;
  1.2722 +  }
  1.2723 +
  1.2724 +  Histogram *h;
  1.2725 +  rv = GetHistogramByEnumId(id, &h);
  1.2726 +  if (NS_SUCCEEDED(rv)) {
  1.2727 +    h->Add(sample);
  1.2728 +  }
  1.2729 +}
  1.2730 +
  1.2731 +void
  1.2732 +AccumulateTimeDelta(ID aHistogram, TimeStamp start, TimeStamp end)
  1.2733 +{
  1.2734 +  Accumulate(aHistogram,
  1.2735 +             static_cast<uint32_t>((end - start).ToMilliseconds()));
  1.2736 +}
  1.2737 +
  1.2738 +bool
  1.2739 +CanRecord()
  1.2740 +{
  1.2741 +  return TelemetryImpl::CanRecord();
  1.2742 +}
  1.2743 +
  1.2744 +base::Histogram*
  1.2745 +GetHistogramById(ID id)
  1.2746 +{
  1.2747 +  Histogram *h = nullptr;
  1.2748 +  GetHistogramByEnumId(id, &h);
  1.2749 +  return h;
  1.2750 +}
  1.2751 +
  1.2752 +void
  1.2753 +RecordSlowSQLStatement(const nsACString &statement,
  1.2754 +                       const nsACString &dbName,
  1.2755 +                       uint32_t delay)
  1.2756 +{
  1.2757 +  TelemetryImpl::RecordSlowStatement(statement, dbName, delay);
  1.2758 +}
  1.2759 +
  1.2760 +void Init()
  1.2761 +{
  1.2762 +  // Make the service manager hold a long-lived reference to the service
  1.2763 +  nsCOMPtr<nsITelemetry> telemetryService =
  1.2764 +    do_GetService("@mozilla.org/base/telemetry;1");
  1.2765 +  MOZ_ASSERT(telemetryService);
  1.2766 +}
  1.2767 +
  1.2768 +#if defined(MOZ_ENABLE_PROFILER_SPS)
  1.2769 +void RecordChromeHang(uint32_t duration,
  1.2770 +                      ProcessedStack &aStack,
  1.2771 +                      int32_t aSystemUptime,
  1.2772 +                      int32_t aFirefoxUptime)
  1.2773 +{
  1.2774 +  TelemetryImpl::RecordChromeHang(duration, aStack,
  1.2775 +                                  aSystemUptime, aFirefoxUptime);
  1.2776 +}
  1.2777 +#endif
  1.2778 +
  1.2779 +void RecordThreadHangStats(ThreadHangStats& aStats)
  1.2780 +{
  1.2781 +  TelemetryImpl::RecordThreadHangStats(aStats);
  1.2782 +}
  1.2783 +
  1.2784 +ProcessedStack::ProcessedStack()
  1.2785 +{
  1.2786 +}
  1.2787 +
  1.2788 +size_t ProcessedStack::GetStackSize() const
  1.2789 +{
  1.2790 +  return mStack.size();
  1.2791 +}
  1.2792 +
  1.2793 +const ProcessedStack::Frame &ProcessedStack::GetFrame(unsigned aIndex) const
  1.2794 +{
  1.2795 +  MOZ_ASSERT(aIndex < mStack.size());
  1.2796 +  return mStack[aIndex];
  1.2797 +}
  1.2798 +
  1.2799 +void ProcessedStack::AddFrame(const Frame &aFrame)
  1.2800 +{
  1.2801 +  mStack.push_back(aFrame);
  1.2802 +}
  1.2803 +
  1.2804 +size_t ProcessedStack::GetNumModules() const
  1.2805 +{
  1.2806 +  return mModules.size();
  1.2807 +}
  1.2808 +
  1.2809 +const ProcessedStack::Module &ProcessedStack::GetModule(unsigned aIndex) const
  1.2810 +{
  1.2811 +  MOZ_ASSERT(aIndex < mModules.size());
  1.2812 +  return mModules[aIndex];
  1.2813 +}
  1.2814 +
  1.2815 +void ProcessedStack::AddModule(const Module &aModule)
  1.2816 +{
  1.2817 +  mModules.push_back(aModule);
  1.2818 +}
  1.2819 +
  1.2820 +void ProcessedStack::Clear() {
  1.2821 +  mModules.clear();
  1.2822 +  mStack.clear();
  1.2823 +}
  1.2824 +
  1.2825 +bool ProcessedStack::Module::operator==(const Module& aOther) const {
  1.2826 +  return  mName == aOther.mName &&
  1.2827 +    mBreakpadId == aOther.mBreakpadId;
  1.2828 +}
  1.2829 +
  1.2830 +struct StackFrame
  1.2831 +{
  1.2832 +  uintptr_t mPC;      // The program counter at this position in the call stack.
  1.2833 +  uint16_t mIndex;    // The number of this frame in the call stack.
  1.2834 +  uint16_t mModIndex; // The index of module that has this program counter.
  1.2835 +};
  1.2836 +
  1.2837 +
  1.2838 +#ifdef MOZ_ENABLE_PROFILER_SPS
  1.2839 +static bool CompareByPC(const StackFrame &a, const StackFrame &b)
  1.2840 +{
  1.2841 +  return a.mPC < b.mPC;
  1.2842 +}
  1.2843 +
  1.2844 +static bool CompareByIndex(const StackFrame &a, const StackFrame &b)
  1.2845 +{
  1.2846 +  return a.mIndex < b.mIndex;
  1.2847 +}
  1.2848 +#endif
  1.2849 +
  1.2850 +ProcessedStack
  1.2851 +GetStackAndModules(const std::vector<uintptr_t>& aPCs)
  1.2852 +{
  1.2853 +  std::vector<StackFrame> rawStack;
  1.2854 +  for (std::vector<uintptr_t>::const_iterator i = aPCs.begin(),
  1.2855 +         e = aPCs.end(); i != e; ++i) {
  1.2856 +    uintptr_t aPC = *i;
  1.2857 +    StackFrame Frame = {aPC, static_cast<uint16_t>(rawStack.size()),
  1.2858 +                        std::numeric_limits<uint16_t>::max()};
  1.2859 +    rawStack.push_back(Frame);
  1.2860 +  }
  1.2861 +
  1.2862 +#ifdef MOZ_ENABLE_PROFILER_SPS
  1.2863 +  // Remove all modules not referenced by a PC on the stack
  1.2864 +  std::sort(rawStack.begin(), rawStack.end(), CompareByPC);
  1.2865 +
  1.2866 +  size_t moduleIndex = 0;
  1.2867 +  size_t stackIndex = 0;
  1.2868 +  size_t stackSize = rawStack.size();
  1.2869 +
  1.2870 +  SharedLibraryInfo rawModules = SharedLibraryInfo::GetInfoForSelf();
  1.2871 +  rawModules.SortByAddress();
  1.2872 +
  1.2873 +  while (moduleIndex < rawModules.GetSize()) {
  1.2874 +    const SharedLibrary& module = rawModules.GetEntry(moduleIndex);
  1.2875 +    uintptr_t moduleStart = module.GetStart();
  1.2876 +    uintptr_t moduleEnd = module.GetEnd() - 1;
  1.2877 +    // the interval is [moduleStart, moduleEnd)
  1.2878 +
  1.2879 +    bool moduleReferenced = false;
  1.2880 +    for (;stackIndex < stackSize; ++stackIndex) {
  1.2881 +      uintptr_t pc = rawStack[stackIndex].mPC;
  1.2882 +      if (pc >= moduleEnd)
  1.2883 +        break;
  1.2884 +
  1.2885 +      if (pc >= moduleStart) {
  1.2886 +        // If the current PC is within the current module, mark
  1.2887 +        // module as used
  1.2888 +        moduleReferenced = true;
  1.2889 +        rawStack[stackIndex].mPC -= moduleStart;
  1.2890 +        rawStack[stackIndex].mModIndex = moduleIndex;
  1.2891 +      } else {
  1.2892 +        // PC does not belong to any module. It is probably from
  1.2893 +        // the JIT. Use a fixed mPC so that we don't get different
  1.2894 +        // stacks on different runs.
  1.2895 +        rawStack[stackIndex].mPC =
  1.2896 +          std::numeric_limits<uintptr_t>::max();
  1.2897 +      }
  1.2898 +    }
  1.2899 +
  1.2900 +    if (moduleReferenced) {
  1.2901 +      ++moduleIndex;
  1.2902 +    } else {
  1.2903 +      // Remove module if no PCs within its address range
  1.2904 +      rawModules.RemoveEntries(moduleIndex, moduleIndex + 1);
  1.2905 +    }
  1.2906 +  }
  1.2907 +
  1.2908 +  for (;stackIndex < stackSize; ++stackIndex) {
  1.2909 +    // These PCs are past the last module.
  1.2910 +    rawStack[stackIndex].mPC = std::numeric_limits<uintptr_t>::max();
  1.2911 +  }
  1.2912 +
  1.2913 +  std::sort(rawStack.begin(), rawStack.end(), CompareByIndex);
  1.2914 +#endif
  1.2915 +
  1.2916 +  // Copy the information to the return value.
  1.2917 +  ProcessedStack Ret;
  1.2918 +  for (std::vector<StackFrame>::iterator i = rawStack.begin(),
  1.2919 +         e = rawStack.end(); i != e; ++i) {
  1.2920 +    const StackFrame &rawFrame = *i;
  1.2921 +    ProcessedStack::Frame frame = { rawFrame.mPC, rawFrame.mModIndex };
  1.2922 +    Ret.AddFrame(frame);
  1.2923 +  }
  1.2924 +
  1.2925 +#ifdef MOZ_ENABLE_PROFILER_SPS
  1.2926 +  for (unsigned i = 0, n = rawModules.GetSize(); i != n; ++i) {
  1.2927 +    const SharedLibrary &info = rawModules.GetEntry(i);
  1.2928 +    const std::string &name = info.GetName();
  1.2929 +    std::string basename = name;
  1.2930 +#ifdef XP_MACOSX
  1.2931 +    // FIXME: We want to use just the basename as the libname, but the
  1.2932 +    // current profiler addon needs the full path name, so we compute the
  1.2933 +    // basename in here.
  1.2934 +    size_t pos = name.rfind('/');
  1.2935 +    if (pos != std::string::npos) {
  1.2936 +      basename = name.substr(pos + 1);
  1.2937 +    }
  1.2938 +#endif
  1.2939 +    ProcessedStack::Module module = {
  1.2940 +      basename,
  1.2941 +      info.GetBreakpadId()
  1.2942 +    };
  1.2943 +    Ret.AddModule(module);
  1.2944 +  }
  1.2945 +#endif
  1.2946 +
  1.2947 +  return Ret;
  1.2948 +}
  1.2949 +
  1.2950 +void
  1.2951 +WriteFailedProfileLock(nsIFile* aProfileDir)
  1.2952 +{
  1.2953 +  nsCOMPtr<nsIFile> file;
  1.2954 +  nsresult rv = GetFailedProfileLockFile(getter_AddRefs(file), aProfileDir);
  1.2955 +  NS_ENSURE_SUCCESS_VOID(rv);
  1.2956 +  int64_t fileSize = 0;
  1.2957 +  rv = file->GetFileSize(&fileSize);
  1.2958 +  // It's expected that the file might not exist yet
  1.2959 +  if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND) {
  1.2960 +    return;
  1.2961 +  }
  1.2962 +  nsCOMPtr<nsIFileStream> fileStream;
  1.2963 +  rv = NS_NewLocalFileStream(getter_AddRefs(fileStream), file,
  1.2964 +                             PR_RDWR | PR_CREATE_FILE, 0640);
  1.2965 +  NS_ENSURE_SUCCESS_VOID(rv);
  1.2966 +  NS_ENSURE_TRUE_VOID(fileSize <= kMaxFailedProfileLockFileSize);
  1.2967 +  unsigned int failedLockCount = 0;
  1.2968 +  if (fileSize > 0) {
  1.2969 +    nsCOMPtr<nsIInputStream> inStream = do_QueryInterface(fileStream);
  1.2970 +    NS_ENSURE_TRUE_VOID(inStream);
  1.2971 +    if (!GetFailedLockCount(inStream, fileSize, failedLockCount)) {
  1.2972 +      failedLockCount = 0;
  1.2973 +    }
  1.2974 +  }
  1.2975 +  ++failedLockCount;
  1.2976 +  nsAutoCString bufStr;
  1.2977 +  bufStr.AppendInt(static_cast<int>(failedLockCount));
  1.2978 +  nsCOMPtr<nsISeekableStream> seekStream = do_QueryInterface(fileStream);
  1.2979 +  NS_ENSURE_TRUE_VOID(seekStream);
  1.2980 +  // If we read in an existing failed lock count, we need to reset the file ptr
  1.2981 +  if (fileSize > 0) {
  1.2982 +    rv = seekStream->Seek(nsISeekableStream::NS_SEEK_SET, 0);
  1.2983 +    NS_ENSURE_SUCCESS_VOID(rv);
  1.2984 +  }
  1.2985 +  nsCOMPtr<nsIOutputStream> outStream = do_QueryInterface(fileStream);
  1.2986 +  uint32_t bytesLeft = bufStr.Length();
  1.2987 +  const char* bytes = bufStr.get();
  1.2988 +  do {
  1.2989 +    uint32_t written = 0;
  1.2990 +    rv = outStream->Write(bytes, bytesLeft, &written);
  1.2991 +    if (NS_FAILED(rv)) {
  1.2992 +      break;
  1.2993 +    }
  1.2994 +    bytes += written;
  1.2995 +    bytesLeft -= written;
  1.2996 +  } while (bytesLeft > 0);
  1.2997 +  seekStream->SetEOF();
  1.2998 +}
  1.2999 +
  1.3000 +void
  1.3001 +InitIOReporting(nsIFile* aXreDir)
  1.3002 +{
  1.3003 +  // Never initialize twice
  1.3004 +  if (sTelemetryIOObserver) {
  1.3005 +    return;
  1.3006 +  }
  1.3007 +
  1.3008 +  sTelemetryIOObserver = new TelemetryIOInterposeObserver(aXreDir);
  1.3009 +  IOInterposer::Register(IOInterposeObserver::OpAllWithStaging,
  1.3010 +                         sTelemetryIOObserver);
  1.3011 +}
  1.3012 +
  1.3013 +void
  1.3014 +SetProfileDir(nsIFile* aProfD)
  1.3015 +{
  1.3016 +  if (!sTelemetryIOObserver || !aProfD) {
  1.3017 +    return;
  1.3018 +  }
  1.3019 +  nsAutoString profDirPath;
  1.3020 +  nsresult rv = aProfD->GetPath(profDirPath);
  1.3021 +  if (NS_FAILED(rv)) {
  1.3022 +    return;
  1.3023 +  }
  1.3024 +  sTelemetryIOObserver->AddPath(profDirPath, NS_LITERAL_STRING("{profile}"));
  1.3025 +}
  1.3026 +
  1.3027 +void
  1.3028 +TimeHistogram::Add(PRIntervalTime aTime)
  1.3029 +{
  1.3030 +  uint32_t timeMs = PR_IntervalToMilliseconds(aTime);
  1.3031 +  size_t index = mozilla::FloorLog2(timeMs);
  1.3032 +  operator[](index)++;
  1.3033 +}
  1.3034 +
  1.3035 +uint32_t
  1.3036 +HangHistogram::GetHash(const Stack& aStack)
  1.3037 +{
  1.3038 +  uint32_t hash = 0;
  1.3039 +  for (const char* const* label = aStack.begin();
  1.3040 +       label != aStack.end(); label++) {
  1.3041 +    /* We only need to hash the pointer instead of the text content
  1.3042 +       because we are assuming constant pointers */
  1.3043 +    hash = AddToHash(hash, *label);
  1.3044 +  }
  1.3045 +  return hash;
  1.3046 +}
  1.3047 +
  1.3048 +bool
  1.3049 +HangHistogram::operator==(const HangHistogram& aOther) const
  1.3050 +{
  1.3051 +  if (mHash != aOther.mHash) {
  1.3052 +    return false;
  1.3053 +  }
  1.3054 +  if (mStack.length() != aOther.mStack.length()) {
  1.3055 +    return false;
  1.3056 +  }
  1.3057 +  return PodEqual(mStack.begin(), aOther.mStack.begin(), mStack.length());
  1.3058 +}
  1.3059 +
  1.3060 +
  1.3061 +} // namespace Telemetry
  1.3062 +} // namespace mozilla
  1.3063 +
  1.3064 +NSMODULE_DEFN(nsTelemetryModule) = &kTelemetryModule;
  1.3065 +
  1.3066 +/**
  1.3067 + * The XRE_TelemetryAdd function is to be used by embedding applications
  1.3068 + * that can't use mozilla::Telemetry::Accumulate() directly.
  1.3069 + */
  1.3070 +void
  1.3071 +XRE_TelemetryAccumulate(int aID, uint32_t aSample)
  1.3072 +{
  1.3073 +  mozilla::Telemetry::Accumulate((mozilla::Telemetry::ID) aID, aSample);
  1.3074 +}

mercurial