Global Metrics
path: .metrics.cognitive.average
old: 0.0
new: 0.9230769230769232
path: .metrics.cognitive.sum
old: 0.0
new: 24.0
path: .metrics.nom.total
old: 5.0
new: 26.0
path: .metrics.nom.closures
old: 0.0
new: 3.0
path: .metrics.nom.functions
old: 5.0
new: 23.0
path: .metrics.cyclomatic.sum
old: 14.0
new: 49.0
path: .metrics.cyclomatic.average
old: 1.0
new: 1.96
path: .metrics.nexits.average
old: 0.2
new: 0.8461538461538461
path: .metrics.nexits.sum
old: 1.0
new: 22.0
path: .metrics.loc.ploc
old: 100.0
new: 224.0
path: .metrics.loc.cloc
old: 93.0
new: 36.0
path: .metrics.loc.lloc
old: 40.0
new: 91.0
path: .metrics.loc.blank
old: 29.0
new: 35.0
path: .metrics.loc.sloc
old: 222.0
new: 295.0
path: .metrics.mi.mi_visual_studio
old: 23.732962059236353
new: 12.509984374811763
path: .metrics.mi.mi_sei
old: 26.42035696689115
new: -14.091834076760538
path: .metrics.mi.mi_original
old: 40.583365121294165
new: 21.392073280928116
path: .metrics.halstead.vocabulary
old: 61.0
new: 159.0
path: .metrics.halstead.level
old: 0.04448563484708063
new: 0.031077348066298343
path: .metrics.halstead.length
old: 347.0
new: 989.0
path: .metrics.halstead.n2
old: 48.0
new: 135.0
path: .metrics.halstead.purity_ratio
old: 0.9111928425661376
new: 1.0772590552266632
path: .metrics.halstead.difficulty
old: 22.479166666666668
new: 32.17777777777778
path: .metrics.halstead.volume
old: 2057.9658561343217
new: 7232.441242776228
path: .metrics.halstead.bugs
old: 0.42956048778563777
new: 1.2611673533606034
path: .metrics.halstead.n1
old: 13.0
new: 24.0
path: .metrics.halstead.N1
old: 181.0
new: 627.0
path: .metrics.halstead.N2
old: 166.0
new: 362.0
path: .metrics.halstead.estimated_program_length
old: 316.1839163704497
new: 1065.4092056191698
path: .metrics.halstead.time
old: 2570.0754152418212
new: 12929.104838938243
path: .metrics.halstead.effort
old: 46261.35747435278
new: 232723.8871008884
path: .metrics.nargs.sum
old: 0.0
new: 9.0
path: .metrics.nargs.average
old: 0.0
new: 0.34615384615384615
Spaces Data
Minimal test - lines (15, 295)
path: .spaces[0].metrics.mi.mi_sei
old: null
new: -14.155229312196912
path: .spaces[0].metrics.mi.mi_visual_studio
old: null
new: 13.139746181025956
path: .spaces[0].metrics.mi.mi_original
old: null
new: 22.46896596955439
path: .spaces[0].metrics.cyclomatic.average
old: 1.0
new: 2.0
path: .spaces[0].metrics.cyclomatic.sum
old: 1.0
new: 48.0
path: .spaces[0].metrics.halstead.level
old: null
new: 0.030345471521942113
path: .spaces[0].metrics.halstead.time
old: 0.0
new: 13090.948257987846
path: .spaces[0].metrics.halstead.N2
old: 1.0
new: 357.0
path: .spaces[0].metrics.halstead.n1
old: 0.0
new: 24.0
path: .spaces[0].metrics.halstead.N1
old: 0.0
new: 627.0
path: .spaces[0].metrics.halstead.effort
old: 0.0
new: 235637.06864378124
path: .spaces[0].metrics.halstead.n2
old: 1.0
new: 130.0
path: .spaces[0].metrics.halstead.estimated_program_length
old: null
new: 1022.9469157110068
path: .spaces[0].metrics.halstead.difficulty
old: 0.0
new: 32.95384615384615
path: .spaces[0].metrics.halstead.length
old: 1.0
new: 984.0
path: .spaces[0].metrics.halstead.vocabulary
old: 1.0
new: 154.0
path: .spaces[0].metrics.halstead.bugs
old: 0.0
new: 1.2716701764742624
path: .spaces[0].metrics.halstead.volume
old: 0.0
new: 7150.517956043783
path: .spaces[0].metrics.halstead.purity_ratio
old: null
new: 1.0395801988932996
path: .spaces[0].metrics.nom.functions
old: 0.0
new: 23.0
path: .spaces[0].metrics.nom.total
old: 0.0
new: 26.0
path: .spaces[0].metrics.nom.closures
old: 0.0
new: 3.0
path: .spaces[0].metrics.nexits.average
old: null
new: 0.8461538461538461
path: .spaces[0].metrics.nexits.sum
old: 0.0
new: 22.0
path: .spaces[0].metrics.cognitive.average
old: null
new: 0.9230769230769232
path: .spaces[0].metrics.cognitive.sum
old: 0.0
new: 24.0
path: .spaces[0].metrics.nargs.sum
old: 0.0
new: 9.0
path: .spaces[0].metrics.nargs.average
old: null
new: 0.34615384615384615
path: .spaces[0].metrics.loc.blank
old: 0.0
new: 32.0
path: .spaces[0].metrics.loc.sloc
old: 1.0
new: 281.0
path: .spaces[0].metrics.loc.lloc
old: 0.0
new: 91.0
path: .spaces[0].metrics.loc.cloc
old: 0.0
new: 30.0
path: .spaces[0].metrics.loc.ploc
old: 1.0
new: 219.0
Code
namespace mozilla {
/* static */ DataMutexBase
ProfilerChild::sPendingChunkManagerUpdate{
"ProfilerChild::sPendingChunkManagerUpdate"};
ProfilerChild::ProfilerChild()
: mThread(NS_GetCurrentThread()), mDestroyed(false) {
MOZ_COUNT_CTOR(ProfilerChild);
}
ProfilerChild::~ProfilerChild() { MOZ_COUNT_DTOR(ProfilerChild); }
void ProfilerChild::ResolveChunkUpdate(
PProfilerChild::AwaitNextChunkManagerUpdateResolver& aResolve) {
MOZ_ASSERT(!!aResolve,
"ResolveChunkUpdate should only be called when there's a pending "
"resolver");
MOZ_ASSERT(
!mChunkManagerUpdate.IsNotUpdate(),
"ResolveChunkUpdate should only be called with a real or final update");
MOZ_ASSERT(
!mDestroyed,
"ResolveChunkUpdate should not be called if the actor was destroyed");
if (mChunkManagerUpdate.IsFinal()) {
// Final update, send a special "unreleased value", but don't clear the
// local copy so we know we got the final update.
std::move(aResolve)(ProfilerParent::MakeFinalUpdate());
} else {
// Optimization note: The ProfileBufferChunkManagerUpdate constructor takes
// the newly-released chunks nsTArray by reference-to-const, therefore
// constructing and then moving the array here would make a copy. So instead
// we first give it an empty array, and then we can write the data directly
// into the update's array.
ProfileBufferChunkManagerUpdate update{
mChunkManagerUpdate.UnreleasedBytes(),
mChunkManagerUpdate.ReleasedBytes(),
mChunkManagerUpdate.OldestDoneTimeStamp(),
{}};
update.newlyReleasedChunks().SetCapacity(
mChunkManagerUpdate.NewlyReleasedChunksRef().size());
for (const ProfileBufferControlledChunkManager::ChunkMetadata& chunk :
mChunkManagerUpdate.NewlyReleasedChunksRef()) {
update.newlyReleasedChunks().EmplaceBack(chunk.mDoneTimeStamp,
chunk.mBufferBytes);
}
std::move(aResolve)(update);
// Clear the update we just sent, so it's ready for later updates to be
// folded into it.
mChunkManagerUpdate.Clear();
}
// Discard the resolver, so it's empty next time there's a new request.
aResolve = nullptr;
}
void ProfilerChild::ProcessChunkManagerUpdate(
ProfileBufferControlledChunkManager::Update&& aUpdate) {
if (mDestroyed) {
return;
}
// Always store the data, it could be the final update.
mChunkManagerUpdate.Fold(std::move(aUpdate));
if (mAwaitNextChunkManagerUpdateResolver) {
// There is already a pending resolver, give it the info now.
ResolveChunkUpdate(mAwaitNextChunkManagerUpdateResolver);
}
}
/* static */ void ProfilerChild::ProcessPendingUpdate() {
auto lockedUpdate = sPendingChunkManagerUpdate.Lock();
if (!lockedUpdate->mProfilerChild || lockedUpdate->mUpdate.IsNotUpdate()) {
return;
}
lockedUpdate->mProfilerChild->mThread->Dispatch(NS_NewRunnableFunction(
"ProfilerChild::ProcessPendingUpdate", []() mutable {
auto lockedUpdate = sPendingChunkManagerUpdate.Lock();
if (!lockedUpdate->mProfilerChild ||
lockedUpdate->mUpdate.IsNotUpdate()) {
return;
}
lockedUpdate->mProfilerChild->ProcessChunkManagerUpdate(
std::move(lockedUpdate->mUpdate));
lockedUpdate->mUpdate.Clear();
}));
}
/* static */ bool ProfilerChild::IsLockedOnCurrentThread() {
return sPendingChunkManagerUpdate.Mutex().IsLockedOnCurrentThread();
}
void ProfilerChild::SetupChunkManager() {
mChunkManager = profiler_get_controlled_chunk_manager();
if (NS_WARN_IF(!mChunkManager)) {
return;
}
// Make sure there are no updates (from a previous run).
mChunkManagerUpdate.Clear();
{
auto lockedUpdate = sPendingChunkManagerUpdate.Lock();
lockedUpdate->mProfilerChild = this;
lockedUpdate->mUpdate.Clear();
}
mChunkManager->SetUpdateCallback(
[](ProfileBufferControlledChunkManager::Update&& aUpdate) {
// Updates from the chunk manager are stored for later processing.
// We avoid dispatching a task, as this could deadlock (if the queueing
// mutex is held elsewhere).
auto lockedUpdate = sPendingChunkManagerUpdate.Lock();
if (!lockedUpdate->mProfilerChild) {
return;
}
lockedUpdate->mUpdate.Fold(std::move(aUpdate));
});
}
void ProfilerChild::ResetChunkManager() {
if (!mChunkManager) {
return;
}
// We have a chunk manager, reset the callback, which will add a final
// pending update.
mChunkManager->SetUpdateCallback({});
// Clear the pending update.
auto lockedUpdate = sPendingChunkManagerUpdate.Lock();
lockedUpdate->mProfilerChild = nullptr;
lockedUpdate->mUpdate.Clear();
// And process a final update right now.
ProcessChunkManagerUpdate(
ProfileBufferControlledChunkManager::Update(nullptr));
mChunkManager = nullptr;
mAwaitNextChunkManagerUpdateResolver = nullptr;
}
mozilla::ipc::IPCResult ProfilerChild::RecvStart(
const ProfilerInitParams& params) {
nsTArray filterArray;
for (size_t i = 0; i < params.filters().Length(); ++i) {
filterArray.AppendElement(params.filters()[i].get());
}
profiler_start(PowerOfTwo32(params.entries()), params.interval(),
params.features(), filterArray.Elements(),
filterArray.Length(), params.activeBrowsingContextID(),
params.duration());
SetupChunkManager();
return IPC_OK();
}
mozilla::ipc::IPCResult ProfilerChild::RecvEnsureStarted(
const ProfilerInitParams& params) {
nsTArray filterArray;
for (size_t i = 0; i < params.filters().Length(); ++i) {
filterArray.AppendElement(params.filters()[i].get());
}
profiler_ensure_started(PowerOfTwo32(params.entries()), params.interval(),
params.features(), filterArray.Elements(),
filterArray.Length(),
params.activeBrowsingContextID(), params.duration());
SetupChunkManager();
return IPC_OK();
}
mozilla::ipc::IPCResult ProfilerChild::RecvStop() {
ResetChunkManager();
profiler_stop();
return IPC_OK();
}
mozilla::ipc::IPCResult ProfilerChild::RecvPause() {
profiler_pause();
return IPC_OK();
}
mozilla::ipc::IPCResult ProfilerChild::RecvResume() {
profiler_resume();
return IPC_OK();
}
mozilla::ipc::IPCResult ProfilerChild::RecvPauseSampling() {
profiler_pause_sampling();
return IPC_OK();
}
mozilla::ipc::IPCResult ProfilerChild::RecvResumeSampling() {
profiler_resume_sampling();
return IPC_OK();
}
mozilla::ipc::IPCResult ProfilerChild::RecvClearAllPages() {
profiler_clear_all_pages();
return IPC_OK();
}
static nsCString CollectProfileOrEmptyString(bool aIsShuttingDown) {
nsCString profileCString;
UniquePtr profile =
profiler_get_profile(/* aSinceTime */ 0, aIsShuttingDown);
if (profile) {
size_t len = strlen(profile.get());
profileCString.Adopt(profile.release(), len);
}
return profileCString;
}
mozilla::ipc::IPCResult ProfilerChild::RecvAwaitNextChunkManagerUpdate(
AwaitNextChunkManagerUpdateResolver&& aResolve) {
MOZ_ASSERT(!mDestroyed,
"Recv... should not be called if the actor was destroyed");
// Pick up pending updates if any.
{
auto lockedUpdate = sPendingChunkManagerUpdate.Lock();
if (lockedUpdate->mProfilerChild && !lockedUpdate->mUpdate.IsNotUpdate()) {
mChunkManagerUpdate.Fold(std::move(lockedUpdate->mUpdate));
lockedUpdate->mUpdate.Clear();
}
}
if (mChunkManagerUpdate.IsNotUpdate()) {
// No data yet, store the resolver for later.
mAwaitNextChunkManagerUpdateResolver = std::move(aResolve);
} else {
// We have data, send it now.
ResolveChunkUpdate(aResolve);
}
return IPC_OK();
}
mozilla::ipc::IPCResult ProfilerChild::RecvDestroyReleasedChunksAtOrBefore(
const TimeStamp& aTimeStamp) {
if (mChunkManager) {
mChunkManager->DestroyChunksAtOrBefore(aTimeStamp);
}
return IPC_OK();
}
mozilla::ipc::IPCResult ProfilerChild::RecvGatherProfile(
GatherProfileResolver&& aResolve) {
mozilla::ipc::Shmem shmem;
profiler_get_profile_json_into_lazily_allocated_buffer(
[&](size_t allocationSize) -> char* {
if (AllocShmem(allocationSize,
mozilla::ipc::Shmem::SharedMemory::TYPE_BASIC, &shmem)) {
return shmem.get();
}
return nullptr;
},
/* aSinceTime */ 0,
/* aIsShuttingDown */ false);
aResolve(std::move(shmem));
return IPC_OK();
}
void ProfilerChild::ActorDestroy(ActorDestroyReason aActorDestroyReason) {
mDestroyed = true;
}
void ProfilerChild::Destroy() {
ResetChunkManager();
if (!mDestroyed) {
Close();
}
}
nsCString ProfilerChild::GrabShutdownProfile() {
return CollectProfileOrEmptyString(/* aIsShuttingDown */ true);
}
} // namespace mozilla