mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-30 19:17:36 +00:00
Write more runtime tests and fix bugs
This change adds tests for the new memory manager code particularly with its windows support. Function call tracing now works reliably on Silicon since our function hooker was missing new Apple self-modifying code APIs Many tests that were disabled a long time ago on aarch64 are reactivated by this change, now that arm support is on equal terms with x86. There's been a lot of places where ftrace could cause deadlocks, which have been hunted down across all platforms thanks to new tests. A bug in Windows's kill() function has been identified.
This commit is contained in:
parent
0b3c81dd4e
commit
f24c854b28
45 changed files with 550 additions and 872 deletions
|
@ -17,6 +17,7 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/proc/ntspawn.h"
|
||||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/calls/struct/sigset.internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/intrin/strace.h"
|
||||
|
|
|
@ -190,7 +190,9 @@ textstartup void __init_fds(int argc, char **argv, char **envp) {
|
|||
map->prot = PROT_READ | PROT_WRITE;
|
||||
map->flags = MAP_SHARED | MAP_ANONYMOUS;
|
||||
map->hand = shand;
|
||||
__maps_lock();
|
||||
__maps_insert(map);
|
||||
__maps_unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -112,6 +112,13 @@ void __maps_init(void) {
|
|||
}
|
||||
|
||||
bool __maps_held(void) {
|
||||
return !__tls_enabled || (__get_tls()->tib_flags & TIB_FLAG_VFORKED) ||
|
||||
MUTEX_OWNER(
|
||||
atomic_load_explicit(&__maps.lock.word, memory_order_relaxed)) ==
|
||||
atomic_load_explicit(&__get_tls()->tib_ptid, memory_order_relaxed);
|
||||
}
|
||||
|
||||
bool __maps_reentrant(void) {
|
||||
return __tls_enabled && !(__get_tls()->tib_flags & TIB_FLAG_VFORKED) &&
|
||||
MUTEX_OWNER(
|
||||
atomic_load_explicit(&__maps.lock.word, memory_order_relaxed)) ==
|
||||
|
|
|
@ -84,6 +84,7 @@ void __maps_init(void);
|
|||
void __maps_lock(void);
|
||||
void __maps_check(void);
|
||||
void __maps_unlock(void);
|
||||
bool __maps_reentrant(void);
|
||||
void *__maps_randaddr(void);
|
||||
void __maps_add(struct Map *);
|
||||
void __maps_free(struct Map *);
|
||||
|
@ -103,28 +104,28 @@ forceinline optimizespeed int __maps_search(const void *key,
|
|||
return (addr > map->addr) - (addr < map->addr);
|
||||
}
|
||||
|
||||
dontinstrument static inline struct Map *__maps_next(struct Map *map) {
|
||||
static inline struct Map *__maps_next(struct Map *map) {
|
||||
struct Tree *node;
|
||||
if ((node = tree_next(&map->tree)))
|
||||
return MAP_TREE_CONTAINER(node);
|
||||
return 0;
|
||||
}
|
||||
|
||||
dontinstrument static inline struct Map *__maps_prev(struct Map *map) {
|
||||
static inline struct Map *__maps_prev(struct Map *map) {
|
||||
struct Tree *node;
|
||||
if ((node = tree_prev(&map->tree)))
|
||||
return MAP_TREE_CONTAINER(node);
|
||||
return 0;
|
||||
}
|
||||
|
||||
dontinstrument static inline struct Map *__maps_first(void) {
|
||||
static inline struct Map *__maps_first(void) {
|
||||
struct Tree *node;
|
||||
if ((node = tree_first(__maps.maps)))
|
||||
return MAP_TREE_CONTAINER(node);
|
||||
return 0;
|
||||
}
|
||||
|
||||
dontinstrument static inline struct Map *__maps_last(void) {
|
||||
static inline struct Map *__maps_last(void) {
|
||||
struct Tree *node;
|
||||
if ((node = tree_last(__maps.maps)))
|
||||
return MAP_TREE_CONTAINER(node);
|
||||
|
|
|
@ -49,7 +49,7 @@
|
|||
#include "libc/thread/thread.h"
|
||||
#include "libc/thread/tls.h"
|
||||
|
||||
#define MMDEBUG 1
|
||||
#define MMDEBUG 0
|
||||
#define MAX_SIZE 0x0ff800000000ul
|
||||
|
||||
#define MAP_FIXED_NOREPLACE_linux 0x100000
|
||||
|
@ -94,8 +94,11 @@ privileged optimizespeed struct Map *__maps_floor(const char *addr) {
|
|||
}
|
||||
|
||||
static bool __maps_overlaps(const char *addr, size_t size) {
|
||||
struct Map *map, *floor = __maps_floor(addr);
|
||||
for (map = floor; map && map->addr <= addr + size; map = __maps_next(map))
|
||||
struct Map *map;
|
||||
ASSERT(__maps_held());
|
||||
if (!(map = __maps_floor(addr)))
|
||||
map = __maps_first();
|
||||
for (; map && map->addr <= addr + size; map = __maps_next(map))
|
||||
if (MAX(addr, map->addr) <
|
||||
MIN(addr + PGUP(size), map->addr + PGUP(map->size)))
|
||||
return true;
|
||||
|
@ -105,30 +108,33 @@ static bool __maps_overlaps(const char *addr, size_t size) {
|
|||
// returns true if all fragments of all allocations which overlap
|
||||
// [addr,addr+size) are completely contained by [addr,addr+size).
|
||||
textwindows static bool __maps_envelops(const char *addr, size_t size) {
|
||||
struct Map *map, *next;
|
||||
struct Map *map;
|
||||
size = PGUP(size);
|
||||
ASSERT(__maps_held());
|
||||
if (!(map = __maps_floor(addr)))
|
||||
if (!(map = __maps_first()))
|
||||
return true;
|
||||
do {
|
||||
if (MAX(addr, map->addr) >= MIN(addr + size, map->addr + map->size))
|
||||
break; // didn't overlap mapping
|
||||
if (!__maps_isalloc(map))
|
||||
return false; // didn't include first fragment of alloc
|
||||
if (addr > map->addr)
|
||||
return false; // excluded leading pages of first fragment
|
||||
// set map to last fragment in allocation
|
||||
for (; (next = __maps_next(map)) && !__maps_isalloc(next); map = next)
|
||||
// fragments within an allocation must be perfectly contiguous
|
||||
ASSERT(map->addr + map->size == next->addr);
|
||||
if (addr + size < map->addr + PGUP(map->size))
|
||||
return false; // excluded trailing pages of allocation
|
||||
} while ((map = next));
|
||||
map = __maps_first();
|
||||
while (map && map->addr <= addr + size) {
|
||||
if (MAX(addr, map->addr) < MIN(addr + size, map->addr + map->size)) {
|
||||
if (!__maps_isalloc(map))
|
||||
return false; // didn't include first fragment of alloc
|
||||
if (addr > map->addr)
|
||||
return false; // excluded leading pages of first fragment
|
||||
struct Map *next; // set map to last fragment in allocation
|
||||
for (; (next = __maps_next(map)) && !__maps_isalloc(next); map = next)
|
||||
ASSERT(map->addr + map->size == next->addr); // contiguous
|
||||
if (addr + size < map->addr + PGUP(map->size))
|
||||
return false; // excluded trailing pages of allocation
|
||||
map = next;
|
||||
} else {
|
||||
map = __maps_next(map);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void __maps_check(void) {
|
||||
#if MMDEBUG
|
||||
ASSERT(__maps_held());
|
||||
size_t maps = 0;
|
||||
size_t pages = 0;
|
||||
static unsigned mono;
|
||||
|
@ -152,6 +158,22 @@ void __maps_check(void) {
|
|||
#endif
|
||||
}
|
||||
|
||||
#if MMDEBUG
|
||||
static void __maps_ok(void) {
|
||||
ASSERT(!__maps_reentrant());
|
||||
__maps_lock();
|
||||
__maps_check();
|
||||
__maps_unlock();
|
||||
}
|
||||
__attribute__((__constructor__)) static void __maps_ctor(void) {
|
||||
atexit(__maps_ok);
|
||||
__maps_ok();
|
||||
}
|
||||
__attribute__((__destructor__)) static void __maps_dtor(void) {
|
||||
__maps_ok();
|
||||
}
|
||||
#endif
|
||||
|
||||
static int __muntrack(char *addr, size_t size, struct Map **deleted,
|
||||
struct Map **untracked, struct Map temp[2]) {
|
||||
int rc = 0;
|
||||
|
@ -159,13 +181,14 @@ static int __muntrack(char *addr, size_t size, struct Map **deleted,
|
|||
struct Map *map;
|
||||
struct Map *next;
|
||||
size = PGUP(size);
|
||||
ASSERT(__maps_held());
|
||||
if (!(map = __maps_floor(addr)))
|
||||
map = __maps_first();
|
||||
for (; map && map->addr <= addr + size; map = next) {
|
||||
next = __maps_next(map);
|
||||
char *map_addr = map->addr;
|
||||
size_t map_size = map->size;
|
||||
if (!(MAX(addr, map_addr) < MIN(addr + size, map_addr + PGUP(map_size))))
|
||||
if (MAX(addr, map_addr) >= MIN(addr + size, map_addr + PGUP(map_size)))
|
||||
continue;
|
||||
if (addr <= map_addr && addr + size >= map_addr + PGUP(map_size)) {
|
||||
if (map->hand == MAPS_RESERVATION)
|
||||
|
@ -350,6 +373,7 @@ static bool __maps_mergeable(const struct Map *x, const struct Map *y) {
|
|||
void __maps_insert(struct Map *map) {
|
||||
struct Map *left, *right;
|
||||
ASSERT(map->size);
|
||||
ASSERT(__maps_held());
|
||||
ASSERT(!__maps_overlaps(map->addr, map->size));
|
||||
__maps.pages += (map->size + __pagesize - 1) / __pagesize;
|
||||
|
||||
|
@ -460,7 +484,7 @@ static int __munmap(char *addr, size_t size) {
|
|||
return einval();
|
||||
|
||||
// test for signal handler tragedy
|
||||
if (__maps_held())
|
||||
if (__maps_reentrant())
|
||||
return edeadlk();
|
||||
|
||||
// lock the memory manager
|
||||
|
@ -469,8 +493,10 @@ static int __munmap(char *addr, size_t size) {
|
|||
|
||||
// on windows we can only unmap whole allocations
|
||||
if (IsWindows())
|
||||
if (!__maps_envelops(addr, size))
|
||||
if (!__maps_envelops(addr, size)) {
|
||||
__maps_unlock();
|
||||
return enotsup();
|
||||
}
|
||||
|
||||
// untrack mappings
|
||||
int rc;
|
||||
|
@ -500,6 +526,7 @@ void *__maps_randaddr(void) {
|
|||
}
|
||||
|
||||
static void *__maps_pickaddr(size_t size) {
|
||||
ASSERT(__maps_held());
|
||||
char *addr = 0;
|
||||
struct Map *map, *prev;
|
||||
size = GRUP(size);
|
||||
|
@ -569,11 +596,15 @@ static void *__mmap_impl(char *addr, size_t size, int prot, int flags, int fd,
|
|||
noreplace = true;
|
||||
sysflags |= MAP_FIXED_NOREPLACE_linux;
|
||||
} else if (IsFreebsd() || IsNetbsd()) {
|
||||
// todo: insert a reservation like windows
|
||||
sysflags |= MAP_FIXED;
|
||||
__maps_lock();
|
||||
if (__maps_overlaps(addr, size)) {
|
||||
__maps_unlock();
|
||||
__maps_free(map);
|
||||
return (void *)eexist();
|
||||
}
|
||||
__maps_unlock();
|
||||
} else {
|
||||
noreplace = true;
|
||||
}
|
||||
|
@ -729,7 +760,7 @@ static void *__mmap(char *addr, size_t size, int prot, int flags, int fd,
|
|||
return (void *)enomem();
|
||||
|
||||
// test for signal handler reentry
|
||||
if (__maps_held())
|
||||
if (__maps_reentrant())
|
||||
return (void *)edeadlk();
|
||||
|
||||
// create memory mappping
|
||||
|
@ -874,7 +905,7 @@ static void *__mremap(char *old_addr, size_t old_size, size_t new_size,
|
|||
return (void *)enomem();
|
||||
|
||||
// test for signal handler reentry
|
||||
if (__maps_held())
|
||||
if (__maps_reentrant())
|
||||
return (void *)edeadlk();
|
||||
|
||||
// lock the memory manager
|
||||
|
|
|
@ -67,16 +67,17 @@ int __mprotect(char *addr, size_t size, int prot) {
|
|||
size = (size + pagesz - 1) & -pagesz;
|
||||
|
||||
// test for signal handler reentry
|
||||
if (__maps_held())
|
||||
if (__maps_reentrant())
|
||||
return edeadlk();
|
||||
|
||||
// change mappings
|
||||
int rc = 0;
|
||||
bool found = false;
|
||||
__maps_lock();
|
||||
struct Map *map, *floor;
|
||||
floor = __maps_floor(addr);
|
||||
for (map = floor; map && map->addr <= addr + size; map = __maps_next(map)) {
|
||||
struct Map *map;
|
||||
if (!(map = __maps_floor(addr)))
|
||||
map = __maps_first();
|
||||
for (; map && map->addr <= addr + size; map = __maps_next(map)) {
|
||||
char *map_addr = map->addr;
|
||||
size_t map_size = map->size;
|
||||
char *beg = MAX(addr, map_addr);
|
||||
|
@ -85,7 +86,7 @@ int __mprotect(char *addr, size_t size, int prot) {
|
|||
continue;
|
||||
found = true;
|
||||
if (addr <= map_addr && addr + size >= map_addr + PGUP(map_size)) {
|
||||
// change protection of entire mapping
|
||||
// change protection status of pages
|
||||
if (!__mprotect_chunk(map_addr, map_size, prot, map->iscow)) {
|
||||
map->prot = prot;
|
||||
} else {
|
||||
|
|
|
@ -31,32 +31,29 @@ textwindows int sys_msync_nt(char *addr, size_t size, int flags) {
|
|||
|
||||
if ((uintptr_t)addr & (__pagesize - 1))
|
||||
return einval();
|
||||
if (__maps_held())
|
||||
if (__maps_reentrant())
|
||||
return edeadlk();
|
||||
|
||||
int rc = 0;
|
||||
__maps_lock();
|
||||
struct Map *map, *next;
|
||||
struct Map *next, *map;
|
||||
if (!(map = __maps_floor(addr)))
|
||||
if (!(map = __maps_first()))
|
||||
return true;
|
||||
for (; map; map = next) {
|
||||
map = __maps_first();
|
||||
for (; map && map->addr <= addr + size; map = next) {
|
||||
next = __maps_next(map);
|
||||
if (!__maps_isalloc(map))
|
||||
continue;
|
||||
if (map->flags & MAP_ANONYMOUS)
|
||||
continue;
|
||||
if (MAX(addr, map->addr) >= MIN(addr + size, map->addr + map->size))
|
||||
break; // didn't overlap mapping
|
||||
continue; // didn't overlap mapping
|
||||
|
||||
// get true size of win32 allocation
|
||||
size_t allocsize = map->size;
|
||||
for (struct Map *map2 = next; map2; map2 = __maps_next(map2)) {
|
||||
if (!__maps_isalloc(map2) && map->addr + allocsize == map2->addr) {
|
||||
allocsize += map2->size;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
while (next && !__maps_isalloc(next) &&
|
||||
next->addr + allocsize == next->addr) {
|
||||
allocsize += next->size;
|
||||
next = __maps_next(next);
|
||||
}
|
||||
|
||||
// perform the flush
|
||||
|
|
|
@ -47,6 +47,7 @@
|
|||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/runtime/stack.h"
|
||||
#include "libc/runtime/symbols.internal.h"
|
||||
#include "libc/runtime/syslib.internal.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/auxv.h"
|
||||
|
|
|
@ -82,11 +82,7 @@ void ShowCrashReports(void) {
|
|||
ss.ss_sp = crashstack;
|
||||
unassert(!sigaltstack(&ss, 0));
|
||||
InstallCrashHandler(SIGQUIT, 0);
|
||||
#ifdef __x86_64__
|
||||
InstallCrashHandler(SIGTRAP, 0);
|
||||
#else
|
||||
InstallCrashHandler(SIGTRAP, 0);
|
||||
#endif
|
||||
InstallCrashHandler(SIGFPE, 0);
|
||||
InstallCrashHandler(SIGILL, 0);
|
||||
InstallCrashHandler(SIGBUS, 0);
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/directmap.h"
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
#include "libc/errno.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/intrin/dll.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/strace.h"
|
||||
#include "libc/nt/console.h"
|
||||
#include "libc/nt/enum/creationdisposition.h"
|
||||
|
@ -84,6 +83,23 @@ textwindows int sys_kill_nt(int pid, int sig) {
|
|||
}
|
||||
}
|
||||
|
||||
// attempt to signal via shared memory file
|
||||
//
|
||||
// now that we know the process exists, if it has a shared memory file
|
||||
// then we can be reasonably certain it's a cosmo process which should
|
||||
// be trusted to deliver its signal, unless it's a nine exterminations
|
||||
if (pid > 0 && sig != 9) {
|
||||
atomic_ulong *sigproc;
|
||||
if ((sigproc = __sig_map_process(pid, kNtOpenExisting))) {
|
||||
if (sig > 0)
|
||||
atomic_fetch_or_explicit(sigproc, 1ull << (sig - 1),
|
||||
memory_order_release);
|
||||
UnmapViewOfFile(sigproc);
|
||||
if (sig != 9)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// find existing handle we own for process
|
||||
//
|
||||
// this step should come first to verify process existence. this is
|
||||
|
@ -91,31 +107,9 @@ textwindows int sys_kill_nt(int pid, int sig) {
|
|||
// file exists, the process actually exists.
|
||||
int64_t handle, closeme = 0;
|
||||
if (!(handle = __proc_handle(pid))) {
|
||||
if ((handle = OpenProcess(kNtProcessTerminate, false, pid))) {
|
||||
STRACE("warning: kill() using raw win32 pid");
|
||||
closeme = handle;
|
||||
} else {
|
||||
goto OnError;
|
||||
}
|
||||
}
|
||||
|
||||
// attempt to signal via shared memory file
|
||||
//
|
||||
// now that we know the process exists, if it has a shared memory file
|
||||
// then we can be reasonably certain it's a cosmo process which should
|
||||
// be trusted to deliver its signal, unless it's a nine exterminations
|
||||
if (pid > 0) {
|
||||
atomic_ulong *sigproc;
|
||||
if ((sigproc = __sig_map_process(pid, kNtOpenExisting))) {
|
||||
if (sig > 0)
|
||||
atomic_fetch_or_explicit(sigproc, 1ull << (sig - 1),
|
||||
memory_order_release);
|
||||
UnmapViewOfFile(sigproc);
|
||||
if (closeme)
|
||||
CloseHandle(closeme);
|
||||
if (sig != 9)
|
||||
return 0;
|
||||
}
|
||||
if (!(handle = OpenProcess(kNtProcessTerminate, false, pid)))
|
||||
return eperm();
|
||||
closeme = handle;
|
||||
}
|
||||
|
||||
// perform actual kill
|
||||
|
@ -127,16 +121,7 @@ textwindows int sys_kill_nt(int pid, int sig) {
|
|||
CloseHandle(closeme);
|
||||
if (ok)
|
||||
return 0;
|
||||
|
||||
// handle error
|
||||
OnError:
|
||||
switch (GetLastError()) {
|
||||
case kNtErrorInvalidHandle:
|
||||
case kNtErrorInvalidParameter:
|
||||
return esrch();
|
||||
default:
|
||||
return eperm();
|
||||
}
|
||||
return esrch();
|
||||
}
|
||||
|
||||
#endif /* __x86_64__ */
|
||||
|
|
|
@ -112,7 +112,7 @@ __msabi extern typeof(GetCurrentThreadId) *const __imp_GetCurrentThreadId;
|
|||
__msabi extern typeof(TlsSetValue) *const __imp_TlsSetValue;
|
||||
__msabi extern typeof(WakeByAddressAll) *const __imp_WakeByAddressAll;
|
||||
|
||||
static textwindows dontinstrument wontreturn void //
|
||||
textwindows dontinstrument wontreturn static void //
|
||||
WinThreadEntry(int rdi, // rcx
|
||||
int rsi, // rdx
|
||||
int rdx, // r8
|
||||
|
@ -185,7 +185,7 @@ asm("XnuThreadThunk:\n\t"
|
|||
".size\tXnuThreadThunk,.-XnuThreadThunk");
|
||||
__attribute__((__used__))
|
||||
|
||||
static wontreturn void
|
||||
static dontinstrument wontreturn void
|
||||
XnuThreadMain(void *pthread, // rdi
|
||||
int tid, // rsi
|
||||
int (*func)(void *arg, int tid), // rdx
|
||||
|
@ -265,7 +265,7 @@ static errno_t CloneXnu(int (*fn)(void *), char *stk, size_t stksz, int flags,
|
|||
|
||||
// we can't use address sanitizer because:
|
||||
// 1. __asan_handle_no_return wipes stack [todo?]
|
||||
relegated static wontreturn void OpenbsdThreadMain(void *p) {
|
||||
relegated dontinstrument wontreturn static void OpenbsdThreadMain(void *p) {
|
||||
struct CloneArgs *wt = p;
|
||||
atomic_init(wt->ptid, wt->tid);
|
||||
atomic_init(wt->ctid, wt->tid);
|
||||
|
@ -318,11 +318,12 @@ relegated errno_t CloneOpenbsd(int (*func)(void *, int), char *stk,
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
// NET BESIYATA DISHMAYA
|
||||
|
||||
static wontreturn void NetbsdThreadMain(void *arg, // rdi
|
||||
int (*func)(void *, int), // rsi
|
||||
int flags, // rdx
|
||||
atomic_int *ctid, // rcx
|
||||
atomic_int *ptid) { // r8
|
||||
wontreturn dontinstrument static void NetbsdThreadMain(
|
||||
void *arg, // rdi
|
||||
int (*func)(void *, int), // rsi
|
||||
int flags, // rdx
|
||||
atomic_int *ctid, // rcx
|
||||
atomic_int *ptid) { // r8
|
||||
int ax, dx;
|
||||
static atomic_int clobber;
|
||||
atomic_int *ztid = &clobber;
|
||||
|
@ -420,7 +421,7 @@ static int CloneNetbsd(int (*func)(void *, int), char *stk, size_t stksz,
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
// FREE BESIYATA DISHMAYA
|
||||
|
||||
static wontreturn void FreebsdThreadMain(void *p) {
|
||||
wontreturn dontinstrument static void FreebsdThreadMain(void *p) {
|
||||
struct CloneArgs *wt = p;
|
||||
#ifdef __aarch64__
|
||||
asm volatile("mov\tx28,%0" : /* no outputs */ : "r"(wt->tls));
|
||||
|
@ -519,7 +520,7 @@ static errno_t CloneFreebsd(int (*func)(void *, int), char *stk, size_t stksz,
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
// APPLE SILICON
|
||||
|
||||
static void *SiliconThreadMain(void *arg) {
|
||||
dontinstrument static void *SiliconThreadMain(void *arg) {
|
||||
struct CloneArgs *wt = arg;
|
||||
asm volatile("mov\tx28,%0" : /* no outputs */ : "r"(wt->tls));
|
||||
atomic_init(wt->ctid, wt->this);
|
||||
|
@ -595,7 +596,7 @@ int sys_clone_linux(int flags, // rdi
|
|||
void *func, // r9
|
||||
void *arg); // 8(rsp)
|
||||
|
||||
static int LinuxThreadEntry(void *arg, int tid) {
|
||||
dontinstrument static int LinuxThreadEntry(void *arg, int tid) {
|
||||
struct LinuxCloneArgs *wt = arg;
|
||||
#if defined(__x86_64__)
|
||||
sys_set_tls(ARCH_SET_GS, wt->tls);
|
||||
|
|
|
@ -119,6 +119,7 @@ privileged int __hook(void *dest, struct SymbolTable *st) {
|
|||
if (!st)
|
||||
return -1;
|
||||
__morph_begin();
|
||||
__jit_begin();
|
||||
lowest = MAX((intptr_t)__executable_start, (intptr_t)_ereal);
|
||||
for (i = 0; i < st->count; ++i) {
|
||||
if (st->symbols[i].x < 9)
|
||||
|
@ -138,6 +139,9 @@ privileged int __hook(void *dest, struct SymbolTable *st) {
|
|||
// kprintf("can't hook %t at %lx\n", p, p);
|
||||
}
|
||||
}
|
||||
__clear_cache(MAX((char *)__executable_start, (char *)_ereal),
|
||||
MIN((char *)__privileged_start, (char *)_etext));
|
||||
__jit_end();
|
||||
__morph_end();
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/runtime/syslib.internal.h"
|
||||
|
||||
void __jit_begin(void) {
|
||||
privileged void __jit_begin(void) {
|
||||
if (IsXnuSilicon()) {
|
||||
if (__syslib->__pthread_jit_write_protect_supported_np()) {
|
||||
__syslib->__pthread_jit_write_protect_np(false);
|
||||
|
@ -28,7 +28,7 @@ void __jit_begin(void) {
|
|||
}
|
||||
}
|
||||
|
||||
void __jit_end(void) {
|
||||
privileged void __jit_end(void) {
|
||||
if (IsXnuSilicon()) {
|
||||
if (__syslib->__pthread_jit_write_protect_supported_np()) {
|
||||
__syslib->__pthread_jit_write_protect_np(true);
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/macros.h"
|
||||
#include "libc/runtime/zipos.internal.h"
|
||||
#include "libc/str/str.h"
|
||||
|
@ -44,9 +43,8 @@ ssize_t __zipos_scan(struct Zipos *zipos, struct ZiposUri *name) {
|
|||
|
||||
// strip trailing slash from search name
|
||||
int len = name->len;
|
||||
if (len && name->path[len - 1] == '/') {
|
||||
if (len && name->path[len - 1] == '/')
|
||||
--len;
|
||||
}
|
||||
|
||||
// empty string means the /zip root directory
|
||||
if (!len) {
|
||||
|
@ -91,9 +89,8 @@ ssize_t __zipos_scan(struct Zipos *zipos, struct ZiposUri *name) {
|
|||
dx = dx < -1 ? -1 : dx;
|
||||
for (l += dx; 0 <= l && l < zipos->records; l += dx) {
|
||||
ssize_t cf;
|
||||
if ((cf = __zipos_match(zipos, name, len, l)) != -1) {
|
||||
if ((cf = __zipos_match(zipos, name, len, l)) != -1)
|
||||
return cf;
|
||||
}
|
||||
cfile = zipos->index[l];
|
||||
zname = ZIP_CFILE_NAME(zipos->map + cfile);
|
||||
zsize = ZIP_CFILE_NAMESIZE(zipos->map + cfile);
|
||||
|
|
|
@ -118,9 +118,10 @@ int main(int argc, char *argv[]) {
|
|||
|
||||
GetOpts(argc, argv);
|
||||
|
||||
for (fd = 3; fd < 100; ++fd) {
|
||||
int oe = errno;
|
||||
for (fd = 3; fd < 100; ++fd)
|
||||
close(fd);
|
||||
}
|
||||
errno = oe;
|
||||
|
||||
#ifndef TINY
|
||||
setenv("GDB", "", true);
|
||||
|
|
|
@ -94,7 +94,6 @@ TEST(dup2, zipossrc) {
|
|||
ASSERT_SYS(0, 0, close(3));
|
||||
}
|
||||
|
||||
#ifdef __x86_64__
|
||||
TEST(dup, clearsCloexecFlag) {
|
||||
static bool once;
|
||||
int ws;
|
||||
|
@ -112,4 +111,3 @@ TEST(dup, clearsCloexecFlag) {
|
|||
ASSERT_EQ(72 << 8, ws);
|
||||
ASSERT_SYS(0, 0, close(3));
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -39,8 +39,6 @@
|
|||
#include "libc/x/xsigaction.h"
|
||||
#include "libc/x/xspawn.h"
|
||||
|
||||
#ifdef __x86_64__
|
||||
|
||||
#define MEM (64 * 1024 * 1024)
|
||||
|
||||
static char tmpname[PATH_MAX];
|
||||
|
@ -104,7 +102,7 @@ TEST(setrlimit, testFileSizeLimit) {
|
|||
firstnonnull(getenv("TMPDIR"), "/tmp"),
|
||||
firstnonnull(program_invocation_short_name, "unknown"), getpid());
|
||||
ASSERT_NE(-1, (fd = open(tmpname, O_RDWR | O_CREAT | O_TRUNC, 0644)));
|
||||
rngset(junkdata, 512, _rand64, -1);
|
||||
rngset(junkdata, 512, lemur64, -1);
|
||||
for (i = 0; i < 5 * 1024 * 1024 / 512; ++i) {
|
||||
ASSERT_EQ(512, write(fd, junkdata, 512));
|
||||
}
|
||||
|
@ -143,7 +141,7 @@ TEST(setrlimit, testMemoryLimit) {
|
|||
ASSERT_EQ(ENOMEM, errno);
|
||||
_Exit(0);
|
||||
}
|
||||
rngset(p, getpagesize(), _rand64, -1);
|
||||
rngset(p, getpagesize(), lemur64, -1);
|
||||
}
|
||||
_Exit(1);
|
||||
}
|
||||
|
@ -160,14 +158,13 @@ TEST(setrlimit, testVirtualMemoryLimit) {
|
|||
if (wstatus == -2) {
|
||||
ASSERT_EQ(0, setrlimit(RLIMIT_AS, &(struct rlimit){MEM, MEM}));
|
||||
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) {
|
||||
if ((p = mmap(0, getpagesize(), PROT_READ | PROT_WRITE,
|
||||
MAP_ANONYMOUS | MAP_PRIVATE | MAP_POPULATE, -1, 0)) ==
|
||||
MAP_FAILED) {
|
||||
ASSERT_EQ(ENOMEM, errno);
|
||||
_Exit(0);
|
||||
}
|
||||
rngset(p, getpagesize(), _rand64, -1);
|
||||
rngset(p, getpagesize(), lemur64, -1);
|
||||
}
|
||||
_Exit(1);
|
||||
}
|
||||
|
@ -201,7 +198,7 @@ TEST(setrlimit, testDataMemoryLimit) {
|
|||
ASSERT_EQ(ENOMEM, errno);
|
||||
_Exit(0);
|
||||
}
|
||||
rngset(p, getpagesize(), _rand64, -1);
|
||||
rngset(p, getpagesize(), lemur64, -1);
|
||||
}
|
||||
_Exit(1);
|
||||
}
|
||||
|
@ -243,5 +240,3 @@ TEST(setrlimit, isVforkSafe) {
|
|||
EXPECT_EQ(rlim[0].rlim_cur, rlim[1].rlim_cur);
|
||||
EXPECT_EQ(rlim[0].rlim_max, rlim[1].rlim_max);
|
||||
}
|
||||
|
||||
#endif /* __x86_64__ */
|
||||
|
|
|
@ -1,105 +0,0 @@
|
|||
/*-*- 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 2020 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/calls/calls.h"
|
||||
#include "libc/calls/struct/sigaction.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/calls/ucontext.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/log/check.h"
|
||||
#include "libc/log/log.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/sysv/consts/sa.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/testlib/ezbench.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
|
||||
void OnUsr1(int sig) {
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
void SetUpOnce(void) {
|
||||
sigset_t ss;
|
||||
sigprocmask(SIG_SETMASK, 0, &ss);
|
||||
ASSERT_SYS(0, 0, pledge("stdio proc", 0));
|
||||
}
|
||||
|
||||
TEST(signal, test) {
|
||||
ASSERT_NE(SIG_ERR, signal(SIGUSR1, OnUsr1));
|
||||
ASSERT_NE(-1, raise(SIGUSR1));
|
||||
__die();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// signal round-trip delivery takes about 1µs
|
||||
|
||||
void OnSigTrap(int sig, siginfo_t *si, void *ctx) {
|
||||
}
|
||||
|
||||
void TrapBench(int n) {
|
||||
for (int i = 0; i < n; ++i) {
|
||||
DebugBreak();
|
||||
}
|
||||
}
|
||||
|
||||
BENCH(signal, trapBench) {
|
||||
struct sigaction old;
|
||||
struct sigaction sabus = {.sa_sigaction = OnSigTrap};
|
||||
ASSERT_SYS(0, 0, sigaction(SIGTRAP, &sabus, &old));
|
||||
EZBENCH_N("signal trap", 16, TrapBench(16));
|
||||
EZBENCH_N("signal trap", 256, TrapBench(256));
|
||||
EZBENCH_N("signal trap", 1024, TrapBench(1024));
|
||||
sigaction(SIGTRAP, &old, 0);
|
||||
}
|
||||
|
||||
BENCH(signal, trapBenchSiginfo) {
|
||||
struct sigaction old;
|
||||
struct sigaction sabus = {.sa_sigaction = OnSigTrap, .sa_flags = SA_SIGINFO};
|
||||
ASSERT_SYS(0, 0, sigaction(SIGTRAP, &sabus, &old));
|
||||
EZBENCH_N("siginfo trap", 16, TrapBench(16));
|
||||
EZBENCH_N("siginfo trap", 256, TrapBench(256));
|
||||
EZBENCH_N("siginfo trap", 1024, TrapBench(1024));
|
||||
sigaction(SIGTRAP, &old, 0);
|
||||
}
|
||||
|
||||
#ifdef __x86_64__
|
||||
|
||||
void OnSigHlt(int sig, siginfo_t *si, void *vctx) {
|
||||
struct ucontext *ctx = vctx;
|
||||
ctx->uc_mcontext.rip += 1;
|
||||
}
|
||||
|
||||
void HltBench(int n) {
|
||||
for (int i = 0; i < n; ++i) {
|
||||
asm("hlt");
|
||||
}
|
||||
}
|
||||
|
||||
BENCH(signal, hltBenchSiginfo) {
|
||||
struct sigaction old[2];
|
||||
struct sigaction sabus = {.sa_sigaction = OnSigHlt, .sa_flags = SA_SIGINFO};
|
||||
ASSERT_SYS(0, 0, sigaction(SIGSEGV, &sabus, old + 0));
|
||||
ASSERT_SYS(0, 0, sigaction(SIGBUS, &sabus, old + 1));
|
||||
EZBENCH_N("siginfo hlt", 16, HltBench(16));
|
||||
EZBENCH_N("siginfo hlt", 256, HltBench(256));
|
||||
EZBENCH_N("siginfo hlt", 1024, HltBench(1024));
|
||||
sigaction(SIGSEGV, old + 0, 0);
|
||||
sigaction(SIGBUS, old + 1, 0);
|
||||
}
|
||||
|
||||
#endif /* __x86_64__ */
|
|
@ -17,6 +17,7 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "ape/sections.internal.h"
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
|
@ -27,12 +28,14 @@
|
|||
#include "libc/runtime/sysconf.h"
|
||||
#include "libc/stdio/rand.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/stdio/sysparam.h"
|
||||
#include "libc/str/str.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/testlib/benchmark.h"
|
||||
#include "libc/testlib/subprocess.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
#include "libc/x/xspawn.h"
|
||||
|
||||
|
@ -56,6 +59,10 @@ void SetUpOnce(void) {
|
|||
// ASSERT_SYS(0, 0, pledge("stdio rpath wpath cpath proc", 0));
|
||||
}
|
||||
|
||||
void TearDown(void) {
|
||||
ASSERT_FALSE(__maps_held());
|
||||
}
|
||||
|
||||
TEST(mmap, zeroSize) {
|
||||
ASSERT_SYS(EINVAL, MAP_FAILED,
|
||||
mmap(NULL, 0, PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0));
|
||||
|
@ -331,6 +338,172 @@ TEST(mmap, pml5t) {
|
|||
}
|
||||
}
|
||||
|
||||
TEST(mmap, windows) {
|
||||
if (!IsWindows())
|
||||
return;
|
||||
int count = __maps.count;
|
||||
char *base = __maps_randaddr();
|
||||
|
||||
ASSERT_EQ(base, mmap(base, pagesz * 3, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
|
||||
ASSERT_EQ((count += 1), __maps.count);
|
||||
|
||||
// isn't granularity aligned
|
||||
ASSERT_SYS(EINVAL, -1, munmap(base + pagesz, pagesz));
|
||||
|
||||
// doesn't overlap any maps
|
||||
ASSERT_SYS(0, 0, munmap(base + gransz, pagesz));
|
||||
ASSERT_EQ(count, __maps.count);
|
||||
|
||||
// doesn't overlap any maps
|
||||
ASSERT_SYS(0, 0, munmap(base - gransz, gransz));
|
||||
ASSERT_EQ(count, __maps.count);
|
||||
|
||||
// partially overlaps map
|
||||
ASSERT_SYS(ENOTSUP, -1, munmap(base, pagesz));
|
||||
ASSERT_EQ(count, __maps.count);
|
||||
|
||||
// envelops map
|
||||
ASSERT_SYS(0, 0, munmap(base - gransz, gransz + pagesz * 4));
|
||||
ASSERT_EQ((count -= 1), __maps.count);
|
||||
|
||||
// win32 actually unmapped map
|
||||
ASSERT_EQ(base, mmap(base, pagesz * 3, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
|
||||
ASSERT_EQ((count += 1), __maps.count);
|
||||
|
||||
// change status of middle page results in three fragments
|
||||
ASSERT_SYS(0, 0, mprotect(base + pagesz, pagesz, PROT_NONE));
|
||||
ASSERT_EQ((count += 2), __maps.count);
|
||||
|
||||
// change status back (todo: should reunite fragments)
|
||||
ASSERT_SYS(0, 0, mprotect(base + pagesz, pagesz, PROT_READ | PROT_WRITE));
|
||||
ASSERT_EQ(count, __maps.count);
|
||||
|
||||
// clean up
|
||||
ASSERT_SYS(0, 0, munmap(base, pagesz * 3));
|
||||
ASSERT_EQ((count -= 3), __maps.count);
|
||||
}
|
||||
|
||||
TEST(mmap, windows_partial_overlap_enotsup) {
|
||||
if (!IsWindows())
|
||||
return;
|
||||
int count = __maps.count;
|
||||
char *base = __maps_randaddr();
|
||||
|
||||
ASSERT_EQ(base, mmap(base, gransz * 3, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
|
||||
ASSERT_EQ((count += 1), __maps.count);
|
||||
|
||||
// partially overlaps on left
|
||||
ASSERT_SYS(ENOTSUP, -1, munmap(base - gransz, gransz * 2));
|
||||
ASSERT_SYS(ENOTSUP, -1, munmap(base, gransz * 2));
|
||||
ASSERT_EQ(count, __maps.count);
|
||||
|
||||
// partially overlaps the middle
|
||||
ASSERT_SYS(ENOTSUP, -1, munmap(base + gransz * 1, gransz));
|
||||
ASSERT_SYS(ENOTSUP, -1, munmap(base + gransz * 1, gransz * 2));
|
||||
ASSERT_EQ(count, __maps.count);
|
||||
|
||||
// partially overlaps on right
|
||||
ASSERT_SYS(ENOTSUP, -1, munmap(base + gransz * 2, gransz * 2));
|
||||
ASSERT_EQ(count, __maps.count);
|
||||
|
||||
// doesn't overlap any maps
|
||||
ASSERT_SYS(0, 0, munmap(base - gransz, gransz));
|
||||
ASSERT_SYS(0, 0, munmap(base + gransz * 3, gransz));
|
||||
ASSERT_EQ(count, __maps.count);
|
||||
|
||||
// unmap envelops
|
||||
ASSERT_SYS(0, 0, munmap(base - gransz, gransz * 4));
|
||||
ASSERT_EQ((count -= 1), __maps.count);
|
||||
|
||||
// win32 actually removed the memory
|
||||
ASSERT_EQ(base, mmap(base, gransz * 3, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
|
||||
ASSERT_EQ((count += 1), __maps.count);
|
||||
|
||||
// clean up
|
||||
ASSERT_SYS(0, 0, munmap(base, gransz * 3));
|
||||
ASSERT_EQ((count -= 1), __maps.count);
|
||||
}
|
||||
|
||||
TEST(munmap, windows_not_all_fragments_included_enotsup) {
|
||||
if (!IsWindows())
|
||||
return;
|
||||
int count = __maps.count;
|
||||
char *base = __maps_randaddr();
|
||||
|
||||
ASSERT_EQ(base, mmap(base, gransz * 3, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
|
||||
ASSERT_EQ((count += 1), __maps.count);
|
||||
|
||||
// win32 memory actually exists
|
||||
ASSERT_SYS(EEXIST, MAP_FAILED,
|
||||
mmap(base, gransz * 3, PROT_READ | PROT_WRITE,
|
||||
MAP_FIXED_NOREPLACE | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
|
||||
ASSERT_SYS(EEXIST, MAP_FAILED,
|
||||
mmap(base + gransz * 0, gransz, PROT_READ | PROT_WRITE,
|
||||
MAP_FIXED_NOREPLACE | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
|
||||
ASSERT_SYS(EEXIST, MAP_FAILED,
|
||||
mmap(base + gransz * 1, gransz, PROT_READ | PROT_WRITE,
|
||||
MAP_FIXED_NOREPLACE | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
|
||||
ASSERT_SYS(EEXIST, MAP_FAILED,
|
||||
mmap(base + gransz * 2, gransz, PROT_READ | PROT_WRITE,
|
||||
MAP_FIXED_NOREPLACE | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
|
||||
|
||||
// change status of middle page results in three fragments
|
||||
ASSERT_SYS(0, 0, mprotect(base + gransz, gransz, PROT_NONE));
|
||||
ASSERT_EQ((count += 2), __maps.count);
|
||||
|
||||
// partially overlaps on left
|
||||
ASSERT_SYS(ENOTSUP, -1, munmap(base - gransz, gransz * 2));
|
||||
ASSERT_SYS(ENOTSUP, -1, munmap(base, gransz * 2));
|
||||
ASSERT_EQ(count, __maps.count);
|
||||
|
||||
// partially overlaps the middle
|
||||
ASSERT_SYS(ENOTSUP, -1, munmap(base + gransz * 1, gransz));
|
||||
ASSERT_SYS(ENOTSUP, -1, munmap(base + gransz * 1, gransz * 2));
|
||||
ASSERT_EQ(count, __maps.count);
|
||||
|
||||
// partially overlaps on right
|
||||
ASSERT_SYS(ENOTSUP, -1, munmap(base + gransz * 2, gransz * 2));
|
||||
ASSERT_EQ(count, __maps.count);
|
||||
|
||||
// doesn't overlap any maps
|
||||
ASSERT_SYS(0, 0, munmap(base - gransz, gransz));
|
||||
ASSERT_SYS(0, 0, munmap(base + gransz * 3, gransz));
|
||||
ASSERT_EQ(count, __maps.count);
|
||||
|
||||
// unmap envelops
|
||||
ASSERT_SYS(0, 0, munmap(base - gransz, gransz * 4));
|
||||
ASSERT_EQ((count -= 3), __maps.count);
|
||||
|
||||
// win32 actually removed the memory
|
||||
ASSERT_EQ(base, mmap(base, gransz * 3, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
|
||||
ASSERT_EQ((count += 1), __maps.count);
|
||||
|
||||
// clean up
|
||||
ASSERT_SYS(0, 0, munmap(base, gransz * 3));
|
||||
ASSERT_EQ((count -= 1), __maps.count);
|
||||
}
|
||||
|
||||
TEST(mmap, windows_private_memory_fork_uses_virtualfree) {
|
||||
if (IsFreebsd())
|
||||
return; // freebsd can't take a hint
|
||||
char *base;
|
||||
ASSERT_NE(MAP_FAILED, (base = mmap(0, gransz * 3, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)));
|
||||
SPAWN(fork);
|
||||
ASSERT_SYS(0, 0, munmap(base, gransz * 3));
|
||||
ASSERT_EQ(base, mmap(base, gransz * 3, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
|
||||
ASSERT_SYS(0, 0, munmap(base, gransz * 3));
|
||||
EXITS(0);
|
||||
ASSERT_SYS(0, 0, munmap(base, gransz * 3));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// zipos NON-SHARED READ-ONLY FILE MEMORY
|
||||
|
||||
|
|
|
@ -178,6 +178,9 @@ void search_test(void) {
|
|||
// ↑ ↑ ↑
|
||||
// 4 3 8
|
||||
//
|
||||
x = tree_floor(tree, (void *)0l, number_search);
|
||||
if (x)
|
||||
exit(4);
|
||||
x = tree_floor(tree, (void *)4l, number_search);
|
||||
if (!x)
|
||||
exit(4);
|
||||
|
|
|
@ -5,14 +5,8 @@ PKGS += TEST_LIBC_LOG
|
|||
|
||||
TEST_LIBC_LOG_SRCS := $(wildcard test/libc/log/*.c)
|
||||
TEST_LIBC_LOG_SRCS_TEST = $(filter %_test.c,$(TEST_LIBC_LOG_SRCS))
|
||||
|
||||
TEST_LIBC_LOG_OBJS = \
|
||||
$(TEST_LIBC_LOG_SRCS:%.c=o/$(MODE)/%.o) \
|
||||
o/$(MODE)/test/libc/log/backtrace.zip.o \
|
||||
o/$(MODE)/test/libc/log/backtrace.dbg.zip.o
|
||||
|
||||
TEST_LIBC_LOG_COMS = \
|
||||
$(TEST_LIBC_LOG_SRCS:%.c=o/$(MODE)/%)
|
||||
TEST_LIBC_LOG_OBJS = $(TEST_LIBC_LOG_SRCS:%.c=o/$(MODE)/%.o)
|
||||
TEST_LIBC_LOG_COMS = $(TEST_LIBC_LOG_SRCS:%.c=o/$(MODE)/%)
|
||||
|
||||
TEST_LIBC_LOG_BINS = \
|
||||
$(TEST_LIBC_LOG_COMS) \
|
||||
|
@ -26,19 +20,17 @@ TEST_LIBC_LOG_CHECKS = \
|
|||
|
||||
TEST_LIBC_LOG_DIRECTDEPS = \
|
||||
LIBC_CALLS \
|
||||
LIBC_RUNTIME \
|
||||
NET_HTTP \
|
||||
LIBC_STDIO \
|
||||
LIBC_X \
|
||||
LIBC_INTRIN \
|
||||
LIBC_FMT \
|
||||
LIBC_INTRIN \
|
||||
LIBC_LOG \
|
||||
LIBC_MEM \
|
||||
LIBC_NEXGEN32E \
|
||||
LIBC_LOG \
|
||||
LIBC_PROC \
|
||||
LIBC_RUNTIME \
|
||||
LIBC_STDIO \
|
||||
LIBC_STR \
|
||||
LIBC_TESTLIB \
|
||||
LIBC_SYSV \
|
||||
LIBC_LOG
|
||||
LIBC_TESTLIB \
|
||||
|
||||
TEST_LIBC_LOG_DEPS := \
|
||||
$(call uniq,$(foreach x,$(TEST_LIBC_LOG_DIRECTDEPS),$($(x))))
|
||||
|
@ -56,29 +48,6 @@ o/$(MODE)/test/libc/log/%.dbg: \
|
|||
$(APE_NO_MODIFY_SELF)
|
||||
@$(APELINK)
|
||||
|
||||
o/$(MODE)/test/libc/log/backtrace_test.dbg: \
|
||||
$(TEST_LIBC_LOG_DEPS) \
|
||||
o/$(MODE)/test/libc/log/backtrace.zip.o \
|
||||
o/$(MODE)/test/libc/log/backtrace.dbg.zip.o \
|
||||
o/$(MODE)/test/libc/log/backtrace_test.o \
|
||||
o/$(MODE)/test/libc/log/log.pkg \
|
||||
$(LIBC_TESTMAIN) \
|
||||
$(CRT) \
|
||||
$(APE_NO_MODIFY_SELF)
|
||||
@$(APELINK)
|
||||
|
||||
o/$(MODE)/test/libc/log/backtrace.dbg: \
|
||||
$(TEST_LIBC_LOG_DEPS) \
|
||||
o/$(MODE)/test/libc/log/backtrace.o \
|
||||
$(CRT) \
|
||||
$(APE_NO_MODIFY_SELF)
|
||||
@$(APELINK)
|
||||
|
||||
o/$(MODE)/test/libc/log/backtrace.zip.o \
|
||||
o/$(MODE)/test/libc/log/backtrace.dbg.zip.o: private \
|
||||
ZIPOBJ_FLAGS += \
|
||||
-B
|
||||
|
||||
.PHONY: o/$(MODE)/test/libc/log
|
||||
o/$(MODE)/test/libc/log: \
|
||||
$(TEST_LIBC_LOG_BINS) \
|
||||
|
|
|
@ -1,154 +0,0 @@
|
|||
/*-*- 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 2022 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/fmt/conv.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/log/log.h"
|
||||
#include "libc/macros.h"
|
||||
#include "libc/mem/leaks.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/runtime/internal.h"
|
||||
#include "libc/runtime/symbols.internal.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
#ifdef __x86_64__
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
int StackOverflow(int d) {
|
||||
char A[8];
|
||||
for (int i = 0; i < sizeof(A); i++)
|
||||
A[i] = d + i;
|
||||
if (__veil("r", d))
|
||||
return StackOverflow(d + 1) + A[d % sizeof(A)];
|
||||
return 0;
|
||||
}
|
||||
|
||||
void FpuCrash(void) {
|
||||
typedef char xmm_t __attribute__((__vector_size__(16)));
|
||||
xmm_t v = {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
|
||||
0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf};
|
||||
volatile int x = 0;
|
||||
asm volatile("fldpi");
|
||||
asm volatile("mov\t%0,%%r15" : /* no outputs */ : "g"(0x3133731337));
|
||||
asm volatile("movaps\t%0,%%xmm15" : /* no outputs */ : "x"(v));
|
||||
fputc(7 / x, stdout);
|
||||
}
|
||||
|
||||
char bss[10];
|
||||
void BssOverrunCrash(int n) {
|
||||
int i;
|
||||
for (i = 0; i < n; ++i) {
|
||||
bss[i] = i;
|
||||
}
|
||||
}
|
||||
|
||||
char data[10] = "abcdeabcde";
|
||||
void DataOverrunCrash(int n) {
|
||||
int i;
|
||||
for (i = 0; i < n; ++i) {
|
||||
data[i] = i;
|
||||
}
|
||||
}
|
||||
|
||||
const char rodata[10] = "abcdeabcde";
|
||||
int RodataOverrunCrash(int i) {
|
||||
return rodata[i];
|
||||
}
|
||||
|
||||
char *StackOverrunCrash(int n) {
|
||||
int i;
|
||||
char stack[10];
|
||||
bzero(stack, sizeof(stack));
|
||||
for (i = 0; i < n; ++i) {
|
||||
stack[i] = i;
|
||||
}
|
||||
return strdup(stack);
|
||||
}
|
||||
|
||||
char *MemoryLeakCrash(void) {
|
||||
char *p = strdup("doge");
|
||||
CheckForMemoryLeaks();
|
||||
return p;
|
||||
}
|
||||
|
||||
int NpeCrash(char *p) {
|
||||
asm("nop"); // xxx: due to backtrace addr-1 thing
|
||||
return *p;
|
||||
}
|
||||
|
||||
int StackOverflowCrash(int d) {
|
||||
char A[8];
|
||||
for (int i = 0; i < sizeof(A); i++)
|
||||
A[i] = d + i;
|
||||
if (__veil("r", d))
|
||||
return StackOverflowCrash(d + 1) + A[d % sizeof(A)];
|
||||
return 0;
|
||||
}
|
||||
|
||||
void (*pFpuCrash)(void) = FpuCrash;
|
||||
void (*pBssOverrunCrash)(int) = BssOverrunCrash;
|
||||
void (*pDataOverrunCrash)(int) = DataOverrunCrash;
|
||||
int (*pRodataOverrunCrash)(int) = RodataOverrunCrash;
|
||||
char *(*pMemoryLeakCrash)(void) = MemoryLeakCrash;
|
||||
int (*pNpeCrash)(char *) = NpeCrash;
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
ShowCrashReports();
|
||||
if (argc > 1) {
|
||||
switch (atoi(argv[1])) {
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
pFpuCrash();
|
||||
exit(0);
|
||||
case 2:
|
||||
pBssOverrunCrash(10 + 1);
|
||||
exit(0);
|
||||
case 3:
|
||||
exit(pRodataOverrunCrash(10 + 1));
|
||||
case 4:
|
||||
pDataOverrunCrash(10 + 1);
|
||||
exit(0);
|
||||
case 5:
|
||||
exit(StackOverflowCrash(0));
|
||||
case 6:
|
||||
exit((intptr_t)pMemoryLeakCrash());
|
||||
case 7:
|
||||
exit(pNpeCrash(0));
|
||||
case 8:
|
||||
exit(pNpeCrash(0));
|
||||
case 9:
|
||||
exit(StackOverflow(0));
|
||||
default:
|
||||
fputs("error: unrecognized argument\n", stderr);
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
fputs("error: too few args\n", stderr);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
}
|
||||
|
||||
#endif /* __x86_64__ */
|
|
@ -1,402 +0,0 @@
|
|||
/*-*- 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 2021 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/assert.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/fmt/conv.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/log/libfatal.internal.h"
|
||||
#include "libc/log/log.h"
|
||||
#include "libc/mem/gc.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/runtime/internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/runtime/symbols.internal.h"
|
||||
#include "libc/stdio/append.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
#include "libc/x/xasprintf.h"
|
||||
#include "net/http/escape.h"
|
||||
#ifdef __x86_64__
|
||||
|
||||
#if 0
|
||||
__static_yoink("backtrace");
|
||||
__static_yoink("backtrace.dbg");
|
||||
|
||||
void SetUpOnce(void) {
|
||||
testlib_enable_tmp_setup_teardown_once();
|
||||
ASSERT_NE(-1, mkdir("bin", 0755));
|
||||
testlib_extract("/zip/backtrace", "bin/backtrace", 0755);
|
||||
testlib_extract("/zip/backtrace.dbg", "bin/backtrace.dbg", 0755);
|
||||
}
|
||||
|
||||
static bool OutputHasSymbol(const char *output, const char *s) {
|
||||
return strstr(output, s) || (!FindDebugBinary() && strstr(output, "NULL"));
|
||||
}
|
||||
|
||||
// UNFREED MEMORY
|
||||
// o/dbg/test/libc/log/backtrace_test
|
||||
// max allocated space 655,360
|
||||
// total allocated space 80
|
||||
// total free space 327,600
|
||||
// releasable space 0
|
||||
// mmaped space 65,360
|
||||
// non-mmapped space 327,680
|
||||
//
|
||||
// 100080040020 64 bytes 5 used
|
||||
// 421871 strdup
|
||||
// 416529 MemoryLeakCrash
|
||||
// 41666d SetUp
|
||||
// 45428c testlib_runtestcases
|
||||
//
|
||||
// 00007fff0000-000080010000 rw-pa-F 2x shadow of 000000000000
|
||||
// 000080070000-0000800a0000 rw-pa-F 3x shadow of 0000003c0000
|
||||
// 02008fff0000-020090020000 rw-pa-F 3x shadow of 10007ffc0000
|
||||
// 020090060000-020090080000 rw-pa-F 2x shadow of 100080340000
|
||||
// 0e007fff0000-0e0080010000 rw-pa-F 2x shadow of 6ffffffc0000
|
||||
// 100006560000-100006580000 rw-pa-F 2x shadow of 7ffc32b40000
|
||||
// 100080000000-100080050000 rw-pa-- 5x automap w/ 50 frame hole
|
||||
// 100080370000-100080390000 rw-pa-- 2x automap w/ 1 frame hole
|
||||
// 1000803a0000-1000803b0000 rw-pa-- 1x automap
|
||||
// 6ffffffe0000-700000000000 rw-paSF 2x stack
|
||||
// # 24 frames mapped w/ 51 frames gapped
|
||||
TEST(ShowCrashReports, testMemoryLeakCrash) {
|
||||
size_t got;
|
||||
ssize_t rc;
|
||||
int ws, pid, fds[2];
|
||||
char *output, buf[512];
|
||||
ASSERT_NE(-1, pipe2(fds, O_CLOEXEC));
|
||||
ASSERT_NE(-1, (pid = fork()));
|
||||
if (!pid) {
|
||||
dup2(fds[1], 1);
|
||||
dup2(fds[1], 2);
|
||||
execv("bin/backtrace", (char *const[]){"bin/backtrace", "6", 0});
|
||||
_Exit(127);
|
||||
}
|
||||
close(fds[1]);
|
||||
output = 0;
|
||||
appends(&output, "");
|
||||
for (;;) {
|
||||
rc = read(fds[0], buf, sizeof(buf));
|
||||
if (rc == -1) {
|
||||
ASSERT_EQ(EINTR, errno);
|
||||
continue;
|
||||
}
|
||||
if ((got = rc)) {
|
||||
appendd(&output, buf, got);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
close(fds[0]);
|
||||
ASSERT_NE(-1, wait(&ws));
|
||||
// tinyprint(2, gc(IndentLines(output, -1, 0, 4)), "\n", NULL);
|
||||
EXPECT_EQ(78 << 8, ws);
|
||||
ASSERT_TRUE(!!strstr(output, "UNFREED MEMORY"));
|
||||
if (IsAsan()) {
|
||||
ASSERT_TRUE(OutputHasSymbol(output, "strdup") &&
|
||||
OutputHasSymbol(output, "MemoryLeakCrash"));
|
||||
}
|
||||
free(output);
|
||||
}
|
||||
|
||||
// error: Uncaught SIGFPE (FPE_INTDIV) on nightmare pid 11724
|
||||
// /home/jart/cosmo/o/dbg/test/libc/log/backtrace_test.tmp.11721
|
||||
// ENOTTY[25]
|
||||
// Linux nightmare SMP Thu, 12 Aug 2021 06:16:45 UTC
|
||||
//
|
||||
// 0x0000000000414659: FpuCrash at test/libc/log/backtrace_test.c:35
|
||||
// 0x000000000045003b: testlib_runtestcases at libc/testlib/testrunner.c:98
|
||||
// 0x000000000044b770: testlib_runalltests at libc/testlib/runner.c:37
|
||||
// 0x000000000040278e: main at libc/testlib/testmain.c:86
|
||||
// 0x0000000000403210: cosmo at libc/runtime/cosmo.S:65
|
||||
// 0x0000000000402247: _start at libc/crt/crt.S:67
|
||||
//
|
||||
// RAX 0000000000000007 RBX 00006fffffffff10 RDI 00007ffe0745fde1 ST(0) 0.0
|
||||
// RCX 0000000000000000 RDX 0000000000000000 RSI 0000000000489900 ST(1) 0.0
|
||||
// RBP 00006fffffffff70 RSP 00006fffffffff10 RIP 000000000041465a ST(2) 0.0
|
||||
// R8 0000000000000001 R9 00006ffffffffcc0 R10 00006ffffffffe60 ST(3) 0.0
|
||||
// R11 000000000000000d R12 00000dffffffffe2 R13 00006fffffffff10 ST(4) 0.0
|
||||
// R14 0000000000000003 R15 000000000049b700 VF PF ZF IF
|
||||
//
|
||||
// XMM0 00000000000000000000000000000000 XMM8 00000000000000000000000000000000
|
||||
// XMM1 000000008000000400000000004160ea XMM9 00000000000000000000000000000000
|
||||
// XMM2 00000000000000000000000000000000 XMM10 00000000000000000000000000000000
|
||||
// XMM3 00000000000000000000000000000000 XMM11 00000000000000000000000000000000
|
||||
// XMM4 00000000000000000000000000000000 XMM12 00000000000000000000000000000000
|
||||
// XMM5 00000000000000000000000000000000 XMM13 00000000000000000000000000000000
|
||||
// XMM6 00000000000000000000000000000000 XMM14 00000000000000000000000000000000
|
||||
// XMM7 00000000000000000000000000000000 XMM15 00000000000000000000000000000000
|
||||
//
|
||||
// mm->i == 4;
|
||||
// mm->p[ 0]=={0x00008007,0x00008008,-1,3,50}; /* 2 */
|
||||
// /* 234,881,012 */
|
||||
// mm->p[ 1]=={0x0e007ffd,0x0e007fff,-1,3,50}; /* 3 */
|
||||
// /* 33,538,280 */
|
||||
// mm->p[ 2]=={0x100040e8,0x100040e8,-1,3,50}; /* 1 */
|
||||
// /* 1,610,596,103 */
|
||||
// mm->p[ 3]=={0x6ffffff0,0x6fffffff,12884901888,306,0}; /* 16 */
|
||||
// /* 22 frames mapped w/ 1,879,015,395 frames gapped */
|
||||
//
|
||||
// 00400000-0045b000 r-xp 00000000 08:03 4587526
|
||||
// /home/jart/cosmo/o/dbg/test/libc/log/backtrace_test.tmp.11721
|
||||
// 0045b000-00461000 rw-p 0005b000 08:03 4587526
|
||||
// /home/jart/cosmo/o/dbg/test/libc/log/backtrace_test.tmp.11721
|
||||
// 00461000-004a0000 rw-p 00000000 00:00 0
|
||||
// 80070000-80090000 rw-p 00000000 00:00 0
|
||||
// e007ffd0000-e0080000000 rw-p 00000000 00:00 0
|
||||
// 100040e80000-100040e90000 rw-p 00000000 00:00 0
|
||||
// 6ffffff00000-700000000000 rw-p 00000000 00:00 0
|
||||
// 7ffe0743f000-7ffe07460000 rw-p 00000000 00:00 0 [stack]
|
||||
// 7ffe075a8000-7ffe075ab000 r--p 00000000 00:00 0 [vvar]
|
||||
// 7ffe075ab000-7ffe075ac000 r-xp 00000000 00:00 0 [vdso]
|
||||
//
|
||||
// /home/jart/cosmo/o/dbg/test/libc/log/backtrace_test.tmp.11721 1
|
||||
TEST(ShowCrashReports, testDivideByZero) {
|
||||
size_t got;
|
||||
ssize_t rc;
|
||||
int ws, pid, fds[2];
|
||||
char *output, buf[512];
|
||||
ASSERT_NE(-1, pipe2(fds, O_CLOEXEC));
|
||||
ASSERT_NE(-1, (pid = fork()));
|
||||
if (!pid) {
|
||||
dup2(fds[1], 1);
|
||||
dup2(fds[1], 2);
|
||||
execv("bin/backtrace", (char *const[]){"bin/backtrace", "1", 0});
|
||||
_Exit(127);
|
||||
}
|
||||
close(fds[1]);
|
||||
output = 0;
|
||||
appends(&output, "");
|
||||
for (;;) {
|
||||
rc = read(fds[0], buf, sizeof(buf));
|
||||
if (rc == -1) {
|
||||
ASSERT_EQ(EINTR, errno);
|
||||
continue;
|
||||
}
|
||||
if ((got = rc)) {
|
||||
appendd(&output, buf, got);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
close(fds[0]);
|
||||
ASSERT_NE(-1, wait(&ws));
|
||||
// tinyprint(2, gc(IndentLines(output, -1, 0, 4)), "\n", NULL);
|
||||
if (IsModeDbg()) {
|
||||
EXPECT_EQ(77 << 8, ws);
|
||||
} else {
|
||||
EXPECT_TRUE(WIFSIGNALED(ws));
|
||||
EXPECT_EQ(SIGFPE, WTERMSIG(ws));
|
||||
}
|
||||
/* NULL is stopgap until we can copy symbol tables into binary */
|
||||
#ifdef __FNO_OMIT_FRAME_POINTER__
|
||||
ASSERT_TRUE(OutputHasSymbol(output, "FpuCrash"));
|
||||
#endif
|
||||
if (strstr(output, "divrem overflow")) {
|
||||
// UBSAN handled it
|
||||
} else {
|
||||
// ShowCrashReports() handled it
|
||||
if (!strstr(output, gc(xasprintf("%d", pid)))) {
|
||||
fprintf(stderr, "ERROR: crash report didn't have pid\n%s\n",
|
||||
gc(IndentLines(output, -1, 0, 4)));
|
||||
__die();
|
||||
}
|
||||
if (!strstr(output, "SIGFPE")) {
|
||||
fprintf(stderr, "ERROR: crash report didn't have signal name\n%s\n",
|
||||
gc(IndentLines(output, -1, 0, 4)));
|
||||
__die();
|
||||
}
|
||||
// XXX: WSL doesn't save and restore x87 registers to ucontext_t
|
||||
if (!__iswsl1()) {
|
||||
if (!strstr(output, "3.141")) {
|
||||
fprintf(stderr, "ERROR: crash report didn't have fpu register\n%s\n",
|
||||
gc(IndentLines(output, -1, 0, 4)));
|
||||
__die();
|
||||
}
|
||||
}
|
||||
if (!strstr(output, "0f0e0d0c0b0a09080706050403020100")) {
|
||||
fprintf(stderr, "ERROR: crash report didn't have sse register\n%s\n",
|
||||
gc(IndentLines(output, -1, 0, 4)));
|
||||
__die();
|
||||
}
|
||||
if (!strstr(output, "3133731337")) {
|
||||
fprintf(stderr, "ERROR: crash report didn't have general register\n%s\n",
|
||||
gc(IndentLines(output, -1, 0, 4)));
|
||||
__die();
|
||||
}
|
||||
}
|
||||
free(output);
|
||||
}
|
||||
|
||||
TEST(ShowCrashReports, testBssOverrunCrash) {
|
||||
if (!IsAsan()) return;
|
||||
size_t got;
|
||||
ssize_t rc;
|
||||
int ws, pid, fds[2];
|
||||
char *output, buf[512];
|
||||
ASSERT_NE(-1, pipe2(fds, O_CLOEXEC));
|
||||
ASSERT_NE(-1, (pid = fork()));
|
||||
if (!pid) {
|
||||
dup2(fds[1], 1);
|
||||
dup2(fds[1], 2);
|
||||
execv("bin/backtrace", (char *const[]){"bin/backtrace", "2", 0});
|
||||
_Exit(127);
|
||||
}
|
||||
close(fds[1]);
|
||||
output = 0;
|
||||
appends(&output, "");
|
||||
for (;;) {
|
||||
rc = read(fds[0], buf, sizeof(buf));
|
||||
if (rc == -1) {
|
||||
ASSERT_EQ(EINTR, errno);
|
||||
continue;
|
||||
}
|
||||
if ((got = rc)) {
|
||||
appendd(&output, buf, got);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
close(fds[0]);
|
||||
ASSERT_NE(-1, wait(&ws));
|
||||
// tinyprint(2, gc(IndentLines(output, -1, 0, 4)), "\n", NULL);
|
||||
EXPECT_EQ(77 << 8, ws);
|
||||
/* NULL is stopgap until we can copy symbol tablces into binary */
|
||||
#ifdef __FNO_OMIT_FRAME_POINTER__
|
||||
ASSERT_TRUE(OutputHasSymbol(output, "BssOverrunCrash"));
|
||||
#endif
|
||||
if (IsAsan()) {
|
||||
ASSERT_TRUE(
|
||||
!!strstr(output, "'int' index 10 into 'char [10]' out of bounds"));
|
||||
} else {
|
||||
ASSERT_TRUE(!!strstr(output, "☺☻♥♦♣♠•◘○"));
|
||||
ASSERT_TRUE(!!strstr(output, "global redzone"));
|
||||
}
|
||||
free(output);
|
||||
}
|
||||
|
||||
TEST(ShowCrashReports, testDataOverrunCrash) {
|
||||
if (!IsAsan()) return;
|
||||
size_t got;
|
||||
ssize_t rc;
|
||||
int ws, pid, fds[2];
|
||||
char *output, buf[512];
|
||||
ASSERT_NE(-1, pipe2(fds, O_CLOEXEC));
|
||||
ASSERT_NE(-1, (pid = fork()));
|
||||
if (!pid) {
|
||||
dup2(fds[1], 1);
|
||||
dup2(fds[1], 2);
|
||||
execv("bin/backtrace", (char *const[]){"bin/backtrace", "4", 0});
|
||||
_Exit(127);
|
||||
}
|
||||
close(fds[1]);
|
||||
output = 0;
|
||||
appends(&output, "");
|
||||
for (;;) {
|
||||
rc = read(fds[0], buf, sizeof(buf));
|
||||
if (rc == -1) {
|
||||
ASSERT_EQ(EINTR, errno);
|
||||
continue;
|
||||
}
|
||||
if ((got = rc)) {
|
||||
appendd(&output, buf, got);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
close(fds[0]);
|
||||
ASSERT_NE(-1, wait(&ws));
|
||||
// tinyprint(2, gc(IndentLines(output, -1, 0, 4)), "\n", NULL);
|
||||
EXPECT_EQ(77 << 8, ws);
|
||||
/* NULL is stopgap until we can copy symbol tablces into binary */
|
||||
#ifdef __FNO_OMIT_FRAME_POINTER__
|
||||
ASSERT_TRUE(OutputHasSymbol(output, "DataOverrunCrash"));
|
||||
#endif
|
||||
if (!strstr(output, "'int' index 10 into 'char [10]' out")) { // ubsan
|
||||
ASSERT_TRUE(!!strstr(output, "☺☻♥♦♣♠•◘○")); // asan
|
||||
ASSERT_TRUE(!!strstr(output, "global redzone")); // asan
|
||||
}
|
||||
free(output);
|
||||
}
|
||||
|
||||
TEST(ShowCrashReports, testNpeCrashAfterFinalize) {
|
||||
/*
|
||||
* this test makes sure we're not doing things like depending on
|
||||
* environment variables after __cxa_finalize is called in cases
|
||||
* where putenv() is used
|
||||
*/
|
||||
size_t got;
|
||||
ssize_t rc;
|
||||
int ws, pid, fds[2];
|
||||
char *output, buf[512];
|
||||
ASSERT_NE(-1, pipe2(fds, O_CLOEXEC));
|
||||
ASSERT_NE(-1, (pid = fork()));
|
||||
if (!pid) {
|
||||
dup2(fds[1], 1);
|
||||
dup2(fds[1], 2);
|
||||
execv("bin/backtrace", (char *const[]){"bin/backtrace", "8", 0});
|
||||
_Exit(127);
|
||||
}
|
||||
close(fds[1]);
|
||||
output = 0;
|
||||
appends(&output, "");
|
||||
for (;;) {
|
||||
rc = read(fds[0], buf, sizeof(buf));
|
||||
if (rc == -1) {
|
||||
ASSERT_EQ(EINTR, errno);
|
||||
continue;
|
||||
}
|
||||
if ((got = rc)) {
|
||||
appendd(&output, buf, got);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
close(fds[0]);
|
||||
ASSERT_NE(-1, wait(&ws));
|
||||
// tinyprint(2, gc(IndentLines(output, -1, 0, 4)), "\n", NULL);
|
||||
if (IsModeDbg()) {
|
||||
EXPECT_EQ(77 << 8, ws);
|
||||
} else {
|
||||
EXPECT_TRUE(WIFSIGNALED(ws));
|
||||
EXPECT_EQ(SIGSEGV, WTERMSIG(ws));
|
||||
}
|
||||
/* NULL is stopgap until we can copy symbol tables into binary */
|
||||
if (!strstr(output, IsAsan() ? "null pointer" : "Uncaught SIGSEGV (SEGV_")) {
|
||||
fprintf(stderr, "ERROR: crash report didn't diagnose the problem\n%s\n",
|
||||
gc(IndentLines(output, -1, 0, 4)));
|
||||
__die();
|
||||
}
|
||||
#ifdef __FNO_OMIT_FRAME_POINTER__
|
||||
if (!OutputHasSymbol(output, "NpeCrash")) {
|
||||
fprintf(stderr, "ERROR: crash report didn't have backtrace\n%s\n",
|
||||
gc(IndentLines(output, -1, 0, 4)));
|
||||
__die();
|
||||
}
|
||||
#endif
|
||||
free(output);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __x86_64__ */
|
|
@ -60,6 +60,17 @@ o/$(MODE)/test/libc/proc/%.dbg: \
|
|||
o/$(MODE)/test/libc/proc/posix_spawn_test.runs: \
|
||||
private QUOTA += -M8192m
|
||||
|
||||
o/$(MODE)/test/libc/proc/fork_test.dbg: \
|
||||
$(TEST_LIBC_PROC_DEPS) \
|
||||
o/$(MODE)/test/libc/proc/fork_test.o \
|
||||
o/$(MODE)/test/libc/proc/proc.pkg \
|
||||
o/$(MODE)/tool/hello/life-pe.ape.zip.o \
|
||||
o/$(MODE)/test/libc/proc/life.zip.o \
|
||||
$(LIBC_TESTMAIN) \
|
||||
$(CRT) \
|
||||
$(APE_NO_MODIFY_SELF)
|
||||
@$(APELINK)
|
||||
|
||||
o/$(MODE)/test/libc/proc/posix_spawn_test.dbg: \
|
||||
$(TEST_LIBC_PROC_DEPS) \
|
||||
o/$(MODE)/test/libc/proc/posix_spawn_test.o \
|
||||
|
@ -99,6 +110,14 @@ o/$(MODE)/test/libc/proc/fexecve_test.dbg: \
|
|||
$(APE_NO_MODIFY_SELF)
|
||||
@$(APELINK)
|
||||
|
||||
o/$(MODE)/test/libc/proc/life.dbg: \
|
||||
$(TEST_LIBC_PROC_DEPS) \
|
||||
o/$(MODE)/test/libc/proc/life.o \
|
||||
$(CRT) \
|
||||
$(APE_NO_MODIFY_SELF)
|
||||
@$(APELINK)
|
||||
|
||||
o/$(MODE)/test/libc/proc/life.zip.o \
|
||||
o/$(MODE)/test/libc/proc/execve_test_prog1.zip.o \
|
||||
o/$(MODE)/test/libc/proc/life-pe.zip.o: private \
|
||||
ZIPOBJ_FLAGS += \
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "libc/log/check.h"
|
||||
#include "libc/macros.h"
|
||||
#include "libc/nexgen32e/rdtsc.h"
|
||||
#include "libc/proc/posix_spawn.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/map.h"
|
||||
|
@ -39,6 +40,10 @@
|
|||
#include "libc/testlib/testlib.h"
|
||||
#include "libc/thread/tls.h"
|
||||
|
||||
void SetUpOnce(void) {
|
||||
testlib_enable_tmp_setup_teardown();
|
||||
}
|
||||
|
||||
TEST(fork, testPipes) {
|
||||
int a, b;
|
||||
int ws, pid;
|
||||
|
@ -142,7 +147,7 @@ TEST(fork, preservesTlsMemory) {
|
|||
EXITS(0);
|
||||
}
|
||||
|
||||
void ForkInSerial(void) {
|
||||
void fork_wait_in_serial(void) {
|
||||
int pid, ws;
|
||||
ASSERT_NE(-1, (pid = fork()));
|
||||
if (!pid)
|
||||
|
@ -152,7 +157,19 @@ void ForkInSerial(void) {
|
|||
ASSERT_EQ(0, WEXITSTATUS(ws));
|
||||
}
|
||||
|
||||
void VforkInSerial(void) {
|
||||
void vfork_execl_wait_in_serial(void) {
|
||||
int pid, ws;
|
||||
ASSERT_NE(-1, (pid = vfork()));
|
||||
if (!pid) {
|
||||
execl("./life", "./life", NULL);
|
||||
_Exit(127);
|
||||
}
|
||||
ASSERT_NE(-1, waitpid(pid, &ws, 0));
|
||||
ASSERT_TRUE(WIFEXITED(ws));
|
||||
ASSERT_EQ(42, WEXITSTATUS(ws));
|
||||
}
|
||||
|
||||
void vfork_wait_in_serial(void) {
|
||||
int pid, ws;
|
||||
ASSERT_NE(-1, (pid = vfork()));
|
||||
if (!pid)
|
||||
|
@ -162,7 +179,7 @@ void VforkInSerial(void) {
|
|||
ASSERT_EQ(0, WEXITSTATUS(ws));
|
||||
}
|
||||
|
||||
void SysForkInSerial(void) {
|
||||
void sys_fork_wait_in_serial(void) {
|
||||
int pid, ws;
|
||||
ASSERT_NE(-1, (pid = sys_fork()));
|
||||
if (!pid)
|
||||
|
@ -172,11 +189,31 @@ void SysForkInSerial(void) {
|
|||
ASSERT_EQ(0, WEXITSTATUS(ws));
|
||||
}
|
||||
|
||||
TEST(fork, bench) {
|
||||
VforkInSerial();
|
||||
BENCHMARK(10, 1, VforkInSerial());
|
||||
if (!IsWindows())
|
||||
BENCHMARK(10, 1, SysForkInSerial());
|
||||
ForkInSerial();
|
||||
BENCHMARK(10, 1, ForkInSerial());
|
||||
void posix_spawn_in_serial(void) {
|
||||
int ws, pid;
|
||||
char *prog = "./life";
|
||||
char *args[] = {prog, NULL};
|
||||
char *envs[] = {NULL};
|
||||
ASSERT_EQ(0, posix_spawn(&pid, prog, NULL, NULL, args, envs));
|
||||
ASSERT_NE(-1, waitpid(pid, &ws, 0));
|
||||
ASSERT_TRUE(WIFEXITED(ws));
|
||||
ASSERT_EQ(42, WEXITSTATUS(ws));
|
||||
}
|
||||
|
||||
TEST(fork, bench) {
|
||||
if (IsWindows()) {
|
||||
testlib_extract("/zip/life-pe.ape", "life", 0755);
|
||||
} else {
|
||||
testlib_extract("/zip/life", "life", 0755);
|
||||
}
|
||||
vfork_wait_in_serial();
|
||||
vfork_execl_wait_in_serial();
|
||||
posix_spawn_in_serial();
|
||||
BENCHMARK(10, 1, vfork_wait_in_serial());
|
||||
if (!IsWindows())
|
||||
BENCHMARK(10, 1, sys_fork_wait_in_serial());
|
||||
fork_wait_in_serial();
|
||||
BENCHMARK(10, 1, fork_wait_in_serial());
|
||||
BENCHMARK(10, 1, posix_spawn_in_serial());
|
||||
BENCHMARK(10, 1, vfork_execl_wait_in_serial());
|
||||
}
|
||||
|
|
3
test/libc/proc/life.c
Normal file
3
test/libc/proc/life.c
Normal file
|
@ -0,0 +1,3 @@
|
|||
int main(int argc, char *argv[]) {
|
||||
return 42;
|
||||
}
|
|
@ -90,7 +90,6 @@ __attribute__((__constructor__)) static void init(void) {
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef __x86_64__
|
||||
TEST(sched_setaffinity, isInheritedAcrossExecve) {
|
||||
cpu_set_t x;
|
||||
CPU_ZERO(&x);
|
||||
|
@ -105,7 +104,6 @@ TEST(sched_setaffinity, isInheritedAcrossExecve) {
|
|||
EXPECT_TRUE(WIFEXITED(ws));
|
||||
EXPECT_EQ(42, WEXITSTATUS(ws));
|
||||
}
|
||||
#endif /* __x86_64__ */
|
||||
|
||||
TEST(sched_getaffinity, getpid) {
|
||||
cpu_set_t x, y;
|
||||
|
|
|
@ -18,8 +18,8 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/intrin/fds.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/fds.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/nt/winsock.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
|
@ -141,8 +141,6 @@ TEST(socket, canBeInheritedByForkedWorker) {
|
|||
WAIT(exit, 0);
|
||||
}
|
||||
|
||||
#ifdef __x86_64__
|
||||
|
||||
__attribute__((__constructor__)) static void StdioPro(int argc, char *argv[]) {
|
||||
if (argc >= 2 && !strcmp(argv[1], "StdioProg")) {
|
||||
ASSERT_EQ(NULL, getenv("__STDIO_SOCKETS"));
|
||||
|
@ -184,5 +182,3 @@ TEST(socket, canBeUsedAsExecutedStdio) {
|
|||
EXPECT_SYS(0, 0, close(3));
|
||||
WAIT(exit, 0);
|
||||
}
|
||||
|
||||
#endif /* __x86_64__ */
|
||||
|
|
|
@ -30,9 +30,9 @@ TEST_LIBC_SYSTEM_DIRECTDEPS = \
|
|||
LIBC_LOG \
|
||||
LIBC_MEM \
|
||||
LIBC_NEXGEN32E \
|
||||
LIBC_PROC \
|
||||
LIBC_RUNTIME \
|
||||
LIBC_STDIO \
|
||||
LIBC_STDIO \
|
||||
LIBC_STR \
|
||||
LIBC_SYSTEM \
|
||||
LIBC_SYSV \
|
||||
|
@ -82,6 +82,21 @@ o/$(MODE)/test/libc/system/system_test.dbg: \
|
|||
$(APE_NO_MODIFY_SELF)
|
||||
@$(APELINK)
|
||||
|
||||
o/$(MODE)/test/libc/system/trace_test.dbg: \
|
||||
$(TEST_LIBC_SYSTEM_DEPS) \
|
||||
o/$(MODE)/test/libc/system/trace_test.o \
|
||||
o/$(MODE)/test/libc/system/system.pkg \
|
||||
o/$(MODE)/test/libc/system/popen_test.zip.o \
|
||||
o/$(MODE)/test/libc/system/popen_test.dbg.zip.o \
|
||||
o/$(MODE)/tool/build/echo.zip.o \
|
||||
$(LIBC_TESTMAIN) \
|
||||
$(CRT) \
|
||||
$(APE_NO_MODIFY_SELF)
|
||||
@$(APELINK)
|
||||
|
||||
o/$(MODE)/test/libc/system/popen_test.zip.o: private ZIPOBJ_FLAGS += -B
|
||||
o/$(MODE)/test/libc/system/popen_test.dbg.zip.o: private ZIPOBJ_FLAGS += -B
|
||||
|
||||
$(TEST_LIBC_SYSTEM_OBJS): test/libc/system/BUILD.mk
|
||||
|
||||
.PHONY: o/$(MODE)/test/libc/system
|
||||
|
|
|
@ -27,10 +27,9 @@
|
|||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/testlib/ezbench.h"
|
||||
#include "libc/testlib/benchmark.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
#include "libc/x/x.h"
|
||||
#ifdef __x86_64__
|
||||
|
||||
#define GETEXITSTATUS(x) \
|
||||
({ \
|
||||
|
@ -276,15 +275,9 @@ TEST(system, pipelineCanOutputBackToSelf) {
|
|||
RestoreStdout();
|
||||
}
|
||||
|
||||
int system2(const char *);
|
||||
|
||||
BENCH(system, bench) {
|
||||
TEST(system, bench) {
|
||||
testlib_extract("/zip/echo", "echo", 0755);
|
||||
EZBENCH2("system cmd", donothing, system("./echo hi >/dev/null"));
|
||||
EZBENCH2("systemvpe cmd", donothing,
|
||||
systemvpe("./echo", (char *[]){"./echo", "hi", 0}, 0));
|
||||
EZBENCH2("cocmd echo", donothing, system("echo hi >/dev/null"));
|
||||
EZBENCH2("cocmd exit", donothing, system("exit"));
|
||||
BENCHMARK(10, 1, system("./echo hi >/dev/null"));
|
||||
BENCHMARK(10, 1, system("echo hi >/dev/null"));
|
||||
BENCHMARK(10, 1, system("exit"));
|
||||
}
|
||||
|
||||
#endif /* __x86_64__ */
|
||||
|
|
74
test/libc/system/trace_test.c
Normal file
74
test/libc/system/trace_test.c
Normal file
|
@ -0,0 +1,74 @@
|
|||
/*-*- 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/assert.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
#include "libc/x/x.h"
|
||||
|
||||
// make sure that running `popen_test --ftrace --strace` doesn't crash
|
||||
//
|
||||
// function and system call tracing are invasive runtime features that
|
||||
// can easily break if interrupting the other magical, deeply embedded
|
||||
// parts of the runtime, like mutations to the rbtree ftrace needs for
|
||||
// validating stack pointers (kisdangerous() locks the mmap lock), and
|
||||
// that's why we use dontinstrument in so many places in the codebase.
|
||||
//
|
||||
// we like popen_test because it tests the intersection of forking and
|
||||
// threads, and it activates other subsystems like the signal / itimer
|
||||
// worker threads on windows. if we can ftrace and strace it, then you
|
||||
// can be assured cosmo's tracing support works right on all platforms
|
||||
|
||||
void SetUpOnce(void) {
|
||||
testlib_enable_tmp_setup_teardown();
|
||||
}
|
||||
|
||||
TEST(trace, test) {
|
||||
unsetenv("MAKEFLAGS"); // avoid testmain.c 254 status
|
||||
testlib_extract("/zip/popen_test", "popen_test", 0755);
|
||||
testlib_extract("/zip/popen_test.dbg", "popen_test.dbg", 0755);
|
||||
if (!fork()) {
|
||||
close(1);
|
||||
close(2);
|
||||
open("log", O_CREAT | O_TRUNC | O_WRONLY | O_APPEND, 0644);
|
||||
dup(1);
|
||||
execl("./popen_test", "./popen_test", "--ftrace", "--strace", NULL);
|
||||
_Exit(128);
|
||||
}
|
||||
int ws;
|
||||
unassert(wait(&ws));
|
||||
if (WIFSIGNALED(ws)) {
|
||||
fprintf(stderr,
|
||||
"%s:%d: error: trace_test got %s signal running "
|
||||
"popen_test --strace --ftrace (see %s for output)\n",
|
||||
__FILE__, __LINE__, strsignal(WTERMSIG(ws)), realpath("log", 0));
|
||||
_Exit(1);
|
||||
}
|
||||
if (WEXITSTATUS(ws)) {
|
||||
fprintf(stderr,
|
||||
"%s:%d: error: trace_test got %d exit status running "
|
||||
"popen_test --strace --ftrace (see %s for output)\n",
|
||||
__FILE__, __LINE__, WEXITSTATUS(ws), realpath("log", 0));
|
||||
_Exit(1);
|
||||
}
|
||||
}
|
|
@ -39,7 +39,6 @@
|
|||
#include "libc/testlib/testlib.h"
|
||||
#include "libc/x/x.h"
|
||||
#include "third_party/regex/regex.h"
|
||||
#ifdef __x86_64__
|
||||
|
||||
__static_yoink("zipos");
|
||||
__static_yoink("o/" MODE "/test/tool/net/redbean-tester");
|
||||
|
@ -292,5 +291,3 @@ Z\n",
|
|||
EXPECT_NE(-1, wait(0));
|
||||
EXPECT_NE(-1, sigprocmask(SIG_SETMASK, &savemask, 0));
|
||||
}
|
||||
|
||||
#endif /* __x86_64__ */
|
||||
|
|
4
third_party/compiler_rt/clear_cache.c
vendored
4
third_party/compiler_rt/clear_cache.c
vendored
|
@ -15,7 +15,7 @@
|
|||
// It is expected to invalidate the instruction cache for the
|
||||
// specified range.
|
||||
|
||||
void __clear_cache(void *start, void *end) {
|
||||
privileged void __clear_cache(void *start, void *end) {
|
||||
|
||||
#ifdef __aarch64__
|
||||
if (IsXnu()) {
|
||||
|
@ -59,6 +59,8 @@ void __clear_cache(void *start, void *end) {
|
|||
}
|
||||
__asm__ volatile("isync");
|
||||
|
||||
#elif defined(__x86_64__)
|
||||
// do nothing
|
||||
#else
|
||||
compilerrt_abort();
|
||||
#endif
|
||||
|
|
28
third_party/nsync/common.c
vendored
28
third_party/nsync/common.c
vendored
|
@ -110,6 +110,8 @@ uint32_t nsync_spin_test_and_set_ (nsync_atomic_uint32_ *w, uint32_t test,
|
|||
|
||||
/* ====================================================================================== */
|
||||
|
||||
#if NSYNC_DEBUG
|
||||
|
||||
struct nsync_waiter_s *nsync_dll_nsync_waiter_ (struct Dll *e) {
|
||||
struct nsync_waiter_s *nw = DLL_CONTAINER(struct nsync_waiter_s, q, e);
|
||||
ASSERT (nw->tag == NSYNC_WAITER_TAG);
|
||||
|
@ -133,6 +135,8 @@ waiter *nsync_dll_waiter_samecond_ (struct Dll *e) {
|
|||
return (w);
|
||||
}
|
||||
|
||||
#endif /* NSYNC_DEBUG */
|
||||
|
||||
/* -------------------------------- */
|
||||
|
||||
// TODO(jart): enforce in dbg mode once off-by-one flake is fixed
|
||||
|
@ -249,8 +253,10 @@ static bool free_waiters_populate (void) {
|
|||
return (false);
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
waiter *w = &waiters[i];
|
||||
#if NSYNC_DEBUG
|
||||
w->tag = WAITER_TAG;
|
||||
w->nw.tag = NSYNC_WAITER_TAG;
|
||||
#endif
|
||||
if (!nsync_mu_semaphore_init (&w->sem)) {
|
||||
if (!i) {
|
||||
// netbsd can run out of semaphores
|
||||
|
@ -327,18 +333,26 @@ void nsync_waiter_wipe_ (void) {
|
|||
nsync_mu_semaphore_destroy (&w->sem);
|
||||
for (w = wall; w; w = next) {
|
||||
next = w->next_all;
|
||||
w->tag = 0;
|
||||
w->flags = 0;
|
||||
w->nw.tag = 0;
|
||||
#if NSYNC_DEBUG
|
||||
w->tag = WAITER_TAG;
|
||||
w->nw.tag = NSYNC_WAITER_TAG;
|
||||
#endif
|
||||
w->nw.flags = NSYNC_WAITER_FLAG_MUCV;
|
||||
atomic_init(&w->nw.waiting, 0);
|
||||
w->l_type = 0;
|
||||
bzero (&w->cond, sizeof (w->cond));
|
||||
w->cond.f = 0;
|
||||
w->cond.v = 0;
|
||||
w->cond.eq = 0;
|
||||
dll_init (&w->same_condition);
|
||||
if (w->wipe_mu)
|
||||
bzero (w->wipe_mu, sizeof (*w->wipe_mu));
|
||||
if (w->wipe_cv)
|
||||
bzero (w->wipe_cv, sizeof (*w->wipe_cv));
|
||||
if (w->wipe_mu) {
|
||||
atomic_init(&w->wipe_mu->word, 0);
|
||||
w->wipe_mu->waiters = 0;
|
||||
}
|
||||
if (w->wipe_cv) {
|
||||
atomic_init(&w->wipe_cv->word, 0);
|
||||
w->wipe_cv->waiters = 0;
|
||||
}
|
||||
if (!nsync_mu_semaphore_init (&w->sem))
|
||||
continue; /* leak it */
|
||||
w->next_free = prev;
|
||||
|
|
11
third_party/nsync/common.internal.h
vendored
11
third_party/nsync/common.internal.h
vendored
|
@ -9,15 +9,10 @@
|
|||
#include "third_party/nsync/mu_semaphore.h"
|
||||
#include "third_party/nsync/note.h"
|
||||
#include "third_party/nsync/time.h"
|
||||
#include "third_party/nsync/defs.h"
|
||||
#include "third_party/nsync/wait_s.internal.h"
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
#ifdef MODE_DBG
|
||||
#define NSYNC_DEBUG 1
|
||||
#else
|
||||
#define NSYNC_DEBUG 0
|
||||
#endif
|
||||
|
||||
/* Yield the CPU. Platform specific. */
|
||||
void nsync_yield_(void);
|
||||
|
||||
|
@ -191,13 +186,15 @@ struct wait_condition_s {
|
|||
ATM_STORE_REL (&w.waiting, 0);
|
||||
nsync_mu_semaphore_v (&w.sem); */
|
||||
typedef struct waiter_s {
|
||||
#if NSYNC_DEBUG
|
||||
uint32_t tag; /* Debug DLL_NSYNC_WAITER, DLL_WAITER, DLL_WAITER_SAMECOND. */
|
||||
#endif
|
||||
int flags; /* See WAITER_* bits below. */
|
||||
nsync_atomic_uint32_ remove_count; /* Monotonic count of removals from queue. */
|
||||
nsync_semaphore sem; /* Thread waits on this semaphore. */
|
||||
struct nsync_waiter_s nw; /* An embedded nsync_waiter_s. */
|
||||
struct nsync_mu_s_ *cv_mu; /* Pointer to nsync_mu associated with a cv wait. */
|
||||
lock_type *l_type; /* Lock type of the mu, or nil if not associated with a mu. */
|
||||
nsync_atomic_uint32_ remove_count; /* Monotonic count of removals from queue. */
|
||||
struct wait_condition_s cond; /* A condition on which to acquire a mu. */
|
||||
struct Dll same_condition; /* Links neighbours in nw.q with same non-nil condition. */
|
||||
struct waiter_s * next_all;
|
||||
|
|
12
third_party/nsync/defs.h
vendored
Normal file
12
third_party/nsync/defs.h
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
#ifndef COSMOPOLITAN_THIRD_PARTY_NSYNC_DEFS_H_
|
||||
#define COSMOPOLITAN_THIRD_PARTY_NSYNC_DEFS_H_
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
#ifdef MODE_DBG
|
||||
#define NSYNC_DEBUG 1
|
||||
#else
|
||||
#define NSYNC_DEBUG 0
|
||||
#endif
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* COSMOPOLITAN_THIRD_PARTY_NSYNC_DEFS_H_ */
|
9
third_party/nsync/mem/nsync_debug.c
vendored
9
third_party/nsync/mem/nsync_debug.c
vendored
|
@ -20,6 +20,7 @@
|
|||
#include "third_party/nsync/common.internal.h"
|
||||
#include "third_party/nsync/mu_semaphore.h"
|
||||
#include "third_party/nsync/races.internal.h"
|
||||
#include "third_party/nsync/defs.h"
|
||||
#include "third_party/nsync/wait_s.internal.h"
|
||||
__static_yoink("nsync_notice");
|
||||
|
||||
|
@ -148,15 +149,23 @@ static void emit_waiters (struct emit_buf *b, struct Dll *list) {
|
|||
waiter *w = DLL_WAITER (p);
|
||||
next = NULL;
|
||||
emit_print (b, " %i", (uintptr_t) w);
|
||||
#if NSYNC_DEBUG
|
||||
if (w->tag != WAITER_TAG) {
|
||||
emit_print (b, "bad WAITER_TAG %i",
|
||||
(uintptr_t) w->tag);
|
||||
} else {
|
||||
#else
|
||||
{
|
||||
#endif
|
||||
next = dll_next (list, p);
|
||||
#if NSYNC_DEBUG
|
||||
if (nw->tag != NSYNC_WAITER_TAG) {
|
||||
emit_print (b, " bad WAITER_TAG %i",
|
||||
(uintptr_t) nw->tag);
|
||||
} else {
|
||||
#else
|
||||
{
|
||||
#endif
|
||||
emit_print (b, " embedded=%i waiting=%i",
|
||||
(uintptr_t) (w->flags & NSYNC_WAITER_FLAG_MUCV),
|
||||
(uintptr_t) ATM_LOAD (&nw->waiting));
|
||||
|
|
2
third_party/nsync/mem/nsync_sem_wait.c
vendored
2
third_party/nsync/mem/nsync_sem_wait.c
vendored
|
@ -40,7 +40,9 @@ int nsync_sem_wait_with_cancel_ (waiter *w, int clock, nsync_time abs_deadline,
|
|||
sem_outcome = ECANCELED;
|
||||
if (nsync_time_cmp (cancel_time, nsync_time_zero) > 0) {
|
||||
struct nsync_waiter_s nw;
|
||||
#if NSYNC_DEBUG
|
||||
nw.tag = NSYNC_WAITER_TAG;
|
||||
#endif
|
||||
nw.sem = &w->sem;
|
||||
dll_init (&nw.q);
|
||||
ATM_STORE (&nw.waiting, 1);
|
||||
|
|
2
third_party/nsync/mem/nsync_wait.c
vendored
2
third_party/nsync/mem/nsync_wait.c
vendored
|
@ -51,7 +51,9 @@ int nsync_wait_n (void *mu, void (*lock) (void *), void (*unlock) (void *),
|
|||
nw = (struct nsync_waiter_s *) malloc (count * sizeof (nw[0]));
|
||||
}
|
||||
for (i = 0; i != count && enqueued; i++) {
|
||||
#if NSYNC_DEBUG
|
||||
nw[i].tag = NSYNC_WAITER_TAG;
|
||||
#endif
|
||||
nw[i].sem = &w->sem;
|
||||
dll_init (&nw[i].q);
|
||||
ATM_STORE (&nw[i].waiting, 0);
|
||||
|
|
1
third_party/nsync/mu.h
vendored
1
third_party/nsync/mu.h
vendored
|
@ -48,7 +48,6 @@ COSMOPOLITAN_C_START_
|
|||
*/
|
||||
typedef struct nsync_mu_s_ {
|
||||
nsync_atomic_uint32_ word; /* internal use only */
|
||||
int _zero; /* c pthread_mutex_t */
|
||||
struct Dll *waiters; /* internal use only */
|
||||
} nsync_mu;
|
||||
|
||||
|
|
|
@ -60,7 +60,7 @@ typedef struct cv_stress_data_s {
|
|||
/* The delays in cv_stress_inc_loop(), cv_stress_reader_loop(), mu_stress_inc_loop(),
|
||||
and mu_stress_reader_loop() are uniformly distributed from 0 to
|
||||
STRESS_MAX_DELAY_MICROS-1 microseconds. */
|
||||
#define STRESS_MAX_DELAY_MICROS (IsNetbsd() || IsOpenbsd() ? 20000 : 4000) /* maximum delay */
|
||||
#define STRESS_MAX_DELAY_MICROS (IsNetbsd() || IsOpenbsd() ? 30000 : 4000) /* maximum delay */
|
||||
#define STRESS_MEAN_DELAY_MICROS (STRESS_MAX_DELAY_MICROS / 2) /* mean delay */
|
||||
#define STRESS_EXPECT_TIMEOUTS_PER_SEC (1000000 / STRESS_MEAN_DELAY_MICROS) /* expect timeouts/s*/
|
||||
|
||||
|
|
3
third_party/nsync/testing/note_test.c
vendored
3
third_party/nsync/testing/note_test.c
vendored
|
@ -20,6 +20,7 @@
|
|||
#include "third_party/nsync/testing/smprintf.h"
|
||||
#include "third_party/nsync/testing/testing.h"
|
||||
#include "third_party/nsync/testing/time_extra.h"
|
||||
#include "libc/dce.h"
|
||||
#include "third_party/nsync/time.h"
|
||||
|
||||
/* Verify the properties of a prenotified note. */
|
||||
|
@ -78,7 +79,7 @@ static void test_note_unnotified (testing t) {
|
|||
TEST_ERROR (t, ("timed wait on unnotified note returned too quickly (1s wait took %s)",
|
||||
nsync_time_str (waited, 2)));
|
||||
}
|
||||
if (nsync_time_cmp (waited, nsync_time_ms (2000)) > 0) {
|
||||
if (nsync_time_cmp (waited, nsync_time_ms (IsNetbsd() || IsOpenbsd() || IsFreebsd() ? 4000 : 2000)) > 0) {
|
||||
TEST_ERROR (t, ("timed wait on unnotified note returned too slowly (1s wait took %s)",
|
||||
nsync_time_str (waited, 2)));
|
||||
}
|
||||
|
|
5
third_party/nsync/wait_s.internal.h
vendored
5
third_party/nsync/wait_s.internal.h
vendored
|
@ -1,6 +1,7 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_THREAD_WAIT_INTERNAL_H_
|
||||
#define COSMOPOLITAN_LIBC_THREAD_WAIT_INTERNAL_H_
|
||||
#include "libc/intrin/dll.h"
|
||||
#include "third_party/nsync/defs.h"
|
||||
#include "third_party/nsync/atomic.h"
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
|
@ -10,10 +11,12 @@ COSMOPOLITAN_C_START_
|
|||
with v pointing to the client's object and nw pointing to a struct
|
||||
nsync_waiter_s. */
|
||||
struct nsync_waiter_s {
|
||||
#if NSYNC_DEBUG
|
||||
uint32_t tag; /* used for debugging */
|
||||
#endif
|
||||
uint32_t flags; /* see below */
|
||||
struct Dll q; /* used to link children of parent */
|
||||
nsync_atomic_uint32_ waiting; /* non-zero <=> the waiter is waiting */
|
||||
struct Dll q; /* used to link children of parent */
|
||||
struct nsync_semaphore_s_ *sem; /* *sem will be Ved when waiter is woken */
|
||||
};
|
||||
|
||||
|
|
|
@ -94,4 +94,6 @@ o/$(MODE)/tool/hello/wait-pe.ape: \
|
|||
o/$(MODE)/tool/build/elf2pe
|
||||
@$(COMPILE) -AELF2PE o/$(MODE)/tool/build/elf2pe -R 64kb -S 4kb -o $@ $<
|
||||
|
||||
o/$(MODE)/tool/hello/life-pe.ape.zip.o: private ZIPOBJ_FLAGS += -B
|
||||
|
||||
$(TOOL_HELLO_OBJS): tool/hello/BUILD.mk
|
||||
|
|
Loading…
Reference in a new issue