Bug 1592625 - All threads' allocation markers get stored on the main thread; r=gerald

This change makes it so that all allocations (from any thread in a
process) are stored on the main thread in the profile. This way it's
easy to balance allocations with frees. Memory happens more in a
per-process model, than a per-thread model. The front-end can then
process the information and display it in more interesting ways.

Before allocations and deallocations were only stored on the
thread where they were being generated. It was easy to miss
deallocations with the old model.

Differential Revision: https://phabricator.services.mozilla.com/D51937

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Greg Tatum 2019-11-13 16:19:11 +00:00
parent ca92c2e288
commit 9edf1442b6
4 changed files with 44 additions and 13 deletions

View File

@ -270,6 +270,12 @@ class ThreadIntercept {
mozilla::recordreplay::Behavior::DontPreserve>
sAllocationsFeatureEnabled;
// The markers will be stored on the main thread. Retain the id to the main
// thread of this process here.
static mozilla::Atomic<int, mozilla::Relaxed,
mozilla::recordreplay::Behavior::DontPreserve>
sMainThreadId;
ThreadIntercept() = default;
// Only allow consumers to access this information if they run
@ -316,16 +322,26 @@ class ThreadIntercept {
bool IsBlocked() const { return ThreadIntercept::IsBlocked_(); }
static void EnableAllocationFeature() { sAllocationsFeatureEnabled = true; }
static void EnableAllocationFeature(int aMainThreadId) {
sAllocationsFeatureEnabled = true;
sMainThreadId = aMainThreadId;
}
static void DisableAllocationFeature() { sAllocationsFeatureEnabled = false; }
static int MainThreadId() { return sMainThreadId; }
};
PROFILER_THREAD_LOCAL(bool) ThreadIntercept::tlsIsBlocked;
mozilla::Atomic<bool, mozilla::Relaxed,
mozilla::recordreplay::Behavior::DontPreserve>
ThreadIntercept::sAllocationsFeatureEnabled(false);
mozilla::Atomic<int, mozilla::Relaxed,
mozilla::recordreplay::Behavior::DontPreserve>
ThreadIntercept::sMainThreadId(0);
// An object of this class must be created (on the stack) before running any
// code that might allocate.
class AutoBlockIntercepts {
@ -382,7 +398,8 @@ static void AllocCallback(void* aPtr, size_t aReqSize) {
// First perform the Bernoulli trial.
gBernoulli->trial(actualSize) &&
// Second, attempt to add a marker if the Bernoulli trial passed.
profiler_add_native_allocation_marker(static_cast<int64_t>(actualSize))) {
profiler_add_native_allocation_marker(ThreadIntercept::MainThreadId(),
static_cast<int64_t>(actualSize))) {
MOZ_ASSERT(gAllocationTracker,
"gAllocationTracker must be properly installed for the memory "
"hooks.");
@ -424,7 +441,8 @@ static void FreeCallback(void* aPtr) {
"gAllocationTracker must be properly installed for the memory hooks.");
if (gAllocationTracker->RemoveMemoryAddressIfFound(aPtr)) {
// This size here is negative, indicating a deallocation.
profiler_add_native_allocation_marker(signedSize);
profiler_add_native_allocation_marker(ThreadIntercept::MainThreadId(),
signedSize);
}
}
@ -559,7 +577,7 @@ void install_memory_hooks() {
// leak these values.
void remove_memory_hooks() { jemalloc_replace_dynamic(nullptr); }
void enable_native_allocations() {
void enable_native_allocations(int aMainThreadId) {
// The bloat log tracks allocations and deallocations. This can conflict
// with the memory hook machinery, as the bloat log creates its own
// allocations. This means we can re-enter inside the bloat log machinery. At
@ -585,7 +603,7 @@ void enable_native_allocations() {
if (!PR_GetEnv("XPCOM_MEM_BLOAT_LOG")) {
EnsureBernoulliIsInstalled();
EnsureAllocationTrackerIsInstalled();
ThreadIntercept::EnableAllocationFeature();
ThreadIntercept::EnableAllocationFeature(aMainThreadId);
}
}

View File

@ -13,7 +13,7 @@ namespace profiler {
void install_memory_hooks();
void remove_memory_hooks();
void enable_native_allocations();
void enable_native_allocations(int aMainThreadId);
void disable_native_allocations();
} // namespace profiler

View File

@ -3980,6 +3980,7 @@ static void locked_profiler_start(PSLockRef aLock, PowerOfTwo32 aCapacity,
#endif
// Set up profiling for each registered thread, if appropriate.
Maybe<int> mainThreadId;
int tid = profiler_current_thread_id();
const Vector<UniquePtr<RegisteredThread>>& registeredThreads =
CorePS::RegisteredThreads(aLock);
@ -4005,6 +4006,9 @@ static void locked_profiler_start(PSLockRef aLock, PowerOfTwo32 aCapacity,
TriggerPollJSSamplingOnMainThread();
}
}
if (info->IsMainThread()) {
mainThreadId = Some(info->ThreadId());
}
registeredThread->RacyRegisteredThread().ReinitializeOnResume();
if (registeredThread->GetJSContext()) {
profiledThreadData->NotifyReceivedJSContext(0);
@ -4034,7 +4038,14 @@ static void locked_profiler_start(PSLockRef aLock, PowerOfTwo32 aCapacity,
#if defined(MOZ_REPLACE_MALLOC) && defined(MOZ_PROFILER_MEMORY)
if (ActivePS::FeatureNativeAllocations(aLock)) {
mozilla::profiler::enable_native_allocations();
if (mainThreadId.isSome()) {
mozilla::profiler::enable_native_allocations(mainThreadId.value());
} else {
NS_WARNING(
"The nativeallocations feature is turned on, but the main thread is "
"not being profiled. The allocations are only stored on the main "
"thread.");
}
}
#endif
@ -4643,14 +4654,16 @@ bool profiler_is_locked_on_current_thread() {
return gPSMutex.IsLockedOnCurrentThread();
}
bool profiler_add_native_allocation_marker(const int64_t aSize) {
bool profiler_add_native_allocation_marker(int aMainThreadId, int64_t aSize) {
if (!profiler_can_accept_markers()) {
return;
return false;
}
AUTO_PROFILER_STATS(add_marker_with_NativeAllocationMarkerPayload);
profiler_add_marker("Native allocation", JS::ProfilingCategoryPair::OTHER,
NativeAllocationMarkerPayload(TimeStamp::Now(), aSize,
profiler_add_marker_for_thread(
aMainThreadId, JS::ProfilingCategoryPair::OTHER, "Native allocation",
MakeUnique<NativeAllocationMarkerPayload>(TimeStamp::Now(), aSize,
profiler_get_backtrace()));
return true;
}
void profiler_add_network_marker(

View File

@ -778,7 +778,7 @@ void profiler_add_js_allocation_marker(JS::RecordAllocationInfo&& info);
// Returns true or or false depending on whether the marker was actually added
// or not.
bool profiler_add_native_allocation_marker(int64_t aSize);
bool profiler_add_native_allocation_marker(int aMainThreadId, int64_t aSize);
// Returns true if the profiler lock is currently held *on the current thread*.
// This may be used by re-entrant code that may call profiler functions while