# Global Metrics

path: .metrics.mi.mi_sei
old: 22.890477867599735
new: -20.106512789302787
path: .metrics.mi.mi_original
old: 36.4251273767445
new: 25.011851586450845
path: .metrics.mi.mi_visual_studio
old: 21.301244079967542
new: 14.626813793246107
path: .metrics.halstead.volume
old: 3231.3909820522326
new: 7795.941977063929
path: .metrics.halstead.length
old: 499.0
new: 1033.0
path: .metrics.halstead.time
old: 8130.596664518521
new: 16614.347511597916
path: .metrics.halstead.n1
old: 27.0
new: 29.0
path: .metrics.halstead.vocabulary
old: 89.0
new: 187.0
path: .metrics.halstead.effort
old: 146350.73996133337
new: 299058.2552087625
path: .metrics.halstead.N1
old: 291.0
new: 615.0
path: .metrics.halstead.purity_ratio
old: 0.9970784244537072
new: 1.25351288196581
path: .metrics.halstead.N2
old: 208.0
new: 418.0
path: .metrics.halstead.difficulty
old: 45.29032258064516
new: 38.36075949367088
path: .metrics.halstead.bugs
old: 0.9257108635252772
new: 1.4906737668451473
path: .metrics.halstead.estimated_program_length
old: 497.5421338023999
new: 1294.878807070682
path: .metrics.halstead.level
old: 0.02207977207977208
new: 0.02606830556013859
path: .metrics.halstead.n2
old: 62.0
new: 158.0
path: .metrics.nom.functions
old: 6.0
new: 14.0
path: .metrics.nom.total
old: 6.0
new: 18.0
path: .metrics.nom.closures
old: 0.0
new: 4.0
path: .metrics.cyclomatic.average
old: 2.857142857142857
new: 2.0
path: .metrics.cyclomatic.sum
old: 20.0
new: 36.0
path: .metrics.loc.ploc
old: 106.0
new: 215.0
path: .metrics.loc.sloc
old: 228.0
new: 277.0
path: .metrics.loc.cloc
old: 110.0
new: 12.0
path: .metrics.loc.blank
old: 12.0
new: 50.0
path: .metrics.loc.lloc
old: 31.0
new: 82.0
path: .metrics.nargs.sum
old: 33.0
new: 14.0
path: .metrics.nargs.average
old: 5.5
new: 0.7777777777777778
path: .metrics.cognitive.average
old: 3.1666666666666665
new: 1.2222222222222223
path: .metrics.cognitive.sum
old: 19.0
new: 22.0
path: .metrics.nexits.average
old: 1.3333333333333333
new: 0.3333333333333333
path: .metrics.nexits.sum
old: 8.0
new: 6.0

# Spaces Data

## Minimal test - lines (21, 277)

path: .spaces[0].metrics.nom.closures
old: 0.0
new: 4.0
path: .spaces[0].metrics.nom.functions
old: 1.0
new: 14.0
path: .spaces[0].metrics.nom.total
old: 1.0
new: 18.0
path: .spaces[0].metrics.mi.mi_original
old: 109.32690234865424
new: 26.603424725710894
path: .spaces[0].metrics.mi.mi_visual_studio
old: 63.933861022604816
new: 15.557558319129177
path: .spaces[0].metrics.mi.mi_sei
old: 108.26538255658764
new: -22.031296693380508
path: .spaces[0].metrics.nargs.sum
old: 0.0
new: 14.0
path: .spaces[0].metrics.nargs.average
old: 0.0
new: 0.7777777777777778
path: .spaces[0].metrics.cyclomatic.sum
old: 2.0
new: 35.0
path: .spaces[0].metrics.cyclomatic.average
old: 2.0
new: 2.0588235294117645
path: .spaces[0].metrics.loc.cloc
old: 1.0
new: 6.0
path: .spaces[0].metrics.loc.lloc
old: 3.0
new: 82.0
path: .spaces[0].metrics.loc.sloc
old: 8.0
new: 257.0
path: .spaces[0].metrics.loc.ploc
old: 8.0
new: 203.0
path: .spaces[0].metrics.loc.blank
old: 0.0
new: 48.0
path: .spaces[0].metrics.cognitive.average
old: 1.0
new: 1.2222222222222223
path: .spaces[0].metrics.cognitive.sum
old: 1.0
new: 22.0
path: .spaces[0].metrics.nexits.average
old: 1.0
new: 0.3333333333333333
path: .spaces[0].metrics.nexits.sum
old: 1.0
new: 6.0
path: .spaces[0].metrics.halstead.n2
old: 10.0
new: 146.0
path: .spaces[0].metrics.halstead.time
old: 129.37387194323057
new: 16975.26239429908
path: .spaces[0].metrics.halstead.volume
old: 199.03672606650855
new: 7577.881700733478
path: .spaces[0].metrics.halstead.estimated_program_length
old: 81.32499728470782
new: 1190.595834455182
path: .spaces[0].metrics.halstead.n1
old: 13.0
new: 29.0
path: .spaces[0].metrics.halstead.N1
old: 26.0
new: 611.0
path: .spaces[0].metrics.halstead.purity_ratio
old: 1.8482953928342687
new: 1.1706940358458031
path: .spaces[0].metrics.halstead.length
old: 44.0
new: 1017.0
path: .spaces[0].metrics.halstead.vocabulary
old: 23.0
new: 175.0
path: .spaces[0].metrics.halstead.bugs
old: 0.058563200005583176
new: 1.5121843742215806
path: .spaces[0].metrics.halstead.difficulty
old: 11.7
new: 40.321917808219176
path: .spaces[0].metrics.halstead.level
old: 0.08547008547008547
new: 0.02480040767793443
path: .spaces[0].metrics.halstead.N2
old: 18.0
new: 406.0
path: .spaces[0].metrics.halstead.effort
old: 2328.7296949781503
new: 305554.72309738345

