Allocate executable memory backed by named mmaps on Linux.
Executable heap memory can confuse profiling tools. Use memfd_create
on Linux, if possible, to create a named anonymous memory region.
Only enabled if LINUX_ENABLE_NAMED_MMAP is defined.
Bug b/73721724
Change-Id: I420711e4f64725ae834ab54264038683e4c445fe
Reviewed-on: https://swiftshader-review.googlesource.com/17208
Reviewed-by: Alexis Hétu <sugoi@google.com>
Reviewed-by: Ian Rogers <irogers@google.com>
Tested-by: Nicolas Capens <nicolascapens@google.com>
diff --git a/src/Common/Memory.cpp b/src/Common/Memory.cpp
index f946d2f..938223e 100644
--- a/src/Common/Memory.cpp
+++ b/src/Common/Memory.cpp
@@ -24,7 +24,9 @@
#include <windows.h>
#include <intrin.h>
#else
+ #include <errno.h>
#include <sys/mman.h>
+ #include <stdlib.h>
#include <unistd.h>
#endif
@@ -39,6 +41,93 @@
namespace sw
{
+namespace
+{
+struct Allocation
+{
+// size_t bytes;
+ unsigned char *block;
+};
+
+void *allocateRaw(size_t bytes, size_t alignment)
+{
+ ASSERT((alignment & (alignment - 1)) == 0); // Power of 2 alignment.
+
+ #if defined(LINUX_ENABLE_NAMED_MMAP)
+ void *allocation;
+ int result = posix_memalign(&allocation, alignment, bytes);
+ if(result != 0)
+ {
+ errno = result;
+ allocation = nullptr;
+ }
+ return allocation;
+ #else
+ unsigned char *block = new unsigned char[bytes + sizeof(Allocation) + alignment];
+ unsigned char *aligned = nullptr;
+
+ if(block)
+ {
+ aligned = (unsigned char*)((uintptr_t)(block + sizeof(Allocation) + alignment - 1) & -(intptr_t)alignment);
+ Allocation *allocation = (Allocation*)(aligned - sizeof(Allocation));
+
+ // allocation->bytes = bytes;
+ allocation->block = block;
+ }
+
+ return aligned;
+ #endif
+}
+
+#if defined(LINUX_ENABLE_NAMED_MMAP)
+// Create a file descriptor for anonymous memory with the given
+// name. Returns -1 on failure.
+// TODO: remove once libc wrapper exists.
+int memfd_create(const char* name, unsigned int flags)
+{
+ #if __aarch64__
+ #define __NR_memfd_create 279
+ #elif __arm__
+ #define __NR_memfd_create 279
+ #elif __powerpc64__
+ #define __NR_memfd_create 360
+ #elif __i386__
+ #define __NR_memfd_create 356
+ #elif __x86_64__
+ #define __NR_memfd_create 319
+ #endif /* __NR_memfd_create__ */
+ #ifdef __NR_memfd_create
+ // In the event of no system call this returns -1 with errno set
+ // as ENOSYS.
+ return syscall(__NR_memfd_create, name, flags);
+ #else
+ return -1;
+ #endif
+}
+
+// Returns a file descriptor for use with an anonymous mmap, if
+// memfd_create fails, -1 is returned. Note, the mappings should be
+// MAP_PRIVATE so that underlying pages aren't shared.
+int anonymousFd()
+{
+ static int fd = memfd_create("SwiftShader JIT", 0);
+ return fd;
+}
+
+// Ensure there is enough space in the "anonymous" fd for length.
+void ensureAnonFileSize(int anonFd, size_t length)
+{
+ static size_t fileSize = 0;
+ if(length > fileSize)
+ {
+ ftruncate(anonFd, length);
+ fileSize = length;
+ }
+}
+#endif // defined(LINUX_ENABLE_NAMED_MMAP)
+
+} // anonymous namespace
+
size_t memoryPageSize()
{
static int pageSize = 0;
@@ -57,29 +146,6 @@
return pageSize;
}
-struct Allocation
-{
-// size_t bytes;
- unsigned char *block;
-};
-
-inline void *allocateRaw(size_t bytes, size_t alignment)
-{
- unsigned char *block = new unsigned char[bytes + sizeof(Allocation) + alignment];
- unsigned char *aligned = nullptr;
-
- if(block)
- {
- aligned = (unsigned char*)((uintptr_t)(block + sizeof(Allocation) + alignment - 1) & -(intptr_t)alignment);
- Allocation *allocation = (Allocation*)(aligned - sizeof(Allocation));
-
- // allocation->bytes = bytes;
- allocation->block = block;
- }
-
- return aligned;
-}
-
void *allocate(size_t bytes, size_t alignment)
{
void *memory = allocateRaw(bytes, alignment);
@@ -94,20 +160,50 @@
void deallocate(void *memory)
{
- if(memory)
- {
- unsigned char *aligned = (unsigned char*)memory;
- Allocation *allocation = (Allocation*)(aligned - sizeof(Allocation));
+ #if defined(LINUX_ENABLE_NAMED_MMAP)
+ free(memory);
+ #else
+ if(memory)
+ {
+ unsigned char *aligned = (unsigned char*)memory;
+ Allocation *allocation = (Allocation*)(aligned - sizeof(Allocation));
- delete[] allocation->block;
- }
+ delete[] allocation->block;
+ }
+ #endif
}
void *allocateExecutable(size_t bytes)
{
size_t pageSize = memoryPageSize();
+ size_t length = (bytes + pageSize - 1) & ~(pageSize - 1);
+ void *mapping;
- return allocate((bytes + pageSize - 1) & ~(pageSize - 1), pageSize);
+ #if defined(LINUX_ENABLE_NAMED_MMAP)
+ // Try to name the memory region for the executable code,
+ // to aid profilers.
+ int anonFd = anonymousFd();
+ if(anonFd == -1)
+ {
+ mapping = mmap(nullptr, length, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ }
+ else
+ {
+ ensureAnonFileSize(anonFd, length);
+ mapping = mmap(nullptr, length, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE, anonFd, 0);
+ }
+
+ if(mapping == MAP_FAILED)
+ {
+ mapping = nullptr;
+ }
+ #else
+ mapping = allocate(length, pageSize);
+ #endif
+
+ return mapping;
}
void markExecutable(void *memory, size_t bytes)
@@ -125,11 +221,15 @@
#if defined(_WIN32)
unsigned long oldProtection;
VirtualProtect(memory, bytes, PAGE_READWRITE, &oldProtection);
+ deallocate(memory);
+ #elif defined(LINUX_ENABLE_NAMED_MMAP)
+ size_t pageSize = memoryPageSize();
+ size_t length = (bytes + pageSize - 1) & ~(pageSize - 1);
+ munmap(memory, length);
#else
mprotect(memory, bytes, PROT_READ | PROT_WRITE);
+ deallocate(memory);
#endif
-
- deallocate(memory);
}
void clear(uint16_t *memory, uint16_t element, size_t count)