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
}