Global Metrics
path: .metrics.nexits.average
old: 1.75
new: 0.6111111111111112
path: .metrics.nexits.sum
old: 49.0
new: 11.0
path: .metrics.halstead.vocabulary
old: 214.0
new: 129.0
path: .metrics.halstead.volume
old: 12618.591187833868
new: 5384.622532165059
path: .metrics.halstead.bugs
old: 3.0026464307431935
new: 1.2694197569665069
path: .metrics.halstead.effort
old: 854944.9984566093
new: 235011.850416344
path: .metrics.halstead.purity_ratio
old: 0.930550828689535
new: 1.048524827911682
path: .metrics.halstead.N2
old: 670.0
new: 301.0
path: .metrics.halstead.difficulty
old: 67.75280898876404
new: 43.645
path: .metrics.halstead.n1
old: 36.0
new: 29.0
path: .metrics.halstead.n2
old: 178.0
new: 100.0
path: .metrics.halstead.N1
old: 960.0
new: 467.0
path: .metrics.halstead.length
old: 1630.0
new: 768.0
path: .metrics.halstead.time
old: 47496.944358700515
new: 13056.213912019111
path: .metrics.halstead.estimated_program_length
old: 1516.797850763942
new: 805.2670678361719
path: .metrics.halstead.level
old: 0.014759535655058043
new: 0.02291213197388017
path: .metrics.cognitive.sum
old: 63.0
new: 12.0
path: .metrics.cognitive.average
old: 2.25
new: 0.6666666666666666
path: .metrics.nom.functions
old: 28.0
new: 18.0
path: .metrics.nom.total
old: 28.0
new: 18.0
path: .metrics.mi.mi_original
old: 3.191983010934578
new: 28.56313599357672
path: .metrics.mi.mi_sei
old: -42.31526119622495
new: 2.407163592796863
path: .metrics.mi.mi_visual_studio
old: 1.866656731540689
new: 16.703588300337262
path: .metrics.nargs.average
old: 0.7857142857142857
new: 0.6666666666666666
path: .metrics.nargs.sum
old: 22.0
new: 12.0
path: .metrics.loc.lloc
old: 144.0
new: 51.0
path: .metrics.loc.blank
old: 45.0
new: 34.0
path: .metrics.loc.sloc
old: 418.0
new: 247.0
path: .metrics.loc.ploc
old: 345.0
new: 159.0
path: .metrics.loc.cloc
old: 28.0
new: 54.0
path: .metrics.cyclomatic.average
old: 3.033333333333333
new: 1.48
path: .metrics.cyclomatic.sum
old: 91.0
new: 37.0
Spaces Data
Minimal test - lines (20, 245)
path: .spaces[0].metrics.cognitive.sum
old: 0.0
new: 12.0
path: .spaces[0].metrics.cognitive.average
old: 0.0
new: 0.6666666666666666
path: .spaces[0].metrics.loc.cloc
old: 0.0
new: 47.0
path: .spaces[0].metrics.loc.ploc
old: 11.0
new: 147.0
path: .spaces[0].metrics.loc.lloc
old: 0.0
new: 51.0
path: .spaces[0].metrics.loc.blank
old: 3.0
new: 32.0
path: .spaces[0].metrics.loc.sloc
old: 14.0
new: 226.0
path: .spaces[0].metrics.mi.mi_sei
old: 65.88158499817104
new: 4.204249248104773
path: .spaces[0].metrics.mi.mi_original
old: 97.9963147196096
new: 30.34705289034588
path: .spaces[0].metrics.mi.mi_visual_studio
old: 57.3077863857366
new: 17.746814555757823
path: .spaces[0].metrics.halstead.volume
old: 307.67071501168664
new: 5267.360376547794
path: .spaces[0].metrics.halstead.difficulty
old: 5.921052631578948
new: 45.68279569892473
path: .spaces[0].metrics.halstead.vocabulary
old: 28.0
new: 122.0
path: .spaces[0].metrics.halstead.effort
old: 1821.7344967797237
new: 240627.7479544441
path: .spaces[0].metrics.halstead.length
old: 64.0
new: 760.0
path: .spaces[0].metrics.halstead.n2
old: 19.0
new: 93.0
path: .spaces[0].metrics.halstead.time
old: 101.20747204331798
new: 13368.208219691338
path: .spaces[0].metrics.halstead.N2
old: 25.0
new: 293.0
path: .spaces[0].metrics.halstead.N1
old: 39.0
new: 467.0
path: .spaces[0].metrics.halstead.bugs
old: 0.04972049764924493
new: 1.2895629575446417
path: .spaces[0].metrics.halstead.estimated_program_length
old: 109.23994776840892
new: 749.0232182917464
path: .spaces[0].metrics.halstead.n1
old: 9.0
new: 29.0
path: .spaces[0].metrics.halstead.level
old: 0.16888888888888887
new: 0.021890078851359303
path: .spaces[0].metrics.halstead.purity_ratio
old: 1.7068741838813897
new: 0.9855568661733504
path: .spaces[0].metrics.cyclomatic.average
old: 1.0
new: 1.5
path: .spaces[0].metrics.cyclomatic.sum
old: 2.0
new: 36.0
path: .spaces[0].metrics.nexits.average
old: 0.0
new: 0.6111111111111112
path: .spaces[0].metrics.nexits.sum
old: 0.0
new: 11.0
path: .spaces[0].metrics.nargs.average
old: 0.0
new: 0.6666666666666666
path: .spaces[0].metrics.nargs.sum
old: 0.0
new: 12.0
path: .spaces[0].metrics.nom.total
old: 1.0
new: 18.0
path: .spaces[0].metrics.nom.functions
old: 1.0
new: 18.0
Code
namespace mozilla {
namespace detail {
template
class CodeAddressServiceAllocPolicy : public AllocPolicy {
public:
char* strdup_(const char* aStr) {
char* s = AllocPolicy::template pod_malloc(strlen(aStr) + 1);
if (!s) {
MOZ_CRASH("CodeAddressService OOM");
}
strcpy(s, aStr);
return s;
}
};
// Default implementation of DescribeCodeAddressLock.
struct DefaultDescribeCodeAddressLock {
static void Unlock() {}
static void Lock() {}
// Because CodeAddressService asserts that IsLocked() is true, returning true
// here is a sensible default when there is no relevant lock.
static bool IsLocked() { return true; }
};
} // namespace detail
// This class is used to print details about code locations.
//
// |AllocPolicy_| must adhere to the description in mfbt/AllocPolicy.h.
//
// |DescribeCodeAddressLock| is needed when the callers may be holding a lock
// used by MozDescribeCodeAddress. |DescribeCodeAddressLock| must implement
// static methods IsLocked(), Unlock() and Lock().
template
class CodeAddressService
: private detail::CodeAddressServiceAllocPolicy {
protected:
// GetLocation() is the key function in this class. It's basically a wrapper
// around MozDescribeCodeAddress.
//
// However, MozDescribeCodeAddress is very slow on some platforms, and we
// have lots of repeated (i.e. same PC) calls to it. So we do some caching
// of results. Each cached result includes two strings (|mFunction| and
// |mLibrary|), so we also optimize them for space in the following ways.
//
// - The number of distinct library names is small, e.g. a few dozen. There
// is lots of repetition, especially of libxul. So we intern them in their
// own table, which saves space over duplicating them for each cache entry.
//
// - The number of distinct function names is much higher, so we duplicate
// them in each cache entry. That's more space-efficient than interning
// because entries containing single-occurrence function names are quickly
// overwritten, and their copies released. In addition, empty function
// names are common, so we use nullptr to represent them compactly.
using AllocPolicy = detail::CodeAddressServiceAllocPolicy;
using StringHashSet = HashSet;
StringHashSet mLibraryStrings;
struct Entry : private AllocPolicy {
const void* mPc;
char* mFunction; // owned by the Entry; may be null
const char* mLibrary; // owned by mLibraryStrings; never null
// in a non-empty entry is in use
ptrdiff_t mLOffset;
char* mFileName; // owned by the Entry; may be null
uint32_t mLineNo : 31;
uint32_t mInUse : 1; // is the entry used?
Entry()
: mPc(0),
mFunction(nullptr),
mLibrary(nullptr),
mLOffset(0),
mFileName(nullptr),
mLineNo(0),
mInUse(0) {}
~Entry() {
// We don't free mLibrary because it's externally owned.
AllocPolicy::free_(mFunction);
AllocPolicy::free_(mFileName);
}
void Replace(const void* aPc, const char* aFunction, const char* aLibrary,
ptrdiff_t aLOffset, const char* aFileName,
unsigned long aLineNo) {
mPc = aPc;
// Convert "" to nullptr. Otherwise, make a copy of the name.
AllocPolicy::free_(mFunction);
mFunction = !aFunction[0] ? nullptr : AllocPolicy::strdup_(aFunction);
AllocPolicy::free_(mFileName);
mFileName = !aFileName[0] ? nullptr : AllocPolicy::strdup_(aFileName);
mLibrary = aLibrary;
mLOffset = aLOffset;
mLineNo = aLineNo;
mInUse = 1;
}
size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const {
// Don't measure mLibrary because it's externally owned.
size_t n = 0;
n += aMallocSizeOf(mFunction);
n += aMallocSizeOf(mFileName);
return n;
}
};
const char* InternLibraryString(const char* aString) {
auto p = mLibraryStrings.lookupForAdd(aString);
if (p) {
return *p;
}
const char* newString = AllocPolicy::strdup_(aString);
if (!mLibraryStrings.add(p, newString)) {
MOZ_CRASH("CodeAddressService OOM");
}
return newString;
}
Entry& GetEntry(const void* aPc) {
MOZ_ASSERT(DescribeCodeAddressLock::IsLocked());
uint32_t index = HashGeneric(aPc) & kMask;
MOZ_ASSERT(index < kNumEntries);
Entry& entry = mEntries[index];
if (!entry.mInUse || entry.mPc != aPc) {
mNumCacheMisses++;
// MozDescribeCodeAddress can (on Linux) acquire a lock inside
// the shared library loader. Another thread might call malloc
// while holding that lock (when loading a shared library). So
// we have to exit the lock around this call. For details, see
// https://bugzilla.mozilla.org/show_bug.cgi?id=363334#c3
MozCodeAddressDetails details;
{
DescribeCodeAddressLock::Unlock();
(void)MozDescribeCodeAddress(const_cast(aPc), &details);
DescribeCodeAddressLock::Lock();
}
const char* library = InternLibraryString(details.library);
entry.Replace(aPc, details.function, library, details.loffset,
details.filename, details.lineno);
} else {
mNumCacheHits++;
}
MOZ_ASSERT(entry.mPc == aPc);
return entry;
}
// A direct-mapped cache. When doing dmd::Analyze() just after starting
// desktop Firefox (which is similar to analyzing after a longer-running
// session, thanks to the limit on how many records we print), a cache with
// 2^24 entries (which approximates an infinite-entry cache) has a ~91% hit
// rate. A cache with 2^12 entries has a ~83% hit rate, and takes up ~85 KiB
// (on 32-bit platforms) or ~150 KiB (on 64-bit platforms).
static const size_t kNumEntries = 1 << 12;
static const size_t kMask = kNumEntries - 1;
Entry mEntries[kNumEntries];
size_t mNumCacheHits;
size_t mNumCacheMisses;
public:
CodeAddressService()
: mLibraryStrings(64), mEntries(), mNumCacheHits(0), mNumCacheMisses(0) {}
~CodeAddressService() {
for (auto iter = mLibraryStrings.iter(); !iter.done(); iter.next()) {
AllocPolicy::free_(const_cast(iter.get()));
}
}
void GetLocation(uint32_t aFrameNumber, const void* aPc, char* aBuf,
size_t aBufLen) {
Entry& entry = GetEntry(aPc);
MozFormatCodeAddress(aBuf, aBufLen, aFrameNumber, entry.mPc,
entry.mFunction, entry.mLibrary, entry.mLOffset,
entry.mFileName, entry.mLineNo);
}
size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const {
size_t n = aMallocSizeOf(this);
for (uint32_t i = 0; i < kNumEntries; i++) {
n += mEntries[i].SizeOfExcludingThis(aMallocSizeOf);
}
n += mLibraryStrings.shallowSizeOfExcludingThis(aMallocSizeOf);
for (auto iter = mLibraryStrings.iter(); !iter.done(); iter.next()) {
n += aMallocSizeOf(iter.get());
}
return n;
}
size_t CacheCapacity() const { return kNumEntries; }
size_t CacheCount() const {
size_t n = 0;
for (size_t i = 0; i < kNumEntries; i++) {
if (mEntries[i].mInUse) {
n++;
}
}
return n;
}
size_t NumCacheHits() const { return mNumCacheHits; }
size_t NumCacheMisses() const { return mNumCacheMisses; }
};
} // namespace mozilla
Minimal test - lines (22, 46)
path: .spaces[0].spaces[0].metrics.mi.mi_visual_studio
old: 89.23284250314286
new: 50.50321903589104
path: .spaces[0].spaces[0].metrics.mi.mi_sei
old: 144.53915057933605
new: 75.2688392743859
path: .spaces[0].spaces[0].metrics.mi.mi_original
old: 152.5881606803743
new: 86.3605045513737
path: .spaces[0].spaces[0].metrics.loc.blank
old: 0.0
new: 3.0
path: .spaces[0].spaces[0].metrics.loc.cloc
old: 0.0
new: 3.0
path: .spaces[0].spaces[0].metrics.loc.sloc
old: 1.0
new: 25.0
path: .spaces[0].spaces[0].metrics.loc.lloc
old: 0.0
new: 5.0
path: .spaces[0].spaces[0].metrics.loc.ploc
old: 1.0
new: 19.0
path: .spaces[0].spaces[0].metrics.cyclomatic.sum
old: 1.0
new: 8.0
path: .spaces[0].spaces[0].metrics.cyclomatic.average
old: 1.0
new: 1.1428571428571428
path: .spaces[0].spaces[0].metrics.nexits.average
old: 0.0
new: 0.5
path: .spaces[0].spaces[0].metrics.nexits.sum
old: 0.0
new: 2.0
path: .spaces[0].spaces[0].metrics.halstead.estimated_program_length
old: 16.0
new: 133.6620089600894
path: .spaces[0].spaces[0].metrics.halstead.n1
old: 4.0
new: 15.0
path: .spaces[0].spaces[0].metrics.halstead.effort
old: 82.5
new: 3631.963765938086
path: .spaces[0].spaces[0].metrics.halstead.N1
old: 6.0
new: 48.0
path: .spaces[0].spaces[0].metrics.halstead.difficulty
old: 2.5
new: 10.0
path: .spaces[0].spaces[0].metrics.halstead.N2
old: 5.0
new: 24.0
path: .spaces[0].spaces[0].metrics.halstead.length
old: 11.0
new: 72.0
path: .spaces[0].spaces[0].metrics.halstead.n2
old: 4.0
new: 18.0
path: .spaces[0].spaces[0].metrics.halstead.vocabulary
old: 8.0
new: 33.0
path: .spaces[0].spaces[0].metrics.halstead.bugs
old: 0.006317055768674653
new: 0.07876012862433353
path: .spaces[0].spaces[0].metrics.halstead.level
old: 0.4
new: 0.1
path: .spaces[0].spaces[0].metrics.halstead.purity_ratio
old: 1.4545454545454546
new: 1.8564167911123528
path: .spaces[0].spaces[0].metrics.halstead.time
old: 4.583333333333333
new: 201.7757647743381
path: .spaces[0].spaces[0].metrics.halstead.volume
old: 33.0
new: 363.1963765938086
path: .spaces[0].spaces[0].metrics.nom.functions
old: 1.0
new: 4.0
path: .spaces[0].spaces[0].metrics.nom.total
old: 1.0
new: 4.0
path: .spaces[0].spaces[0].metrics.cognitive.average
old: 0.0
new: 0.25
path: .spaces[0].spaces[0].metrics.cognitive.sum
old: 0.0
new: 1.0
Code
namespace detail {
template
class CodeAddressServiceAllocPolicy : public AllocPolicy {
public:
char* strdup_(const char* aStr) {
char* s = AllocPolicy::template pod_malloc(strlen(aStr) + 1);
if (!s) {
MOZ_CRASH("CodeAddressService OOM");
}
strcpy(s, aStr);
return s;
}
};
// Default implementation of DescribeCodeAddressLock.
struct DefaultDescribeCodeAddressLock {
static void Unlock() {}
static void Lock() {}
// Because CodeAddressService asserts that IsLocked() is true, returning true
// here is a sensible default when there is no relevant lock.
static bool IsLocked() { return true; }
};
} // namespace detail