Make improvements

- Every unit test now passes on Apple Silicon. The final piece of this
  puzzle was porting our POSIX threads cancelation support, since that
  works differently on ARM64 XNU vs. AMD64. Our semaphore support on
  Apple Silicon is also superior now compared to AMD64, thanks to the
  grand central dispatch library which lets *NSYNC locks go faster.

- The Cosmopolitan runtime is now more stable, particularly on Windows.
  To do this, thread local storage is mandatory at all runtime levels,
  and the innermost packages of the C library is no longer being built
  using ASAN. TLS is being bootstrapped with a 128-byte TIB during the
  process startup phase, and then later on the runtime re-allocates it
  either statically or dynamically to support code using _Thread_local.
  fork() and execve() now do a better job cooperating with threads. We
  can now check how much stack memory is left in the process or thread
  when functions like kprintf() / execve() etc. call alloca(), so that
  ENOMEM can be raised, reduce a buffer size, or just print a warning.

- POSIX signal emulation is now implemented the same way kernels do it
  with pthread_kill() and raise(). Any thread can interrupt any other
  thread, regardless of what it's doing. If it's blocked on read/write
  then the killer thread will cancel its i/o operation so that EINTR can
  be returned in the mark thread immediately. If it's doing a tight CPU
  bound operation, then that's also interrupted by the signal delivery.
  Signal delivery works now by suspending a thread and pushing context
  data structures onto its stack, and redirecting its execution to a
  trampoline function, which calls SetThreadContext(GetCurrentThread())
  when it's done.

- We're now doing a better job managing locks and handles. On NetBSD we
  now close semaphore file descriptors in forked children. Semaphores on
  Windows can now be canceled immediately, which means mutexes/condition
  variables will now go faster. Apple Silicon semaphores can be canceled
  too. We're now using Apple's pthread_yield() funciton. Apple _nocancel
  syscalls are now used on XNU when appropriate to ensure pthread_cancel
  requests aren't lost. The MbedTLS library has been updated to support
  POSIX thread cancelations. See tool/build/runitd.c for an example of
  how it can be used for production multi-threaded tls servers. Handles
  on Windows now leak less often across processes. All i/o operations on
  Windows are now overlapped, which means file pointers can no longer be
  inherited across dup() and fork() for the time being.

- We now spawn a thread on Windows to deliver SIGCHLD and wakeup wait4()
  which means, for example, that posix_spawn() now goes 3x faster. POSIX
  spawn is also now more correct. Like Musl, it's now able to report the
  failure code of execve() via a pipe although our approach favors using
  shared memory to do that on systems that have a true vfork() function.

- We now spawn a thread to deliver SIGALRM to threads when setitimer()
  is used. This enables the most precise wakeups the OS makes possible.

- The Cosmopolitan runtime now uses less memory. On NetBSD for example,
  it turned out the kernel would actually commit the PT_GNU_STACK size
  which caused RSS to be 6mb for every process. Now it's down to ~4kb.
  On Apple Silicon, we reduce the mandatory upstream thread size to the
  smallest possible size to reduce the memory overhead of Cosmo threads.
  The examples directory has a program called greenbean which can spawn
  a web server on Linux with 10,000 worker threads and have the memory
  usage of the process be ~77mb. The 1024 byte overhead of POSIX-style
  thread-local storage is now optional; it won't be allocated until the
  pthread_setspecific/getspecific functions are called. On Windows, the
  threads that get spawned which are internal to the libc implementation
  use reserve rather than commit memory, which shaves a few hundred kb.

- sigaltstack() is now supported on Windows, however it's currently not
  able to be used to handle stack overflows, since crash signals are
  still generated by WIN32. However the crash handler will still switch
  to the alt stack, which is helpful in environments with tiny threads.

- Test binaries are now smaller. Many of the mandatory dependencies of
  the test runner have been removed. This ensures many programs can do a
  better job only linking the the thing they're testing. This caused the
  test binaries for LIBC_FMT for example, to decrease from 200kb to 50kb

- long double is no longer used in the implementation details of libc,
  except in the APIs that define it. The old code that used long double
  for time (instead of struct timespec) has now been thoroughly removed.

- ShowCrashReports() is now much tinier in MODE=tiny. Instead of doing
  backtraces itself, it'll just print a command you can run on the shell
  using our new `cosmoaddr2line` program to view the backtrace.

- Crash report signal handling now works in a much better way. Instead
  of terminating the process, it now relies on SA_RESETHAND so that the
  default SIG_IGN behavior can terminate the process if necessary.

- Our pledge() functionality has now been fully ported to AARCH64 Linux.
This commit is contained in:
Justine Tunney 2023-09-18 20:44:45 -07:00
parent c4eb838516
commit ec480f5aa0
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
638 changed files with 7925 additions and 8282 deletions

View file

@ -7,36 +7,8 @@
http://creativecommons.org/publicdomain/zero/1.0/ │
*/
#endif
#include "libc/intrin/kprintf.h"
#include "libc/runtime/runtime.h"
/**
* @fileoverview example of how to embed symbol table in .com file
*
* # build our binary
* make -j16 o//examples/symtab.com
*
* # move binary somewhere else
* # so it can't find the .com.dbg binary
* cp o//examples/symtab.com /tmp
*
* # run program
* # notice that it has a symbolic backtrace
* /tmp/symtab.com
*
* @see examples/examples.mk
*/
int main(int argc, char *argv[]) {
// this links all the debugging and zip functionality
ShowCrashReports();
kprintf("----------------\n");
kprintf(" THIS IS A TEST \n");
kprintf("SIMULATING CRASH\n");
kprintf("----------------\n");
volatile int64_t x;
return 1 / (x = 0);
abort();
}

View file

@ -11,6 +11,7 @@
#include "libc/math.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/symbols.internal.h"
#include "libc/stdio/stdio.h"
/**
* @fileoverview How to print backtraces and cpu state on crash.
@ -38,6 +39,7 @@ dontubsan int main(int argc, char *argv[]) {
volatile double c = exp(b) / a;
(void)c;
volatile int64_t x;
return 1 / (x = 0);
volatile int x = 0;
volatile int y = 1 / x;
return y;
}

14
examples/die.c Normal file
View file

@ -0,0 +1,14 @@
#if 0
/*─────────────────────────────────────────────────────────────────╗
To the extent possible under law, Justine Tunney has waived
all copyright and related or neighboring rights to this file,
as it is written in the following disclaimers:
http://unlicense.org/ │
http://creativecommons.org/publicdomain/zero/1.0/ │
*/
#endif
#include "libc/log/log.h"
int main(int argc, char *argv[]) {
__die();
}

View file

@ -55,6 +55,7 @@ EXAMPLES_DIRECTDEPS = \
LIBC_NT_NTDLL \
LIBC_NT_USER32 \
LIBC_NT_WS2_32 \
LIBC_PROC \
LIBC_RUNTIME \
LIBC_SOCK \
LIBC_STDIO \
@ -146,25 +147,6 @@ o/$(MODE)/examples/nesemu1.com.dbg: \
$(EXAMPLES_BOOTLOADER)
@$(APELINK)
# # force symtab.com to be a zip file, by pulling a zip asset into linkage
# # we wouldn't need to do this if we depended on functions like localtime
# o/$(MODE)/examples/symtab.com.dbg: \
# $(EXAMPLES_DEPS) \
# o/$(MODE)/examples/symtab.o \
# o/$(MODE)/examples/symtab.c.zip.o \
# o/$(MODE)/examples/examples.pkg \
# $(EXAMPLES_BOOTLOADER)
# @$(APELINK)
# modify .com so it can read the symbol table without needing the .com.dbg file
o/$(MODE)/examples/symtab.com: \
o/$(MODE)/examples/symtab.com.dbg \
o/$(MODE)/third_party/zip/zip.com \
o/$(MODE)/tool/build/symtab.com
@$(MAKE_OBJCOPY)
@$(MAKE_SYMTAB_CREATE)
@$(MAKE_SYMTAB_ZIP)
o/$(MODE)/examples/picol.o: private \
CPPFLAGS += \
-DSTACK_FRAME_UNLIMITED

