mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-05-28 00:02:28 +00:00
Make realloc() go 100x faster on Linux/NetBSD
Cosmopolitan now supports mremap(), which is only supported on Linux and NetBSD. First, it allows memory mappings to be relocated without copying them; this can dramatically speed up data structures like std::vector if the array size grows larger than 256kb. The mremap() system call is also 10x faster than munmap() when shrinking large memory mappings. There's now two functions, getpagesize() and getgransize() which help to write portable code that uses mmap(MAP_FIXED). Alternative sysconf() may be called with our new _SC_GRANSIZE. The madvise() system call now has a better wrapper with improved documentation.
This commit is contained in:
parent
196942084b
commit
f7780de24b
71 changed files with 1301 additions and 640 deletions
|
@ -33,63 +33,100 @@ void SetUpOnce(void) {
|
|||
|
||||
TEST(madvise, anon) {
|
||||
char *p;
|
||||
ASSERT_NE(MAP_FAILED, (p = mmap(0, __granularity(), PROT_READ | PROT_WRITE,
|
||||
ASSERT_NE(MAP_FAILED, (p = mmap(0, getpagesize(), PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)));
|
||||
ASSERT_SYS(0, 0, madvise(p, __granularity(), MADV_WILLNEED));
|
||||
ASSERT_SYS(0, 0, munmap(p, __granularity()));
|
||||
ASSERT_SYS(0, 0, madvise(p, getpagesize(), MADV_WILLNEED));
|
||||
ASSERT_SYS(0, 0, munmap(p, getpagesize()));
|
||||
}
|
||||
|
||||
TEST(madvise, file) {
|
||||
char *p;
|
||||
ASSERT_SYS(0, 3, creat("foo.dat", 0644));
|
||||
ASSERT_SYS(0, 0, ftruncate(3, __granularity()));
|
||||
ASSERT_SYS(0, 0, ftruncate(3, getpagesize()));
|
||||
ASSERT_SYS(0, 0, close(3));
|
||||
ASSERT_SYS(0, 3, open("foo.dat", O_RDWR));
|
||||
ASSERT_NE(MAP_FAILED, (p = mmap(0, __granularity(), PROT_READ | PROT_WRITE,
|
||||
ASSERT_NE(MAP_FAILED, (p = mmap(0, getpagesize(), PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE, 3, 0)));
|
||||
ASSERT_SYS(0, 0, madvise(p, __granularity(), MADV_WILLNEED));
|
||||
ASSERT_SYS(0, 0, munmap(p, __granularity()));
|
||||
ASSERT_SYS(0, 0, madvise(p, getpagesize(), MADV_WILLNEED));
|
||||
ASSERT_SYS(0, 0, munmap(p, getpagesize()));
|
||||
ASSERT_SYS(0, 0, close(3));
|
||||
}
|
||||
|
||||
TEST(madvise, short) {
|
||||
char *p;
|
||||
ASSERT_NE(MAP_FAILED, (p = mmap(0, __granularity(), PROT_READ | PROT_WRITE,
|
||||
ASSERT_NE(MAP_FAILED, (p = mmap(0, getpagesize(), PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)));
|
||||
ASSERT_SYS(0, 0, madvise(p, __granularity() - 1, MADV_WILLNEED));
|
||||
ASSERT_SYS(0, 0, munmap(p, __granularity()));
|
||||
ASSERT_SYS(0, 0, madvise(p, getpagesize() - 1, MADV_WILLNEED));
|
||||
ASSERT_SYS(0, 0, munmap(p, getpagesize()));
|
||||
}
|
||||
|
||||
TEST(madvise, zero) {
|
||||
char *p;
|
||||
ASSERT_NE(MAP_FAILED, (p = mmap(0, getpagesize(), PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)));
|
||||
ASSERT_SYS(0, 0, madvise(p, 0, MADV_WILLNEED));
|
||||
ASSERT_SYS(0, 0, madvise(0, 0, MADV_WILLNEED));
|
||||
ASSERT_SYS(EINVAL, -1, madvise(p, 0, 666));
|
||||
ASSERT_SYS(EINVAL, -1, madvise(0, 0, 666));
|
||||
ASSERT_SYS(0, 0, munmap(p, getpagesize()));
|
||||
}
|
||||
|
||||
TEST(madvise, subPages) {
|
||||
char *p;
|
||||
ASSERT_NE(MAP_FAILED, (p = mmap(0, __granularity(), PROT_READ | PROT_WRITE,
|
||||
ASSERT_NE(MAP_FAILED, (p = mmap(0, getpagesize() * 2, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)));
|
||||
ASSERT_SYS(0, 0,
|
||||
madvise(p + getpagesize(), __granularity() - getpagesize(),
|
||||
MADV_WILLNEED));
|
||||
ASSERT_SYS(0, 0, munmap(p, __granularity()));
|
||||
ASSERT_SYS(0, 0, madvise(p + getpagesize(), getpagesize(), MADV_WILLNEED));
|
||||
ASSERT_SYS(0, 0, munmap(p, getpagesize() * 2));
|
||||
}
|
||||
|
||||
TEST(madvise, madvWillNeed_unmappedRegion) {
|
||||
char *p;
|
||||
ASSERT_NE(MAP_FAILED, (p = mmap(0, getgransize() * 3, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)));
|
||||
ASSERT_SYS(0, 0, munmap(p + getgransize(), getgransize()));
|
||||
if (IsXnu())
|
||||
ASSERT_SYS(EINVAL, -1, madvise(p, getgransize() * 3, MADV_WILLNEED));
|
||||
else if (IsBsd() || IsWindows())
|
||||
ASSERT_SYS(0, 0, madvise(p, getgransize() * 3, MADV_WILLNEED));
|
||||
else
|
||||
ASSERT_SYS(ENOMEM, -1, madvise(p, getgransize() * 3, MADV_WILLNEED));
|
||||
ASSERT_SYS(0, 0, munmap(p, getgransize() * 3));
|
||||
}
|
||||
|
||||
TEST(madvise, madvFree_unmappedRegion) {
|
||||
char *p;
|
||||
ASSERT_NE(MAP_FAILED, (p = mmap(0, getgransize() * 3, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)));
|
||||
ASSERT_SYS(0, 0, munmap(p + getgransize(), getgransize()));
|
||||
if (IsXnu())
|
||||
ASSERT_SYS(EINVAL, -1, madvise(p, getgransize() * 3, MADV_FREE));
|
||||
else if (IsNetbsd() || IsOpenbsd())
|
||||
ASSERT_SYS(EFAULT, -1, madvise(p, getgransize() * 3, MADV_FREE));
|
||||
else if (IsFreebsd() || IsWindows())
|
||||
ASSERT_SYS(0, 0, madvise(p, getgransize() * 3, MADV_FREE));
|
||||
else
|
||||
ASSERT_SYS(ENOMEM, -1, madvise(p, getgransize() * 3, MADV_FREE));
|
||||
ASSERT_SYS(0, 0, munmap(p, getgransize() * 3));
|
||||
}
|
||||
|
||||
TEST(madvise, misalign) {
|
||||
char *p;
|
||||
if (!IsLinux())
|
||||
return; // most platforms don't care
|
||||
if (IsQemuUser())
|
||||
return; // qemu claims to be linux but doesn't care
|
||||
ASSERT_NE(MAP_FAILED, (p = mmap(0, __granularity(), PROT_READ | PROT_WRITE,
|
||||
ASSERT_NE(MAP_FAILED, (p = mmap(0, getpagesize(), PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)));
|
||||
ASSERT_SYS(EINVAL, -1, madvise(p + 1, __granularity() - 1, MADV_WILLNEED));
|
||||
ASSERT_SYS(0, 0, munmap(p, __granularity()));
|
||||
ASSERT_SYS(EINVAL, -1, madvise(p + 1, getpagesize(), MADV_WILLNEED));
|
||||
ASSERT_SYS(0, 0, madvise(p, 1, MADV_WILLNEED));
|
||||
ASSERT_SYS(EINVAL, -1, madvise(p, -1, MADV_WILLNEED));
|
||||
ASSERT_SYS(0, 0, munmap(p, getpagesize()));
|
||||
}
|
||||
|
||||
TEST(madvise, badAdvice) {
|
||||
char *p;
|
||||
if (IsAarch64() && IsQemuUser())
|
||||
return; // qemu doesn't validate advice
|
||||
ASSERT_NE(MAP_FAILED, (p = mmap(0, __granularity(), PROT_READ | PROT_WRITE,
|
||||
ASSERT_NE(MAP_FAILED, (p = mmap(0, getpagesize(), PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)));
|
||||
ASSERT_SYS(EINVAL, -1, madvise(p, __granularity(), 127));
|
||||
ASSERT_SYS(0, 0, munmap(p, __granularity()));
|
||||
ASSERT_SYS(EINVAL, -1, madvise(p, getpagesize(), 127));
|
||||
ASSERT_SYS(0, 0, munmap(p, getpagesize()));
|
||||
}
|
||||
|
||||
TEST(madvise, missingMemory) {
|
||||
|
@ -98,5 +135,5 @@ TEST(madvise, missingMemory) {
|
|||
if (IsQemuUser())
|
||||
return; // qemu claims to be linux but doesn't care
|
||||
ASSERT_SYS(ENOMEM, -1,
|
||||
madvise((char *)0x83483838000, __granularity(), MADV_WILLNEED));
|
||||
madvise((char *)0x83483838000, getpagesize(), MADV_WILLNEED));
|
||||
}
|
||||
|
|
|
@ -86,7 +86,7 @@ TEST(pledge, default_allowsExit) {
|
|||
int *job;
|
||||
int ws, pid;
|
||||
// create small shared memory region
|
||||
ASSERT_NE(-1, (job = mmap(0, __granularity(), PROT_READ | PROT_WRITE,
|
||||
ASSERT_NE(-1, (job = mmap(0, getpagesize(), PROT_READ | PROT_WRITE,
|
||||
MAP_SHARED | MAP_ANONYMOUS, -1, 0)));
|
||||
job[0] = 2; // create workload
|
||||
job[1] = 2;
|
||||
|
@ -100,7 +100,7 @@ TEST(pledge, default_allowsExit) {
|
|||
EXPECT_TRUE(WIFEXITED(ws));
|
||||
EXPECT_EQ(0, WEXITSTATUS(ws));
|
||||
EXPECT_EQ(4, job[0]); // check result
|
||||
EXPECT_SYS(0, 0, munmap(job, __granularity()));
|
||||
EXPECT_SYS(0, 0, munmap(job, getpagesize()));
|
||||
}
|
||||
|
||||
TEST(pledge, execpromises_notok) {
|
||||
|
@ -298,7 +298,7 @@ TEST(pledge, stdioTty_sendtoRestricted_requiresNullAddr) {
|
|||
errno = 0;
|
||||
// set lower 32-bit word of pointer to zero lool
|
||||
struct sockaddr_in *sin =
|
||||
mmap((void *)0x300000000000, __granularity(), PROT_READ | PROT_WRITE,
|
||||
mmap((void *)0x300000000000, getpagesize(), PROT_READ | PROT_WRITE,
|
||||
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
||||
sin->sin_family = AF_INET;
|
||||
ASSERT_SYS(
|
||||
|
@ -334,7 +334,7 @@ TEST(pledge, unix_forbidsInetSockets) {
|
|||
TEST(pledge, wpath_doesNotImplyRpath) {
|
||||
int ws, pid;
|
||||
bool *gotsome;
|
||||
ASSERT_NE(-1, (gotsome = _mapshared(__granularity())));
|
||||
ASSERT_NE(-1, (gotsome = _mapshared(getpagesize())));
|
||||
ASSERT_SYS(0, 0, touch("foo", 0644));
|
||||
ASSERT_NE(-1, (pid = fork()));
|
||||
if (!pid) {
|
||||
|
@ -412,13 +412,13 @@ TEST(pledge, mmap) {
|
|||
ASSERT_NE(-1, (pid = fork()));
|
||||
if (!pid) {
|
||||
ASSERT_SYS(0, 0, pledge("stdio", 0));
|
||||
ASSERT_NE(MAP_FAILED, (p = mmap(0, __granularity(), PROT_READ | PROT_WRITE,
|
||||
ASSERT_NE(MAP_FAILED, (p = mmap(0, getpagesize(), PROT_READ | PROT_WRITE,
|
||||
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)));
|
||||
ASSERT_SYS(0, 0, mprotect(p, __granularity(), PROT_READ));
|
||||
ASSERT_SYS(0, 0, mprotect(p, getpagesize(), PROT_READ));
|
||||
ASSERT_SYS(EPERM, MAP_FAILED,
|
||||
mprotect(p, __granularity(), PROT_READ | PROT_EXEC));
|
||||
mprotect(p, getpagesize(), PROT_READ | PROT_EXEC));
|
||||
ASSERT_SYS(EPERM, MAP_FAILED,
|
||||
mmap(0, __granularity(), PROT_EXEC | PROT_READ,
|
||||
mmap(0, getpagesize(), PROT_EXEC | PROT_READ,
|
||||
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0));
|
||||
_Exit(0);
|
||||
}
|
||||
|
@ -434,11 +434,11 @@ TEST(pledge, mmapProtExec) {
|
|||
ASSERT_NE(-1, (pid = fork()));
|
||||
if (!pid) {
|
||||
ASSERT_SYS(0, 0, pledge("stdio prot_exec", 0));
|
||||
ASSERT_NE(MAP_FAILED, (p = mmap(0, __granularity(), PROT_READ | PROT_WRITE,
|
||||
ASSERT_NE(MAP_FAILED, (p = mmap(0, getpagesize(), PROT_READ | PROT_WRITE,
|
||||
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)));
|
||||
ASSERT_SYS(0, 0, mprotect(p, __granularity(), PROT_READ));
|
||||
ASSERT_SYS(0, 0, mprotect(p, __granularity(), PROT_READ | PROT_EXEC));
|
||||
ASSERT_NE(MAP_FAILED, mmap(0, __granularity(), PROT_EXEC | PROT_READ,
|
||||
ASSERT_SYS(0, 0, mprotect(p, getpagesize(), PROT_READ));
|
||||
ASSERT_SYS(0, 0, mprotect(p, getpagesize(), PROT_READ | PROT_EXEC));
|
||||
ASSERT_NE(MAP_FAILED, mmap(0, getpagesize(), PROT_EXEC | PROT_READ,
|
||||
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0));
|
||||
_Exit(0);
|
||||
}
|
||||
|
|
|
@ -159,15 +159,15 @@ TEST(setrlimit, testVirtualMemoryLimit) {
|
|||
ASSERT_NE(-1, (wstatus = xspawn(0)));
|
||||
if (wstatus == -2) {
|
||||
ASSERT_EQ(0, setrlimit(RLIMIT_AS, &(struct rlimit){MEM, MEM}));
|
||||
for (i = 0; i < (MEM * 2) / __granularity(); ++i) {
|
||||
p = sys_mmap(0, __granularity(), PROT_READ | PROT_WRITE,
|
||||
for (i = 0; i < (MEM * 2) / getpagesize(); ++i) {
|
||||
p = sys_mmap(0, getpagesize(), PROT_READ | PROT_WRITE,
|
||||
MAP_ANONYMOUS | MAP_PRIVATE | MAP_POPULATE, -1, 0)
|
||||
.addr;
|
||||
if (p == MAP_FAILED) {
|
||||
ASSERT_EQ(ENOMEM, errno);
|
||||
_Exit(0);
|
||||
}
|
||||
rngset(p, __granularity(), _rand64, -1);
|
||||
rngset(p, getpagesize(), _rand64, -1);
|
||||
}
|
||||
_Exit(1);
|
||||
}
|
||||
|
@ -193,15 +193,15 @@ TEST(setrlimit, testDataMemoryLimit) {
|
|||
ASSERT_NE(-1, (wstatus = xspawn(0)));
|
||||
if (wstatus == -2) {
|
||||
ASSERT_EQ(0, setrlimit(RLIMIT_DATA, &(struct rlimit){MEM, MEM}));
|
||||
for (i = 0; i < (MEM * 2) / __granularity(); ++i) {
|
||||
p = sys_mmap(0, __granularity(), PROT_READ | PROT_WRITE,
|
||||
for (i = 0; i < (MEM * 2) / getpagesize(); ++i) {
|
||||
p = sys_mmap(0, getpagesize(), PROT_READ | PROT_WRITE,
|
||||
MAP_ANONYMOUS | MAP_PRIVATE | MAP_POPULATE, -1, 0)
|
||||
.addr;
|
||||
if (p == MAP_FAILED) {
|
||||
ASSERT_EQ(ENOMEM, errno);
|
||||
_Exit(0);
|
||||
}
|
||||
rngset(p, __granularity(), _rand64, -1);
|
||||
rngset(p, getpagesize(), _rand64, -1);
|
||||
}
|
||||
_Exit(1);
|
||||
}
|
||||
|
|
|
@ -335,7 +335,7 @@ TEST(unveil, isThreadSpecificOnLinux_isProcessWideOnOpenbsd) {
|
|||
TEST(unveil, usedTwice_forbidden_worksWithPledge) {
|
||||
int ws, pid;
|
||||
bool *gotsome;
|
||||
ASSERT_NE(-1, (gotsome = _mapshared(__granularity())));
|
||||
ASSERT_NE(-1, (gotsome = _mapshared(getpagesize())));
|
||||
ASSERT_NE(-1, (pid = fork()));
|
||||
if (!pid) {
|
||||
__pledge_mode = PLEDGE_PENALTY_KILL_PROCESS;
|
||||
|
@ -359,7 +359,7 @@ TEST(unveil, usedTwice_forbidden_worksWithPledge) {
|
|||
ASSERT_TRUE(*gotsome);
|
||||
ASSERT_TRUE(WIFSIGNALED(ws));
|
||||
ASSERT_EQ(IsOpenbsd() ? SIGABRT : SIGSYS, WTERMSIG(ws));
|
||||
EXPECT_SYS(0, 0, munmap(gotsome, __granularity()));
|
||||
EXPECT_SYS(0, 0, munmap(gotsome, getpagesize()));
|
||||
}
|
||||
|
||||
TEST(unveil, lotsOfPaths) {
|
||||
|
|
|
@ -277,7 +277,7 @@ TEST(ksnprintf, testMisalignedPointer_wontFormat) {
|
|||
TEST(ksnprintf, testUnterminatedOverrun_truncatesAtPageBoundary) {
|
||||
char *m;
|
||||
char b[32];
|
||||
int gran = __granularity();
|
||||
int gran = getgransize();
|
||||
m = memset(_mapanon(gran * 2), 1, gran);
|
||||
EXPECT_SYS(0, 0, munmap(m + gran, gran));
|
||||
EXPECT_EQ(12, ksnprintf(b, 32, "%'s", m + gran - 3));
|
||||
|
|
|
@ -47,7 +47,7 @@ TEST(lockipc, mutex) {
|
|||
int e, rc, ws, pid;
|
||||
|
||||
// create shared memory
|
||||
shm = _mapshared(__granularity());
|
||||
shm = _mapshared(getpagesize());
|
||||
|
||||
// create shared mutex
|
||||
pthread_mutexattr_t mattr;
|
||||
|
@ -86,5 +86,5 @@ TEST(lockipc, mutex) {
|
|||
|
||||
EXPECT_EQ(PROCESSES * ITERATIONS, shm->x);
|
||||
ASSERT_EQ(0, pthread_mutex_destroy(&shm->mutex));
|
||||
ASSERT_SYS(0, 0, munmap(shm, __granularity()));
|
||||
ASSERT_SYS(0, 0, munmap(shm, getpagesize()));
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/maps.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/sysv/consts/map.h"
|
||||
#include "libc/sysv/consts/prot.h"
|
||||
|
@ -38,13 +39,6 @@
|
|||
kprintf("%'20ld ns %2dx %s\n", (long)nanos, (ITERATIONS), #CODE); \
|
||||
} while (0)
|
||||
|
||||
void *randaddr(void) {
|
||||
static unsigned long lcg = 1;
|
||||
lcg *= 6364136223846793005;
|
||||
lcg += 1442695040888963407;
|
||||
return (void *)(lcg >> 48 << 28);
|
||||
}
|
||||
|
||||
void map_unmap_one_page(void) {
|
||||
void *p;
|
||||
if ((p = mmap(randaddr(), 1, PROT_READ | PROT_WRITE,
|
||||
|
|
|
@ -18,37 +18,22 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "ape/sections.internal.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/calls/ucontext.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/safemacros.internal.h"
|
||||
#include "libc/intrin/xchg.internal.h"
|
||||
#include "libc/intrin/maps.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/log/log.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/mem/gc.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/runtime/memtrack.internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/runtime/stack.h"
|
||||
#include "libc/runtime/sysconf.h"
|
||||
#include "libc/stdio/rand.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/auxv.h"
|
||||
#include "libc/sysv/consts/map.h"
|
||||
#include "libc/sysv/consts/msync.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/consts/prot.h"
|
||||
#include "libc/sysv/consts/sa.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/testlib/ezbench.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
#include "libc/x/xspawn.h"
|
||||
#include "third_party/xed/x86.h"
|
||||
|
||||
// this is also a good torture test for mmap
|
||||
//
|
||||
|
@ -61,11 +46,11 @@
|
|||
__static_yoink("zipos");
|
||||
|
||||
int pagesz;
|
||||
int granularity;
|
||||
int gransz;
|
||||
|
||||
void SetUpOnce(void) {
|
||||
pagesz = getpagesize();
|
||||
granularity = __granularity();
|
||||
gransz = getgransize();
|
||||
testlib_enable_tmp_setup_teardown();
|
||||
// ASSERT_SYS(0, 0, pledge("stdio rpath wpath cpath proc", 0));
|
||||
}
|
||||
|
@ -109,7 +94,7 @@ TEST(mmap, pageBeyondGone) {
|
|||
MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
||||
ASSERT_NE(MAP_FAILED, p);
|
||||
EXPECT_TRUE(testlib_memoryexists(p));
|
||||
EXPECT_TRUE(testlib_memoryexists(p + pagesz - 1));
|
||||
EXPECT_FALSE(testlib_memoryexists(p + 1)); // b/c kisdangerous
|
||||
EXPECT_FALSE(testlib_memoryexists(p + pagesz));
|
||||
ASSERT_EQ(0, munmap(p, 1));
|
||||
}
|
||||
|
@ -124,47 +109,76 @@ TEST(mmap, fixedTaken) {
|
|||
}
|
||||
|
||||
TEST(mmap, hint) {
|
||||
char *p, *q;
|
||||
char *p;
|
||||
|
||||
// obtain four pages
|
||||
EXPECT_NE(MAP_FAILED, (p = mmap(NULL, granularity * 4, PROT_READ,
|
||||
ASSERT_NE(MAP_FAILED, (p = mmap(randaddr(), gransz * 4, PROT_READ,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)));
|
||||
|
||||
// unmap two of those pages
|
||||
EXPECT_SYS(0, 0, munmap(p + granularity, granularity));
|
||||
EXPECT_SYS(0, 0, munmap(p + granularity * 3, granularity));
|
||||
EXPECT_SYS(0, 0, munmap(p + gransz, gransz));
|
||||
EXPECT_SYS(0, 0, munmap(p + gransz * 3, gransz));
|
||||
|
||||
// test AVAILABLE nonfixed nonzero addr is granted
|
||||
// - posix doesn't mandate this behavior (but should)
|
||||
// - freebsd always chooses for you (which has no acceptable workaround)
|
||||
// - netbsd manual claims it'll be like freebsd, but is actually like openbsd
|
||||
if (!IsFreebsd())
|
||||
EXPECT_EQ(p + granularity, mmap(p + granularity, granularity, PROT_READ,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
|
||||
ASSERT_EQ(p + gransz, mmap(p + gransz, gransz, PROT_READ,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
|
||||
|
||||
// test UNAVAILABLE nonfixed nonzero addr picks something nearby
|
||||
// - posix actually does require this, but doesn't say how close
|
||||
// - xnu / linux / openbsd always choose nearest on the right
|
||||
// - freebsd goes about 16mb to the right
|
||||
// - qemu-user is off the wall
|
||||
if (!IsQemuUser()) {
|
||||
q = mmap(p + granularity * 2, granularity, PROT_READ,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
EXPECT_LE(ABS(q - (p + granularity * 2)), 64 * 1024 * 1024);
|
||||
EXPECT_SYS(0, 0, munmap(q, granularity));
|
||||
}
|
||||
/*if (!IsQemuUser()) {
|
||||
q = mmap(p + gransz * 2, gransz, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1,
|
||||
0);
|
||||
EXPECT_LE(ABS(q - (p + gransz * 2)), 128 * 1024 * 1024);
|
||||
EXPECT_SYS(0, 0, munmap(q, gransz));
|
||||
}*/
|
||||
|
||||
// clean up
|
||||
EXPECT_SYS(0, 0, munmap(p, granularity * 4));
|
||||
EXPECT_SYS(0, 0, munmap(p, gransz * 4));
|
||||
}
|
||||
|
||||
TEST(mprotect, punchHoleAndFillHole) {
|
||||
char *p;
|
||||
int count = __maps.count;
|
||||
int gransz = getgransize();
|
||||
|
||||
// obtain memory
|
||||
ASSERT_NE(MAP_FAILED,
|
||||
(p = mmap(randaddr(), gransz * 3, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)));
|
||||
ASSERT_EQ((count += IsWindows() ? 3 : 1), __maps.count);
|
||||
|
||||
// if munmap punches a hole...
|
||||
// the number of mappings may increase
|
||||
// this is why it's possible for munmap() to ENOMEM lool
|
||||
EXPECT_SYS(0, 0, munmap(p + gransz, gransz));
|
||||
ASSERT_EQ((count += IsWindows() ? -1 : +1), __maps.count);
|
||||
|
||||
// now if we fill that hole
|
||||
// the memory manager will coalesce the mappings
|
||||
ASSERT_NE(MAP_FAILED,
|
||||
(p = mmap(p + gransz, gransz, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0)));
|
||||
ASSERT_EQ((count += IsWindows() ? +1 : -1), __maps.count);
|
||||
|
||||
// clean up
|
||||
EXPECT_SYS(0, 0, munmap(p, gransz * 3));
|
||||
}
|
||||
|
||||
TEST(mmap, smallerThanPage_mapsRemainder) {
|
||||
long pagesz = sysconf(_SC_PAGESIZE);
|
||||
char *map =
|
||||
mmap(0, 4096, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
mmap(0, 1, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
ASSERT_NE(MAP_FAILED, map);
|
||||
EXPECT_TRUE(testlib_memoryexists(map));
|
||||
EXPECT_TRUE(testlib_memoryexists(map + (pagesz - 1)));
|
||||
EXPECT_TRUE(testlib_pokememory(map + (pagesz - 1)));
|
||||
EXPECT_TRUE(!testlib_memoryexists(map + (pagesz - 1)));
|
||||
EXPECT_SYS(0, 0, munmap(map, 1));
|
||||
EXPECT_FALSE(testlib_memoryexists(map));
|
||||
EXPECT_FALSE(testlib_memoryexists(map + (pagesz - 1)));
|
||||
|
@ -178,7 +192,7 @@ TEST(mmap, testMapFile) {
|
|||
ASSERT_NE(-1, (fd = open(path, O_CREAT | O_TRUNC | O_RDWR, 0644)));
|
||||
EXPECT_EQ(5, write(fd, "hello", 5));
|
||||
EXPECT_NE(-1, fdatasync(fd));
|
||||
EXPECT_NE(MAP_FAILED, (p = mmap(NULL, 5, PROT_READ, MAP_PRIVATE, fd, 0)));
|
||||
ASSERT_NE(MAP_FAILED, (p = mmap(NULL, 5, PROT_READ, MAP_PRIVATE, fd, 0)));
|
||||
EXPECT_STREQN("hello", p, 5);
|
||||
EXPECT_NE(-1, munmap(p, 5));
|
||||
EXPECT_NE(-1, close(fd));
|
||||
|
@ -192,7 +206,7 @@ TEST(mmap, testMapFile_fdGetsClosed_makesNoDifference) {
|
|||
ASSERT_NE(-1, (fd = open(path, O_CREAT | O_TRUNC | O_RDWR, 0644)));
|
||||
EXPECT_EQ(5, write(fd, "hello", 5));
|
||||
EXPECT_NE(-1, fdatasync(fd));
|
||||
EXPECT_NE(MAP_FAILED,
|
||||
ASSERT_NE(MAP_FAILED,
|
||||
(p = mmap(NULL, 5, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)));
|
||||
EXPECT_NE(-1, close(fd));
|
||||
EXPECT_STREQN("hello", p, 5);
|
||||
|
@ -209,7 +223,7 @@ TEST(mmap, testMapFile_fdGetsClosed_makesNoDifference) {
|
|||
TEST(mmap, fileOffset) {
|
||||
int fd;
|
||||
char *map;
|
||||
int offset_align = IsWindows() ? granularity : getpagesize();
|
||||
int offset_align = IsWindows() ? gransz : getpagesize();
|
||||
ASSERT_NE(-1, (fd = open("foo", O_CREAT | O_RDWR, 0644)));
|
||||
EXPECT_NE(-1, ftruncate(fd, offset_align * 2));
|
||||
EXPECT_NE(-1, pwrite(fd, "hello", 5, offset_align * 0));
|
||||
|
@ -226,13 +240,13 @@ TEST(mmap, mapPrivate_writesDontChangeFile) {
|
|||
int fd;
|
||||
char *map, buf[6];
|
||||
ASSERT_NE(-1, (fd = open("bar", O_CREAT | O_RDWR, 0644)));
|
||||
EXPECT_NE(-1, ftruncate(fd, granularity));
|
||||
EXPECT_NE(-1, ftruncate(fd, gransz));
|
||||
EXPECT_NE(-1, pwrite(fd, "hello", 5, 0));
|
||||
ASSERT_NE(MAP_FAILED, (map = mmap(NULL, granularity, PROT_READ | PROT_WRITE,
|
||||
ASSERT_NE(MAP_FAILED, (map = mmap(NULL, gransz, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE, fd, 0)));
|
||||
memcpy(map, "there", 5);
|
||||
EXPECT_NE(-1, msync(map, granularity, MS_SYNC));
|
||||
EXPECT_NE(-1, munmap(map, granularity));
|
||||
EXPECT_NE(-1, msync(map, gransz, MS_SYNC));
|
||||
EXPECT_NE(-1, munmap(map, gransz));
|
||||
EXPECT_NE(-1, pread(fd, buf, 6, 0));
|
||||
EXPECT_EQ(0, memcmp(buf, "hello", 5), "%#.*s", 5, buf);
|
||||
EXPECT_NE(-1, close(fd));
|
||||
|
@ -245,7 +259,7 @@ TEST(mmap, ziposCannotBeShared) {
|
|||
void *p;
|
||||
ASSERT_NE(-1, (fd = open(ziposLifePath, O_RDONLY), "%s", ziposLifePath));
|
||||
EXPECT_SYS(EINVAL, MAP_FAILED,
|
||||
(p = mmap(NULL, granularity, PROT_READ, MAP_SHARED, fd, 0)));
|
||||
(p = mmap(NULL, gransz, PROT_READ, MAP_SHARED, fd, 0)));
|
||||
close(fd);
|
||||
}
|
||||
|
||||
|
@ -257,9 +271,9 @@ TEST(mmap, ziposCow) {
|
|||
void *p;
|
||||
ASSERT_NE(-1, (fd = open(ziposLifePath, O_RDONLY), "%s", ziposLifePath));
|
||||
EXPECT_NE(MAP_FAILED,
|
||||
(p = mmap(NULL, granularity, PROT_READ, MAP_PRIVATE, fd, 0)));
|
||||
(p = mmap(NULL, gransz, PROT_READ, MAP_PRIVATE, fd, 0)));
|
||||
EXPECT_STREQN("\177ELF", ((const char *)p), 4);
|
||||
EXPECT_NE(-1, munmap(p, granularity));
|
||||
EXPECT_NE(-1, munmap(p, gransz));
|
||||
EXPECT_NE(-1, close(fd));
|
||||
}
|
||||
|
||||
|
@ -270,7 +284,7 @@ TEST(mmap, ziposCowFileMapReadonlyFork) {
|
|||
int fd, ws;
|
||||
void *p;
|
||||
ASSERT_NE(-1, (fd = open(ziposLifePath, O_RDONLY), "%s", ziposLifePath));
|
||||
EXPECT_NE(MAP_FAILED, (p = mmap(NULL, 4, PROT_READ, MAP_PRIVATE, fd, 0)));
|
||||
ASSERT_NE(MAP_FAILED, (p = mmap(NULL, 4, PROT_READ, MAP_PRIVATE, fd, 0)));
|
||||
EXPECT_STREQN("ELF", ((const char *)p) + 1, 3);
|
||||
ASSERT_NE(-1, (ws = xspawn(0)));
|
||||
if (ws == -2) {
|
||||
|
@ -291,7 +305,7 @@ TEST(mmap, ziposCowFileMapFork) {
|
|||
void *p;
|
||||
char lol[4];
|
||||
ASSERT_NE(-1, (fd = open(ziposLifePath, O_RDONLY), "%s", ziposLifePath));
|
||||
EXPECT_NE(MAP_FAILED,
|
||||
ASSERT_NE(MAP_FAILED,
|
||||
(p = mmap(NULL, 6, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0)));
|
||||
memcpy(p, "parnt", 6);
|
||||
ASSERT_NE(-1, (ws = xspawn(0)));
|
||||
|
@ -321,7 +335,7 @@ TEST(mmap, cow) {
|
|||
path);
|
||||
EXPECT_EQ(5, write(fd, "hello", 5));
|
||||
EXPECT_NE(-1, fdatasync(fd));
|
||||
EXPECT_NE(MAP_FAILED, (p = mmap(NULL, 5, PROT_READ, MAP_PRIVATE, fd, 0)));
|
||||
ASSERT_NE(MAP_FAILED, (p = mmap(NULL, 5, PROT_READ, MAP_PRIVATE, fd, 0)));
|
||||
EXPECT_STREQN("hello", p, 5);
|
||||
EXPECT_NE(-1, munmap(p, 5));
|
||||
EXPECT_NE(-1, close(fd));
|
||||
|
@ -340,7 +354,7 @@ TEST(mmap, cowFileMapReadonlyFork) {
|
|||
EXPECT_EQ(6, write(fd, "hello", 6));
|
||||
EXPECT_NE(-1, close(fd));
|
||||
ASSERT_NE(-1, (fd = open(path, O_RDONLY)));
|
||||
EXPECT_NE(MAP_FAILED, (p = mmap(NULL, 6, PROT_READ, MAP_PRIVATE, fd, 0)));
|
||||
ASSERT_NE(MAP_FAILED, (p = mmap(NULL, 6, PROT_READ, MAP_PRIVATE, fd, 0)));
|
||||
EXPECT_STREQN("hello", p, 5);
|
||||
ASSERT_NE(-1, (ws = xspawn(0)));
|
||||
if (ws == -2) {
|
||||
|
@ -365,7 +379,7 @@ TEST(mmap, cowFileMapFork) {
|
|||
ASSERT_NE(-1, (fd = open(path, O_CREAT | O_TRUNC | O_RDWR, 0644)));
|
||||
EXPECT_EQ(6, write(fd, "parnt", 6));
|
||||
EXPECT_NE(-1, fdatasync(fd));
|
||||
EXPECT_NE(MAP_FAILED,
|
||||
ASSERT_NE(MAP_FAILED,
|
||||
(p = mmap(NULL, 6, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0)));
|
||||
EXPECT_STREQN("parnt", p, 5);
|
||||
ASSERT_NE(-1, (ws = xspawn(0)));
|
||||
|
@ -390,7 +404,7 @@ TEST(mmap, cowFileMapFork) {
|
|||
TEST(mmap, sharedAnonMapFork) {
|
||||
int ws;
|
||||
char *p;
|
||||
EXPECT_NE(MAP_FAILED, (p = mmap(NULL, 6, PROT_READ | PROT_WRITE,
|
||||
ASSERT_NE(MAP_FAILED, (p = mmap(NULL, 6, PROT_READ | PROT_WRITE,
|
||||
MAP_SHARED | MAP_ANONYMOUS, -1, 0)));
|
||||
strcpy(p, "parnt");
|
||||
EXPECT_STREQN("parnt", p, 5);
|
||||
|
@ -417,7 +431,7 @@ TEST(mmap, sharedFileMapFork) {
|
|||
ASSERT_NE(-1, (fd = open(path, O_CREAT | O_TRUNC | O_RDWR, 0644)));
|
||||
EXPECT_EQ(6, write(fd, "parnt", 6));
|
||||
EXPECT_NE(-1, fdatasync(fd));
|
||||
EXPECT_NE(MAP_FAILED,
|
||||
ASSERT_NE(MAP_FAILED,
|
||||
(p = mmap(NULL, 6, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)));
|
||||
EXPECT_STREQN("parnt", p, 5);
|
||||
ASSERT_NE(-1, (ws = xspawn(0)));
|
||||
|
@ -449,15 +463,15 @@ void *ptrs[N];
|
|||
|
||||
void BenchMmapPrivate(void) {
|
||||
void *p;
|
||||
p = mmap(0, granularity * 10, PROT_READ | PROT_WRITE,
|
||||
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
||||
p = mmap(0, gransz * 10, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE,
|
||||
-1, 0);
|
||||
if (p == MAP_FAILED)
|
||||
__builtin_trap();
|
||||
ptrs[count++] = p;
|
||||
}
|
||||
|
||||
void BenchUnmap(void) {
|
||||
if (munmap(ptrs[--count], granularity * 10))
|
||||
if (munmap(ptrs[--count], gransz * 10))
|
||||
__builtin_trap();
|
||||
}
|
||||
|
||||
|
|
|
@ -147,18 +147,18 @@ TEST(mprotect, testSegfault_writeToReadOnlyAnonymous) {
|
|||
}
|
||||
|
||||
TEST(mprotect, testExecOnly_canExecute) {
|
||||
char *p = _mapanon(__granularity());
|
||||
char *p = _mapanon(getpagesize());
|
||||
void (*f)(void) = (void *)p;
|
||||
memcpy(p, kRet31337, sizeof(kRet31337));
|
||||
ASSERT_SYS(0, 0, mprotect(p, __granularity(), PROT_EXEC | PROT_READ));
|
||||
ASSERT_SYS(0, 0, mprotect(p, getpagesize(), PROT_EXEC | PROT_READ));
|
||||
f();
|
||||
// On all supported platforms, PROT_EXEC implies PROT_READ. There is
|
||||
// one exception to this rule: Chromebook's fork of the Linux kernel
|
||||
// which has been reported, to have the ability to prevent a program
|
||||
// from reading its own code.
|
||||
ASSERT_SYS(0, 0, mprotect(p, __granularity(), PROT_EXEC));
|
||||
ASSERT_SYS(0, 0, mprotect(p, getpagesize(), PROT_EXEC));
|
||||
f();
|
||||
munmap(p, __granularity());
|
||||
munmap(p, getpagesize());
|
||||
}
|
||||
|
||||
TEST(mprotect, testProtNone_cantEvenRead) {
|
||||
|
@ -249,3 +249,41 @@ TEST(mprotect, image) {
|
|||
EXPECT_SYS(0, 0, mprotect(p, 16384, PROT_READ | PROT_WRITE));
|
||||
EXPECT_EQ(2, ++p[0]);
|
||||
}
|
||||
|
||||
TEST(mprotect, weirdSize) {
|
||||
char *p;
|
||||
EXPECT_NE(MAP_FAILED, (p = mmap(0, 1, PROT_READ | PROT_EXEC,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)));
|
||||
EXPECT_SYS(0, 0, mprotect(p, 2, PROT_NONE));
|
||||
EXPECT_SYS(0, 0, munmap(p, 1));
|
||||
}
|
||||
|
||||
TEST(mprotect, outerOverlap) {
|
||||
char *p;
|
||||
int gransz = getgransize();
|
||||
EXPECT_NE(MAP_FAILED, (p = mmap(0, gransz * 3, PROT_READ | PROT_EXEC,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)));
|
||||
EXPECT_TRUE(testlib_memoryexists(p + gransz * 0));
|
||||
EXPECT_TRUE(testlib_memoryexists(p + gransz * 1));
|
||||
EXPECT_TRUE(testlib_memoryexists(p + gransz * 2));
|
||||
EXPECT_SYS(0, 0, munmap(p, gransz));
|
||||
EXPECT_FALSE(testlib_memoryexists(p + gransz * 0));
|
||||
EXPECT_TRUE(testlib_memoryexists(p + gransz * 1));
|
||||
EXPECT_TRUE(testlib_memoryexists(p + gransz * 2));
|
||||
EXPECT_SYS(0, 0, munmap(p + gransz * 2, gransz));
|
||||
EXPECT_FALSE(testlib_memoryexists(p + gransz * 0));
|
||||
EXPECT_TRUE(testlib_memoryexists(p + gransz * 1));
|
||||
EXPECT_FALSE(testlib_memoryexists(p + gransz * 2));
|
||||
EXPECT_SYS(0, 0, mprotect(p, gransz * 3, PROT_NONE));
|
||||
EXPECT_FALSE(testlib_memoryexists(p + gransz * 0));
|
||||
EXPECT_FALSE(testlib_memoryexists(p + gransz * 1));
|
||||
EXPECT_FALSE(testlib_memoryexists(p + gransz * 2));
|
||||
EXPECT_SYS(0, 0, mprotect(p + gransz, gransz, PROT_READ));
|
||||
EXPECT_FALSE(testlib_memoryexists(p + gransz * 0));
|
||||
EXPECT_TRUE(testlib_memoryexists(p + gransz * 1));
|
||||
EXPECT_FALSE(testlib_memoryexists(p + gransz * 2));
|
||||
EXPECT_SYS(0, 0, munmap(p, gransz * 3));
|
||||
EXPECT_FALSE(testlib_memoryexists(p + gransz * 0));
|
||||
EXPECT_FALSE(testlib_memoryexists(p + gransz * 1));
|
||||
EXPECT_FALSE(testlib_memoryexists(p + gransz * 2));
|
||||
}
|
||||
|
|
178
test/libc/intrin/mremap_test.c
Normal file
178
test/libc/intrin/mremap_test.c
Normal file
|
@ -0,0 +1,178 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2024 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/sysv/consts/mremap.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/maps.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/map.h"
|
||||
#include "libc/sysv/consts/mremap.h"
|
||||
#include "libc/sysv/consts/prot.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
|
||||
void SetUpOnce(void) {
|
||||
if (!IsLinux() && !IsNetbsd()) {
|
||||
tinyprint(2, "warning: skipping mremap() tests on this os\n", NULL);
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(mremap, dontMove_hasRoom_itMoves) {
|
||||
if (IsNetbsd())
|
||||
return; // NetBSD requires MREMAP_MAYMOVE
|
||||
char *p;
|
||||
int pagesz = getpagesize();
|
||||
ASSERT_NE(MAP_FAILED, (p = mmap(randaddr(), pagesz, PROT_READ | PROT_EXEC,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)));
|
||||
EXPECT_TRUE(testlib_memoryexists(p));
|
||||
EXPECT_FALSE(testlib_memoryexists(p + pagesz));
|
||||
ASSERT_SYS(0, p, mremap(p, pagesz, pagesz * 2, 0));
|
||||
EXPECT_TRUE(testlib_memoryexists(p));
|
||||
EXPECT_TRUE(testlib_memoryexists(p + pagesz));
|
||||
ASSERT_SYS(0, 0, munmap(p, pagesz * 2));
|
||||
EXPECT_FALSE(testlib_memoryexists(p));
|
||||
EXPECT_FALSE(testlib_memoryexists(p + pagesz));
|
||||
}
|
||||
|
||||
TEST(mremap, dontMove_noRoom_itFailsWithEnomem) {
|
||||
if (IsNetbsd())
|
||||
return; // NetBSD requires MREMAP_MAYMOVE
|
||||
char *p;
|
||||
int pagesz = getpagesize();
|
||||
ASSERT_NE(MAP_FAILED, (p = mmap(randaddr(), pagesz * 2, PROT_READ | PROT_EXEC,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)));
|
||||
EXPECT_TRUE(testlib_memoryexists(p + pagesz * 0));
|
||||
EXPECT_TRUE(testlib_memoryexists(p + pagesz * 1));
|
||||
EXPECT_FALSE(testlib_memoryexists(p + pagesz * 2));
|
||||
ASSERT_SYS(ENOMEM, MAP_FAILED, mremap(p, pagesz, pagesz * 3, 0));
|
||||
EXPECT_TRUE(testlib_memoryexists(p + pagesz * 0));
|
||||
EXPECT_TRUE(testlib_memoryexists(p + pagesz * 1));
|
||||
EXPECT_FALSE(testlib_memoryexists(p + pagesz * 2));
|
||||
ASSERT_SYS(0, 0, munmap(p, pagesz * 2));
|
||||
EXPECT_FALSE(testlib_memoryexists(p + pagesz * 0));
|
||||
EXPECT_FALSE(testlib_memoryexists(p + pagesz * 1));
|
||||
EXPECT_FALSE(testlib_memoryexists(p + pagesz * 2));
|
||||
}
|
||||
|
||||
TEST(mremap, mayMove_noRoom_itRelocates) {
|
||||
char *p, *p2;
|
||||
int pagesz = getpagesize();
|
||||
ASSERT_NE(MAP_FAILED, (p = mmap(randaddr(), pagesz * 2, PROT_READ | PROT_EXEC,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)));
|
||||
EXPECT_TRUE(testlib_memoryexists(p + pagesz * 0));
|
||||
EXPECT_TRUE(testlib_memoryexists(p + pagesz * 1));
|
||||
EXPECT_FALSE(testlib_memoryexists(p + pagesz * 2));
|
||||
ASSERT_NE(MAP_FAILED, (p2 = mremap(p, pagesz, pagesz * 3, MREMAP_MAYMOVE)));
|
||||
ASSERT_NE(p, p2);
|
||||
EXPECT_FALSE(testlib_memoryexists(p + pagesz * 0));
|
||||
EXPECT_TRUE(testlib_memoryexists(p + pagesz * 1));
|
||||
EXPECT_FALSE(testlib_memoryexists(p + pagesz * 2));
|
||||
EXPECT_TRUE(testlib_memoryexists(p2 + pagesz * 0));
|
||||
EXPECT_TRUE(testlib_memoryexists(p2 + pagesz * 1));
|
||||
EXPECT_TRUE(testlib_memoryexists(p2 + pagesz * 2));
|
||||
ASSERT_SYS(0, 0, munmap(p + pagesz, pagesz));
|
||||
ASSERT_SYS(0, 0, munmap(p2, pagesz * 3));
|
||||
EXPECT_FALSE(testlib_memoryexists(p + pagesz * 0));
|
||||
EXPECT_FALSE(testlib_memoryexists(p + pagesz * 1));
|
||||
EXPECT_FALSE(testlib_memoryexists(p + pagesz * 2));
|
||||
EXPECT_FALSE(testlib_memoryexists(p2 + pagesz * 0));
|
||||
EXPECT_FALSE(testlib_memoryexists(p2 + pagesz * 1));
|
||||
EXPECT_FALSE(testlib_memoryexists(p2 + pagesz * 2));
|
||||
}
|
||||
|
||||
// demonstrate value of mremap() system call
|
||||
//
|
||||
// mmap(1'048'576) took 1'130 ns
|
||||
// mremap(1'048'576 -> 2'097'152) took 3'117 ns
|
||||
// mremap(2'097'152 -> 1'048'576) took 3'596 ns
|
||||
// mremap(1'048'576 -> 2'097'152) took 796'381 ns [simulated]
|
||||
// munmap(2'097'152) took 50'020 ns
|
||||
//
|
||||
|
||||
TEST(mremap, bench) {
|
||||
#define N 10
|
||||
long size = 1024 * 1024;
|
||||
char *rollo = randaddr();
|
||||
char *addr[N];
|
||||
|
||||
// create mappings
|
||||
struct timespec ts1 = timespec_real();
|
||||
for (long i = 0; i < N; ++i)
|
||||
if ((addr[i] = mmap((rollo += size), size, PROT_READ | PROT_WRITE,
|
||||
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)) == MAP_FAILED) {
|
||||
kprintf("first mmap failed: %m\n");
|
||||
exit(1);
|
||||
}
|
||||
kprintf("mmap(%'zu) took %'ld ns\n", size,
|
||||
timespec_tonanos(timespec_sub(timespec_real(), ts1)) / N);
|
||||
|
||||
// use mremap to grow mappings
|
||||
ts1 = timespec_real();
|
||||
for (long i = 0; i < N; ++i)
|
||||
if ((addr[i] = mremap(addr[i], size, size * 2, MREMAP_MAYMOVE)) ==
|
||||
MAP_FAILED) {
|
||||
kprintf("grow mremap failed: %m\n");
|
||||
exit(1);
|
||||
}
|
||||
kprintf("mremap(%'zu -> %'zu) took %'ld ns\n", size, size * 2,
|
||||
timespec_tonanos(timespec_sub(timespec_real(), ts1)) / N);
|
||||
|
||||
// use mremap to shrink mappings
|
||||
ts1 = timespec_real();
|
||||
for (long i = 0; i < N; ++i)
|
||||
if (mremap(addr[i], size * 2, size, 0) != addr[i]) {
|
||||
kprintf("shrink mremap failed: %m\n");
|
||||
exit(1);
|
||||
}
|
||||
kprintf("mremap(%'zu -> %'zu) took %'ld ns\n", size * 2, size,
|
||||
timespec_tonanos(timespec_sub(timespec_real(), ts1)) / N);
|
||||
|
||||
// do the thing that mremap is trying to optimize
|
||||
ts1 = timespec_real();
|
||||
for (long i = 0; i < N; ++i) {
|
||||
char *addr2;
|
||||
if ((addr2 = mmap(0, size * 2, PROT_READ | PROT_WRITE,
|
||||
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)) == MAP_FAILED) {
|
||||
kprintf("simulated mmap failed: %m\n");
|
||||
exit(1);
|
||||
}
|
||||
memmove(addr2, addr[i], size);
|
||||
if (munmap(addr[i], size)) {
|
||||
kprintf("simulated munmap failed: %m\n");
|
||||
exit(1);
|
||||
}
|
||||
addr[i] = addr2;
|
||||
}
|
||||
kprintf("mremap(%'zu -> %'zu) took %'ld ns [simulated]\n", size, size * 2,
|
||||
timespec_tonanos(timespec_sub(timespec_real(), ts1)) / N);
|
||||
|
||||
// unmap mappings
|
||||
ts1 = timespec_real();
|
||||
for (long i = 0; i < N; ++i)
|
||||
if (munmap(addr[i], size * 2)) {
|
||||
kprintf("munmap failed: %m\n");
|
||||
exit(1);
|
||||
}
|
||||
kprintf("munmap(%'zu) took %'ld ns\n", size * 2,
|
||||
timespec_tonanos(timespec_sub(timespec_real(), ts1)) / N);
|
||||
}
|
|
@ -19,6 +19,7 @@
|
|||
#include "libc/calls/calls.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/maps.h"
|
||||
#include "libc/log/log.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/sysv/consts/map.h"
|
||||
|
@ -26,116 +27,116 @@
|
|||
#include "libc/sysv/consts/prot.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
|
||||
int granularity;
|
||||
int gransz;
|
||||
|
||||
void SetUpOnce(void) {
|
||||
granularity = __granularity();
|
||||
gransz = getgransize();
|
||||
testlib_enable_tmp_setup_teardown();
|
||||
}
|
||||
|
||||
TEST(munmap, doesntExist_doesntCare) {
|
||||
EXPECT_SYS(0, 0, munmap(0, granularity * 8));
|
||||
EXPECT_SYS(0, 0, munmap(0, gransz * 8));
|
||||
}
|
||||
|
||||
TEST(munmap, invalidParams) {
|
||||
EXPECT_SYS(EINVAL, -1, munmap(0, 0));
|
||||
EXPECT_SYS(EINVAL, -1, munmap((void *)0x100080000000, 0));
|
||||
EXPECT_SYS(EINVAL, -1, munmap((void *)0x100080000001, granularity));
|
||||
EXPECT_SYS(EINVAL, -1, munmap((void *)0x100080000001, gransz));
|
||||
}
|
||||
|
||||
TEST(munmap, test) {
|
||||
char *p;
|
||||
ASSERT_NE(MAP_FAILED, (p = mmap(0, granularity, PROT_READ | PROT_WRITE,
|
||||
ASSERT_NE(MAP_FAILED, (p = mmap(0, gransz, PROT_READ | PROT_WRITE,
|
||||
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)));
|
||||
EXPECT_TRUE(testlib_memoryexists(p));
|
||||
EXPECT_SYS(0, 0, munmap(p, granularity));
|
||||
EXPECT_SYS(0, 0, munmap(p, gransz));
|
||||
EXPECT_FALSE(testlib_memoryexists(p));
|
||||
}
|
||||
|
||||
TEST(munmap, punchHoleInMemory) {
|
||||
char *p;
|
||||
ASSERT_NE(MAP_FAILED, (p = mmap(0, granularity * 3, PROT_READ | PROT_WRITE,
|
||||
ASSERT_NE(MAP_FAILED, (p = mmap(0, gransz * 3, PROT_READ | PROT_WRITE,
|
||||
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)));
|
||||
EXPECT_TRUE(testlib_memoryexists(p + granularity * 0));
|
||||
EXPECT_TRUE(testlib_memoryexists(p + granularity * 1));
|
||||
EXPECT_TRUE(testlib_memoryexists(p + granularity * 2));
|
||||
EXPECT_SYS(0, 0, munmap(p + granularity, granularity));
|
||||
EXPECT_TRUE(testlib_memoryexists(p + granularity * 0));
|
||||
EXPECT_FALSE(testlib_memoryexists(p + granularity * 1));
|
||||
EXPECT_TRUE(testlib_memoryexists(p + granularity * 2));
|
||||
EXPECT_SYS(0, 0, munmap(p, granularity));
|
||||
EXPECT_SYS(0, 0, munmap(p + granularity * 2, granularity));
|
||||
EXPECT_FALSE(testlib_memoryexists(p + granularity * 0));
|
||||
EXPECT_FALSE(testlib_memoryexists(p + granularity * 1));
|
||||
EXPECT_FALSE(testlib_memoryexists(p + granularity * 2));
|
||||
EXPECT_TRUE(testlib_memoryexists(p + gransz * 0));
|
||||
EXPECT_TRUE(testlib_memoryexists(p + gransz * 1));
|
||||
EXPECT_TRUE(testlib_memoryexists(p + gransz * 2));
|
||||
EXPECT_SYS(0, 0, munmap(p + gransz, gransz));
|
||||
EXPECT_TRUE(testlib_memoryexists(p + gransz * 0));
|
||||
EXPECT_FALSE(testlib_memoryexists(p + gransz * 1));
|
||||
EXPECT_TRUE(testlib_memoryexists(p + gransz * 2));
|
||||
EXPECT_SYS(0, 0, munmap(p, gransz));
|
||||
EXPECT_SYS(0, 0, munmap(p + gransz * 2, gransz));
|
||||
EXPECT_FALSE(testlib_memoryexists(p + gransz * 0));
|
||||
EXPECT_FALSE(testlib_memoryexists(p + gransz * 1));
|
||||
EXPECT_FALSE(testlib_memoryexists(p + gransz * 2));
|
||||
}
|
||||
|
||||
TEST(munmap, memoryHasHole) {
|
||||
char *p;
|
||||
ASSERT_NE(MAP_FAILED, (p = mmap(0, granularity * 3, PROT_READ | PROT_WRITE,
|
||||
ASSERT_NE(MAP_FAILED, (p = mmap(0, gransz * 3, PROT_READ | PROT_WRITE,
|
||||
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)));
|
||||
EXPECT_SYS(0, 0, munmap(p + granularity, granularity));
|
||||
EXPECT_TRUE(testlib_memoryexists(p + granularity * 0));
|
||||
EXPECT_FALSE(testlib_memoryexists(p + granularity * 1));
|
||||
EXPECT_TRUE(testlib_memoryexists(p + granularity * 2));
|
||||
EXPECT_SYS(0, 0, munmap(p, granularity * 3));
|
||||
EXPECT_FALSE(testlib_memoryexists(p + granularity * 0));
|
||||
EXPECT_FALSE(testlib_memoryexists(p + granularity * 1));
|
||||
EXPECT_FALSE(testlib_memoryexists(p + granularity * 2));
|
||||
EXPECT_SYS(0, 0, munmap(p + gransz, gransz));
|
||||
EXPECT_TRUE(testlib_memoryexists(p + gransz * 0));
|
||||
EXPECT_FALSE(testlib_memoryexists(p + gransz * 1));
|
||||
EXPECT_TRUE(testlib_memoryexists(p + gransz * 2));
|
||||
EXPECT_SYS(0, 0, munmap(p, gransz * 3));
|
||||
EXPECT_FALSE(testlib_memoryexists(p + gransz * 0));
|
||||
EXPECT_FALSE(testlib_memoryexists(p + gransz * 1));
|
||||
EXPECT_FALSE(testlib_memoryexists(p + gransz * 2));
|
||||
}
|
||||
|
||||
TEST(munmap, blanketFree) {
|
||||
char *p;
|
||||
ASSERT_NE(MAP_FAILED, (p = mmap(0, granularity * 3, PROT_READ | PROT_WRITE,
|
||||
ASSERT_NE(MAP_FAILED, (p = mmap(0, gransz * 3, PROT_READ | PROT_WRITE,
|
||||
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)));
|
||||
EXPECT_TRUE(testlib_memoryexists(p + granularity * 0));
|
||||
EXPECT_TRUE(testlib_memoryexists(p + granularity * 1));
|
||||
EXPECT_TRUE(testlib_memoryexists(p + granularity * 2));
|
||||
EXPECT_SYS(0, 0, munmap(p + granularity * 0, granularity));
|
||||
EXPECT_SYS(0, 0, munmap(p + granularity * 2, granularity));
|
||||
EXPECT_FALSE(testlib_memoryexists(p + granularity * 0));
|
||||
EXPECT_TRUE(testlib_memoryexists(p + granularity * 1));
|
||||
EXPECT_FALSE(testlib_memoryexists(p + granularity * 2));
|
||||
EXPECT_SYS(0, 0, munmap(p, granularity * 3));
|
||||
EXPECT_FALSE(testlib_memoryexists(p + granularity * 0));
|
||||
EXPECT_FALSE(testlib_memoryexists(p + granularity * 1));
|
||||
EXPECT_FALSE(testlib_memoryexists(p + granularity * 2));
|
||||
EXPECT_TRUE(testlib_memoryexists(p + gransz * 0));
|
||||
EXPECT_TRUE(testlib_memoryexists(p + gransz * 1));
|
||||
EXPECT_TRUE(testlib_memoryexists(p + gransz * 2));
|
||||
EXPECT_SYS(0, 0, munmap(p + gransz * 0, gransz));
|
||||
EXPECT_SYS(0, 0, munmap(p + gransz * 2, gransz));
|
||||
EXPECT_FALSE(testlib_memoryexists(p + gransz * 0));
|
||||
EXPECT_TRUE(testlib_memoryexists(p + gransz * 1));
|
||||
EXPECT_FALSE(testlib_memoryexists(p + gransz * 2));
|
||||
EXPECT_SYS(0, 0, munmap(p, gransz * 3));
|
||||
EXPECT_FALSE(testlib_memoryexists(p + gransz * 0));
|
||||
EXPECT_FALSE(testlib_memoryexists(p + gransz * 1));
|
||||
EXPECT_FALSE(testlib_memoryexists(p + gransz * 2));
|
||||
}
|
||||
|
||||
TEST(munmap, trimLeft) {
|
||||
char *p;
|
||||
ASSERT_NE(MAP_FAILED, (p = mmap(0, granularity * 2, PROT_READ | PROT_WRITE,
|
||||
ASSERT_NE(MAP_FAILED, (p = mmap(0, gransz * 2, PROT_READ | PROT_WRITE,
|
||||
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)));
|
||||
EXPECT_TRUE(testlib_memoryexists(p + granularity * 0));
|
||||
EXPECT_TRUE(testlib_memoryexists(p + granularity * 1));
|
||||
EXPECT_SYS(0, 0, munmap(p, granularity));
|
||||
EXPECT_FALSE(testlib_memoryexists(p + granularity * 0));
|
||||
EXPECT_TRUE(testlib_memoryexists(p + granularity * 1));
|
||||
EXPECT_SYS(0, 0, munmap(p, granularity * 2));
|
||||
EXPECT_FALSE(testlib_memoryexists(p + granularity * 0));
|
||||
EXPECT_FALSE(testlib_memoryexists(p + granularity * 1));
|
||||
EXPECT_TRUE(testlib_memoryexists(p + gransz * 0));
|
||||
EXPECT_TRUE(testlib_memoryexists(p + gransz * 1));
|
||||
EXPECT_SYS(0, 0, munmap(p, gransz));
|
||||
EXPECT_FALSE(testlib_memoryexists(p + gransz * 0));
|
||||
EXPECT_TRUE(testlib_memoryexists(p + gransz * 1));
|
||||
EXPECT_SYS(0, 0, munmap(p, gransz * 2));
|
||||
EXPECT_FALSE(testlib_memoryexists(p + gransz * 0));
|
||||
EXPECT_FALSE(testlib_memoryexists(p + gransz * 1));
|
||||
}
|
||||
|
||||
TEST(munmap, trimRight) {
|
||||
char *p;
|
||||
ASSERT_NE(MAP_FAILED, (p = mmap(0, granularity * 2, PROT_READ | PROT_WRITE,
|
||||
ASSERT_NE(MAP_FAILED, (p = mmap(0, gransz * 2, PROT_READ | PROT_WRITE,
|
||||
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)));
|
||||
EXPECT_TRUE(testlib_memoryexists(p + granularity * 0));
|
||||
EXPECT_TRUE(testlib_memoryexists(p + granularity * 1));
|
||||
EXPECT_SYS(0, 0, munmap(p + granularity, granularity));
|
||||
EXPECT_TRUE(testlib_memoryexists(p + granularity * 0));
|
||||
EXPECT_FALSE(testlib_memoryexists(p + granularity * 1));
|
||||
EXPECT_SYS(0, 0, munmap(p, granularity * 2));
|
||||
EXPECT_FALSE(testlib_memoryexists(p + granularity * 0));
|
||||
EXPECT_FALSE(testlib_memoryexists(p + granularity * 1));
|
||||
EXPECT_TRUE(testlib_memoryexists(p + gransz * 0));
|
||||
EXPECT_TRUE(testlib_memoryexists(p + gransz * 1));
|
||||
EXPECT_SYS(0, 0, munmap(p + gransz, gransz));
|
||||
EXPECT_TRUE(testlib_memoryexists(p + gransz * 0));
|
||||
EXPECT_FALSE(testlib_memoryexists(p + gransz * 1));
|
||||
EXPECT_SYS(0, 0, munmap(p, gransz * 2));
|
||||
EXPECT_FALSE(testlib_memoryexists(p + gransz * 0));
|
||||
EXPECT_FALSE(testlib_memoryexists(p + gransz * 1));
|
||||
}
|
||||
|
||||
TEST(munmap, memoryGone) {
|
||||
char *p;
|
||||
ASSERT_NE(MAP_FAILED, (p = mmap(0, granularity, PROT_READ | PROT_WRITE,
|
||||
ASSERT_NE(MAP_FAILED, (p = mmap(0, gransz, PROT_READ | PROT_WRITE,
|
||||
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)));
|
||||
EXPECT_SYS(0, 0, munmap(p, granularity));
|
||||
EXPECT_SYS(0, 0, munmap(p, granularity));
|
||||
EXPECT_SYS(0, 0, munmap(p, gransz));
|
||||
EXPECT_SYS(0, 0, munmap(p, gransz));
|
||||
}
|
||||
|
||||
TEST(munmap, tinyFile_roundupUnmapSize) {
|
||||
|
@ -147,7 +148,7 @@ TEST(munmap, tinyFile_roundupUnmapSize) {
|
|||
ASSERT_NE(MAP_FAILED, (p = mmap(0, 5, PROT_READ, MAP_PRIVATE, 3, 0)));
|
||||
ASSERT_SYS(0, 0, close(3));
|
||||
EXPECT_TRUE(testlib_memoryexists(p));
|
||||
EXPECT_SYS(0, 0, munmap(p, granularity));
|
||||
EXPECT_SYS(0, 0, munmap(p, gransz));
|
||||
EXPECT_FALSE(testlib_memoryexists(p));
|
||||
EXPECT_FALSE(testlib_memoryexists(p + 5));
|
||||
}
|
||||
|
@ -171,22 +172,22 @@ TEST(munmap, tinyFile_preciseUnmapSize) {
|
|||
}
|
||||
|
||||
// clang-format off
|
||||
/* TEST(munmap, tinyFile_mapThriceUnmapOnce) { */
|
||||
/* char *p = (char *)0x000063d646e20000; */
|
||||
/* ASSERT_SYS(0, 3, open("doge", O_RDWR | O_CREAT | O_TRUNC, 0644)); */
|
||||
/* ASSERT_SYS (0, 5, write(3, "hello", 5)); */
|
||||
/* ASSERT_EQ(p+granularity*0, mmap(p+granularity*0, granularity, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, -1, 0)); */
|
||||
/* ASSERT_EQ(p+granularity*1, mmap(p+granularity*1, 5, PROT_READ, MAP_PRIVATE|MAP_FIXED, 3, 0)); */
|
||||
/* ASSERT_EQ(p+granularity*3, mmap(p+granularity*3, 5, PROT_READ, MAP_PRIVATE|MAP_FIXED, 3, 0)); */
|
||||
/* ASSERT_SYS(0, 0, close(3)); */
|
||||
/* EXPECT_TRUE(testlib_memoryexists(p+granularity*0)); */
|
||||
/* EXPECT_TRUE(testlib_memoryexists(p+granularity*1)); */
|
||||
/* EXPECT_FALSE(testlib_memoryexists(p+granularity*2)); */
|
||||
/* EXPECT_TRUE(testlib_memoryexists(p+granularity*3)); */
|
||||
/* EXPECT_SYS(0, 0, munmap(p, granularity*5)); */
|
||||
/* EXPECT_FALSE(testlib_memoryexists(p+granularity*0)); */
|
||||
/* EXPECT_FALSE(testlib_memoryexists(p+granularity*1)); */
|
||||
/* EXPECT_FALSE(testlib_memoryexists(p+granularity*2)); */
|
||||
/* EXPECT_FALSE(testlib_memoryexists(p+granularity*3)); */
|
||||
/* } */
|
||||
TEST(munmap, tinyFile_mapThriceUnmapOnce) {
|
||||
char *p = randaddr();
|
||||
ASSERT_SYS(0, 3, open("doge", O_RDWR | O_CREAT | O_TRUNC, 0644));
|
||||
ASSERT_SYS (0, 5, write(3, "hello", 5));
|
||||
ASSERT_EQ(p+gransz*0, mmap(p+gransz*0, gransz, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, -1, 0));
|
||||
ASSERT_EQ(p+gransz*1, mmap(p+gransz*1, 5, PROT_READ, MAP_PRIVATE|MAP_FIXED, 3, 0));
|
||||
ASSERT_EQ(p+gransz*3, mmap(p+gransz*3, 5, PROT_READ, MAP_PRIVATE|MAP_FIXED, 3, 0));
|
||||
ASSERT_SYS(0, 0, close(3));
|
||||
EXPECT_TRUE(testlib_memoryexists(p+gransz*0));
|
||||
EXPECT_TRUE(testlib_memoryexists(p+gransz*1));
|
||||
EXPECT_FALSE(testlib_memoryexists(p+gransz*2));
|
||||
EXPECT_TRUE(testlib_memoryexists(p+gransz*3));
|
||||
EXPECT_SYS(0, 0, munmap(p, gransz*5));
|
||||
EXPECT_FALSE(testlib_memoryexists(p+gransz*0));
|
||||
EXPECT_FALSE(testlib_memoryexists(p+gransz*1));
|
||||
EXPECT_FALSE(testlib_memoryexists(p+gransz*2));
|
||||
EXPECT_FALSE(testlib_memoryexists(p+gransz*3));
|
||||
}
|
||||
// clang-format on
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include "libc/intrin/tree.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/maps.h"
|
||||
#include "libc/intrin/tree.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
|
@ -71,12 +72,6 @@ struct number {
|
|||
struct Tree elem;
|
||||
};
|
||||
|
||||
int number_search(const void *ra, const struct Tree *rb) {
|
||||
long a = (long)ra;
|
||||
const struct number *b = NUMBER_CONTAINER(rb);
|
||||
return (a > b->number) - (a < b->number);
|
||||
}
|
||||
|
||||
int number_compare(const struct Tree *ra, const struct Tree *rb) {
|
||||
const struct number *a = NUMBER_CONTAINER(ra);
|
||||
const struct number *b = NUMBER_CONTAINER(rb);
|
||||
|
@ -90,23 +85,32 @@ struct number *number_new(int number) {
|
|||
return res;
|
||||
}
|
||||
|
||||
struct Tree *tree = 0;
|
||||
|
||||
void print(void) {
|
||||
void print(struct Tree *tree) {
|
||||
for (struct Tree *e = tree_first(tree); e; e = tree_next(e))
|
||||
kprintf("%3d", NUMBER_CONTAINER(e)->number);
|
||||
kprintf("\n");
|
||||
}
|
||||
|
||||
void print_reversed(void) {
|
||||
void print_reversed(struct Tree *tree) {
|
||||
for (struct Tree *e = tree_last(tree); e; e = tree_prev(e))
|
||||
kprintf("%3d", NUMBER_CONTAINER(e)->number);
|
||||
kprintf("\n");
|
||||
}
|
||||
|
||||
int number_search(const void *ra, const struct Tree *rb) {
|
||||
long a = (long)ra;
|
||||
const struct number *b = NUMBER_CONTAINER(rb);
|
||||
return (a > b->number) - (a < b->number);
|
||||
}
|
||||
|
||||
void simple_test(void) {
|
||||
// 0 2 2 23 30 32 34 34 46 52 53 65 70 74 90 94 95 95 96 96
|
||||
// 96 96 95 95 94 90 74 70 65 53 52 46 34 34 32 30 23 2 2 0
|
||||
static const long kNumba[] = {74, 53, 96, 70, 34, 95, 30, 2, 96, 46,
|
||||
23, 2, 52, 0, 34, 94, 90, 95, 32, 65};
|
||||
|
||||
// test insertion works
|
||||
struct Tree *tree = 0;
|
||||
for (int i = 0; i < 20; ++i) {
|
||||
int number = kNumba[i];
|
||||
kprintf("%3d", number);
|
||||
|
@ -114,26 +118,159 @@ void simple_test(void) {
|
|||
tree_check(tree, number_compare);
|
||||
}
|
||||
kprintf("\n");
|
||||
print();
|
||||
print_reversed();
|
||||
|
||||
// test iteration works
|
||||
print(tree);
|
||||
|
||||
// test reverse iteration works
|
||||
print_reversed(tree);
|
||||
|
||||
// test removal works
|
||||
for (int i = 0; i < 20; ++i) {
|
||||
tree_remove(&tree, tree_get(tree, (void *)kNumba[i], number_search));
|
||||
tree_check(tree, number_compare);
|
||||
print();
|
||||
print(tree);
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
ShowCrashReports();
|
||||
tree_check(__maps.maps, __maps_compare);
|
||||
kprintf("\n");
|
||||
__print_maps(15);
|
||||
kprintf("\n");
|
||||
simple_test();
|
||||
tree_check(__maps.maps, __maps_compare);
|
||||
// use up a bunch of memory
|
||||
for (int i = 0; i < 100000; ++i)
|
||||
tree_insert(&tree, &number_new(rand())->elem, number_compare);
|
||||
tree_check(tree, number_compare);
|
||||
tree_check(__maps.maps, __maps_compare);
|
||||
__print_maps(15);
|
||||
|
||||
// visually verify maps get coalesced
|
||||
__print_maps(0);
|
||||
}
|
||||
|
||||
void search_test(void) {
|
||||
struct Tree *x, *tree = 0;
|
||||
tree_insert(&tree, &number_new(1)->elem, number_compare);
|
||||
tree_insert(&tree, &number_new(3)->elem, number_compare);
|
||||
tree_insert(&tree, &number_new(5)->elem, number_compare);
|
||||
tree_insert(&tree, &number_new(7)->elem, number_compare);
|
||||
|
||||
// Test tree_get()
|
||||
//
|
||||
// Returns node equal to given key.
|
||||
//
|
||||
// [1 3 5 7] [1 3 5 7] [1 3 5 7]
|
||||
// NULL ↑ NULL
|
||||
// 4 3 8
|
||||
//
|
||||
x = tree_get(tree, (void *)4l, number_search);
|
||||
if (x)
|
||||
exit(1);
|
||||
x = tree_get(tree, (void *)3l, number_search);
|
||||
if (!x)
|
||||
exit(2);
|
||||
if (NUMBER_CONTAINER(x)->number != 3)
|
||||
exit(3);
|
||||
if (!tree_get(tree, (void *)7l, number_search))
|
||||
exit(27);
|
||||
if (tree_get(tree, (void *)8l, number_search))
|
||||
exit(28);
|
||||
|
||||
// Test tree_floor()
|
||||
//
|
||||
// Returns last node less than or equal to given key.
|
||||
//
|
||||
// [1 3 5 7] [1 3 5 7] [1 3 5 7]
|
||||
// ↑ ↑ ↑
|
||||
// 4 3 8
|
||||
//
|
||||
x = tree_floor(tree, (void *)4l, number_search);
|
||||
if (!x)
|
||||
exit(4);
|
||||
if (NUMBER_CONTAINER(x)->number != 3)
|
||||
exit(5);
|
||||
x = tree_floor(tree, (void *)3l, number_search);
|
||||
if (!x)
|
||||
exit(6);
|
||||
if (NUMBER_CONTAINER(x)->number != 3)
|
||||
exit(7);
|
||||
if (!tree_floor(tree, (void *)7l, number_search))
|
||||
exit(24);
|
||||
x = tree_floor(tree, (void *)8l, number_search);
|
||||
if (!x)
|
||||
exit(25);
|
||||
if (NUMBER_CONTAINER(x)->number != 7)
|
||||
exit(30);
|
||||
if (tree_floor(tree, (void *)0l, number_search))
|
||||
exit(31);
|
||||
|
||||
// Test tree_lower()
|
||||
//
|
||||
// Returns first node not less than given key.
|
||||
//
|
||||
// [1 3 5 7] [1 3 5 7] [1 3 5 7]
|
||||
// ↑ ↑ NULL
|
||||
// 4 3 8
|
||||
//
|
||||
x = tree_lower(tree, (void *)4l, number_search);
|
||||
if (!x)
|
||||
exit(4);
|
||||
if (NUMBER_CONTAINER(x)->number != 5)
|
||||
exit(8);
|
||||
x = tree_lower(tree, (void *)3l, number_search);
|
||||
if (!x)
|
||||
exit(9);
|
||||
if (NUMBER_CONTAINER(x)->number != 3)
|
||||
exit(10);
|
||||
if (!tree_lower(tree, (void *)7l, number_search))
|
||||
exit(22);
|
||||
if (tree_lower(tree, (void *)8l, number_search))
|
||||
exit(23);
|
||||
|
||||
// Test tree_ceil()
|
||||
//
|
||||
// Returns first node greater than than given key.
|
||||
//
|
||||
// [1 3 5 7] [1 3 5 7] [1 3 5 7]
|
||||
// ↑ ↑ NULL
|
||||
// 4 3 8
|
||||
//
|
||||
x = tree_ceil(tree, (void *)4l, number_search);
|
||||
if (!x)
|
||||
exit(11);
|
||||
if (NUMBER_CONTAINER(x)->number != 5)
|
||||
exit(12);
|
||||
x = tree_ceil(tree, (void *)3l, number_search);
|
||||
if (!x)
|
||||
exit(13);
|
||||
if (NUMBER_CONTAINER(x)->number != 5)
|
||||
exit(14);
|
||||
if (tree_ceil(tree, (void *)7l, number_search))
|
||||
exit(21);
|
||||
|
||||
// Test tree_first()
|
||||
if (tree_first(NULL))
|
||||
exit(15);
|
||||
x = tree_first(tree);
|
||||
if (!x)
|
||||
exit(16);
|
||||
if (NUMBER_CONTAINER(x)->number != 1)
|
||||
exit(17);
|
||||
|
||||
// Test tree_last()
|
||||
if (tree_last(NULL))
|
||||
exit(18);
|
||||
x = tree_last(tree);
|
||||
if (!x)
|
||||
exit(19);
|
||||
if (NUMBER_CONTAINER(x)->number != 7)
|
||||
exit(20);
|
||||
}
|
||||
|
||||
int main() {
|
||||
ShowCrashReports();
|
||||
|
||||
// show memory maps at startup
|
||||
tree_check(__maps.maps, __maps_compare);
|
||||
kprintf("\n");
|
||||
__print_maps(0);
|
||||
kprintf("\n");
|
||||
|
||||
// run tests
|
||||
simple_test();
|
||||
search_test();
|
||||
}
|
||||
|
|
|
@ -188,20 +188,22 @@ BENCH(bulk_free, bench) {
|
|||
EZBENCH2("free(malloc(16)) MT", donothing, MallocFree());
|
||||
}
|
||||
|
||||
#define ITERATIONS 10000
|
||||
#define ITERATIONS 1000
|
||||
#define THREADS 10
|
||||
#define SIZE (256 * 1024)
|
||||
|
||||
void *Worker(void *arg) {
|
||||
/* for (int i = 0; i < ITERATIONS; ++i) { */
|
||||
/* char *p; */
|
||||
/* ASSERT_NE(NULL, (p = malloc(lemur64() % 128))); */
|
||||
/* ASSERT_NE(NULL, (p = realloc(p, max(lemur64() % 128, 1)))); */
|
||||
/* free(p); */
|
||||
/* } */
|
||||
for (int i = 0; i < ITERATIONS; ++i) {
|
||||
char *p;
|
||||
ASSERT_NE(NULL, (p = malloc(lemur64() % SIZE)));
|
||||
ASSERT_NE(NULL, (p = realloc(p, max(lemur64() % SIZE, 1))));
|
||||
free(p);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
BENCH(malloc, torture) {
|
||||
int i, n = __get_cpu_count();
|
||||
TEST(malloc, torture) {
|
||||
int i, n = THREADS;
|
||||
pthread_t *t = gc(malloc(sizeof(pthread_t) * n));
|
||||
if (!n)
|
||||
return;
|
||||
|
|
|
@ -85,7 +85,7 @@ TEST(lz4, zoneFileGmt) {
|
|||
(mapsize = roundup(
|
||||
LZ4_FRAME_BLOCKCONTENTSIZE(lz4check(dict)) +
|
||||
(gmtsize = LZ4_FRAME_BLOCKCONTENTSIZE(lz4check(gmt))),
|
||||
__granularity())))),
|
||||
getpagesize())))),
|
||||
dict)),
|
||||
gmt);
|
||||
ASSERT_BINEQ(
|
||||
|
|
|
@ -63,10 +63,10 @@ TEST(fork, testSharedMemory) {
|
|||
int *sharedvar;
|
||||
int *privatevar;
|
||||
EXPECT_NE(MAP_FAILED,
|
||||
(sharedvar = mmap(NULL, __granularity(), PROT_READ | PROT_WRITE,
|
||||
(sharedvar = mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE,
|
||||
MAP_SHARED | MAP_ANONYMOUS, -1, 0)));
|
||||
EXPECT_NE(MAP_FAILED,
|
||||
(privatevar = mmap(NULL, __granularity(), PROT_READ | PROT_WRITE,
|
||||
(privatevar = mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)));
|
||||
stackvar = 1;
|
||||
*sharedvar = 1;
|
||||
|
@ -77,18 +77,18 @@ TEST(fork, testSharedMemory) {
|
|||
++stackvar;
|
||||
++*sharedvar;
|
||||
++*privatevar;
|
||||
msync((void *)ROUNDDOWN((intptr_t)&stackvar, __granularity()),
|
||||
__granularity(), MS_SYNC);
|
||||
EXPECT_NE(-1, msync(privatevar, __granularity(), MS_SYNC));
|
||||
EXPECT_NE(-1, msync(sharedvar, __granularity(), MS_SYNC));
|
||||
msync((void *)ROUNDDOWN((intptr_t)&stackvar, getpagesize()), getpagesize(),
|
||||
MS_SYNC);
|
||||
EXPECT_NE(-1, msync(privatevar, getpagesize(), MS_SYNC));
|
||||
EXPECT_NE(-1, msync(sharedvar, getpagesize(), MS_SYNC));
|
||||
_exit(0);
|
||||
}
|
||||
EXPECT_NE(-1, waitpid(pid, &ws, 0));
|
||||
EXPECT_EQ(1, stackvar);
|
||||
EXPECT_EQ(2, *sharedvar);
|
||||
EXPECT_EQ(1, *privatevar);
|
||||
EXPECT_NE(-1, munmap(sharedvar, __granularity()));
|
||||
EXPECT_NE(-1, munmap(privatevar, __granularity()));
|
||||
EXPECT_NE(-1, munmap(sharedvar, getpagesize()));
|
||||
EXPECT_NE(-1, munmap(privatevar, getpagesize()));
|
||||
}
|
||||
|
||||
static volatile bool gotsigusr1;
|
||||
|
|
|
@ -28,11 +28,11 @@
|
|||
int i, *p;
|
||||
|
||||
void SetUp(void) {
|
||||
p = _mapshared(__granularity());
|
||||
p = _mapshared(getpagesize());
|
||||
}
|
||||
|
||||
void TearDown(void) {
|
||||
munmap(p, __granularity());
|
||||
munmap(p, getpagesize());
|
||||
}
|
||||
|
||||
void AtExit3(void) {
|
||||
|
|
|
@ -169,5 +169,5 @@ BENCH(memcpy, bench) {
|
|||
BB(1023);
|
||||
BB(1024);
|
||||
BB(4096);
|
||||
BB(__granularity());
|
||||
BB(getpagesize());
|
||||
}
|
||||
|
|
|
@ -129,7 +129,7 @@ TEST(sem_timedwait, threads) {
|
|||
|
||||
TEST(sem_timedwait, processes) {
|
||||
int i, r, rc, n = 4, pshared = 1;
|
||||
sem_t *sm = _mapshared(__granularity()), *s[2] = {sm, sm + 1};
|
||||
sem_t *sm = _mapshared(getpagesize()), *s[2] = {sm, sm + 1};
|
||||
ASSERT_SYS(0, 0, sem_init(s[0], pshared, 0));
|
||||
ASSERT_SYS(0, 0, sem_init(s[1], pshared, 0));
|
||||
for (i = 0; i < n; ++i) {
|
||||
|
@ -163,5 +163,5 @@ TEST(sem_timedwait, processes) {
|
|||
ASSERT_EQ(0, r);
|
||||
ASSERT_SYS(0, 0, sem_destroy(s[1]));
|
||||
ASSERT_SYS(0, 0, sem_destroy(s[0]));
|
||||
ASSERT_SYS(0, 0, munmap(sm, __granularity()));
|
||||
ASSERT_SYS(0, 0, munmap(sm, getpagesize()));
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue