fextl: Add pmr default resource

This commit is contained in:
Ryan Houdek committed 2023-03-30 08:49:26 -07:00
1 parent 7150c532f3
commit 79f7baffe3
3 files changed
+226 -1

No files matched your search

+9 -1
View File
@@ -2,6 +2,7 @@
#include <FEXCore/Utils/Allocator.h>
#include <FEXCore/Utils/CompilerDefs.h>
#include <FEXCore/Utils/LogManager.h>
#include <FEXCore/fextl/memory_resource.h>
#include <FEXHeaderUtils/Syscalls.h>
#include <FEXHeaderUtils/TypeDefines.h>
@@ -32,6 +33,13 @@ extern "C" {
#endif
}
namespace fextl::pmr {
static fextl::pmr::default_resource FEXDefaultResource;
std::pmr::memory_resource* get_default_resource() {
return &FEXDefaultResource;
}
}
namespace FEXCore::Allocator {
MMAP_Hook mmap {::mmap};
MUNMAP_Hook munmap {::munmap};
@@ -63,7 +71,7 @@ namespace FEXCore::Allocator {
using GLIBC_REALLOC_Hook = void*(*)(void*, size_t, const void *caller);
using GLIBC_FREE_Hook = void(*)(void*, const void *caller);
std::unique_ptr<Alloc::HostAllocator> Alloc64{};
fextl::unique_ptr<Alloc::HostAllocator> Alloc64{};
void *FEX_mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) {
void *Result = Alloc64->Mmap(addr, length, prot, flags, fd, offset);
+140
View File
@@ -0,0 +1,140 @@
#include <FEXCore/Utils/Allocator.h>
#include <FEXCore/Utils/CompilerDefs.h>
#include <FEXCore/Utils/LogManager.h>
#include <fmt/format.h>
#include <cstddef>
#include <cstdint>
#include <fstream>
#include <sstream>
#include <unistd.h>
extern "C" {
#ifdef GLIBC_ALLOCATOR_FAULT
// glibc allocator faulting. If anything uses a glibc allocator symbol then cause a fault.
// Ensures that FEX only ever allocates memory through its allocator routines.
// If thunks are enabled then this should crash immediately. Thunks should still go through glibc allocator.
// These need to be symbol definitions /only/, don't let their visibility to the rest of the source happen.
#define GLIBC_ALIAS_FUNCTION(func) __attribute__((alias(#func), visibility("default")))
extern void *__libc_calloc(size_t, size_t);
void *calloc(size_t, size_t) GLIBC_ALIAS_FUNCTION(fault_calloc);
extern void __libc_free(void*);
void free(void*) GLIBC_ALIAS_FUNCTION(fault_free);
extern void *__libc_malloc(size_t);
void *malloc(size_t) GLIBC_ALIAS_FUNCTION(fault_malloc);
extern void *__libc_memalign(size_t, size_t);
void *memalign(size_t, size_t) GLIBC_ALIAS_FUNCTION(fault_memalign);
extern void *__libc_realloc(void*, size_t);
void *realloc(void*, size_t) GLIBC_ALIAS_FUNCTION(fault_realloc);
extern void *__libc_valloc(size_t);
void *valloc(size_t) GLIBC_ALIAS_FUNCTION(fault_valloc);
extern int __posix_memalign(void **, size_t, size_t);
int posix_memalign(void **, size_t, size_t) GLIBC_ALIAS_FUNCTION(fault_posix_memalign);
extern size_t __malloc_usable_size(void*);
size_t malloc_usable_size(void*) GLIBC_ALIAS_FUNCTION(fault_malloc_usable_size);
// Reuse __libc_memalign
void *aligned_alloc(size_t, size_t) GLIBC_ALIAS_FUNCTION(fault_aligned_alloc);
}
namespace FEXCore::Allocator {
static bool Evaluate{};
thread_local uint64_t SkipEvalForThread{};
CALLOC_Hook calloc_ptr = __libc_calloc;
FREE_Hook free_ptr = __libc_free;
MALLOC_Hook malloc_ptr = __libc_malloc;
MEMALIGN_Hook memalign_ptr = __libc_memalign;
REALLOC_Hook realloc_ptr = __libc_realloc;
VALLOC_Hook valloc_ptr = __libc_valloc;
POSIX_MEMALIGN_Hook posix_memalign_ptr = ::posix_memalign;
MALLOC_USABLE_SIZE_Hook malloc_usable_size_ptr = ::malloc_usable_size;
ALIGNED_ALLOC_Hook aligned_alloc_ptr = __libc_memalign;
YesIKnowImNotSupposedToUseTheGlibcAllocator::YesIKnowImNotSupposedToUseTheGlibcAllocator() {
++SkipEvalForThread;
}
YesIKnowImNotSupposedToUseTheGlibcAllocator::~YesIKnowImNotSupposedToUseTheGlibcAllocator() {
--SkipEvalForThread;
}
FEX_DEFAULT_VISIBILITY void YesIKnowImNotSupposedToUseTheGlibcAllocator::HardDisable() {
// Just set it to half of its maximum value so it never wraps back around.
SkipEvalForThread = std::numeric_limits<decltype(SkipEvalForThread)>::max() / 2;
}
void SetupFaultEvaluate() {
Evaluate = true;
}
void ClearFaultEvaluate() {
Evaluate = false;
}
void EvaluateReturnAddress(void* Return) {
if (!Evaluate) {
return;
}
if (SkipEvalForThread) {
return;
}
// We don't know where we are when allocating. Make sure to be safe and generate the string on the stack.
char Tmp[512];
auto Res = fmt::format_to_n(Tmp, 512, "Allocation from 0x{:x}\n", reinterpret_cast<uint64_t>(Return));
Tmp[Res.size] = 0;
write(STDERR_FILENO, Tmp, Res.size);
LogMan::Msg::AFmt(Tmp);
FEX_UNREACHABLE;
}
}
extern "C" {
void *fault_calloc(size_t n, size_t size) {
FEXCore::Allocator::EvaluateReturnAddress(__builtin_extract_return_addr (__builtin_return_address (0)));
return FEXCore::Allocator::calloc_ptr(n, size);
}
void fault_free(void* ptr) {
FEXCore::Allocator::EvaluateReturnAddress(__builtin_extract_return_addr (__builtin_return_address (0)));
FEXCore::Allocator::free_ptr(ptr);
}
void *fault_malloc(size_t size) {
FEXCore::Allocator::EvaluateReturnAddress(__builtin_extract_return_addr (__builtin_return_address (0)));
return FEXCore::Allocator::malloc_ptr(size);
}
void *fault_memalign(size_t align, size_t s) {
FEXCore::Allocator::EvaluateReturnAddress(__builtin_extract_return_addr (__builtin_return_address (0)));
return FEXCore::Allocator::memalign_ptr(align, s);
}
void *fault_realloc(void* ptr, size_t size) {
FEXCore::Allocator::EvaluateReturnAddress(__builtin_extract_return_addr (__builtin_return_address (0)));
return FEXCore::Allocator::realloc_ptr(ptr, size);
}
void *fault_valloc(size_t size) {
FEXCore::Allocator::EvaluateReturnAddress(__builtin_extract_return_addr (__builtin_return_address (0)));
return FEXCore::Allocator::valloc_ptr(size);
}
int fault_posix_memalign(void ** r, size_t a, size_t s) {
FEXCore::Allocator::EvaluateReturnAddress(__builtin_extract_return_addr (__builtin_return_address (0)));
return FEXCore::Allocator::posix_memalign_ptr(r, a, s);
}
size_t fault_malloc_usable_size(void *ptr) {
FEXCore::Allocator::EvaluateReturnAddress(__builtin_extract_return_addr (__builtin_return_address (0)));
return FEXCore::Allocator::malloc_usable_size_ptr(ptr);
}
void *fault_aligned_alloc(size_t a, size_t s) {
FEXCore::Allocator::EvaluateReturnAddress(__builtin_extract_return_addr (__builtin_return_address (0)));
return FEXCore::Allocator::aligned_alloc_ptr(a, s);
}
#endif
}
@@ -0,0 +1,77 @@
#pragma once
#include <FEXCore/Utils/MathUtils.h>
#include <FEXCore/Utils/LogManager.h>
#include <FEXCore/fextl/allocator.h>
#include <memory_resource>
#include <fmt/format.h>
namespace fextl {
namespace pmr {
class default_resource : public std::pmr::memory_resource {
private:
void* do_allocate( std::size_t bytes, std::size_t alignment ) override {
return FEXCore::Allocator::memalign(alignment, bytes);
}
void do_deallocate( void* p, std::size_t bytes, std::size_t alignment ) override {
return FEXCore::Allocator::free(p);
}
bool do_is_equal( const std::pmr::memory_resource& other ) const noexcept override {
return this == &other;
}
};
FEX_DEFAULT_VISIBILITY std::pmr::memory_resource* get_default_resource();
/**
* @brief This is similar to the std::pmr::monotonic_buffer_resource.
*
* The difference is that class doesn't have ownership of the backing memory and
* it also doesn't have any growth factor.
*
* If the amount of memory allocated is overrun then this will overwrite memory unless assertions are enabled.
*
* Ensure that you know how much memory you're going to use before using this class.
*/
class fixed_size_monotonic_buffer_resource final : public std::pmr::memory_resource {
public:
fixed_size_monotonic_buffer_resource(void* Base, size_t Size)
: Ptr {reinterpret_cast<uint64_t>(Base)}
#if defined(ASSERTIONS_ENABLED) && ASSERTIONS_ENABLED
, PtrEnd {reinterpret_cast<uint64_t>(Base) + Size}
, Size {Size}
#endif
{}
void* do_allocate( std::size_t bytes, std::size_t alignment ) override {
uint64_t NewPtr = FEXCore::AlignUp((uint64_t)Ptr, alignment);
Ptr = NewPtr + bytes;
#if defined(ASSERTIONS_ENABLED) && ASSERTIONS_ENABLED
if (Ptr >= PtrEnd) {
LogMan::Msg::AFmt("Fail: Only allocated: {} ({} this time) bytes. Tried allocating at ptr offset: {}.\n",
Size,
bytes,
(uint64_t)(Ptr - (PtrEnd - Size)));
FEX_TRAP_EXECUTION;
}
#endif
return reinterpret_cast<void*>(NewPtr);
}
void do_deallocate( void* p, std::size_t bytes, std::size_t alignment ) override {
// noop
}
bool do_is_equal( const std::pmr::memory_resource& other ) const noexcept override {
return this == &other;
}
private:
uint64_t Ptr;
#if defined(ASSERTIONS_ENABLED) && ASSERTIONS_ENABLED
uint64_t PtrEnd;
size_t Size;
#endif
};
}
}