### Code

    namespace mozilla {

    static const char* const sMetricNames[] = {"DisplayList Building",
                                               "Rasterizing",
                                               "LayerBuilding",
                                               "Layer Transactions",
                                               "Compositing",
                                               "Reflowing",
                                               "Styling",
                                               "HttpChannelCompletion_Network",
                                               "HttpChannelCompletion_Cache"};

    static_assert(sizeof(sMetricNames) / sizeof(sMetricNames[0]) ==
                  static_cast(PerfStats::Metric::Max));

    PerfStats::MetricMask PerfStats::sCollectionMask = 0;
    StaticMutex PerfStats::sMutex;
    StaticAutoPtr PerfStats::sSingleton;

    void PerfStats::SetCollectionMask(MetricMask aMask) {
      sCollectionMask = aMask;
      for (uint64_t i = 0; i < static_cast(Metric::Max); i++) {
        if (!(sCollectionMask & 1 << i)) {
          continue;
        }

        GetSingleton()->mRecordedTimes[i] = 0;
      }

      if (!XRE_IsParentProcess()) {
        return;
      }

      GPUProcessManager* gpuManager = GPUProcessManager::Get();
      GPUChild* gpuChild = nullptr;

      if (gpuManager) {
        gpuChild = gpuManager->GetGPUChild();
        if (gpuChild) {
          gpuChild->SendUpdatePerfStatsCollectionMask(aMask);
        }
      }

      nsTArray contentParents;
      ContentParent::GetAll(contentParents);

      for (ContentParent* parent : contentParents) {
        Unused << parent->SendUpdatePerfStatsCollectionMask(aMask);
      }
    }

    PerfStats* PerfStats::GetSingleton() {
      if (!sSingleton) {
        sSingleton = new PerfStats;
      }

      return sSingleton.get();
    }

    void PerfStats::RecordMeasurementStartInternal(Metric aMetric) {
      StaticMutexAutoLock lock(sMutex);

      GetSingleton()->mRecordedStarts[static_cast(aMetric)] =
          TimeStamp::Now();
    }

    void PerfStats::RecordMeasurementEndInternal(Metric aMetric) {
      StaticMutexAutoLock lock(sMutex);

      MOZ_ASSERT(sSingleton);

      sSingleton->mRecordedTimes[static_cast(aMetric)] +=
          (TimeStamp::Now() -
           sSingleton->mRecordedStarts[static_cast(aMetric)])
              .ToMilliseconds();
    }

    void PerfStats::RecordMeasurementInternal(Metric aMetric,
                                              TimeDuration aDuration) {
      StaticMutexAutoLock lock(sMutex);

      MOZ_ASSERT(sSingleton);

      sSingleton->mRecordedTimes[static_cast(aMetric)] +=
          aDuration.ToMilliseconds();
    }

    struct StringWriteFunc : public JSONWriteFunc {
      nsCString& mString;

      explicit StringWriteFunc(nsCString& aString) : mString(aString) {}
      virtual void Write(const Span& aStr) override {
        mString.Append(aStr);
      }
    };

    void AppendJSONStringAsProperty(nsCString& aDest, const char* aPropertyName,
                                    const nsCString& aJSON) {
      // We need to manually append into the string here, since JSONWriter has no
      // way to allow us to write an existing JSON object into a property.
      aDest.Append(",\n\"");
      aDest.Append(aPropertyName);
      aDest.Append("\": ");
      aDest.Append(aJSON);
    }

    struct PerfStatsCollector {
      PerfStatsCollector() : writer(MakeUnique(string)) {}

      void AppendPerfStats(const nsCString& aString, ContentParent* aParent) {
        writer.StartObjectElement();
        writer.StringProperty("type", "content");
        writer.IntProperty("id", aParent->ChildID());
        const ManagedContainer& browsers =
            aParent->ManagedPBrowserParent();

        writer.StartArrayProperty("urls");
        for (auto iter = browsers.ConstIter(); !iter.Done(); iter.Next()) {
          RefPtr parent =
              BrowserParent::GetFrom(iter.Get()->GetKey());

          CanonicalBrowsingContext* ctx = parent->GetBrowsingContext();
          if (!ctx) {
            continue;
          }

          WindowGlobalParent* windowGlobal = ctx->GetCurrentWindowGlobal();
          if (!windowGlobal) {
            continue;
          }

          RefPtr uri = windowGlobal->GetDocumentURI();
          if (!uri) {
            continue;
          }

          nsAutoCString url;
          uri->GetSpec(url);

          writer.StringElement(url);
        }
        writer.EndArray();
        AppendJSONStringAsProperty(string, "perfstats", aString);
        writer.EndObject();
      }

      void AppendPerfStats(const nsCString& aString, GPUChild* aChild) {
        writer.StartObjectElement();
        writer.StringProperty("type", "gpu");
        writer.IntProperty("id", aChild->Id());
        AppendJSONStringAsProperty(string, "perfstats", aString);
        writer.EndObject();
      }

      ~PerfStatsCollector() {
        writer.EndArray();
        writer.End();
        promise.Resolve(string, __func__);
      }
      nsCString string;
      JSONWriter writer;
      MozPromiseHolder promise;
    };

    auto PerfStats::CollectPerfStatsJSONInternal() -> RefPtr {
      if (!PerfStats::sCollectionMask) {
        return PerfStatsPromise::CreateAndReject(false, __func__);
      }

      if (!XRE_IsParentProcess()) {
        return PerfStatsPromise::CreateAndResolve(
            CollectLocalPerfStatsJSONInternal(), __func__);
      }

      std::shared_ptr collector =
          std::make_shared();

      JSONWriter& w = collector->writer;

      w.Start();
      {
        w.StartArrayProperty("processes");
        {
          w.StartObjectElement();
          {
            w.StringProperty("type", "parent");
            AppendJSONStringAsProperty(collector->string, "perfstats",
                                       CollectLocalPerfStatsJSONInternal());
          }
          w.EndObject();

          GPUProcessManager* gpuManager = GPUProcessManager::Get();
          GPUChild* gpuChild = nullptr;

          if (gpuManager) {
            gpuChild = gpuManager->GetGPUChild();
          }
          nsTArray contentParents;
          ContentParent::GetAll(contentParents);

          if (gpuChild) {
            gpuChild->SendCollectPerfStatsJSON(
                [collector, gpuChild](const nsCString& aString) {
                  collector->AppendPerfStats(aString, gpuChild);
                },
                // The only feasible errors here are if something goes wrong in the
                // the bridge, we choose to ignore those.
                [](mozilla::ipc::ResponseRejectReason) {});
          }
          for (ContentParent* parent : contentParents) {
            RefPtr parentRef = parent;
            parent->SendCollectPerfStatsJSON(
                [collector, parentRef](const nsCString& aString) {
                  collector->AppendPerfStats(aString, parentRef.get());
                },
                // The only feasible errors here are if something goes wrong in the
                // the bridge, we choose to ignore those.
                [](mozilla::ipc::ResponseRejectReason) {});
          }
        }
      }

      return collector->promise.Ensure(__func__);
    }

    nsCString PerfStats::CollectLocalPerfStatsJSONInternal() {
      StaticMutexAutoLock lock(PerfStats::sMutex);

      nsCString jsonString;

      JSONWriter w(MakeUnique(jsonString));
      w.Start();
      {
        w.StartArrayProperty("metrics");
        {
          for (uint64_t i = 0; i < static_cast(Metric::Max); i++) {
            if (!(sCollectionMask & (1 << i))) {
              continue;
            }

            w.StartObjectElement();
            {
              w.IntProperty("id", i);
              w.StringProperty("metric", MakeStringSpan(sMetricNames[i]));
              w.DoubleProperty("time", mRecordedTimes[i]);
            }
            w.EndObject();
          }
        }
        w.EndArray();
      }
      w.End();

      return jsonString;
    }

    }  // namespace mozilla