# Global Metrics

path: .metrics.halstead.N1
old: 295.0
new: 861.0
path: .metrics.halstead.level
old: 0.036521739130434785
new: 0.021782774543264403
path: .metrics.halstead.bugs
old: 0.6633095336205289
new: 2.1817781290704517
path: .metrics.halstead.estimated_program_length
old: 653.051068257784
new: 1857.2303540429637
path: .metrics.halstead.n2
old: 84.0
new: 217.0
path: .metrics.halstead.purity_ratio
old: 1.3633633992855616
new: 1.2835040456413018
path: .metrics.halstead.time
old: 4931.553353575885
new: 29418.820259039974
path: .metrics.halstead.length
old: 479.0
new: 1447.0
path: .metrics.halstead.vocabulary
old: 109.0
new: 251.0
path: .metrics.halstead.difficulty
old: 27.38095238095238
new: 45.90783410138249
path: .metrics.halstead.effort
old: 88767.96036436594
new: 529538.7646627196
path: .metrics.halstead.N2
old: 184.0
new: 586.0
path: .metrics.halstead.n1
old: 25.0
new: 34.0
path: .metrics.halstead.volume
old: 3241.9602915681476
new: 11534.823522566769
path: .metrics.nargs.average
old: 1.5
new: 0.4642857142857143
path: .metrics.nargs.sum
old: 12.0
new: 13.0
path: .metrics.nom.functions
old: 8.0
new: 28.0
path: .metrics.nom.total
old: 8.0
new: 28.0
path: .metrics.cyclomatic.average
old: 2.4615384615384617
new: 2.2
path: .metrics.cyclomatic.sum
old: 32.0
new: 66.0
path: .metrics.nexits.sum
old: 6.0
new: 15.0
path: .metrics.nexits.average
old: 0.75
new: 0.5357142857142857
path: .metrics.cognitive.sum
old: 14.0
new: 42.0
path: .metrics.cognitive.average
old: 1.75
new: 1.5
path: .metrics.loc.ploc
old: 123.0
new: 376.0
path: .metrics.loc.cloc
old: 18.0
new: 85.0
path: .metrics.loc.blank
old: 24.0
new: 81.0
path: .metrics.loc.lloc
old: 39.0
new: 175.0
path: .metrics.loc.sloc
old: 165.0
new: 542.0
path: .metrics.mi.mi_sei
old: 8.14200981805325
new: -32.69141157438759
path: .metrics.mi.mi_visual_studio
old: 22.741069802064573
new: 3.0411907874204376
path: .metrics.mi.mi_original
old: 38.887229361530416
new: 5.200436246488948

# Spaces Data

## Minimal test - lines (33, 542)

path: .spaces[0].metrics.cognitive.average
old: 0.0
new: 1.5
path: .spaces[0].metrics.cognitive.sum
old: 0.0
new: 42.0
path: .spaces[0].metrics.cyclomatic.average
old: 1.6666666666666667
new: 2.2413793103448274
path: .spaces[0].metrics.cyclomatic.sum
old: 5.0
new: 65.0
path: .spaces[0].metrics.mi.mi_original
old: 98.2992938131474
new: 6.535677759864498
path: .spaces[0].metrics.mi.mi_sei
old: 66.62415101212449
new: -31.340718242370414
path: .spaces[0].metrics.mi.mi_visual_studio
old: 57.48496714219147
new: 3.822033777698537
path: .spaces[0].metrics.nom.functions
old: 1.0
new: 28.0
path: .spaces[0].metrics.nom.total
old: 1.0
new: 28.0
path: .spaces[0].metrics.halstead.difficulty
old: 10.0
new: 47.65024630541872
path: .spaces[0].metrics.halstead.n1
old: 11.0
new: 34.0
path: .spaces[0].metrics.halstead.effort
old: 2541.8760226232594
new: 537161.8985969517
path: .spaces[0].metrics.halstead.N2
old: 20.0
new: 569.0
path: .spaces[0].metrics.halstead.length
old: 57.0
new: 1429.0
path: .spaces[0].metrics.halstead.N1
old: 37.0
new: 860.0
path: .spaces[0].metrics.halstead.purity_ratio
old: 1.3352192212284304
new: 1.209962860595593
path: .spaces[0].metrics.halstead.level
old: 0.1
new: 0.02098625038767704
path: .spaces[0].metrics.halstead.n2
old: 11.0
new: 203.0
path: .spaces[0].metrics.halstead.volume
old: 254.18760226232595
new: 11273.014102675612
path: .spaces[0].metrics.halstead.estimated_program_length
old: 76.10749561002054
new: 1729.0369277911022
path: .spaces[0].metrics.halstead.vocabulary
old: 22.0
new: 237.0
path: .spaces[0].metrics.halstead.bugs
old: 0.062084280859812054
new: 2.2026671694574387
path: .spaces[0].metrics.halstead.time
old: 141.21533459018107
new: 29842.327699830657
path: .spaces[0].metrics.nargs.average
old: 2.0
new: 0.4642857142857143
path: .spaces[0].metrics.nargs.sum
old: 2.0
new: 13.0
path: .spaces[0].metrics.nexits.average
old: 0.0
new: 0.5357142857142857
path: .spaces[0].metrics.nexits.sum
old: 0.0
new: 15.0
path: .spaces[0].metrics.loc.cloc
old: 0.0
new: 77.0
path: .spaces[0].metrics.loc.lloc
old: 0.0
new: 175.0
path: .spaces[0].metrics.loc.ploc
old: 12.0
new: 356.0
path: .spaces[0].metrics.loc.blank
old: 2.0
new: 77.0
path: .spaces[0].metrics.loc.sloc
old: 14.0
new: 510.0

