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:
Justine Tunney 2024-07-07 12:24:25 -07:00
parent 196942084b
commit f7780de24b
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
71 changed files with 1301 additions and 640 deletions

View file

@ -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));
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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) {

View file

@ -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));

View file

@ -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()));
}

View file

@ -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,

View file

@ -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();
}

View file

@ -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));
}

View 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);
}

View file

@ -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

View file

@ -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();
}

View file

@ -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;

View file

@ -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(

View file

@ -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;

View file

@ -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) {

View file

@ -169,5 +169,5 @@ BENCH(memcpy, bench) {
BB(1023);
BB(1024);
BB(4096);
BB(__granularity());
BB(getpagesize());
}

View file

@ -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()));
}