# Global Metrics

path: .metrics.loc.lloc
old: 230.0
new: 52.0
path: .metrics.loc.sloc
old: 515.0
new: 224.0
path: .metrics.loc.ploc
old: 388.0
new: 170.0
path: .metrics.loc.blank
old: 72.0
new: 25.0
path: .metrics.loc.cloc
old: 55.0
new: 29.0
path: .metrics.nargs.sum
old: 34.0
new: 6.0
path: .metrics.nargs.average
old: 1.4782608695652173
new: 0.75
path: .metrics.cognitive.sum
old: 196.0
new: 30.0
path: .metrics.cognitive.average
old: 8.521739130434783
new: 3.75
path: .metrics.halstead.estimated_program_length
old: 1644.403252118508
new: 886.8310173464122
path: .metrics.halstead.bugs
old: 4.417962851769083
new: 0.9253160538109342
path: .metrics.halstead.purity_ratio
old: 0.7508690648942958
new: 1.4054374284412237
path: .metrics.halstead.level
old: 0.011251258364422336
new: 0.030713388245148677
path: .metrics.halstead.N1
old: 1324.0
new: 384.0
path: .metrics.halstead.N2
old: 866.0
new: 247.0
path: .metrics.halstead.difficulty
old: 88.87894736842105
new: 32.55909090909091
path: .metrics.halstead.effort
old: 1525860.9961547835
new: 146257.12347442078
path: .metrics.halstead.length
old: 2190.0
new: 631.0
path: .metrics.halstead.n1
old: 39.0
new: 29.0
path: .metrics.halstead.time
old: 84770.05534193241
new: 8125.395748578932
path: .metrics.halstead.n2
old: 190.0
new: 110.0
path: .metrics.halstead.vocabulary
old: 229.0
new: 139.0
path: .metrics.halstead.volume
old: 17167.856295932306
new: 4492.051816888534
path: .metrics.nom.functions
old: 23.0
new: 8.0
path: .metrics.nom.total
old: 23.0
new: 8.0
path: .metrics.nexits.average
old: 3.0869565217391304
new: 1.25
path: .metrics.nexits.sum
old: 71.0
new: 10.0
path: .metrics.mi.mi_visual_studio
old: 0.0
new: 18.853214464268703
path: .metrics.mi.mi_sei
old: -56.0411544625703
new: 0.5182444800644106
path: .metrics.mi.mi_original
old: -13.0596330815017
new: 32.238996733899484
path: .metrics.cyclomatic.average
old: 4.827586206896552
new: 2.2857142857142856
path: .metrics.cyclomatic.sum
old: 140.0
new: 32.0

# Spaces Data

## Minimal test - lines (30, 51)

path: .spaces[0].spaces[0].metrics.loc.lloc
old: 0.0
new: 1.0
path: .spaces[0].spaces[0].metrics.loc.sloc
old: 7.0
new: 22.0
path: .spaces[0].spaces[0].metrics.loc.ploc
old: 6.0
new: 21.0
path: .spaces[0].spaces[0].metrics.halstead.vocabulary
old: 16.0
new: 20.0
path: .spaces[0].spaces[0].metrics.halstead.difficulty
old: 5.444444444444445
new: 9.0
path: .spaces[0].spaces[0].metrics.halstead.bugs
old: 0.025097778938159565
new: 0.046968253716542153
path: .spaces[0].spaces[0].metrics.halstead.effort
old: 653.3333333333334
new: 1672.5861727214094
path: .spaces[0].spaces[0].metrics.halstead.purity_ratio
old: 1.6060269822461346
new: 1.5484435539067694
path: .spaces[0].spaces[0].metrics.halstead.level
old: 0.18367346938775508
new: 0.1111111111111111
path: .spaces[0].spaces[0].metrics.halstead.volume
old: 120.0
new: 185.8429080801566
path: .spaces[0].spaces[0].metrics.halstead.n1
old: 7.0
new: 9.0
path: .spaces[0].spaces[0].metrics.halstead.n2
old: 9.0
new: 11.0
path: .spaces[0].spaces[0].metrics.halstead.N1
old: 16.0
new: 21.0
path: .spaces[0].spaces[0].metrics.halstead.length
old: 30.0
new: 43.0
path: .spaces[0].spaces[0].metrics.halstead.N2
old: 14.0
new: 22.0
path: .spaces[0].spaces[0].metrics.halstead.time
old: 36.2962962962963
new: 92.9214540400783
path: .spaces[0].spaces[0].metrics.halstead.estimated_program_length
old: 48.18080946738404
new: 66.58307281799108
path: .spaces[0].spaces[0].metrics.mi.mi_sei
old: 89.1450191655025
new: 59.09992069024463
path: .spaces[0].spaces[0].metrics.mi.mi_original
old: 114.1212985228373
new: 93.29562322506636
path: .spaces[0].spaces[0].metrics.mi.mi_visual_studio
old: 66.73760147534345
new: 54.55884399126688
path: .spaces[0].spaces[0].metrics.nargs.sum
old: 0.0
new: 4.0
path: .spaces[0].spaces[0].metrics.nargs.average
old: 0.0
new: 4.0