### Code

    namespace mozilla {
    //
    // BlockingResourceBase implementation
    //

    // static members
    const char* const BlockingResourceBase::kResourceTypeName[] = {
        // needs to be kept in sync with BlockingResourceType
        "Mutex", "ReentrantMonitor", "CondVar", "RecursiveMutex"};

    #ifdef DEBUG

    PRCallOnceType BlockingResourceBase::sCallOnce;
    MOZ_THREAD_LOCAL(BlockingResourceBase*)
    BlockingResourceBase::sResourceAcqnChainFront;
    BlockingResourceBase::DDT* BlockingResourceBase::sDeadlockDetector;

    void BlockingResourceBase::StackWalkCallback(uint32_t aFrameNumber, void* aPc,
                                                 void* aSp, void* aClosure) {
    #  ifndef MOZ_CALLSTACK_DISABLED
      AcquisitionState* state = (AcquisitionState*)aClosure;
      state->ref().AppendElement(aPc);
    #  endif
    }

    void BlockingResourceBase::GetStackTrace(AcquisitionState& aState) {
    #  ifndef MOZ_CALLSTACK_DISABLED
      // Skip this function and the calling function.
      const uint32_t kSkipFrames = 2;

      // Clear the array...
      aState.reset();
      // ...and create a new one; this also puts the state to 'acquired' status
      // regardless of whether we obtain a stack trace or not.
      aState.emplace();

      // NB: Ignore the return value, there's nothing useful we can do if this
      //     this fails.
      MozStackWalk(StackWalkCallback, kSkipFrames, kAcquisitionStateStackSize,
                   aState.ptr());
    #  endif
    }

    /**
     * PrintCycle
     * Append to |aOut| detailed information about the circular
     * dependency in |aCycle|.  Returns true if it *appears* that this
     * cycle may represent an imminent deadlock, but this is merely a
     * heuristic; the value returned may be a false positive or false
     * negative.
     *
     * *NOT* thread safe.  Calls |Print()|.
     *
     * FIXME bug 456272 hack alert: because we can't write call
     * contexts into strings, all info is written to stderr, but only
     * some info is written into |aOut|
     */
    static bool PrintCycle(
        const BlockingResourceBase::DDT::ResourceAcquisitionArray& aCycle,
        nsACString& aOut) {
      NS_ASSERTION(aCycle.Length() > 1, "need > 1 element for cycle!");

      bool maybeImminent = true;

      fputs("=== Cyclical dependency starts at\n", stderr);
      aOut += "Cyclical dependency starts at\n";

      const BlockingResourceBase::DDT::ResourceAcquisitionArray::elem_type res =
          aCycle.ElementAt(0);
      maybeImminent &= res->Print(aOut);

      BlockingResourceBase::DDT::ResourceAcquisitionArray::index_type i;
      BlockingResourceBase::DDT::ResourceAcquisitionArray::size_type len =
          aCycle.Length();
      const BlockingResourceBase::DDT::ResourceAcquisitionArray::elem_type* it =
          1 + aCycle.Elements();
      for (i = 1; i < len - 1; ++i, ++it) {
        fputs("\n--- Next dependency:\n", stderr);
        aOut += "\nNext dependency:\n";

        maybeImminent &= (*it)->Print(aOut);
      }

      fputs("\n=== Cycle completed at\n", stderr);
      aOut += "Cycle completed at\n";
      (*it)->Print(aOut);

      return maybeImminent;
    }

    bool BlockingResourceBase::Print(nsACString& aOut) const {
      fprintf(stderr, "--- %s : %s", kResourceTypeName[mType], mName);
      aOut += BlockingResourceBase::kResourceTypeName[mType];
      aOut += " : ";
      aOut += mName;

      bool acquired = IsAcquired();

      if (acquired) {
        fputs(" (currently acquired)\n", stderr);
        aOut += " (currently acquired)\n";
      }

      fputs(" calling context\n", stderr);
    #  ifdef MOZ_CALLSTACK_DISABLED
      fputs("  [stack trace unavailable]\n", stderr);
    #  else
      const AcquisitionState& state = acquired ? mAcquired : mFirstSeen;

      CodeAddressService<> addressService;

      for (uint32_t i = 0; i < state.ref().Length(); i++) {
        const size_t kMaxLength = 1024;
        char buffer[kMaxLength];
        addressService.GetLocation(i + 1, state.ref()[i], buffer, kMaxLength);
        const char* fmt = "    %s\n";
        aOut.AppendLiteral("    ");
        aOut.Append(buffer);
        aOut.AppendLiteral("\n");
        fprintf(stderr, fmt, buffer);
      }

    #  endif

      return acquired;
    }

    BlockingResourceBase::BlockingResourceBase(
        const char* aName, BlockingResourceBase::BlockingResourceType aType)
        : mName(aName),
          mType(aType)
    #  ifdef MOZ_CALLSTACK_DISABLED
          ,
          mAcquired(false)
    #  else
          ,
          mAcquired()
    #  endif
    {
      MOZ_ASSERT(mName, "Name must be nonnull");
      // PR_CallOnce guaranatees that InitStatics is called in a
      // thread-safe way
      if (PR_SUCCESS != PR_CallOnce(&sCallOnce, InitStatics)) {
        MOZ_CRASH("can't initialize blocking resource static members");
      }

      mChainPrev = 0;
      sDeadlockDetector->Add(this);
    }

    BlockingResourceBase::~BlockingResourceBase() {
      // we don't check for really obviously bad things like freeing
      // Mutexes while they're still locked.  it is assumed that the
      // base class, or its underlying primitive, will check for such
      // stupid mistakes.
      mChainPrev = 0;  // racy only for stupidly buggy client code
      if (sDeadlockDetector) {
        sDeadlockDetector->Remove(this);
      }
    }

    size_t BlockingResourceBase::SizeOfDeadlockDetector(
        MallocSizeOf aMallocSizeOf) {
      return sDeadlockDetector
                 ? sDeadlockDetector->SizeOfIncludingThis(aMallocSizeOf)
                 : 0;
    }

    PRStatus BlockingResourceBase::InitStatics() {
      MOZ_ASSERT(sResourceAcqnChainFront.init());
      sDeadlockDetector = new DDT();
      if (!sDeadlockDetector) {
        MOZ_CRASH("can't allocate deadlock detector");
      }
      return PR_SUCCESS;
    }

    void BlockingResourceBase::Shutdown() {
      delete sDeadlockDetector;
      sDeadlockDetector = 0;
    }

    void BlockingResourceBase::CheckAcquire() {
      if (mType == eCondVar) {
        MOZ_ASSERT_UNREACHABLE(
            "FIXME bug 456272: annots. to allow CheckAcquire()ing condvars");
        return;
      }

      BlockingResourceBase* chainFront = ResourceChainFront();
      mozilla::UniquePtr cycle(
          sDeadlockDetector->CheckAcquisition(chainFront ? chainFront : 0, this));
      if (!cycle) {
        return;
      }

    #  ifndef MOZ_CALLSTACK_DISABLED
      // Update the current stack before printing.
      GetStackTrace(mAcquired);
    #  endif

      fputs("###!!! ERROR: Potential deadlock detected:\n", stderr);
      nsAutoCString out("Potential deadlock detected:\n");
      bool maybeImminent = PrintCycle(*cycle, out);

      if (maybeImminent) {
        fputs("\n###!!! Deadlock may happen NOW!\n\n", stderr);
        out.AppendLiteral("\n###!!! Deadlock may happen NOW!\n\n");
      } else {
        fputs("\nDeadlock may happen for some other execution\n\n", stderr);
        out.AppendLiteral("\nDeadlock may happen for some other execution\n\n");
      }

      // Only error out if we think a deadlock is imminent.
      if (maybeImminent) {
        NS_ERROR(out.get());
      } else {
        NS_WARNING(out.get());
      }
    }

    void BlockingResourceBase::Acquire() {
      if (mType == eCondVar) {
        MOZ_ASSERT_UNREACHABLE(
            "FIXME bug 456272: annots. to allow Acquire()ing condvars");
        return;
      }
      NS_ASSERTION(!IsAcquired(), "reacquiring already acquired resource");

      ResourceChainAppend(ResourceChainFront());

    #  ifdef MOZ_CALLSTACK_DISABLED
      mAcquired = true;
    #  else
      // Take a stack snapshot.
      GetStackTrace(mAcquired);
      MOZ_ASSERT(IsAcquired());

      if (!mFirstSeen) {
        mFirstSeen = mAcquired;
      }
    #  endif
    }

    void BlockingResourceBase::Release() {
      if (mType == eCondVar) {
        MOZ_ASSERT_UNREACHABLE(
            "FIXME bug 456272: annots. to allow Release()ing condvars");
        return;
      }

      BlockingResourceBase* chainFront = ResourceChainFront();
      NS_ASSERTION(chainFront && IsAcquired(),
                   "Release()ing something that hasn't been Acquire()ed");

      if (chainFront == this) {
        ResourceChainRemove();
      } else {
        // not an error, but makes code hard to reason about.
        NS_WARNING("Resource acquired is being released in non-LIFO order; why?\n");
        nsCString tmp;
        Print(tmp);

        // remove this resource from wherever it lives in the chain
        // we walk backwards in order of acquisition:
        //  (1)  ...node<-prev<-curr...
        //              /     /
        //  (2)  ...prev<-curr...
        BlockingResourceBase* curr = chainFront;
        BlockingResourceBase* prev = nullptr;
        while (curr && (prev = curr->mChainPrev) && (prev != this)) {
          curr = prev;
        }
        if (prev == this) {
          curr->mChainPrev = prev->mChainPrev;
        }
      }

      ClearAcquisitionState();
    }

    //
    // Debug implementation of (OffTheBooks)Mutex
    void OffTheBooksMutex::Lock() {
      CheckAcquire();
      this->lock();
      mOwningThread = PR_GetCurrentThread();
      Acquire();
    }

    bool OffTheBooksMutex::TryLock() {
      CheckAcquire();
      bool locked = this->tryLock();
      if (locked) {
        mOwningThread = PR_GetCurrentThread();
        Acquire();
      }
      return locked;
    }

    void OffTheBooksMutex::Unlock() {
      Release();
      mOwningThread = nullptr;
      this->unlock();
    }

    void OffTheBooksMutex::AssertCurrentThreadOwns() const {
      MOZ_ASSERT(IsAcquired() && mOwningThread == PR_GetCurrentThread());
    }

    //
    // Debug implementation of RWLock
    //

    void RWLock::ReadLock() {
      // All we want to ensure here is that we're not attempting to acquire the
      // read lock while this thread is holding the write lock.
      CheckAcquire();
      this->ReadLockInternal();
      MOZ_ASSERT(mOwningThread == nullptr);
    }

    void RWLock::ReadUnlock() {
      MOZ_ASSERT(mOwningThread == nullptr);
      this->ReadUnlockInternal();
    }

    void RWLock::WriteLock() {
      CheckAcquire();
      this->WriteLockInternal();
      mOwningThread = PR_GetCurrentThread();
      Acquire();
    }

    void RWLock::WriteUnlock() {
      Release();
      mOwningThread = nullptr;
      this->WriteUnlockInternal();
    }

    //
    // Debug implementation of ReentrantMonitor
    void ReentrantMonitor::Enter() {
      BlockingResourceBase* chainFront = ResourceChainFront();

      // the code below implements monitor reentrancy semantics

      if (this == chainFront) {
        // immediately re-entered the monitor: acceptable
        PR_EnterMonitor(mReentrantMonitor);
        ++mEntryCount;
        return;
      }

      // this is sort of a hack around not recording the thread that
      // owns this monitor
      if (chainFront) {
        for (BlockingResourceBase* br = ResourceChainPrev(chainFront); br;
             br = ResourceChainPrev(br)) {
          if (br == this) {
            NS_WARNING(
                "Re-entering ReentrantMonitor after acquiring other resources.");

            // show the caller why this is potentially bad
            CheckAcquire();

            PR_EnterMonitor(mReentrantMonitor);
            ++mEntryCount;
            return;
          }
        }
      }

      CheckAcquire();
      PR_EnterMonitor(mReentrantMonitor);
      NS_ASSERTION(mEntryCount == 0, "ReentrantMonitor isn't free!");
      Acquire();  // protected by mReentrantMonitor
      mEntryCount = 1;
    }

    void ReentrantMonitor::Exit() {
      if (--mEntryCount == 0) {
        Release();  // protected by mReentrantMonitor
      }
      PRStatus status = PR_ExitMonitor(mReentrantMonitor);
      NS_ASSERTION(PR_SUCCESS == status, "bad ReentrantMonitor::Exit()");
    }

    nsresult ReentrantMonitor::Wait(PRIntervalTime aInterval) {
      AssertCurrentThreadIn();

      // save monitor state and reset it to empty
      int32_t savedEntryCount = mEntryCount;
      AcquisitionState savedAcquisitionState = GetAcquisitionState();
      BlockingResourceBase* savedChainPrev = mChainPrev;
      mEntryCount = 0;
      ClearAcquisitionState();
      mChainPrev = 0;

      nsresult rv;
      {
    #  if defined(MOZILLA_INTERNAL_API)
        AUTO_PROFILER_THREAD_SLEEP;
    #  endif
        // give up the monitor until we're back from Wait()
        rv = PR_Wait(mReentrantMonitor, aInterval) == PR_SUCCESS ? NS_OK
                                                                 : NS_ERROR_FAILURE;
      }

      // restore saved state
      mEntryCount = savedEntryCount;
      SetAcquisitionState(savedAcquisitionState);
      mChainPrev = savedChainPrev;

      return rv;
    }

    //
    // Debug implementation of RecursiveMutex
    void RecursiveMutex::Lock() {
      BlockingResourceBase* chainFront = ResourceChainFront();

      // the code below implements mutex reentrancy semantics

      if (this == chainFront) {
        // immediately re-entered the mutex: acceptable
        LockInternal();
        ++mEntryCount;
        return;
      }

      // this is sort of a hack around not recording the thread that
      // owns this monitor
      if (chainFront) {
        for (BlockingResourceBase* br = ResourceChainPrev(chainFront); br;
             br = ResourceChainPrev(br)) {
          if (br == this) {
            NS_WARNING(
                "Re-entering RecursiveMutex after acquiring other resources.");

            // show the caller why this is potentially bad
            CheckAcquire();

            LockInternal();
            ++mEntryCount;
            return;
          }
        }
      }

      CheckAcquire();
      LockInternal();
      NS_ASSERTION(mEntryCount == 0, "RecursiveMutex isn't free!");
      Acquire();  // protected by us
      mOwningThread = PR_GetCurrentThread();
      mEntryCount = 1;
    }

    void RecursiveMutex::Unlock() {
      if (--mEntryCount == 0) {
        Release();  // protected by us
        mOwningThread = nullptr;
      }
      UnlockInternal();
    }

    void RecursiveMutex::AssertCurrentThreadIn() {
      MOZ_ASSERT(IsAcquired() && mOwningThread == PR_GetCurrentThread());
    }

    //
    // Debug implementation of CondVar
    void OffTheBooksCondVar::Wait() {
      // Forward to the timed version of OffTheBooksCondVar::Wait to avoid code
      // duplication.
      CVStatus status = Wait(TimeDuration::Forever());
      MOZ_ASSERT(status == CVStatus::NoTimeout);
    }

    CVStatus OffTheBooksCondVar::Wait(TimeDuration aDuration) {
      AssertCurrentThreadOwnsMutex();

      // save mutex state and reset to empty
      AcquisitionState savedAcquisitionState = mLock->GetAcquisitionState();
      BlockingResourceBase* savedChainPrev = mLock->mChainPrev;
      PRThread* savedOwningThread = mLock->mOwningThread;
      mLock->ClearAcquisitionState();
      mLock->mChainPrev = 0;
      mLock->mOwningThread = nullptr;

      // give up mutex until we're back from Wait()
      CVStatus status;
      {
    #  if defined(MOZILLA_INTERNAL_API)
        AUTO_PROFILER_THREAD_SLEEP;
    #  endif
        status = mImpl.wait_for(*mLock, aDuration);
      }

      // restore saved state
      mLock->SetAcquisitionState(savedAcquisitionState);
      mLock->mChainPrev = savedChainPrev;
      mLock->mOwningThread = savedOwningThread;

      return status;
    }

    #endif  // ifdef DEBUG

    }  // namespace mozilla