View file

@ -27,6 +27,7 @@
#include "libc/sock/struct/sockaddr.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/af.h"
#include "libc/sysv/consts/auxv.h"
#include "libc/sysv/consts/sig.h"
#include "libc/sysv/consts/so.h"
#include "libc/sysv/consts/sock.h"
@ -116,9 +117,11 @@ void *Worker(void *id) {
server = socket(AF_INET, SOCK_STREAM, 0);
if (server == -1) {
kprintf("socket() failed %m\n"
" try running: sudo prlimit --pid=$$ --nofile=%d\n",
threads * 2);
kprintf("\r\e[Ksocket() failed %m\n");
if (errno == ENFILE || errno == EMFILE) {
TooManyFileDescriptors:
kprintf("sudo prlimit --pid=$$ --nofile=%d\n", threads * 3);
}
goto WorkerFinished;
}
@ -136,7 +139,7 @@ void *Worker(void *id) {
// possible for our many threads to bind to the same interface!
// otherwise we'd need to create a complex multi-threaded queue
if (bind(server, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
kprintf("%s() failed %m\n", "socket");
kprintf("\r\e[Ksocket() returned %m\n");
goto CloseWorker;
}
unassert(!listen(server, 1));
@ -148,7 +151,7 @@ void *Worker(void *id) {
uint32_t clientaddrsize;
struct sockaddr_in clientaddr;
int client, inmsglen, outmsglen;
char inbuf[1500], outbuf[512], *p, *q;
char inbuf[512], outbuf[512], *p, *q;
// musl libc and cosmopolitan libc support a posix thread extension
// that makes thread cancellation work much better your io routines
@ -165,15 +168,14 @@ void *Worker(void *id) {
// turns cancellation off so we don't interrupt active http clients
unassert(!pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, 0));
// accept() can raise a very diverse number of errors but none of
// them are really true showstoppers that would necessitate us to
// panic and abort the entire server, so we can just ignore these
if (client == -1) {
// we used SO_RCVTIMEO and SO_SNDTIMEO because those settings are
// inherited by the accepted sockets, but using them also has the
// side-effect that the listening socket fails with EAGAIN errors
// which are harmless, and so are most other errors accept raises
// e.g. ECANCELED, which lets us check closingtime without delay!
if (errno != EAGAIN && errno != ECANCELED) {
kprintf("\r\e[Kaccept() returned %m\n");
if (errno == ENFILE || errno == EMFILE) {
goto TooManyFileDescriptors;
}
usleep(10000);
}
continue;
}
@ -301,7 +303,7 @@ int main(int argc, char *argv[]) {
// print cpu registers and backtrace on crash
// note that pledge'll makes backtraces worse
// you can press ctrl+\ to trigger your crash
// you can press ctrl+\ to trigger backtraces
ShowCrashReports();
// listen for ctrl-c, terminal close, and kill
@ -341,10 +343,6 @@ int main(int argc, char *argv[]) {
// the server will be allowed to use. this way if it gets hacked, they
// won't be able to do much damage, like compromising the whole server
//
// we use an internal api to force threads to enable beforehand, since
// cosmopolitan code morphs the binary to support tls across platforms
// and doing that requires extra permissions we don't need for serving
//
// pledge violations on openbsd are logged nicely to the system logger
// but on linux we need to use a cosmopolitan extension to get details
// although doing that slightly weakens the security pledge() provides
@ -353,21 +351,22 @@ int main(int argc, char *argv[]) {
// is too old, then pledge() and unveil() don't consider this an error
// so it works. if security is critical there's a special call to test
// which is npassert(!pledge(0, 0)), and npassert(unveil("", 0) != -1)
__enable_threads();
__pledge_mode = PLEDGE_PENALTY_KILL_THREAD | PLEDGE_STDERR_LOGGING;
__pledge_mode = PLEDGE_PENALTY_RETURN_EPERM; // c. greenbean --strace
unveil("/dev/null", "rw");
unveil(0, 0);
pledge("stdio inet", 0);
// spawn over 9,000 worker threads
// initialize our synchronization data structures, which were written
// by mike burrows in a library called *nsync we've tailored for libc
unassert(!pthread_cond_init(&statuscond, 0));
unassert(!pthread_mutex_init(&statuslock, 0));
// spawn over 9000 worker threads
//
// you don't need weird i/o models, or event driven yoyo pattern code
// to build a massively scalable server. the secret is to use threads
// with tiny stacks. then you can write plain simple imperative code!
//
// we like pthread attributes since they generally make thread spawns
// faster especially in cases where you need to make detached threads
//
// we block signals in our worker threads so we won't need messy code
// to spin on eintr. operating systems also deliver signals to random
// threads, and we'd have ctrl-c, etc. be handled by the main thread.
@ -375,17 +374,15 @@ int main(int argc, char *argv[]) {
// alternatively you can just use signal() instead of sigaction(); it
// uses SA_RESTART because all the syscalls the worker currently uses
// are documented as @restartable which means no EINTR toil is needed
unassert(!pthread_cond_init(&statuscond, 0));
unassert(!pthread_mutex_init(&statuslock, 0));
sigset_t block;
sigfillset(&block);
sigdelset(&block, SIGSEGV); // invalid memory access
sigdelset(&block, SIGBUS); // another kind of bad memory access
sigdelset(&block, SIGFPE); // divide by zero, etc.
sigdelset(&block, SIGSYS); // pledge violations
sigdelset(&block, SIGILL); // bad cpu opcode
sigemptyset(&block);
sigaddset(&block, SIGINT);
sigaddset(&block, SIGHUP);
sigaddset(&block, SIGQUIT);
pthread_attr_t attr;
unassert(!pthread_attr_init(&attr));
unassert(!pthread_attr_setguardsize(&attr, 4096));
unassert(!pthread_attr_setstacksize(&attr, 65536));
unassert(!pthread_attr_setsigmask_np(&attr, &block));
pthread_t *th = gc(calloc(threads, sizeof(pthread_t)));
for (i = 0; i < threads; ++i) {
@ -393,10 +390,10 @@ int main(int argc, char *argv[]) {
++a_workers;
if ((rc = pthread_create(th + i, &attr, Worker, (void *)(intptr_t)i))) {
--a_workers;
// rc will most likely be EAGAIN (we hit the process/thread limit)
kprintf("\r\e[Kerror: pthread_create(%d) failed: %s\n"
" try increasing RLIMIT_NPROC\n",
i, strerror(rc));
kprintf("\r\e[Kpthread_create failed: %s\n", strerror(rc));
if (rc == EAGAIN) {
kprintf("sudo prlimit --pid=$$ --nproc=%d\n", threads * 2);
}
if (!i) exit(1);
threads = i;
break;

View file

@ -13,7 +13,7 @@
#include "libc/intrin/kprintf.h"
#include "libc/mem/mem.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/posix_spawn.h"
#include "libc/proc/posix_spawn.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/sig.h"

View file

@ -1,61 +0,0 @@
#if 0
/*─────────────────────────────────────────────────────────────────╗
To the extent possible under law, Justine Tunney has waived
all copyright and related or neighboring rights to this file,
as it is written in the following disclaimers:
http://unlicense.org/ │
http://creativecommons.org/publicdomain/zero/1.0/ │
*/
#endif
#include "libc/calls/calls.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/time/time.h"
#include "libc/x/x.h"
/**
* 16kb sleep executable that runs on all operating systems.
* https://justine.lol/cosmopolitan/demos/sleep.c
* https://justine.lol/cosmopolitan/demos/sleep.com
* https://justine.lol/cosmopolitan/index.html
*/
#define WRITE(s) write(2, s, strlen(s))
int main(int argc, char *argv[]) {
char *p;
long double x, y;
if (argc != 2) {
WRITE("Usage: ");
WRITE(argv[0]);
WRITE(" SECONDS\n");
exit(1);
}
x = 0;
for (p = argv[1]; isdigit(*p); ++p) {
x *= 10;
x += *p - '0';
}
if (*p == '.') {
y = 1;
for (++p; isdigit(*p); ++p) {
x += (*p - '0') * (y /= 10);
}
}
switch (*p) {
case 'm':
x *= 60;
break;
case 'h':
x *= 60 * 60;
break;
case 'd':
x *= 60 * 60 * 24;
break;
default:
break;
}
dsleep(x);
return 0;
}

View file

@ -40,6 +40,7 @@
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/poll.h"
#include "libc/sysv/consts/sig.h"
#include "libc/thread/thread.h"
#include "libc/time/time.h"
#include "libc/x/xasprintf.h"
#include "libc/x/xsigaction.h"
@ -249,7 +250,7 @@ void Exit(int rc) {
void Cleanup(void) {
ttyraw((enum TtyRawFlags)(-1u));
ttyshowcursor(STDOUT_FILENO);
if (playpid_) kill(playpid_, SIGTERM), sched_yield();
if (playpid_) kill(playpid_, SIGTERM), pthread_yield();
}
void OnTimer(void) {

139
examples/spawn_bench.c Normal file
View file

@ -0,0 +1,139 @@
#if 0
/*─────────────────────────────────────────────────────────────────╗
To the extent possible under law, Justine Tunney has waived
all copyright and related or neighboring rights to this file,
as it is written in the following disclaimers:
http://unlicense.org/ │
http://creativecommons.org/publicdomain/zero/1.0/ │
*/
#endif
#include "libc/atomic.h"
#include "libc/calls/calls.h"
#include "libc/calls/struct/timespec.h"
#include "libc/calls/weirdtypes.h"
#include "libc/mem/mem.h"
#include "libc/proc/posix_spawn.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/clock.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/prot.h"
#define ITERATIONS 10
_Alignas(128) int a;
_Alignas(128) int b;
_Alignas(128) atomic_int lock;
static struct timespec SubtractTime(struct timespec a, struct timespec b) {
a.tv_sec -= b.tv_sec;
if (a.tv_nsec < b.tv_nsec) {
a.tv_nsec += 1000000000;
a.tv_sec--;
}
a.tv_nsec -= b.tv_nsec;
return a;
}
static time_t ToNanoseconds(struct timespec ts) {
return ts.tv_sec * 1000000000 + ts.tv_nsec;
}
static char *Ithoa(char p[27], unsigned long x) {
char m[26];
unsigned i;
i = 0;
do {
m[i++] = x % 10 + '0';
x = x / 10;
} while (x);
for (;;) {
*p++ = m[--i];
if (!i) break;
if (!(i % 3)) *p++ = ',';
}
*p = '\0';
return p;
}
#define BENCH(name, x) \
{ \
int i; \
char ibuf[27]; \
struct timespec t1, t2; \
clock_gettime(CLOCK_REALTIME, &t1); \
for (i = 0; i < ITERATIONS; ++i) { \
x; \
} \
clock_gettime(CLOCK_REALTIME, &t2); \
Ithoa(ibuf, ToNanoseconds(SubtractTime(t2, t1)) / ITERATIONS); \
printf("%-50s %16s ns\n", name, ibuf); \
}
void PosixSpawnWait(const char *prog) {
int ws, err, pid;
char *args[] = {(char *)prog, 0};
char *envs[] = {0};
if ((err = posix_spawn(&pid, prog, NULL, NULL, args, envs))) {
perror(prog);
exit(1);
}
if (waitpid(pid, &ws, 0) == -1) {
perror("waitpid");
exit(1);
}
if (!(WIFEXITED(ws) && WEXITSTATUS(ws) == 42)) {
puts("bad exit status");
exit(1);
}
}
void ForkExecWait(const char *prog) {
int ws;
if (!fork()) {
execve(prog, (char *[]){(char *)prog, 0}, (char *[]){0});
_Exit(127);
}
if (wait(&ws) == -1) {
perror("wait");
exit(1);
}
if (!(WIFEXITED(ws) && WEXITSTATUS(ws) == 42)) {
puts("bad exit status");
exit(1);
}
}
char *FillMemory(char *p, long n) {
return memset(p, -1, n);
}
char *(*pFillMemory)(char *, long) = FillMemory;
int main(int argc, char *argv[]) {
long n;
void *p;
const char *prog;
if (argc <= 1) {
prog = "tiny64";
} else {
prog = argv[1];
}
BENCH("fork() + exec(tiny)", ForkExecWait(prog));
BENCH("posix_spawn(tiny)", PosixSpawnWait(prog));
n = 128L * 1024 * 1024;
p = pFillMemory(
mmap(0, n, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0), n);
BENCH("mmap(128mb, MAP_SHARED) + fork() + exec(tiny)", ForkExecWait(prog));
BENCH("mmap(128mb, MAP_SHARED) + posix_spawn(tiny)", PosixSpawnWait(prog));
munmap(p, n);
n = 128L * 1024 * 1024;
p = pFillMemory(malloc(n), n);
BENCH("malloc(128mb) + fork() + exec(tiny)", ForkExecWait(prog));
BENCH("malloc(128mb) + posix_spawn(tiny)", PosixSpawnWait(prog));
free(p);
}

View file

@ -8,6 +8,7 @@
*/
#endif
#include "libc/calls/calls.h"
#include "libc/dce.h"
#include "libc/limits.h"
#include "libc/log/check.h"
#include "libc/runtime/runtime.h"
@ -21,8 +22,6 @@
#define N INT_MAX
STATIC_STACK_SIZE(FRAMESIZE);
int A(int f(), int n) {
if (n < N) {
return f(f, n + 1) - 1;
@ -34,6 +33,10 @@ int A(int f(), int n) {
int (*Ap)(int (*)(), int) = A;
int main(int argc, char *argv[]) {
if (IsWindows()) {
fprintf(stderr, "stack overflow not possible to catch on windows yet\n");
exit(1);
}
ShowCrashReports();
return !!Ap(Ap, 0);
}

View file

@ -7,37 +7,13 @@
http://creativecommons.org/publicdomain/zero/1.0/ │
*/
#endif
#include "libc/assert.h"
#include "libc/calls/struct/timespec.h"
#include "libc/stdio/stdio.h"
#include "libc/sysv/consts/clock.h"
#include "libc/thread/tls.h"
/**
* @fileoverview shows how accurate clock_nanosleep() is
*/
void *hog(void) {
return &__get_tls()->tib_errno;
}
int main(int argc, char *argv[]) {
long i, ns;
struct timespec x, y;
timespec_sleep(timespec_fromnanos(0)); // warmup
printf("\nrelative sleep\n");
for (i = 0; i < 28; ++i) {
ns = 1l << i;
x = timespec_real();
timespec_sleep(timespec_fromnanos(ns));
y = timespec_real();
printf("%,11ld ns sleep took %,ld ns\n", ns,
timespec_tonanos(timespec_sub(y, x)));
}
printf("\nabsolute sleep\n");
for (i = 0; i < 28; ++i) {
ns = 1l << i;
x = timespec_real();
timespec_sleep_until(timespec_add(x, timespec_fromnanos(ns)));
y = timespec_real();
printf("%,11ld ns sleep took %,ld ns\n", ns,
timespec_tonanos(timespec_sub(y, x)));
}
printf("%zu\n", sizeof(struct CosmoTib));
}

View file

@ -1,100 +0,0 @@
#if 0
/*─────────────────────────────────────────────────────────────────╗
To the extent possible under law, Justine Tunney has waived
all copyright and related or neighboring rights to this file,
as it is written in the following disclaimers:
http://unlicense.org/ │
http://creativecommons.org/publicdomain/zero/1.0/ │
*/
#endif
#include "libc/time/time.h"
#include "libc/calls/calls.h"
#include "libc/calls/struct/timespec.h"
#include "libc/fmt/itoa.h"
#include "libc/limits.h"
#include "libc/log/log.h"
#include "libc/math.h"
#include "libc/runtime/runtime.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/ex.h"
#include "libc/x/xspawn.h"
/**
* @fileoverview command for showing how long a command takes
*
* This offers the following improvements over the existing `time`
* command that's incorporated into most shells:
*
* - This will show microseconds if seconds < 1
* - This will launch APE binaries on systems with broken libc execv()
*/
#define WRITE(FD, STR) write(FD, STR, strlen(STR))
void OnChild(void *arg) {
char **argv = arg;
execv(argv[0], argv);
_exit(127);
}
long double GetTimeval(struct timeval t) {
return t.tv_sec + t.tv_usec * 1e-6l;
}
void PrintMetric(const char *name, long double d) {
char buf[256], *p = buf;
long mins, secs, mils, mics;
mins = d / 60;
secs = fmodl(d, 60);
mils = fmodl(d * 1000, 1000);
mics = fmodl(d * 1000000, 1000);
p = stpcpy(p, name), *p++ = '\t';
p = FormatInt64(p, mins), *p++ = 'm';
p = FormatInt64(p, secs), *p++ = '.';
*p++ = '0' + mils / 100;
*p++ = '0' + mils / 10 % 10;
*p++ = '0' + mils % 10;
if (!secs) {
*p++ = '0' + mics / 100;
*p++ = '0' + mics / 10 % 10;
*p++ = '0' + mics % 10;
}
*p++ = 's';
*p++ = '\n';
write(2, buf, p - buf);
}
int main(int argc, char *argv[]) {
int ws;
char *exepath;
struct rusage r;
long double real;
char exebuf[PATH_MAX];
if (argc >= 2) {
if ((exepath = commandv(argv[1], exebuf, sizeof(exebuf)))) {
real = nowl();
argv[1] = exepath;
if ((ws = xvspawn(OnChild, argv + 1, &r)) != -1) {
PrintMetric("real", nowl() - real);
PrintMetric("user", GetTimeval(r.ru_utime));
PrintMetric("sys", GetTimeval(r.ru_stime));
if (WIFEXITED(ws)) {
return WEXITSTATUS(ws);
} else {
return 128 + WTERMSIG(ws);
}
} else {
perror("xvspawn");
return 127;
}
} else {
perror(argv[1]);
return 127;
}
} else {
WRITE(2, "Usage: ");
WRITE(2, argv[0]);
WRITE(2, " PROG [ARGS...]\n");
return EX_USAGE;
}
}

View file

@ -90,9 +90,9 @@ int rawmode(void) {
perror("tcsetattr");
}
WRITE(1, ENABLE_SAFE_PASTE);
WRITE(1, ENABLE_MOUSE_TRACKING);
WRITE(1, PROBE_DISPLAY_SIZE);
/* WRITE(1, ENABLE_SAFE_PASTE); */
/* WRITE(1, ENABLE_MOUSE_TRACKING); */
/* WRITE(1, PROBE_DISPLAY_SIZE); */
return 0;
}
@ -146,7 +146,7 @@ const char *describemouseevent(int e) {
// change the code above to enable ISIG if you want to trigger this
// then press ctrl-c or ctrl-\ in your pseudoteletypewriter console
void OnSignalThatWontEintrRead(int sig) {
dprintf(1, "got %s\n", strsignal(sig));
dprintf(1, "got %s (read()ing will SA_RESTART)\n", strsignal(sig));
}
int main(int argc, char *argv[]) {
@ -170,6 +170,10 @@ int main(int argc, char *argv[]) {
perror("read");
exit(1);
}
if (!n) {
printf("got stdin eof\n");
exit(0);
}
printf("%`'.*s (got %d) ", n, code, n);
if (iscntrl(code[0]) && !code[1]) {
printf("is CTRL-%c a.k.a. ^%c\r\n", CTRL(code[0]), CTRL(code[0]));