### Code

    struct ObservationWithStack {
      explicit ObservationWithStack(mozilla::IOInterposeObserver::Observation& aObs
    #ifdef MOZ_GECKO_PROFILER
                                    ,
                                    ProfilerBacktrace* aStack
    #endif
                                    )
          : mObservation(aObs)
    #ifdef MOZ_GECKO_PROFILER
            ,
            mStack(aStack)
    #endif
      {
        aObs.Filename(mFilename);
      }

      mozilla::IOInterposeObserver::Observation mObservation;
    #ifdef MOZ_GECKO_PROFILER
      ProfilerBacktrace* mStack;
    #endif
      nsString mFilename;
    };

## Minimal test - lines (31, 44)

path: .spaces[0].spaces[0].spaces[0].metrics.mi.mi_visual_studio
old: 87.97970938710789
new: 60.332377463615366
path: .spaces[0].spaces[0].spaces[0].metrics.mi.mi_sei
old: 141.4476605054837
new: 73.24145709716794
path: .spaces[0].spaces[0].spaces[0].metrics.mi.mi_original
old: 150.44530305195448
new: 103.16836546278228
path: .spaces[0].spaces[0].spaces[0].metrics.halstead.length
old: 15.0
new: 28.0
path: .spaces[0].spaces[0].spaces[0].metrics.halstead.n2
old: 6.0
new: 10.0
path: .spaces[0].spaces[0].spaces[0].metrics.halstead.volume
old: 49.82892142331043
new: 118.9419703764204
path: .spaces[0].spaces[0].spaces[0].metrics.halstead.difficulty
old: 2.3333333333333335
new: 6.3
path: .spaces[0].spaces[0].spaces[0].metrics.halstead.level
old: 0.42857142857142855
new: 0.15873015873015872
path: .spaces[0].spaces[0].spaces[0].metrics.halstead.vocabulary
old: 10.0
new: 19.0
path: .spaces[0].spaces[0].spaces[0].metrics.halstead.N1
old: 8.0
new: 14.0
path: .spaces[0].spaces[0].spaces[0].metrics.halstead.N2
old: 7.0
new: 14.0
path: .spaces[0].spaces[0].spaces[0].metrics.halstead.purity_ratio
old: 1.5673183336217955
new: 2.2053073557805156
path: .spaces[0].spaces[0].spaces[0].metrics.halstead.bugs
old: 0.007940556250510787
new: 0.027499778601425103
path: .spaces[0].spaces[0].spaces[0].metrics.halstead.effort
old: 116.26748332105768
new: 749.3344133714484
path: .spaces[0].spaces[0].spaces[0].metrics.halstead.time
old: 6.459304628947649
new: 41.629689631747134
path: .spaces[0].spaces[0].spaces[0].metrics.halstead.n1
old: 4.0
new: 9.0
path: .spaces[0].spaces[0].spaces[0].metrics.halstead.estimated_program_length
old: 23.509775004326936
new: 61.74860596185444
path: .spaces[0].spaces[0].spaces[0].metrics.loc.sloc
old: 1.0
new: 14.0
path: .spaces[0].spaces[0].spaces[0].metrics.loc.ploc
old: 1.0
new: 14.0
path: .spaces[0].spaces[0].spaces[0].metrics.loc.lloc
old: 0.0
new: 1.0
path: .spaces[0].spaces[0].spaces[0].metrics.nargs.average
old: 0.0
new: 4.0
path: .spaces[0].spaces[0].spaces[0].metrics.nargs.sum
old: 0.0
new: 4.0

### Code

      explicit ObservationWithStack(mozilla::IOInterposeObserver::Observation& aObs
    #ifdef MOZ_GECKO_PROFILER
                                    ,
                                    ProfilerBacktrace* aStack
    #endif
                                    )
          : mObservation(aObs)
    #ifdef MOZ_GECKO_PROFILER
            ,
            mStack(aStack)
    #endif
      {
        aObs.Filename(mFilename);
      }

## Minimal test - lines (28, 206)

path: .spaces[0].metrics.cognitive.sum
old: 0.0
new: 29.0
path: .spaces[0].metrics.cognitive.average
old: 0.0
new: 4.142857142857143
path: .spaces[0].metrics.cyclomatic.sum
old: 3.0
new: 27.0
path: .spaces[0].metrics.cyclomatic.average
old: 1.0
new: 2.7
path: .spaces[0].metrics.mi.mi_visual_studio
old: 55.13610199277192
new: 21.99866503267546
path: .spaces[0].metrics.mi.mi_original
old: 94.28273440763998
new: 37.61771720587504
path: .spaces[0].metrics.mi.mi_sei
old: 78.02454794791635
new: 4.293782376696974
path: .spaces[0].metrics.nom.total
old: 1.0
new: 7.0
path: .spaces[0].metrics.nom.functions
old: 1.0
new: 7.0
path: .spaces[0].metrics.loc.ploc
old: 15.0
new: 144.0
path: .spaces[0].metrics.loc.lloc
old: 0.0
new: 47.0
path: .spaces[0].metrics.loc.blank
old: 3.0
new: 18.0
path: .spaces[0].metrics.loc.cloc
old: 1.0
new: 17.0
path: .spaces[0].metrics.loc.sloc
old: 19.0
new: 179.0
path: .spaces[0].metrics.nexits.sum
old: 0.0
new: 8.0
path: .spaces[0].metrics.nexits.average
old: 0.0
new: 1.1428571428571428
path: .spaces[0].metrics.nargs.sum
old: 0.0
new: 6.0
path: .spaces[0].metrics.nargs.average
old: 0.0
new: 0.8571428571428571
path: .spaces[0].metrics.halstead.purity_ratio
old: 1.8697373660251153
new: 1.3119805908429767
path: .spaces[0].metrics.halstead.N1
old: 27.0
new: 355.0
path: .spaces[0].metrics.halstead.n2
old: 17.0
new: 94.0
path: .spaces[0].metrics.halstead.difficulty
old: 5.411764705882353
new: 34.244680851063826
path: .spaces[0].metrics.halstead.effort
old: 1256.572851350808
new: 137178.39967213196
path: .spaces[0].metrics.halstead.length
old: 50.0
new: 577.0
path: .spaces[0].metrics.halstead.N2
old: 23.0
new: 222.0
path: .spaces[0].metrics.halstead.bugs
old: 0.0388153816076943
new: 0.8866166851526256
path: .spaces[0].metrics.halstead.level
old: 0.18478260869565216
new: 0.029201615408511964
path: .spaces[0].metrics.halstead.estimated_program_length
old: 93.48686830125575
new: 757.0128009163975
path: .spaces[0].metrics.halstead.vocabulary
old: 25.0
new: 123.0
path: .spaces[0].metrics.halstead.n1
old: 8.0
new: 29.0
path: .spaces[0].metrics.halstead.volume
old: 232.19280948873623
new: 4005.830869580742
path: .spaces[0].metrics.halstead.time
old: 69.80960285282266
new: 7621.022204007331

### Code

    namespace {

    struct ObservationWithStack {
      explicit ObservationWithStack(mozilla::IOInterposeObserver::Observation& aObs
    #ifdef MOZ_GECKO_PROFILER
                                    ,
                                    ProfilerBacktrace* aStack
    #endif
                                    )
          : mObservation(aObs)
    #ifdef MOZ_GECKO_PROFILER
            ,
            mStack(aStack)
    #endif
      {
        aObs.Filename(mFilename);
      }

      mozilla::IOInterposeObserver::Observation mObservation;
    #ifdef MOZ_GECKO_PROFILER
      ProfilerBacktrace* mStack;
    #endif
      nsString mFilename;
    };

    class MainThreadIOLoggerImpl final : public mozilla::IOInterposeObserver {
     public:
      MainThreadIOLoggerImpl();
      ~MainThreadIOLoggerImpl();

      bool Init();

      void Observe(Observation& aObservation) override;

     private:
      static void sIOThreadFunc(void* aArg);
      void IOThreadFunc();

      mozilla::TimeStamp mLogStartTime;
      const char* mFileName;
      PRThread* mIOThread;
      mozilla::IOInterposer::Monitor mMonitor;
      bool mShutdownRequired;
      std::vector mObservations;
    };

    static mozilla::StaticAutoPtr sImpl;

    MainThreadIOLoggerImpl::MainThreadIOLoggerImpl()
        : mFileName(nullptr), mIOThread(nullptr), mShutdownRequired(false) {}

    MainThreadIOLoggerImpl::~MainThreadIOLoggerImpl() {
      if (!mIOThread) {
        return;
      }
      {
        // Scope for lock
        mozilla::IOInterposer::MonitorAutoLock lock(mMonitor);
        mShutdownRequired = true;
        lock.Notify();
      }
      PR_JoinThread(mIOThread);
      mIOThread = nullptr;
    }

    bool MainThreadIOLoggerImpl::Init() {
      if (mFileName) {
        // Already initialized
        return true;
      }
      mFileName = PR_GetEnv("MOZ_MAIN_THREAD_IO_LOG");
      if (!mFileName) {
        // Can't start
        return false;
      }
      mIOThread =
          PR_CreateThread(PR_USER_THREAD, &sIOThreadFunc, this, PR_PRIORITY_LOW,
                          PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0);
      if (!mIOThread) {
        return false;
      }
      return true;
    }

    /* static */
    void MainThreadIOLoggerImpl::sIOThreadFunc(void* aArg) {
      AUTO_PROFILER_REGISTER_THREAD("MainThreadIOLogger");

      NS_SetCurrentThreadName("MainThreadIOLogger");
      MainThreadIOLoggerImpl* obj = static_cast(aArg);
      obj->IOThreadFunc();
    }

    void MainThreadIOLoggerImpl::IOThreadFunc() {
      PRFileDesc* fd = PR_Open(mFileName, PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE,
                               PR_IRUSR | PR_IWUSR | PR_IRGRP);
      if (!fd) {
        mozilla::IOInterposer::MonitorAutoLock lock(mMonitor);
        mShutdownRequired = true;
        std::vector().swap(mObservations);
        return;
      }
      mLogStartTime = mozilla::TimeStamp::Now();
      {
        // Scope for lock
        mozilla::IOInterposer::MonitorAutoLock lock(mMonitor);
        while (true) {
          while (!mShutdownRequired && mObservations.empty()) {
            lock.Wait();
          }
          if (mShutdownRequired) {
            break;
          }
          // Pull events off the shared array onto a local one
          std::vector observationsToWrite;
          observationsToWrite.swap(mObservations);

          // Release the lock so that we're not holding anybody up during I/O
          mozilla::IOInterposer::MonitorAutoUnlock unlock(mMonitor);

          // Now write the events.
          for (auto i = observationsToWrite.begin(), e = observationsToWrite.end();
               i != e; ++i) {
            if (i->mObservation.ObservedOperation() == OpNextStage) {
              PR_fprintf(
                  fd, "%f,NEXT-STAGE\n",
                  (mozilla::TimeStamp::Now() - mLogStartTime).ToMilliseconds());
              continue;
            }
            double durationMs = i->mObservation.Duration().ToMilliseconds();
            nsAutoCString nativeFilename;
            nativeFilename.AssignLiteral("(not available)");
            if (!i->mFilename.IsEmpty()) {
              if (NS_FAILED(NS_CopyUnicodeToNative(i->mFilename, nativeFilename))) {
                nativeFilename.AssignLiteral("(conversion failed)");
              }
            }
            // clang-format off
            /**
             * Format:
             * Start Timestamp (Milliseconds), Operation, Duration (Milliseconds), Event Source, Filename
             */
            // clang-format on
            if (PR_fprintf(
                    fd, "%f,%s,%f,%s,%s\n",
                    (i->mObservation.Start() - mLogStartTime).ToMilliseconds(),
                    i->mObservation.ObservedOperationString(), durationMs,
                    i->mObservation.Reference(), nativeFilename.get()) > 0) {
    #ifdef MOZ_GECKO_PROFILER
              // TODO: Write out the callstack
              i->mStack = nullptr;
    #endif
            }
          }
        }
      }
      PR_Close(fd);
    }

    void MainThreadIOLoggerImpl::Observe(Observation& aObservation) {
      if (!mFileName || !IsMainThread()) {
        return;
      }
      mozilla::IOInterposer::MonitorAutoLock lock(mMonitor);
      if (mShutdownRequired) {
        // The writer thread isn't running. Don't enqueue any more data.
        return;
      }
      // Passing nullptr as aStack parameter for now
      mObservations.push_back(ObservationWithStack(aObservation
    #ifdef MOZ_GECKO_PROFILER
                                                   ,
                                                   nullptr
    #endif
                                                   ));
      lock.Notify();
    }

    }  // namespace

