mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-06-28 15:28:30 +00:00
Make improvements to locking
This change makes pthread_mutex_lock() as fast as _spinlock() by default. Thread instability issues on NetBSD have been resolved. Improvements made to gdtoa thread code. Crash reporting will now synchronize between threads in a slightly better way.
This commit is contained in:
parent
25041b8026
commit
d5312b60f7
60 changed files with 890 additions and 629 deletions
|
@ -16,9 +16,6 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/time/time.h"
|
||||
|
||||
/**
|
||||
|
|
|
@ -25,9 +25,11 @@ unsigned __sighandflags[NSIG];
|
|||
static pthread_mutex_t __sig_lock_obj;
|
||||
|
||||
void(__sig_lock)(void) {
|
||||
__sig_lock_obj.attr = PTHREAD_MUTEX_RECURSIVE;
|
||||
pthread_mutex_lock(&__sig_lock_obj);
|
||||
}
|
||||
|
||||
void(__sig_unlock)(void) {
|
||||
__sig_lock_obj.attr = PTHREAD_MUTEX_RECURSIVE;
|
||||
pthread_mutex_unlock(&__sig_lock_obj);
|
||||
}
|
||||
|
|
|
@ -18,34 +18,44 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/bits/weaken.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/intrin/cmpxchg.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/lockcmpxchg.h"
|
||||
#include "libc/log/log.h"
|
||||
#include "libc/intrin/lockcmpxchgp.h"
|
||||
#include "libc/log/backtrace.internal.h"
|
||||
#include "libc/log/internal.h"
|
||||
#include "libc/runtime/internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/runtime/symbols.internal.h"
|
||||
|
||||
/**
|
||||
* Handles failure of assert() macro.
|
||||
*/
|
||||
relegated wontreturn void __assert_fail(const char *expr, const char *file,
|
||||
int line) {
|
||||
int rc;
|
||||
static bool noreentry;
|
||||
int me, owner;
|
||||
static int sync;
|
||||
--__strace;
|
||||
--__ftrace;
|
||||
kprintf("%s:%d: assert(%s) failed\n", file, line, expr);
|
||||
if (_lockcmpxchg(&noreentry, false, true)) {
|
||||
if (weaken(__die)) {
|
||||
weaken(__die)();
|
||||
owner = 0;
|
||||
me = gettid();
|
||||
kprintf("%s:%d: assert(%s) failed (tid %d)\n", file, line, expr, me);
|
||||
if (_lockcmpxchgp(&sync, &owner, me)) {
|
||||
__restore_tty();
|
||||
if (weaken(ShowBacktrace)) {
|
||||
weaken(ShowBacktrace)(2, __builtin_frame_address(0));
|
||||
} else if (weaken(PrintBacktraceUsingSymbols) && weaken(GetSymbolTable)) {
|
||||
weaken(PrintBacktraceUsingSymbols)(2, __builtin_frame_address(0),
|
||||
weaken(GetSymbolTable)());
|
||||
} else {
|
||||
kprintf("can't backtrace b/c `__die` not linked\n");
|
||||
kprintf("can't backtrace b/c `ShowCrashReports` not linked\n");
|
||||
}
|
||||
rc = 23;
|
||||
__restorewintty();
|
||||
_Exit(23);
|
||||
} else if (owner == me) {
|
||||
kprintf("assert failed while failing\n");
|
||||
__restorewintty();
|
||||
_Exit(24);
|
||||
} else {
|
||||
rc = 24;
|
||||
_Exit1(25);
|
||||
}
|
||||
__restorewintty();
|
||||
_Exit(rc);
|
||||
}
|
||||
|
|
|
@ -31,7 +31,6 @@
|
|||
* @noreturn
|
||||
*/
|
||||
privileged wontreturn void _Exit1(int rc) {
|
||||
jmp_buf *jb;
|
||||
struct WinThread *wt;
|
||||
STRACE("_Exit1(%d)", rc);
|
||||
if (!IsWindows() && !IsMetal()) {
|
||||
|
|
|
@ -30,10 +30,12 @@ struct Fds g_fds;
|
|||
static pthread_mutex_t __fds_lock_obj;
|
||||
|
||||
void(__fds_lock)(void) {
|
||||
__fds_lock_obj.attr = PTHREAD_MUTEX_RECURSIVE;
|
||||
pthread_mutex_lock(&__fds_lock_obj);
|
||||
}
|
||||
|
||||
void(__fds_unlock)(void) {
|
||||
__fds_lock_obj.attr = PTHREAD_MUTEX_RECURSIVE;
|
||||
pthread_mutex_unlock(&__fds_lock_obj);
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
STATIC_YOINK("_init__mmi");
|
||||
|
||||
struct MemoryIntervals _mmi;
|
||||
static pthread_mutex_t __mmi_lock_obj;
|
||||
pthread_mutex_t __mmi_lock_obj; // recursive :'(
|
||||
|
||||
void(__mmi_lock)(void) {
|
||||
pthread_mutex_lock(&__mmi_lock_obj);
|
||||
|
|
|
@ -16,9 +16,11 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/intrin/pthread.h"
|
||||
#include "libc/macros.internal.h"
|
||||
|
||||
.init.start 200,_init__mmi
|
||||
movb $OPEN_MAX,_mmi+8
|
||||
movl $_mmi+24,_mmi+16
|
||||
movb $PTHREAD_MUTEX_RECURSIVE,__mmi_lock_obj(%rip)
|
||||
.init.end 200,_init__mmi
|
||||
|
|
|
@ -1,19 +1,21 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_RUNTIME_PTHREAD_H_
|
||||
#define COSMOPOLITAN_LIBC_RUNTIME_PTHREAD_H_
|
||||
#include "libc/bits/atomic.h"
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
#include "libc/dce.h"
|
||||
|
||||
#define PTHREAD_ONCE_INIT 0
|
||||
|
||||
#define PTHREAD_MUTEX_DEFAULT PTHREAD_MUTEX_NORMAL
|
||||
#define PTHREAD_MUTEX_RECURSIVE 0
|
||||
#define PTHREAD_MUTEX_NORMAL 1
|
||||
#define PTHREAD_MUTEX_NORMAL 0
|
||||
#define PTHREAD_MUTEX_RECURSIVE 1
|
||||
#define PTHREAD_MUTEX_ERRORCHECK 2
|
||||
#define PTHREAD_MUTEX_STALLED 0
|
||||
#define PTHREAD_MUTEX_ROBUST 1
|
||||
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
/* clang-format off */
|
||||
#define PTHREAD_MUTEX_INITIALIZER {PTHREAD_MUTEX_DEFAULT}
|
||||
#define PTHREAD_RWLOCK_INITIALIZER {{{0}}}
|
||||
|
@ -66,8 +68,8 @@ typedef struct {
|
|||
} __u;
|
||||
} pthread_rwlock_t;
|
||||
|
||||
wontreturn void pthread_exit(void *);
|
||||
pureconst pthread_t pthread_self(void);
|
||||
void pthread_exit(void *) wontreturn;
|
||||
pthread_t pthread_self(void) pureconst;
|
||||
int pthread_create(pthread_t *, const pthread_attr_t *, void *(*)(void *),
|
||||
void *);
|
||||
int pthread_yield(void);
|
||||
|
@ -104,6 +106,30 @@ int pthread_rwlock_trywrlock(pthread_rwlock_t *);
|
|||
int pthread_rwlock_timedwrlock(pthread_rwlock_t *, const struct timespec *);
|
||||
int pthread_rwlock_unlock(pthread_rwlock_t *);
|
||||
|
||||
#define pthread_mutexattr_init(pAttr) ((pAttr)->attr = PTHREAD_MUTEX_DEFAULT, 0)
|
||||
#define pthread_mutexattr_destroy(pAttr) ((pAttr)->attr = 0)
|
||||
#define pthread_mutexattr_gettype(pAttr, pType) (*(pType) = (pAttr)->attr, 0)
|
||||
#define pthread_mutexattr_settype(pAttr, type) ((pAttr)->attr = type, 0)
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define pthread_mutex_lock(mutex) \
|
||||
(((mutex)->attr == PTHREAD_MUTEX_NORMAL && \
|
||||
!atomic_load_explicit(&(mutex)->lock, memory_order_relaxed) && \
|
||||
!atomic_exchange(&(mutex)->lock, 1)) \
|
||||
? 0 \
|
||||
: pthread_mutex_lock(mutex))
|
||||
#define pthread_mutex_unlock(mutex) \
|
||||
((mutex)->attr == PTHREAD_MUTEX_NORMAL \
|
||||
? (atomic_store_explicit(&(mutex)->lock, 0, memory_order_relaxed), \
|
||||
(IsLinux() && \
|
||||
atomic_load_explicit(&(mutex)->waits, memory_order_relaxed) && \
|
||||
_pthread_mutex_wake(mutex)), \
|
||||
0) \
|
||||
: pthread_mutex_unlock(mutex))
|
||||
#endif
|
||||
|
||||
int _pthread_mutex_wake(pthread_mutex_t *) hidden;
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_LIBC_RUNTIME_PTHREAD_H_ */
|
||||
|
|
|
@ -21,41 +21,68 @@
|
|||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/pthread.h"
|
||||
#include "libc/intrin/spinlock.h"
|
||||
#include "libc/linux/futex.h"
|
||||
#include "libc/nexgen32e/threaded.h"
|
||||
|
||||
static int pthread_mutex_lock_spin(pthread_mutex_t *mutex, int tries) {
|
||||
volatile int i;
|
||||
if (tries < 7) {
|
||||
for (i = 0; i != 1 << tries; i++) {
|
||||
}
|
||||
tries++;
|
||||
} else if (IsLinux()) {
|
||||
atomic_fetch_add(&mutex->waits, 1);
|
||||
LinuxFutexWait(&mutex->lock, 1, 0);
|
||||
atomic_fetch_sub(&mutex->waits, 1);
|
||||
} else {
|
||||
sched_yield();
|
||||
}
|
||||
return tries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Locks mutex.
|
||||
*
|
||||
* _spinlock() l: 181,570c 58,646ns
|
||||
* mutex normal l: 297,965c 96,241ns
|
||||
* mutex recursive l: 1,112,166c 359,223ns
|
||||
* mutex errorcheck l: 1,449,723c 468,252ns
|
||||
*
|
||||
* @return 0 on success, or error number on failure
|
||||
*/
|
||||
int pthread_mutex_lock(pthread_mutex_t *mutex) {
|
||||
volatile int i;
|
||||
int(pthread_mutex_lock)(pthread_mutex_t *mutex) {
|
||||
int me, owner, tries;
|
||||
for (tries = 0, me = gettid();;) {
|
||||
owner = atomic_load_explicit(&mutex->lock, memory_order_relaxed);
|
||||
if (!owner && atomic_compare_exchange_weak_explicit(
|
||||
&mutex->lock, &owner, me, memory_order_acquire,
|
||||
memory_order_relaxed)) {
|
||||
break;
|
||||
} else if (owner == me) {
|
||||
if (mutex->attr != PTHREAD_MUTEX_ERRORCHECK) {
|
||||
break;
|
||||
} else {
|
||||
return EDEADLK;
|
||||
switch (mutex->attr) {
|
||||
case PTHREAD_MUTEX_NORMAL:
|
||||
for (tries = 0;;) {
|
||||
if (!atomic_load_explicit(&mutex->lock, memory_order_relaxed) &&
|
||||
!atomic_exchange_explicit(&mutex->lock, 1, memory_order_acquire)) {
|
||||
break;
|
||||
}
|
||||
tries = pthread_mutex_lock_spin(mutex, tries);
|
||||
}
|
||||
}
|
||||
if (tries < 7) {
|
||||
for (i = 0; i != 1 << tries; i++) {
|
||||
return 0;
|
||||
case PTHREAD_MUTEX_RECURSIVE:
|
||||
case PTHREAD_MUTEX_ERRORCHECK:
|
||||
for (tries = 0, me = gettid();;) {
|
||||
owner = atomic_load_explicit(&mutex->lock, memory_order_relaxed);
|
||||
if (!owner && atomic_compare_exchange_weak_explicit(
|
||||
&mutex->lock, &owner, me, memory_order_acquire,
|
||||
memory_order_relaxed)) {
|
||||
break;
|
||||
} else if (owner == me) {
|
||||
if (mutex->attr != PTHREAD_MUTEX_ERRORCHECK) {
|
||||
break;
|
||||
} else {
|
||||
return EDEADLK;
|
||||
}
|
||||
}
|
||||
tries = pthread_mutex_lock_spin(mutex, tries);
|
||||
}
|
||||
tries++;
|
||||
} else if (IsLinux()) {
|
||||
atomic_fetch_add(&mutex->waits, 1);
|
||||
LinuxFutexWait(&mutex->lock, owner, 0);
|
||||
atomic_fetch_sub(&mutex->waits, 1);
|
||||
} else {
|
||||
sched_yield();
|
||||
}
|
||||
++mutex->reent;
|
||||
return 0;
|
||||
default:
|
||||
return EINVAL;
|
||||
}
|
||||
++mutex->reent;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -18,28 +18,34 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/bits/atomic.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/pthread.h"
|
||||
#include "libc/linux/futex.h"
|
||||
#include "libc/nexgen32e/threaded.h"
|
||||
|
||||
/**
|
||||
* Releases mutex.
|
||||
* @return 0 on success or error number on failure
|
||||
* @raises EPERM if in error check mode and not owned by caller
|
||||
*/
|
||||
int pthread_mutex_unlock(pthread_mutex_t *mutex) {
|
||||
int owner;
|
||||
if (mutex->attr == PTHREAD_MUTEX_ERRORCHECK && mutex->lock != gettid()) {
|
||||
return EPERM;
|
||||
int(pthread_mutex_unlock)(pthread_mutex_t *mutex) {
|
||||
int me, owner;
|
||||
switch (mutex->attr) {
|
||||
case PTHREAD_MUTEX_ERRORCHECK:
|
||||
me = gettid();
|
||||
owner = atomic_load_explicit(&mutex->lock, memory_order_relaxed);
|
||||
if (owner != me) return EPERM;
|
||||
// fallthrough
|
||||
case PTHREAD_MUTEX_RECURSIVE:
|
||||
if (--mutex->reent) return 0;
|
||||
// fallthrough
|
||||
case PTHREAD_MUTEX_NORMAL:
|
||||
atomic_store_explicit(&mutex->lock, 0, memory_order_relaxed);
|
||||
if (IsLinux() &&
|
||||
atomic_load_explicit(&mutex->waits, memory_order_relaxed)) {
|
||||
_pthread_mutex_wake(mutex);
|
||||
}
|
||||
return 0;
|
||||
default:
|
||||
return EINVAL;
|
||||
}
|
||||
if (!--mutex->reent) {
|
||||
atomic_store_explicit(&mutex->lock, 0, memory_order_relaxed);
|
||||
if (IsLinux() &&
|
||||
atomic_load_explicit(&mutex->waits, memory_order_acquire)) {
|
||||
LinuxFutexWake(&mutex->lock, 1);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
26
libc/intrin/pthread_mutex_wake.c
Normal file
26
libc/intrin/pthread_mutex_wake.c
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net 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/bits/atomic.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/pthread.h"
|
||||
#include "libc/linux/futex.h"
|
||||
|
||||
int _pthread_mutex_wake(pthread_mutex_t *mutex) {
|
||||
return LinuxFutexWake(&mutex->lock, 1);
|
||||
}
|
|
@ -17,13 +17,12 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/intrin/pthread.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
/**
|
||||
* Destroys mutex attr.
|
||||
* @return 0 on success, or error number on failure
|
||||
*/
|
||||
int pthread_mutexattr_destroy(pthread_mutexattr_t *attr) {
|
||||
bzero(attr, sizeof(*attr));
|
||||
int(pthread_mutexattr_destroy)(pthread_mutexattr_t *attr) {
|
||||
attr->attr = 0;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
* - `PTHREAD_MUTEX_ERRORCHECK`
|
||||
* @return 0 on success, or error on failure
|
||||
*/
|
||||
int pthread_mutexattr_gettype(const pthread_mutexattr_t *attr, int *type) {
|
||||
int(pthread_mutexattr_gettype)(const pthread_mutexattr_t *attr, int *type) {
|
||||
*type = attr->attr;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -17,14 +17,12 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/intrin/pthread.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
/**
|
||||
* Initializes mutex attr.
|
||||
* @return 0 on success, or error number on failure
|
||||
*/
|
||||
int pthread_mutexattr_init(pthread_mutexattr_t *attr) {
|
||||
bzero(attr, sizeof(*attr));
|
||||
int(pthread_mutexattr_init)(pthread_mutexattr_t *attr) {
|
||||
attr->attr = PTHREAD_MUTEX_DEFAULT;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
* @return 0 on success, or error on failure
|
||||
* @raises EINVAL if `type` is invalid
|
||||
*/
|
||||
int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type) {
|
||||
int(pthread_mutexattr_settype)(pthread_mutexattr_t *attr, int type) {
|
||||
switch (type) {
|
||||
case PTHREAD_MUTEX_NORMAL:
|
||||
case PTHREAD_MUTEX_RECURSIVE:
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_INTRIN_SPINLOCK_H_
|
||||
#define COSMOPOLITAN_LIBC_INTRIN_SPINLOCK_H_
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
/*───────────────────────────────────────────────────────────────────────────│─╗
|
||||
│ cosmopolitan § spinlocks ─╬─│┼
|
||||
╚────────────────────────────────────────────────────────────────────────────│─╝
|
||||
|
@ -11,7 +13,7 @@
|
|||
#define _spinlock(lock) _spinlock_cooperative(lock)
|
||||
#endif
|
||||
|
||||
#define _spunlock(lock) __atomic_store_n(lock, 0, __ATOMIC_RELAXED)
|
||||
#define _spunlock(lock) (__atomic_store_n(lock, 0, __ATOMIC_RELAXED), 0)
|
||||
|
||||
#define _seizelock(lock, value) \
|
||||
({ \
|
||||
|
@ -69,6 +71,8 @@
|
|||
|
||||
#define _trylock(lock) __atomic_test_and_set(lock, __ATOMIC_SEQ_CST)
|
||||
|
||||
void _spinlock_yield(void);
|
||||
int _spinlock_yield(void);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_LIBC_INTRIN_SPINLOCK_H_ */
|
||||
|
|
|
@ -103,12 +103,6 @@ static int PrintBacktraceUsingAddr2line(int fd, const struct StackFrame *bp) {
|
|||
garbage = weaken(__garbage);
|
||||
gi = garbage ? garbage->i : 0;
|
||||
for (frame = bp; frame && i < kBacktraceMaxFrames - 1; frame = frame->next) {
|
||||
__mmi_lock();
|
||||
ok = IsValidStackFramePointer(frame);
|
||||
__mmi_unlock();
|
||||
if (!ok) {
|
||||
return -1;
|
||||
}
|
||||
addr = frame->addr;
|
||||
if (addr == weakaddr("__gc")) {
|
||||
do {
|
||||
|
|
|
@ -57,13 +57,6 @@ noinstrument noasan int PrintBacktraceUsingSymbols(int fd,
|
|||
garbage = weaken(__garbage);
|
||||
gi = garbage ? garbage->i : 0;
|
||||
for (i = 0, frame = bp; frame; frame = frame->next) {
|
||||
__mmi_lock();
|
||||
ok = IsValidStackFramePointer(frame);
|
||||
__mmi_unlock();
|
||||
if (!ok) {
|
||||
kprintf("%p corrupt frame pointer\n", frame);
|
||||
break;
|
||||
}
|
||||
if (++i == LIMIT) {
|
||||
kprintf("<truncated backtrace>\n");
|
||||
break;
|
||||
|
|
|
@ -16,8 +16,10 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/lockcmpxchg.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/lockcmpxchgp.h"
|
||||
#include "libc/log/backtrace.internal.h"
|
||||
#include "libc/log/internal.h"
|
||||
#include "libc/log/libfatal.internal.h"
|
||||
|
@ -32,18 +34,30 @@
|
|||
*/
|
||||
relegated wontreturn void __die(void) {
|
||||
/* asan runtime depends on this function */
|
||||
int rc;
|
||||
static bool once;
|
||||
if (_lockcmpxchg(&once, false, true)) {
|
||||
int me, owner;
|
||||
static int sync;
|
||||
owner = 0;
|
||||
me = gettid();
|
||||
if (_lockcmpxchgp(&sync, &owner, me)) {
|
||||
__restore_tty();
|
||||
if (IsDebuggerPresent(false)) {
|
||||
DebugBreak();
|
||||
}
|
||||
ShowBacktrace(2, NULL);
|
||||
rc = 77;
|
||||
if (weaken(ShowBacktrace)) {
|
||||
weaken(ShowBacktrace)(2, __builtin_frame_address(0));
|
||||
} else if (weaken(PrintBacktraceUsingSymbols) && weaken(GetSymbolTable)) {
|
||||
weaken(PrintBacktraceUsingSymbols)(2, __builtin_frame_address(0),
|
||||
weaken(GetSymbolTable)());
|
||||
} else {
|
||||
kprintf("can't backtrace b/c `ShowCrashReports` not linked\n");
|
||||
}
|
||||
__restorewintty();
|
||||
_Exit(77);
|
||||
} else if (owner == me) {
|
||||
kprintf("die failed while dying\n");
|
||||
__restorewintty();
|
||||
_Exit(78);
|
||||
} else {
|
||||
rc = 78;
|
||||
_Exit1(79);
|
||||
}
|
||||
__restorewintty();
|
||||
_Exit(rc);
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "libc/intrin/asan.internal.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/lockcmpxchg.h"
|
||||
#include "libc/intrin/lockcmpxchgp.h"
|
||||
#include "libc/log/backtrace.internal.h"
|
||||
#include "libc/log/gdb.h"
|
||||
#include "libc/log/internal.h"
|
||||
|
@ -262,15 +263,22 @@ static wontreturn relegated noinstrument void __minicrash(int sig,
|
|||
* simply print addresses which may be cross-referenced using objdump.
|
||||
*
|
||||
* This function never returns, except for traps w/ human supervision.
|
||||
*
|
||||
* @threadsafe
|
||||
* @vforksafe
|
||||
*/
|
||||
relegated void __oncrash(int sig, struct siginfo *si, ucontext_t *ctx) {
|
||||
intptr_t rip;
|
||||
int me, owner;
|
||||
int gdbpid, err;
|
||||
static bool noreentry, notpossible;
|
||||
static int sync;
|
||||
static bool notpossible;
|
||||
STRACE("__oncrash rip %x", ctx->uc_mcontext.rip);
|
||||
--__ftrace;
|
||||
--__strace;
|
||||
if (_lockcmpxchg(&noreentry, false, true)) {
|
||||
owner = 0;
|
||||
me = gettid();
|
||||
if (_lockcmpxchgp(&sync, &owner, me)) {
|
||||
if (!__vforked) {
|
||||
rip = ctx ? ctx->uc_mcontext.rip : 0;
|
||||
err = errno;
|
||||
|
@ -292,20 +300,30 @@ relegated void __oncrash(int sig, struct siginfo *si, ucontext_t *ctx) {
|
|||
__restorewintty();
|
||||
_Exit(128 + sig);
|
||||
}
|
||||
sync = 0;
|
||||
} else {
|
||||
sync = 0;
|
||||
__minicrash(sig, si, ctx, "WHILE VFORKED");
|
||||
}
|
||||
} else if (sig == SIGTRAP) {
|
||||
/* chances are IsDebuggerPresent() confused strace w/ gdb */
|
||||
// chances are IsDebuggerPresent() confused strace w/ gdb
|
||||
goto ItsATrap;
|
||||
} else if (_lockcmpxchg(¬possible, false, true)) {
|
||||
__minicrash(sig, si, ctx, "WHILE CRASHING");
|
||||
} else {
|
||||
for (;;) {
|
||||
asm("ud2");
|
||||
} else if (owner == me) {
|
||||
// we crashed while generating a crash report
|
||||
if (_lockcmpxchg(¬possible, false, true)) {
|
||||
__minicrash(sig, si, ctx, "WHILE CRASHING");
|
||||
} else {
|
||||
// somehow __minicrash() crashed not possible
|
||||
for (;;) {
|
||||
asm("ud2");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// multiple threads have crashed
|
||||
// kill current thread assuming process dies soon
|
||||
// TODO(jart): It'd be nice to report on all threads.
|
||||
_Exit1(8);
|
||||
}
|
||||
noreentry = false;
|
||||
ItsATrap:
|
||||
++__strace;
|
||||
++__ftrace;
|
||||
|
|
|
@ -30,9 +30,12 @@
|
|||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/sysv/consts/ss.h"
|
||||
|
||||
STATIC_YOINK("__die"); /* for backtracing */
|
||||
STATIC_YOINK("malloc_inspect_all"); /* for asan memory origin */
|
||||
STATIC_YOINK("__get_symbol_by_addr"); /* for asan memory origin */
|
||||
STATIC_YOINK("__die"); // for backtracing
|
||||
STATIC_YOINK("ShowBacktrace"); // for backtracing
|
||||
STATIC_YOINK("GetSymbolTable"); // for backtracing
|
||||
STATIC_YOINK("PrintBacktraceUsingSymbols"); // for backtracing
|
||||
STATIC_YOINK("malloc_inspect_all"); // for asan memory origin
|
||||
STATIC_YOINK("__get_symbol_by_addr"); // for asan memory origin
|
||||
|
||||
extern const unsigned char __oncrash_thunks[8][11];
|
||||
static struct sigaltstack g_oldsigaltstack;
|
||||
|
|
|
@ -355,16 +355,15 @@ static wontreturn void NetbsdThreadMain(void *arg, int (*func)(void *arg),
|
|||
static int CloneNetbsd(int (*func)(void *), char *stk, size_t stksz, int flags,
|
||||
void *arg, void *tls, size_t tlssz, int *ctid) {
|
||||
// NetBSD has its own clone() and it works, but it's technically a
|
||||
// second-class API, intended to help Linux folks migrate to this!
|
||||
// We put it on the thread's stack, to avoid locking this function
|
||||
// so its stack doesn't scope. The ucontext struct needs 784 bytes
|
||||
// second-class API, intended to help Linux folks migrate to this.
|
||||
bool failed;
|
||||
int ax, *tid;
|
||||
intptr_t dx, sp;
|
||||
static bool once;
|
||||
static int broken;
|
||||
struct ucontext_netbsd ctx;
|
||||
struct ucontext_netbsd *ctx;
|
||||
static struct ucontext_netbsd netbsd_clone_template;
|
||||
_Static_assert(sizeof(struct ucontext_netbsd) == 784, "fix assembly");
|
||||
|
||||
// memoize arbitrary valid processor state structure
|
||||
if (!once) {
|
||||
|
@ -382,35 +381,48 @@ static int CloneNetbsd(int (*func)(void *), char *stk, size_t stksz, int flags,
|
|||
return -1;
|
||||
}
|
||||
sp = (intptr_t)(stk + stksz);
|
||||
|
||||
// allocate memory for child tid
|
||||
sp -= sizeof(int);
|
||||
sp = sp & -alignof(int);
|
||||
tid = (int *)sp;
|
||||
|
||||
// align the stack
|
||||
sp = sp & -16;
|
||||
|
||||
// simulate call to misalign stack and ensure backtrace looks good
|
||||
sp -= 8;
|
||||
*(intptr_t *)sp = (intptr_t)CloneNetbsd + 1;
|
||||
|
||||
// place the giant 784 byte ucontext structure in the red zone!
|
||||
// it only has to live long enough for the thread to come alive
|
||||
ctx = (struct ucontext_netbsd *)((sp - sizeof(struct ucontext_netbsd)) &
|
||||
-alignof(struct ucontext_netbsd));
|
||||
|
||||
// pass parameters in process state
|
||||
memcpy(&ctx, &netbsd_clone_template, sizeof(ctx));
|
||||
ctx.uc_link = 0;
|
||||
ctx.uc_mcontext.rbp = 0;
|
||||
ctx.uc_mcontext.rsp = sp;
|
||||
ctx.uc_mcontext.rip = (intptr_t)NetbsdThreadMain;
|
||||
ctx.uc_mcontext.rdi = (intptr_t)arg;
|
||||
ctx.uc_mcontext.rsi = (intptr_t)func;
|
||||
ctx.uc_mcontext.rdx = (intptr_t)tid;
|
||||
ctx.uc_mcontext.rcx = (intptr_t)(flags & CLONE_CHILD_SETTID ? ctid : tid);
|
||||
ctx.uc_mcontext.r8 = (intptr_t)(flags & CLONE_CHILD_CLEARTID ? ctid : tid);
|
||||
ctx.uc_flags |= _UC_STACK;
|
||||
ctx.uc_stack.ss_sp = stk;
|
||||
ctx.uc_stack.ss_size = stksz;
|
||||
ctx.uc_stack.ss_flags = 0;
|
||||
memcpy(ctx, &netbsd_clone_template, sizeof(*ctx));
|
||||
ctx->uc_link = 0;
|
||||
ctx->uc_mcontext.rbp = 0;
|
||||
ctx->uc_mcontext.rsp = sp;
|
||||
ctx->uc_mcontext.rip = (intptr_t)NetbsdThreadMain;
|
||||
ctx->uc_mcontext.rdi = (intptr_t)arg;
|
||||
ctx->uc_mcontext.rsi = (intptr_t)func;
|
||||
ctx->uc_mcontext.rdx = (intptr_t)tid;
|
||||
ctx->uc_mcontext.rcx = (intptr_t)(flags & CLONE_CHILD_SETTID ? ctid : tid);
|
||||
ctx->uc_mcontext.r8 = (intptr_t)(flags & CLONE_CHILD_CLEARTID ? ctid : tid);
|
||||
ctx->uc_flags |= _UC_STACK;
|
||||
ctx->uc_stack.ss_sp = stk;
|
||||
ctx->uc_stack.ss_size = stksz;
|
||||
ctx->uc_stack.ss_flags = 0;
|
||||
if (flags & CLONE_SETTLS) {
|
||||
ctx.uc_flags |= _UC_TLSBASE;
|
||||
ctx.uc_mcontext._mc_tlsbase = (intptr_t)tls;
|
||||
ctx->uc_flags |= _UC_TLSBASE;
|
||||
ctx->uc_mcontext._mc_tlsbase = (intptr_t)tls;
|
||||
}
|
||||
|
||||
// perform the system call
|
||||
asm volatile(CFLAG_ASM("syscall")
|
||||
: CFLAG_CONSTRAINT(failed), "=a"(ax), "=d"(dx)
|
||||
: "1"(__NR__lwp_create), "D"(&ctx), "S"(LWP_DETACHED), "2"(tid)
|
||||
: "1"(__NR__lwp_create), "D"(ctx), "S"(LWP_DETACHED), "2"(tid)
|
||||
: "rcx", "r11", "memory");
|
||||
if (!failed) {
|
||||
return *tid;
|
||||
|
|
|
@ -34,10 +34,12 @@
|
|||
static pthread_mutex_t __fflush_lock_obj;
|
||||
|
||||
void(__fflush_lock)(void) {
|
||||
__fflush_lock_obj.attr = PTHREAD_MUTEX_RECURSIVE;
|
||||
pthread_mutex_lock(&__fflush_lock_obj);
|
||||
}
|
||||
|
||||
void(__fflush_unlock)(void) {
|
||||
__fflush_lock_obj.attr = PTHREAD_MUTEX_RECURSIVE;
|
||||
pthread_mutex_unlock(&__fflush_lock_obj);
|
||||
}
|
||||
|
||||
|
|
|
@ -22,5 +22,6 @@
|
|||
* Acquires reentrant lock on stdio object, blocking if needed.
|
||||
*/
|
||||
void(flockfile)(FILE *f) {
|
||||
f->lock.attr = PTHREAD_MUTEX_RECURSIVE;
|
||||
pthread_mutex_lock(&f->lock);
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/intrin/pthread.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
|
|
|
@ -22,5 +22,6 @@
|
|||
* Releases lock on stdio object.
|
||||
*/
|
||||
void(funlockfile)(FILE *f) {
|
||||
f->lock.attr = PTHREAD_MUTEX_RECURSIVE;
|
||||
pthread_mutex_unlock(&f->lock);
|
||||
}
|
||||
|
|
|
@ -25,13 +25,13 @@
|
|||
.init.start 400,_init_stderr
|
||||
ezlea __stderr,ax
|
||||
push $_IOLBF
|
||||
pop (%rax) #→ f.fd
|
||||
pop (%rax) #→ f.fd
|
||||
push STDERR_FILENO
|
||||
pop 12(%rax)
|
||||
mov O_WRONLY,%edx
|
||||
mov %edx,4(%rax) #→ f.iomode
|
||||
mov %edx,4(%rax) #→ f.iomode
|
||||
ezlea __stderr_buf,cx
|
||||
mov %rcx,24(%rax) #→ f.buf
|
||||
movl $BUFSIZ,32(%rax) #→ f.size
|
||||
mov %rcx,0x18(%rax) #→ f.buf
|
||||
movl $BUFSIZ,0x20(%rax) #→ f.size
|
||||
mov %rax,stderr(%rip)
|
||||
.init.end 400,_init_stderr,globl,hidden
|
||||
|
|
|
@ -25,9 +25,9 @@
|
|||
.init.start 400,_init_stdin
|
||||
ezlea __stdin,ax
|
||||
mov O_RDONLY,%edx
|
||||
mov %edx,4(%rax) #→ f.iomode
|
||||
mov %edx,4(%rax) #→ f.iomode
|
||||
ezlea __stdin_buf,cx
|
||||
mov %rcx,24(%rax) #→ f.buf
|
||||
movl $BUFSIZ,32(%rax) #→ f.size
|
||||
mov %rcx,0x18(%rax) #→ f.buf
|
||||
movl $BUFSIZ,0x20(%rax) #→ f.size
|
||||
mov %rax,stdin(%rip)
|
||||
.init.end 400,_init_stdin,globl,hidden
|
||||
|
|
|
@ -16,19 +16,19 @@ COSMOPOLITAN_C_START_
|
|||
╚────────────────────────────────────────────────────────────────────────────│*/
|
||||
|
||||
typedef struct FILE {
|
||||
uint8_t bufmode; /* 0x00 _IOFBF, etc. (ignored if fd=-1) */
|
||||
bool noclose; /* 0x01 for fake dup() todo delete! */
|
||||
uint32_t iomode; /* 0x04 O_RDONLY, etc. (ignored if fd=-1) */
|
||||
int32_t state; /* 0x08 0=OK, -1=EOF, >0=errno */
|
||||
int fd; /* 0x0c ≥0=fd, -1=closed|buffer */
|
||||
uint32_t beg; /* 0x10 */
|
||||
uint32_t end; /* 0x14 */
|
||||
char *buf; /* 0x18 */
|
||||
uint32_t size; /* 0x20 */
|
||||
uint32_t nofree; /* 0x24 */
|
||||
int pid; /* 0x28 */
|
||||
char *getln;
|
||||
pthread_mutex_t lock;
|
||||
uint8_t bufmode; /* 0x00 _IOFBF, etc. (ignored if fd=-1) */
|
||||
bool noclose; /* 0x01 for fake dup() todo delete! */
|
||||
uint32_t iomode; /* 0x04 O_RDONLY, etc. (ignored if fd=-1) */
|
||||
int32_t state; /* 0x08 0=OK, -1=EOF, >0=errno */
|
||||
int fd; /* 0x0c ≥0=fd, -1=closed|buffer */
|
||||
uint32_t beg; /* 0x10 */
|
||||
uint32_t end; /* 0x14 */
|
||||
char *buf; /* 0x18 */
|
||||
uint32_t size; /* 0x20 */
|
||||
uint32_t nofree; /* 0x24 */
|
||||
int pid; /* 0x28 */
|
||||
char *getln; /* 0x30 */
|
||||
pthread_mutex_t lock; /* 0x38 */
|
||||
} FILE;
|
||||
|
||||
extern FILE *stdin;
|
||||
|
|
|
@ -25,11 +25,11 @@
|
|||
.init.start 400,_init_stdout
|
||||
ezlea __stdout,ax
|
||||
push STDOUT_FILENO
|
||||
pop 12(%rax) #→ f.fd
|
||||
pop 0x0c(%rax) #→ f.fd
|
||||
mov O_WRONLY,%edx
|
||||
mov %edx,4(%rax) #→ f.iomode
|
||||
mov %edx,4(%rax) #→ f.iomode
|
||||
ezlea __stdout_buf,cx
|
||||
mov %rcx,24(%rax) #→ f.buf
|
||||
movl $BUFSIZ,32(%rax) #→ f.size
|
||||
mov %rcx,0x18(%rax) #→ f.buf
|
||||
movl $BUFSIZ,0x20(%rax) #→ f.size
|
||||
mov %rax,stdout(%rip)
|
||||
.init.end 400,_init_stdout,globl,hidden
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue