Skip to content

Commit

Permalink
Bug 1854550 - pt 9. Move PHC interface from the bridge to PHC.h r=gla…
Browse files Browse the repository at this point in the history
…ndium

Building PHC in a unified build now also works.

Differential Revision: https://phabricator.services.mozilla.com/D191133
  • Loading branch information
PaulBone committed Oct 19, 2023
1 parent d2bdb85 commit d9dfdcc
Show file tree
Hide file tree
Showing 13 changed files with 166 additions and 231 deletions.
137 changes: 65 additions & 72 deletions memory/build/PHC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,6 @@
#endif

#include "mozjemalloc.h"
#include "replace_malloc.h"

#include "mozjemalloc.h"
#include "FdPrintf.h"
Expand Down Expand Up @@ -1687,90 +1686,84 @@ void* MozJemallocPHC::moz_arena_memalign(arena_id_t aArenaId, size_t aAlignment,
return PageMemalign(Some(aArenaId), aAlignment, aReqSize);
}

class PHCBridge : public ReplaceMallocBridge {
virtual bool IsPHCAllocation(const void* aPtr, phc::AddrInfo* aOut) override {
PtrKind pk = gConst->PtrKind(aPtr);
if (pk.IsNothing()) {
return false;
}

bool isGuardPage = false;
if (pk.IsGuardPage()) {
if ((uintptr_t(aPtr) % kPageSize) < (kPageSize / 2)) {
// The address is in the lower half of a guard page, so it's probably an
// overflow. But first check that it is not on the very first guard
// page, in which case it cannot be an overflow, and we ignore it.
if (gConst->IsInFirstGuardPage(aPtr)) {
return false;
}
namespace mozilla::phc {

// Get the allocation page preceding this guard page.
pk = gConst->PtrKind(static_cast<const uint8_t*>(aPtr) - kPageSize);
bool IsPHCAllocation(const void* aPtr, AddrInfo* aOut) {
PtrKind pk = gConst->PtrKind(aPtr);
if (pk.IsNothing()) {
return false;
}

} else {
// The address is in the upper half of a guard page, so it's probably an
// underflow. Get the allocation page following this guard page.
pk = gConst->PtrKind(static_cast<const uint8_t*>(aPtr) + kPageSize);
bool isGuardPage = false;
if (pk.IsGuardPage()) {
if ((uintptr_t(aPtr) % kPageSize) < (kPageSize / 2)) {
// The address is in the lower half of a guard page, so it's probably an
// overflow. But first check that it is not on the very first guard
// page, in which case it cannot be an overflow, and we ignore it.
if (gConst->IsInFirstGuardPage(aPtr)) {
return false;
}

// Make a note of the fact that we hit a guard page.
isGuardPage = true;
}
// Get the allocation page preceding this guard page.
pk = gConst->PtrKind(static_cast<const uint8_t*>(aPtr) - kPageSize);

// At this point we know we have an allocation page.
uintptr_t index = pk.AllocPageIndex();

if (aOut) {
if (GMut::sMutex.TryLock()) {
gMut->FillAddrInfo(index, aPtr, isGuardPage, *aOut);
LOG("IsPHCAllocation: %zu, %p, %zu, %zu, %zu\n", size_t(aOut->mKind),
aOut->mBaseAddr, aOut->mUsableSize,
aOut->mAllocStack.isSome() ? aOut->mAllocStack->mLength : 0,
aOut->mFreeStack.isSome() ? aOut->mFreeStack->mLength : 0);
GMut::sMutex.Unlock();
} else {
LOG("IsPHCAllocation: PHC is locked\n");
aOut->mPhcWasLocked = true;
}
} else {
// The address is in the upper half of a guard page, so it's probably an
// underflow. Get the allocation page following this guard page.
pk = gConst->PtrKind(static_cast<const uint8_t*>(aPtr) + kPageSize);
}
return true;
}

virtual void DisablePHCOnCurrentThread() override {
GTls::DisableOnCurrentThread();
LOG("DisablePHCOnCurrentThread: %zu\n", 0ul);
}

virtual void ReenablePHCOnCurrentThread() override {
GTls::EnableOnCurrentThread();
LOG("ReenablePHCOnCurrentThread: %zu\n", 0ul);
// Make a note of the fact that we hit a guard page.
isGuardPage = true;
}

virtual bool IsPHCEnabledOnCurrentThread() override {
bool enabled = !GTls::IsDisabledOnCurrentThread();
LOG("IsPHCEnabledOnCurrentThread: %zu\n", size_t(enabled));
return enabled;
}
// At this point we know we have an allocation page.
uintptr_t index = pk.AllocPageIndex();

virtual void PHCMemoryUsage(
mozilla::phc::MemoryUsage& aMemoryUsage) override {
aMemoryUsage.mMetadataBytes = metadata_size();
if (gMut) {
MutexAutoLock lock(GMut::sMutex);
aMemoryUsage.mFragmentationBytes = gMut->FragmentationBytes();
if (aOut) {
if (GMut::sMutex.TryLock()) {
gMut->FillAddrInfo(index, aPtr, isGuardPage, *aOut);
LOG("IsPHCAllocation: %zu, %p, %zu, %zu, %zu\n", size_t(aOut->mKind),
aOut->mBaseAddr, aOut->mUsableSize,
aOut->mAllocStack.isSome() ? aOut->mAllocStack->mLength : 0,
aOut->mFreeStack.isSome() ? aOut->mFreeStack->mLength : 0);
GMut::sMutex.Unlock();
} else {
aMemoryUsage.mFragmentationBytes = 0;
LOG("IsPHCAllocation: PHC is locked\n");
aOut->mPhcWasLocked = true;
}
}
return true;
}

// Enable or Disable PHC at runtime. If PHC is disabled it will still trap
// bad uses of previous allocations, but won't track any new allocations.
virtual void SetPHCState(mozilla::phc::PHCState aState) override {
gMut->SetState(aState);
}
};
void DisablePHCOnCurrentThread() {
GTls::DisableOnCurrentThread();
LOG("DisablePHCOnCurrentThread: %zu\n", 0ul);
}

void ReenablePHCOnCurrentThread() {
GTls::EnableOnCurrentThread();
LOG("ReenablePHCOnCurrentThread: %zu\n", 0ul);
}

bool IsPHCEnabledOnCurrentThread() {
bool enabled = !GTls::IsDisabledOnCurrentThread();
LOG("IsPHCEnabledOnCurrentThread: %zu\n", size_t(enabled));
return enabled;
}

ReplaceMallocBridge* GetPHCBridge() {
static PHCBridge bridge;
return &bridge;
void PHCMemoryUsage(MemoryUsage& aMemoryUsage) {
aMemoryUsage.mMetadataBytes = metadata_size();
if (gMut) {
MutexAutoLock lock(GMut::sMutex);
aMemoryUsage.mFragmentationBytes = gMut->FragmentationBytes();
} else {
aMemoryUsage.mFragmentationBytes = 0;
}
}

// Enable or Disable PHC at runtime. If PHC is disabled it will still trap
// bad uses of previous allocations, but won't track any new allocations.
void SetPHCState(PHCState aState) { gMut->SetState(aState); }

} // namespace mozilla::phc
49 changes: 45 additions & 4 deletions memory/build/PHC.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
#include <stdint.h>
#include <stdlib.h>

#include "mozmemory_wrap.h"

namespace mozilla {
namespace phc {

Expand Down Expand Up @@ -96,11 +98,50 @@ class AddrInfo {

// Global instance that is retrieved by the process generating the crash report
extern AddrInfo gAddrInfo;
} // namespace phc
} // namespace mozilla

struct ReplaceMallocBridge;
// If this is a PHC-handled address, return true, and if an AddrInfo is
// provided, fill in all of its fields. Otherwise, return false and leave
// AddrInfo unchanged.
MOZ_JEMALLOC_API bool IsPHCAllocation(const void*, AddrInfo*);

// Disable PHC allocations on the current thread. Only useful for tests. Note
// that PHC deallocations will still occur as needed.
MOZ_JEMALLOC_API void DisablePHCOnCurrentThread();

// Re-enable PHC allocations on the current thread. Only useful for tests.
MOZ_JEMALLOC_API void ReenablePHCOnCurrentThread();

// Test whether PHC allocations are enabled on the current thread. Only
// useful for tests.
MOZ_JEMALLOC_API bool IsPHCEnabledOnCurrentThread();

// PHC has three different states:
// * Not compiled in
// * OnlyFree - The memory allocator is hooked but new allocations
// requests will be forwarded to mozjemalloc, free() will
// correctly free any PHC allocations and realloc() will
// "move" PHC allocations to mozjemalloc allocations.
// * Enabled - Full use.
enum PHCState {
OnlyFree,
Enabled,
};

MOZ_JEMALLOC_API void SetPHCState(PHCState aState);

struct MemoryUsage {
// The amount of memory used for PHC metadata, eg information about each
// allocation including stacks.
size_t mMetadataBytes = 0;

ReplaceMallocBridge* GetPHCBridge();
// The amount of memory lost due to rounding allocation sizes up to the
// nearest page. AKA internal fragmentation.
size_t mFragmentationBytes = 0;
};

MOZ_JEMALLOC_API void PHCMemoryUsage(MemoryUsage& aMemoryUsage);

} // namespace phc
} // namespace mozilla

#endif /* PHC_h */
2 changes: 1 addition & 1 deletion memory/build/moz.build
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ if CONFIG["MOZ_REPLACE_MALLOC"]:

if CONFIG["MOZ_PHC"]:
DEFINES["MOZ_PHC"] = True
SOURCES += [
UNIFIED_SOURCES += [
"FdPrintf.cpp",
"PHC.cpp",
]
Expand Down
8 changes: 0 additions & 8 deletions memory/build/mozjemalloc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5182,14 +5182,6 @@ static void init() {
}
gOriginalMallocTable = tempTable;
gMallocTablePtr = &gOriginalMallocTable;

# ifdef MOZ_PHC
// For now PHC still uses the bridge, so if no other allocator registered a
// bridge then register PHC's now.
if (!gReplaceMallocBridge) {
gReplaceMallocBridge = GetPHCBridge();
}
# endif
}

// WARNING WARNING WARNING: this function should be used with extreme care. It
Expand Down
90 changes: 0 additions & 90 deletions memory/build/replace_malloc_bridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -117,29 +117,8 @@ struct DMDFuncs;

namespace phc {

// PHC has three different states:
// * Not compiled in
// * OnlyFree - The memory allocator is hooked but new allocations
// requests will be forwarded to mozjemalloc, free() will
// correctly free any PHC allocations and realloc() will
// "move" PHC allocations to mozjemalloc allocations.
// * Enabled - Full use.
enum PHCState {
OnlyFree,
Enabled,
};

class AddrInfo;

struct MemoryUsage {
// The amount of memory used for PHC metadata, eg information about each
// allocation including stacks.
size_t mMetadataBytes = 0;

// The amount of memory lost due to rounding allocation sizes up to the
// nearest page. AKA internal fragmentation.
size_t mFragmentationBytes = 0;
};
} // namespace phc

// Callbacks to register debug file handles for Poison IO interpose.
Expand Down Expand Up @@ -182,37 +161,6 @@ struct ReplaceMallocBridge {
return nullptr;
}

// If this is a PHC-handled address, return true, and if an AddrInfo is
// provided, fill in all of its fields. Otherwise, return false and leave
// AddrInfo unchanged.
// This method was added in version 4 of the bridge.
virtual bool IsPHCAllocation(const void*, mozilla::phc::AddrInfo*) {
return false;
}

// Disable PHC allocations on the current thread. Only useful for tests. Note
// that PHC deallocations will still occur as needed.
// This method was added in version 4 of the bridge.
virtual void DisablePHCOnCurrentThread() {}

// Re-enable PHC allocations on the current thread. Only useful for tests.
// This method was added in version 4 of the bridge.
virtual void ReenablePHCOnCurrentThread() {}

// Test whether PHC allocations are enabled on the current thread. Only
// useful for tests.
// This method was added in version 4 of the bridge.
virtual bool IsPHCEnabledOnCurrentThread() { return false; }

// Return PHC memory usage information by filling in the supplied structure.
// This method was added in version 5 of the bridge.
virtual void PHCMemoryUsage(mozilla::phc::MemoryUsage& aMemoryUsage) {}

// Set PHC's state. See the comments above on `PHCState` for the meaning of
// each state.
// This method was added in version 6 of the bridge.
virtual void SetPHCState(mozilla::phc::PHCState aState) {}

# ifndef REPLACE_MALLOC_IMPL
// Returns the replace-malloc bridge if its version is at least the
// requested one.
Expand Down Expand Up @@ -256,44 +204,6 @@ struct ReplaceMalloc {
return singleton ? singleton->RegisterHook(aName, aTable, aHookTable)
: nullptr;
}

static bool IsPHCAllocation(const void* aPtr, mozilla::phc::AddrInfo* aOut) {
auto singleton = ReplaceMallocBridge::Get(/* minimumVersion */ 4);
return singleton ? singleton->IsPHCAllocation(aPtr, aOut) : false;
}

static void DisablePHCOnCurrentThread() {
auto singleton = ReplaceMallocBridge::Get(/* minimumVersion */ 4);
if (singleton) {
singleton->DisablePHCOnCurrentThread();
}
}

static void ReenablePHCOnCurrentThread() {
auto singleton = ReplaceMallocBridge::Get(/* minimumVersion */ 4);
if (singleton) {
singleton->ReenablePHCOnCurrentThread();
}
}

static bool IsPHCEnabledOnCurrentThread() {
auto singleton = ReplaceMallocBridge::Get(/* minimumVersion */ 4);
return singleton ? singleton->IsPHCEnabledOnCurrentThread() : false;
}

static void PHCMemoryUsage(mozilla::phc::MemoryUsage& aMemoryUsage) {
auto singleton = ReplaceMallocBridge::Get(/* minimumVersion */ 5);
if (singleton) {
singleton->PHCMemoryUsage(aMemoryUsage);
}
}

static void SetPHCState(mozilla::phc::PHCState aPHCState) {
auto singleton = ReplaceMallocBridge::Get(/* minimumVersion */ 6);
if (singleton) {
singleton->SetPHCState(aPHCState);
}
}
};
# endif

Expand Down
Loading

0 comments on commit d9dfdcc

Please sign in to comment.