## Minimal test - lines (208, 224)

path: .spaces[1].metrics.cognitive.sum
old: 119.0
new: 1.0
path: .spaces[1].metrics.cognitive.average
old: 13.22222222222222
new: 1.0
path: .spaces[1].metrics.mi.mi_original
old: 19.72034018044529
new: 96.33948736024982
path: .spaces[1].metrics.mi.mi_sei
old: -18.2437887787733
new: 82.04272597782098
path: .spaces[1].metrics.mi.mi_visual_studio
old: 11.532362678622976
new: 56.33888149722212
path: .spaces[1].metrics.nargs.sum
old: 19.0
new: 0.0
path: .spaces[1].metrics.nargs.average
old: 2.111111111111111
new: 0.0
path: .spaces[1].metrics.cyclomatic.average
old: 7.0
new: 1.3333333333333333
path: .spaces[1].metrics.cyclomatic.sum
old: 70.0
new: 4.0
path: .spaces[1].metrics.halstead.n2
old: 105.0
new: 12.0
path: .spaces[1].metrics.halstead.length
old: 1139.0
new: 45.0
path: .spaces[1].metrics.halstead.purity_ratio
old: 0.7708248603665095
new: 2.1405004203880074
path: .spaces[1].metrics.halstead.N1
old: 683.0
new: 29.0
path: .spaces[1].metrics.halstead.difficulty
old: 73.82857142857142
new: 9.333333333333334
path: .spaces[1].metrics.halstead.estimated_program_length
old: 877.9695159574544
new: 96.32251891746031
path: .spaces[1].metrics.halstead.n1
old: 34.0
new: 14.0
path: .spaces[1].metrics.halstead.time
old: 33257.61350897473
new: 109.67692675662548
path: .spaces[1].metrics.halstead.effort
old: 598637.0431615452
new: 1974.1846816192583
path: .spaces[1].metrics.halstead.level
old: 0.013544891640866877
new: 0.10714285714285714
path: .spaces[1].metrics.halstead.volume
old: 8108.473881832075
new: 211.51978731634912
path: .spaces[1].metrics.halstead.bugs
old: 2.367669810907326
new: 0.0524570580560377
path: .spaces[1].metrics.halstead.N2
old: 456.0
new: 16.0
path: .spaces[1].metrics.halstead.vocabulary
old: 139.0
new: 26.0
path: .spaces[1].metrics.nexits.average
old: 3.7777777777777777
new: 2.0
path: .spaces[1].metrics.nexits.sum
old: 34.0
new: 2.0
path: .spaces[1].metrics.nom.functions
old: 9.0
new: 1.0
path: .spaces[1].metrics.nom.total
old: 9.0
new: 1.0
path: .spaces[1].metrics.loc.sloc
old: 234.0
new: 17.0
path: .spaces[1].metrics.loc.blank
old: 26.0
new: 3.0
path: .spaces[1].metrics.loc.cloc
old: 20.0
new: 1.0
path: .spaces[1].metrics.loc.lloc
old: 119.0
new: 5.0
path: .spaces[1].metrics.loc.ploc
old: 188.0
new: 13.0

### Code

    namespace mozilla {

    namespace MainThreadIOLogger {

    bool Init() {
      auto impl = MakeUnique();
      if (!impl->Init()) {
        return false;
      }
      sImpl = impl.release();
      IOInterposer::Register(IOInterposeObserver::OpAllWithStaging, sImpl);
      return true;
    }

    }  // namespace MainThreadIOLogger

    }  // namespace mozilla

## Minimal test - lines (210, 222)

path: .spaces[1].spaces[0].metrics.cyclomatic.sum
old: 2.0
new: 3.0
path: .spaces[1].spaces[0].metrics.cyclomatic.average
old: 2.0
new: 1.5
path: .spaces[1].spaces[0].metrics.loc.sloc
old: 3.0
new: 13.0
path: .spaces[1].spaces[0].metrics.loc.lloc
old: 2.0
new: 5.0
path: .spaces[1].spaces[0].metrics.loc.blank
old: 0.0
new: 2.0
path: .spaces[1].spaces[0].metrics.loc.ploc
old: 3.0
new: 11.0
path: .spaces[1].spaces[0].metrics.halstead.level
old: 0.1111111111111111
new: 0.10476190476190476
path: .spaces[1].spaces[0].metrics.halstead.length
old: 25.0
new: 43.0
path: .spaces[1].spaces[0].metrics.halstead.purity_ratio
old: 1.6055586194967049
new: 2.1245748072980635
path: .spaces[1].spaces[0].metrics.halstead.N2
old: 10.0
new: 15.0
path: .spaces[1].spaces[0].metrics.halstead.vocabulary
old: 14.0
new: 25.0
path: .spaces[1].spaces[0].metrics.halstead.difficulty
old: 9.0
new: 9.545454545454543
path: .spaces[1].spaces[0].metrics.halstead.time
old: 47.59193652572005
new: 105.89399341834788
path: .spaces[1].spaces[0].metrics.halstead.volume
old: 95.1838730514401
new: 199.68581616031315
path: .spaces[1].spaces[0].metrics.halstead.N1
old: 15.0
new: 28.0
path: .spaces[1].spaces[0].metrics.halstead.effort
old: 856.6548574629609
new: 1906.0918815302616
path: .spaces[1].spaces[0].metrics.halstead.estimated_program_length
old: 40.13896548741762
new: 91.35671671381672
path: .spaces[1].spaces[0].metrics.halstead.n1
old: 9.0
new: 14.0
path: .spaces[1].spaces[0].metrics.halstead.bugs
old: 0.030066485534799937
new: 0.05124379671641141
path: .spaces[1].spaces[0].metrics.halstead.n2
old: 5.0
new: 11.0
path: .spaces[1].spaces[0].metrics.mi.mi_sei
old: 110.6858521667939
new: 70.62661870383211
path: .spaces[1].spaces[0].metrics.mi.mi_visual_studio
old: 75.46916151145908
new: 59.1899095356019
path: .spaces[1].spaces[0].metrics.mi.mi_original
old: 129.05226618459503
new: 101.21474530587923
path: .spaces[1].spaces[0].metrics.nexits.sum
old: 0.0
new: 2.0
path: .spaces[1].spaces[0].metrics.nexits.average
old: 0.0
new: 2.0

### Code

    namespace MainThreadIOLogger {

    bool Init() {
      auto impl = MakeUnique();
      if (!impl->Init()) {
        return false;
      }
      sImpl = impl.release();
      IOInterposer::Register(IOInterposeObserver::OpAllWithStaging, sImpl);
      return true;
    }

    }  // namespace MainThreadIOLogger