## Minimal test - lines (50, 56)

path: .spaces[0].spaces[0].metrics.mi.mi_visual_studio
old: 60.91571817306542
new: 65.91787083380372
path: .spaces[0].spaces[0].metrics.mi.mi_sei
old: 74.98602317561084
new: 87.02091682937993
path: .spaces[0].spaces[0].metrics.mi.mi_original
old: 104.16587807594188
new: 112.71955912580437
path: .spaces[0].spaces[0].metrics.nargs.sum
old: 2.0
new: 4.0
path: .spaces[0].spaces[0].metrics.nargs.average
old: 2.0
new: 4.0
path: .spaces[0].spaces[0].metrics.loc.sloc
old: 10.0
new: 7.0
path: .spaces[0].spaces[0].metrics.loc.lloc
old: 0.0
new: 1.0
path: .spaces[0].spaces[0].metrics.loc.ploc
old: 10.0
new: 7.0
path: .spaces[0].spaces[0].metrics.halstead.vocabulary
old: 22.0
new: 20.0
path: .spaces[0].spaces[0].metrics.halstead.estimated_program_length
old: 76.10749561002054
new: 66.43856189774725
path: .spaces[0].spaces[0].metrics.halstead.N2
old: 20.0
new: 14.0
path: .spaces[0].spaces[0].metrics.halstead.bugs
old: 0.060623388992107605
new: 0.03658065923319219
path: .spaces[0].spaces[0].metrics.halstead.n2
old: 11.0
new: 10.0
path: .spaces[0].spaces[0].metrics.halstead.length
old: 55.0
new: 38.0
path: .spaces[0].spaces[0].metrics.halstead.N1
old: 35.0
new: 24.0
path: .spaces[0].spaces[0].metrics.halstead.purity_ratio
old: 1.3837726474549188
new: 1.748383207835454
path: .spaces[0].spaces[0].metrics.halstead.time
old: 136.26041056947298
new: 63.868492957779914
path: .spaces[0].spaces[0].metrics.halstead.n1
old: 11.0
new: 10.0
path: .spaces[0].spaces[0].metrics.halstead.volume
old: 245.26873902505136
new: 164.2332676057198
path: .spaces[0].spaces[0].metrics.halstead.difficulty
old: 10.0
new: 7.0
path: .spaces[0].spaces[0].metrics.halstead.level
old: 0.1
new: 0.14285714285714285
path: .spaces[0].spaces[0].metrics.halstead.effort
old: 2452.6873902505135
new: 1149.6328732400384
path: .spaces[0].spaces[0].metrics.cyclomatic.average
old: 2.0
new: 1.0
path: .spaces[0].spaces[0].metrics.cyclomatic.sum
old: 4.0
new: 1.0

### Code

    void BlockingResourceBase::StackWalkCallback(uint32_t aFrameNumber, void* aPc,
                                                 void* aSp, void* aClosure) {
    #  ifndef MOZ_CALLSTACK_DISABLED
      AcquisitionState* state = (AcquisitionState*)aClosure;
      state->ref().AppendElement(aPc);
    #  endif
    }