mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-06-30 16:28:30 +00:00
Do more quality assurance work
This commit is contained in:
parent
67b19ae733
commit
d461c6f47d
31 changed files with 194 additions and 108 deletions
|
@ -135,6 +135,8 @@ o/$(MODE)/libc/intrin/kweekdayname.o: libc/intrin/kweekdayname.S
|
|||
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
|
||||
o/$(MODE)/libc/intrin/kweekdaynameshort.o: libc/intrin/kweekdaynameshort.S
|
||||
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
|
||||
o/$(MODE)/libc/intrin/sched_yield.o: libc/intrin/sched_yield.S
|
||||
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
|
||||
|
||||
LIBC_INTRIN_LIBS = $(foreach x,$(LIBC_INTRIN_ARTIFACTS),$($(x)))
|
||||
LIBC_INTRIN_HDRS = $(foreach x,$(LIBC_INTRIN_ARTIFACTS),$($(x)_HDRS))
|
||||
|
|
|
@ -26,6 +26,8 @@ struct Maps {
|
|||
struct Dll *free;
|
||||
struct Map stack;
|
||||
struct Dll *used;
|
||||
size_t count;
|
||||
size_t pages;
|
||||
};
|
||||
|
||||
struct AddrSize {
|
||||
|
@ -35,9 +37,9 @@ struct AddrSize {
|
|||
|
||||
extern struct Maps __maps;
|
||||
|
||||
int maps_check(void);
|
||||
void __maps_init(void);
|
||||
void __maps_lock(void);
|
||||
void __maps_check(void);
|
||||
void __maps_unlock(void);
|
||||
struct Map *__maps_alloc(void);
|
||||
void __maps_free(struct Map *);
|
||||
|
|
|
@ -25,9 +25,11 @@
|
|||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/intrin/describebacktrace.internal.h"
|
||||
#include "libc/intrin/describeflags.internal.h"
|
||||
#include "libc/intrin/directmap.internal.h"
|
||||
#include "libc/intrin/dll.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/maps.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
|
@ -44,6 +46,7 @@
|
|||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/thread/thread.h"
|
||||
|
||||
#define MMDEBUG 0 // this code is too slow for openbsd/windows
|
||||
#define WINBASE 0x100080040000 // TODO: Can we support Windows Vista again?
|
||||
#define WINMAXX 0x200080000000
|
||||
|
||||
|
@ -51,21 +54,80 @@
|
|||
|
||||
#define PGUP(x) (((x) + granularity - 1) & -granularity)
|
||||
|
||||
#if MMDEBUG
|
||||
#define ASSERT(x) (void)0
|
||||
#else
|
||||
#define ASSERT(x) \
|
||||
do { \
|
||||
if (!(x)) { \
|
||||
char bt[160]; \
|
||||
struct StackFrame *bp = __builtin_frame_address(0); \
|
||||
kprintf("%!s:%d: assertion failed: %!s\n", __FILE__, __LINE__, #x); \
|
||||
kprintf("bt %!s\n", (DescribeBacktrace)(bt, bp)); \
|
||||
__print_maps(); \
|
||||
_Exit(99); \
|
||||
} \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
static atomic_ulong rollo;
|
||||
|
||||
static bool overlaps_existing_map(const char *addr, size_t size) {
|
||||
int granularity = __granularity();
|
||||
for (struct Map *map = __maps.maps; map; map = map->next)
|
||||
if (MAX(addr, map->addr) <
|
||||
MIN(addr + PGUP(size), map->addr + PGUP(map->size)))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void __maps_check(void) {
|
||||
#if MMDEBUG
|
||||
size_t maps = 0;
|
||||
size_t pages = 0;
|
||||
int granularity = getauxval(AT_PAGESZ);
|
||||
for (struct Map *map = __maps.maps; map; map = map->next) {
|
||||
ASSERT(map->addr != MAP_FAILED);
|
||||
ASSERT(map->size);
|
||||
pages += PGUP(map->size) / granularity;
|
||||
maps += 1;
|
||||
}
|
||||
ASSERT(maps = __maps.count);
|
||||
ASSERT(pages == __maps.pages);
|
||||
for (struct Map *m1 = __maps.maps; m1; m1 = m1->next)
|
||||
for (struct Map *m2 = m1->next; m2; m2 = m2->next)
|
||||
ASSERT(MAX(m1->addr, m2->addr) >=
|
||||
MIN(m1->addr + PGUP(m1->size), m2->addr + PGUP(m2->size)));
|
||||
#endif
|
||||
}
|
||||
|
||||
void __maps_free(struct Map *map) {
|
||||
map->next = 0;
|
||||
map->size = 0;
|
||||
map->addr = MAP_FAILED;
|
||||
ASSERT(dll_is_alone(&map->elem));
|
||||
dll_make_last(&__maps.free, &map->elem);
|
||||
}
|
||||
|
||||
void __maps_insert(struct Map *map) {
|
||||
struct Map *last = __maps.maps;
|
||||
if (last && //
|
||||
int granularity = getauxval(AT_PAGESZ);
|
||||
__maps.pages += PGUP(map->size) / granularity;
|
||||
if (last && !IsWindows() && //
|
||||
map->addr == last->addr + last->size && //
|
||||
(map->flags & MAP_ANONYMOUS) && //
|
||||
map->flags == last->flags && //
|
||||
map->prot == last->prot && //
|
||||
map->off == last->off && //
|
||||
map->h == last->h && //
|
||||
map->off == -1) {
|
||||
map->prot == last->prot) {
|
||||
last->size += map->size;
|
||||
dll_remove(&__maps.used, &last->elem);
|
||||
dll_make_first(&__maps.used, &last->elem);
|
||||
__maps_free(map);
|
||||
} else if (last && !IsWindows() && //
|
||||
map->addr + map->size == last->addr && //
|
||||
(map->flags & MAP_ANONYMOUS) && //
|
||||
map->flags == last->flags && //
|
||||
map->prot == last->prot) {
|
||||
last->addr -= map->size;
|
||||
last->size += map->size;
|
||||
dll_remove(&__maps.used, &last->elem);
|
||||
dll_make_first(&__maps.used, &last->elem);
|
||||
|
@ -74,7 +136,9 @@ void __maps_insert(struct Map *map) {
|
|||
dll_make_first(&__maps.used, &map->elem);
|
||||
map->next = __maps.maps;
|
||||
__maps.maps = map;
|
||||
++__maps.count;
|
||||
}
|
||||
__maps_check();
|
||||
}
|
||||
|
||||
struct Map *__maps_alloc(void) {
|
||||
|
@ -104,14 +168,6 @@ struct Map *__maps_alloc(void) {
|
|||
return map;
|
||||
}
|
||||
|
||||
static bool __overlaps_existing_map(const char *addr, size_t size) {
|
||||
for (struct Map *map = __maps.maps; map; map = map->next) {
|
||||
if (MAX(addr, map->addr) < MIN(addr + size, map->addr + map->size))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static int __munmap_chunk(void *addr, size_t size) {
|
||||
return sys_munmap(addr, size);
|
||||
}
|
||||
|
@ -119,6 +175,7 @@ static int __munmap_chunk(void *addr, size_t size) {
|
|||
static int __munmap(char *addr, size_t size, bool untrack_only) {
|
||||
|
||||
// validate arguments
|
||||
int pagesz = getauxval(AT_PAGESZ);
|
||||
int granularity = __granularity();
|
||||
if (((uintptr_t)addr & (granularity - 1)) || //
|
||||
!size || (uintptr_t)addr + size < size)
|
||||
|
@ -127,7 +184,6 @@ static int __munmap(char *addr, size_t size, bool untrack_only) {
|
|||
// untrack and delete mapping
|
||||
int rc = 0;
|
||||
__maps_lock();
|
||||
// we can't call strace, kprintf, or nothing
|
||||
StartOver:;
|
||||
struct Map *map = __maps.maps;
|
||||
_Atomic(struct Map *) *prev = &__maps.maps;
|
||||
|
@ -141,12 +197,16 @@ StartOver:;
|
|||
// remove mapping completely
|
||||
dll_remove(&__maps.used, &map->elem);
|
||||
*prev = next;
|
||||
map->size = 0;
|
||||
map->addr = MAP_FAILED;
|
||||
__maps.pages -= (map_size + pagesz - 1) / pagesz;
|
||||
__maps.count -= 1;
|
||||
if (untrack_only) {
|
||||
__maps_free(map);
|
||||
__maps_check();
|
||||
} else {
|
||||
__maps_unlock();
|
||||
if (!IsWindows()) {
|
||||
ASSERT(addr <= map_addr);
|
||||
ASSERT(map_addr + PGUP(map_size) <= addr + PGUP(size));
|
||||
if (__munmap_chunk(map_addr, map_size))
|
||||
rc = -1;
|
||||
} else {
|
||||
|
@ -155,7 +215,9 @@ StartOver:;
|
|||
if (!CloseHandle(map->h))
|
||||
rc = -1;
|
||||
}
|
||||
__maps_lock();
|
||||
__maps_free(map);
|
||||
__maps_check();
|
||||
goto StartOver;
|
||||
}
|
||||
map = next;
|
||||
|
@ -167,15 +229,24 @@ StartOver:;
|
|||
rc = einval();
|
||||
} else if (addr <= map_addr) {
|
||||
// shave off lefthand side of mapping
|
||||
size_t left = addr + size - map_addr;
|
||||
size_t right = map_addr + map_size - (addr + size);
|
||||
ASSERT(addr + size < map_addr + PGUP(map_size));
|
||||
size_t left = PGUP(addr + size - map_addr);
|
||||
size_t right = map_size - left;
|
||||
ASSERT(right > 0);
|
||||
ASSERT(left > 0);
|
||||
map->addr += left;
|
||||
map->size = right;
|
||||
if (map->off != -1)
|
||||
map->off += left;
|
||||
__maps.pages -= (left + pagesz - 1) / pagesz;
|
||||
__maps_check();
|
||||
if (!untrack_only) {
|
||||
__maps_unlock();
|
||||
ASSERT(addr <= map_addr);
|
||||
ASSERT(map_addr + PGUP(left) <= addr + PGUP(size));
|
||||
if (__munmap_chunk(map_addr, left) == -1)
|
||||
rc = -1;
|
||||
__maps_lock();
|
||||
goto StartOver;
|
||||
}
|
||||
} else if (addr + PGUP(size) >= map_addr + PGUP(map_size)) {
|
||||
|
@ -183,9 +254,14 @@ StartOver:;
|
|||
size_t left = addr - map_addr;
|
||||
size_t right = map_addr + map_size - addr;
|
||||
map->size = left;
|
||||
__maps.pages -= (right + pagesz - 1) / pagesz;
|
||||
__maps_check();
|
||||
if (!untrack_only) {
|
||||
__maps_unlock();
|
||||
ASSERT(PGUP(right) <= PGUP(size));
|
||||
if (__munmap_chunk(addr, right) == -1)
|
||||
rc = -1;
|
||||
__maps_lock();
|
||||
goto StartOver;
|
||||
}
|
||||
} else {
|
||||
|
@ -207,9 +283,14 @@ StartOver:;
|
|||
map->off += left + middle;
|
||||
dll_make_first(&__maps.used, &leftmap->elem);
|
||||
*prev = leftmap;
|
||||
__maps.pages -= (middle + pagesz - 1) / pagesz;
|
||||
__maps.count += 1;
|
||||
__maps_check();
|
||||
if (!untrack_only) {
|
||||
__maps_unlock();
|
||||
if (__munmap_chunk(addr, size) == -1)
|
||||
rc = -1;
|
||||
__maps_lock();
|
||||
goto StartOver;
|
||||
}
|
||||
} else {
|
||||
|
@ -241,7 +322,7 @@ static void *__mmap_chunk(void *addr, size_t size, int prot, int flags, int fd,
|
|||
sysflags |= MAP_FIXED_NOREPLACE_linux;
|
||||
} else if (IsFreebsd() || IsNetbsd()) {
|
||||
sysflags |= MAP_FIXED;
|
||||
if (__overlaps_existing_map(addr, size))
|
||||
if (overlaps_existing_map(addr, size))
|
||||
return (void *)eexist();
|
||||
} else {
|
||||
noreplace = true;
|
||||
|
@ -296,7 +377,7 @@ TryAgain:
|
|||
|
||||
// untrack mapping we blew away
|
||||
if (should_untrack)
|
||||
__munmap(addr, size, true);
|
||||
__munmap(res.addr, size, true);
|
||||
|
||||
// track Map object
|
||||
map->addr = res.addr;
|
||||
|
@ -346,7 +427,7 @@ static void *__mmap_impl(char *addr, size_t size, int prot, int flags, int fd,
|
|||
|
||||
// so we create an separate map for each granule in the mapping
|
||||
if (!(flags & MAP_FIXED)) {
|
||||
while (__overlaps_existing_map(addr, size)) {
|
||||
while (overlaps_existing_map(addr, size)) {
|
||||
if (flags & MAP_FIXED_NOREPLACE)
|
||||
return (void *)eexist();
|
||||
addr += granularity;
|
||||
|
|
|
@ -91,8 +91,8 @@ int __mprotect(char *addr, size_t size, int prot) {
|
|||
rc = -1;
|
||||
} else if (addr <= map_addr) {
|
||||
// cleave lefthand side of mapping
|
||||
size_t left = addr + size - map_addr;
|
||||
size_t right = map_addr + map_size - (addr + size);
|
||||
size_t left = PGUP(addr + size - map_addr);
|
||||
size_t right = map_size - left;
|
||||
struct Map *leftmap;
|
||||
if ((leftmap = __maps_alloc())) {
|
||||
if (!__mprotect_chunk(map_addr, left, prot, false)) {
|
||||
|
@ -108,6 +108,8 @@ int __mprotect(char *addr, size_t size, int prot) {
|
|||
map->off += left;
|
||||
dll_make_first(&__maps.used, &leftmap->elem);
|
||||
*prev = leftmap;
|
||||
__maps.count += 1;
|
||||
__maps_check();
|
||||
} else {
|
||||
__maps_free(leftmap);
|
||||
rc = -1;
|
||||
|
@ -135,6 +137,8 @@ int __mprotect(char *addr, size_t size, int prot) {
|
|||
map->off += left;
|
||||
dll_make_first(&__maps.used, &leftmap->elem);
|
||||
*prev = leftmap;
|
||||
__maps.count += 1;
|
||||
__maps_check();
|
||||
} else {
|
||||
__maps_free(leftmap);
|
||||
rc = -1;
|
||||
|
@ -171,6 +175,8 @@ int __mprotect(char *addr, size_t size, int prot) {
|
|||
dll_make_first(&__maps.used, &leftmap->elem);
|
||||
dll_make_first(&__maps.used, &midlmap->elem);
|
||||
*prev = leftmap;
|
||||
__maps.count += 2;
|
||||
__maps_check();
|
||||
} else {
|
||||
__maps_free(midlmap);
|
||||
__maps_free(leftmap);
|
||||
|
|
|
@ -34,6 +34,7 @@ textwindows int sys_msync_nt(char *addr, size_t size, int flags) {
|
|||
return einval();
|
||||
|
||||
int rc = 0;
|
||||
__maps_lock();
|
||||
for (struct Map *map = __maps.maps; map; map = map->next) {
|
||||
char *beg = MAX(addr, map->addr);
|
||||
char *end = MIN(addr + size, map->addr + map->size);
|
||||
|
@ -42,6 +43,7 @@ textwindows int sys_msync_nt(char *addr, size_t size, int flags) {
|
|||
rc = -1;
|
||||
// TODO(jart): FlushFileBuffers too on g_fds handle if MS_SYNC?
|
||||
}
|
||||
__maps_unlock();
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -24,15 +24,20 @@
|
|||
#include "libc/macros.internal.h"
|
||||
#include "libc/runtime/memtrack.internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/sysv/consts/auxv.h"
|
||||
|
||||
/**
|
||||
* Prints memory mappings.
|
||||
*/
|
||||
void __print_maps(void) {
|
||||
int limit = 10;
|
||||
int limit = 13;
|
||||
long maptally = 0;
|
||||
char mappingbuf[8], sb[16];
|
||||
for (struct Map *map = __maps.maps; map; map = map->next) {
|
||||
__maps_lock();
|
||||
struct Dll *e, *e2;
|
||||
for (e = dll_first(__maps.used); e; e = e2) {
|
||||
e2 = dll_next(__maps.used, e);
|
||||
struct Map *map = MAP_CONTAINER(e);
|
||||
maptally += map->size;
|
||||
kprintf("%012lx-%012lx %!s", map->addr, map->addr + map->size,
|
||||
(DescribeMapping)(mappingbuf, map->prot, map->flags));
|
||||
|
@ -45,11 +50,10 @@ void __print_maps(void) {
|
|||
if (map->readonlyfile)
|
||||
kprintf(" readonlyfile");
|
||||
kprintf("\n");
|
||||
if (!--limit) {
|
||||
kprintf("...\n");
|
||||
if (!--limit)
|
||||
break;
|
||||
}
|
||||
}
|
||||
sizefmt(sb, maptally, 1024);
|
||||
kprintf("# %!sb mapped memory\n", sb);
|
||||
kprintf("# %'zu bytes in %'zu mappings\n",
|
||||
__maps.pages * getauxval(AT_PAGESZ), __maps.count);
|
||||
__maps_unlock();
|
||||
}
|
||||
|
|
111
libc/intrin/sched_yield.S
Normal file
111
libc/intrin/sched_yield.S
Normal file
|
@ -0,0 +1,111 @@
|
|||
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
|
||||
│ vi: set noet ft=asm ts=8 sw=8 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/dce.h"
|
||||
#include "libc/sysv/consts/nr.h"
|
||||
#include "libc/macros.internal.h"
|
||||
.privileged
|
||||
|
||||
// Relinquishes scheduled quantum.
|
||||
//
|
||||
// @return 0 on success, or -1 w/ errno
|
||||
sched_yield:
|
||||
#ifdef __x86_64__
|
||||
push %rbp
|
||||
mov %rsp,%rbp
|
||||
xor %eax,%eax
|
||||
mov __hostos(%rip),%dl
|
||||
|
||||
#if SupportsMetal()
|
||||
testb $_HOSTMETAL,%dl
|
||||
jnz 9f
|
||||
#endif
|
||||
|
||||
#if SupportsWindows()
|
||||
// Windows Support
|
||||
//
|
||||
// A value of zero, together with the bAlertable parameter set to
|
||||
// FALSE, causes the thread to relinquish the remainder of its time
|
||||
// slice to any other thread that is ready to run, if there are no
|
||||
// pending user APCs on the calling thread. If there are no other
|
||||
// threads ready to run and no user APCs are queued, the function
|
||||
// returns immediately, and the thread continues execution.
|
||||
// ──Quoth MSDN
|
||||
testb $_HOSTWINDOWS,%dl
|
||||
jz 1f
|
||||
xor %ecx,%ecx
|
||||
xor %edx,%edx
|
||||
ntcall __imp_SleepEx
|
||||
xor %eax,%eax
|
||||
jmp 9f
|
||||
1:
|
||||
#endif
|
||||
|
||||
#if SupportsSystemv()
|
||||
// On XNU we polyfill sched_yield() using sleep() which'll
|
||||
// be polyfilled using select() with a zero timeout, which
|
||||
// means to wait zero microseconds and then returns a zero
|
||||
// and this hopefully will give other threads a chance too
|
||||
// XNU has a special version we use called select_nocancel
|
||||
//
|
||||
// "If the readfds, writefds, and errorfds arguments are
|
||||
// all null pointers and the timeout argument is not a
|
||||
// null pointer, the pselect() or select() function shall
|
||||
// block for the time specified, or until interrupted by
|
||||
// a signal." ──Quoth IEEE 1003.1-2017 §functions/select
|
||||
//
|
||||
// On other platforms, sched_yield() takes no arguments.
|
||||
push $0 // timeout.tv_usec
|
||||
push $0 // timeout.tv_sec
|
||||
xor %edi,%edi // nfds
|
||||
xor %esi,%esi // readfds
|
||||
xor %edx,%edx // writefds
|
||||
xor %r10d,%r10d // exceptfds
|
||||
mov %rsp,%r8 // timeout
|
||||
mov __NR_sched_yield,%eax // ordinal
|
||||
clc // linux
|
||||
syscall
|
||||
// It should not be possible for this to fail so we don't
|
||||
// bother going through the errno ritual. If this somehow
|
||||
// fails a positive or negative errno might get returned.
|
||||
#endif
|
||||
|
||||
9: leave
|
||||
ret
|
||||
|
||||
#elif defined(__aarch64__)
|
||||
|
||||
stp x29,x30,[sp,-32]!
|
||||
mov x29,sp
|
||||
mov x3,0
|
||||
mov x2,0
|
||||
add x4,sp,16
|
||||
mov x1,0
|
||||
mov w0,0
|
||||
stp xzr,xzr,[sp,16]
|
||||
mov x8,#0x7c // sched_yield() for gnu/systemd
|
||||
mov x16,#0x5d // select(0,0,0,0,&blah) for xnu
|
||||
svc 0
|
||||
ldp x29,x30,[sp],32
|
||||
ret
|
||||
|
||||
#else
|
||||
#error "arch unsupported"
|
||||
#endif
|
||||
.endfn sched_yield,globl
|
||||
.previous
|
Loading…
Add table
Add a link
Reference in a new issue