From 4bcb107cb0392eb0c3c6bc110bc6a7cfc11b8f5f Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Fri, 13 Oct 2023 07:43:47 -0700 Subject: [PATCH] Fix ctrl-c in redbean on Windows --- examples/greenbean.c | 3 - libc/calls/sig.c | 1 + libc/intrin/kprintf.greg.c | 15 +- libc/log/backtrace2.c | 4 +- libc/log/backtrace3.c | 15 +- libc/log/vflogf.c | 2 +- libc/nt/winsock.h | 2 +- libc/proc/kill-nt.c | 12 ++ libc/sock/accept-nt.c | 4 + tool/net/net.mk | 2 + tool/net/redbean.c | 4 +- tool/net/winbench.c | 294 +++++++++++++++++++++++++++++++++++++ 12 files changed, 326 insertions(+), 32 deletions(-) create mode 100644 tool/net/winbench.c diff --git a/examples/greenbean.c b/examples/greenbean.c index d4a81e8d7..bf789ec8a 100644 --- a/examples/greenbean.c +++ b/examples/greenbean.c @@ -157,9 +157,6 @@ void *Worker(void *id) { // wait for next http message (non-fragmented required) unassert(!pthread_setcancelstate(PTHREAD_CANCEL_MASKED, 0)); got = read(client, inbuf, sizeof(inbuf)); - for (int i = 0; i < got; ++i) { - if (!inbuf[i]) inbuf[i] = 1; - } unassert(!pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, 0)); if (got <= 0) { if (!got) { diff --git a/libc/calls/sig.c b/libc/calls/sig.c index 6bf83af28..7650e8f48 100644 --- a/libc/calls/sig.c +++ b/libc/calls/sig.c @@ -247,6 +247,7 @@ static textwindows int __sig_killer(struct PosixThread *pt, int sig, int sic) { STRACE("kill contention of %u on tid %d", old_suspend_count, _pthread_tid(pt)); pt->tib->tib_sigpending |= 1ull << (sig - 1); + ResumeThread(th); return 0; } struct NtContext nc; diff --git a/libc/intrin/kprintf.greg.c b/libc/intrin/kprintf.greg.c index c4c043048..32db36881 100644 --- a/libc/intrin/kprintf.greg.c +++ b/libc/intrin/kprintf.greg.c @@ -1059,15 +1059,12 @@ privileged size_t kvsnprintf(char *b, size_t n, const char *fmt, va_list v) { privileged void kvprintf(const char *fmt, va_list v) { #pragma GCC push_options #pragma GCC diagnostic ignored "-Walloca-larger-than=" - - long size = 3000; - /* long size = __get_safe_size(8000, 3000); */ - /* if (size < 80) { */ - /* asm("int3"); */ - /* klog(STACK_ERROR, sizeof(STACK_ERROR) - 1); */ - /* return; */ - /* } */ - + long size = __get_safe_size(8000, 3000); + if (size < 80) { + asm("int3"); + klog(STACK_ERROR, sizeof(STACK_ERROR) - 1); + return; + } char *buf = alloca(size); CheckLargeStackAllocation(buf, size); #pragma GCC pop_options diff --git a/libc/log/backtrace2.c b/libc/log/backtrace2.c index 9a464cd88..e0d328963 100644 --- a/libc/log/backtrace2.c +++ b/libc/log/backtrace2.c @@ -107,7 +107,7 @@ static int PrintBacktraceUsingAddr2line(int fd, const struct StackFrame *bp) { } addr = frame->addr; #ifdef __x86_64__ - if (addr == (uintptr_t)_weaken(__gc)) { + if (gi && addr == (uintptr_t)_weaken(__gc)) { do { --gi; } while ((addr = garbage->p[gi].ret) == (uintptr_t)_weaken(__gc)); @@ -116,7 +116,7 @@ static int PrintBacktraceUsingAddr2line(int fd, const struct StackFrame *bp) { argv[i++] = buf + j; buf[j++] = '0'; buf[j++] = 'x'; - j += uint64toarray_radix16(addr - 1, buf + j) + 1; + j += uint64toarray_radix16(addr, buf + j) + 1; } argv[i++] = NULL; if (sys_pipe2(pipefds, O_CLOEXEC) == -1) { diff --git a/libc/log/backtrace3.c b/libc/log/backtrace3.c index b57f7e2ee..e49f6f78c 100644 --- a/libc/log/backtrace3.c +++ b/libc/log/backtrace3.c @@ -73,20 +73,7 @@ dontinstrument dontasan int PrintBacktraceUsingSymbols( } #endif if (addr) { - if ( -#ifdef __x86_64__ - /* - * we subtract one to handle the case of noreturn functions - * with a call instruction at the end, since %rip in such - * cases will point to the start of the next function. - * generally %rip always points to the byte after the - * instruction. one exception is in case like __restore_rt - * where the kernel creates a stack frame that points to the - * beginning of the function. - */ - (symbol = __get_symbol(st, addr - 1)) != -1 || -#endif - (symbol = __get_symbol(st, addr)) != -1) { + if ((symbol = __get_symbol(st, addr)) != -1) { addend = addr - st->addr_base; addend -= st->symbols[symbol].x; } else { diff --git a/libc/log/vflogf.c b/libc/log/vflogf.c index 68a9b2a47..cd49350d4 100644 --- a/libc/log/vflogf.c +++ b/libc/log/vflogf.c @@ -135,7 +135,7 @@ void(vflogf)(unsigned level, const char *file, int line, FILE *f, (dprintf)(STDERR_FILENO, "exiting due to aforementioned error (host %s pid %d tid %d)\n", buf32, getpid(), gettid()); - __die(); + _Exit(22); } ALLOW_SIGNALS; diff --git a/libc/nt/winsock.h b/libc/nt/winsock.h index b1a3adff1..6a7a808be 100644 --- a/libc/nt/winsock.h +++ b/libc/nt/winsock.h @@ -291,7 +291,7 @@ struct NtInterfaceInfo { */ int32_t WSAStartup(uint16_t wVersionRequested, struct NtWsaData *lpWSAData) - paramsnonnull() __wur; + paramsnonnull(); int WSACleanup(void); int WSAGetLastError(void) nosideeffect; diff --git a/libc/proc/kill-nt.c b/libc/proc/kill-nt.c index 38fbc4269..7d9d6df77 100644 --- a/libc/proc/kill-nt.c +++ b/libc/proc/kill-nt.c @@ -17,8 +17,10 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" +#include "libc/calls/struct/sigset.internal.h" #include "libc/calls/syscall-nt.internal.h" #include "libc/errno.h" +#include "libc/intrin/dll.h" #include "libc/intrin/strace.internal.h" #include "libc/nt/console.h" #include "libc/nt/enum/ctrlevent.h" @@ -53,6 +55,16 @@ textwindows int sys_kill_nt(int pid, int sig) { // just call raise() if we're targeting self if (pid <= 0 || pid == getpid()) { if (sig) { + if (pid <= 0) { + struct Dll *e; + BLOCK_SIGNALS; + __proc_lock(); + for (e = dll_first(__proc.list); e; e = dll_next(__proc.list, e)) { + TerminateProcess(PROC_CONTAINER(e)->handle, sig); + } + __proc_unlock(); + ALLOW_SIGNALS; + } return raise(sig); } else { return 0; // ability check passes diff --git a/libc/sock/accept-nt.c b/libc/sock/accept-nt.c index 4039a972f..aaa870d48 100644 --- a/libc/sock/accept-nt.c +++ b/libc/sock/accept-nt.c @@ -22,6 +22,7 @@ #include "libc/calls/struct/fd.internal.h" #include "libc/calls/struct/sigset.internal.h" #include "libc/cosmo.h" +#include "libc/errno.h" #include "libc/nt/enum/wsaid.h" #include "libc/nt/thunk/msabi.h" #include "libc/nt/winsock.h" @@ -140,6 +141,9 @@ textwindows int sys_accept_nt(struct Fd *f, struct sockaddr_storage *addr, WeFailed: pthread_cleanup_pop(false); __sig_unblock(m); + if (client == -1 && errno == ECONNRESET) { + errno = ECONNABORTED; + } return client; } diff --git a/tool/net/net.mk b/tool/net/net.mk index fd16ca27e..82b262d92 100644 --- a/tool/net/net.mk +++ b/tool/net/net.mk @@ -16,6 +16,7 @@ TOOL_NET_BINS = \ TOOL_NET_COMS = \ o/$(MODE)/tool/net/dig.com \ + o/$(MODE)/tool/net/winbench.com \ o/$(MODE)/tool/net/redbean.com \ o/$(MODE)/tool/net/redbean-demo.com \ o/$(MODE)/tool/net/redbean-static.com \ @@ -37,6 +38,7 @@ TOOL_NET_DIRECTDEPS = \ LIBC_NEXGEN32E \ LIBC_NT_IPHLPAPI \ LIBC_NT_KERNEL32 \ + LIBC_NT_WS2_32 \ LIBC_PROC \ LIBC_RUNTIME \ LIBC_SOCK \ diff --git a/tool/net/redbean.c b/tool/net/redbean.c index a1221b460..fc411d083 100644 --- a/tool/net/redbean.c +++ b/tool/net/redbean.c @@ -6894,7 +6894,7 @@ static int HandleConnection(size_t i) { } else if (errno == ECONNABORTED) { LockInc(&shared->c.accepterrors); LockInc(&shared->c.acceptresets); - WARNF("(srvr) %S accept error: %s", DescribeServer(), + WARNF("(srvr) %s accept error: %s", DescribeServer(), "acceptreset: connection reset before accept"); } else if (errno == ENETUNREACH || errno == EHOSTUNREACH || errno == EOPNOTSUPP || errno == ENOPROTOOPT || errno == EPROTO) { @@ -6903,7 +6903,7 @@ static int HandleConnection(size_t i) { WARNF("(srvr) accept error: %s ephemeral accept error: %m", DescribeServer()); } else { - DIEF("(srvr) %s accept error: %m", DescribeServer()); + WARNF("(srvr) %s accept error: %m", DescribeServer()); } errno = 0; } diff --git a/tool/net/winbench.c b/tool/net/winbench.c new file mode 100644 index 000000000..321253af6 --- /dev/null +++ b/tool/net/winbench.c @@ -0,0 +1,294 @@ +#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/assert.h" +#include "libc/atomic.h" +#include "libc/calls/calls.h" +#include "libc/calls/struct/sigaction.h" +#include "libc/calls/struct/timespec.h" +#include "libc/dns/dns.h" +#include "libc/errno.h" +#include "libc/fmt/conv.h" +#include "libc/intrin/kprintf.h" +#include "libc/macros.internal.h" +#include "libc/mem/mem.h" +#include "libc/nt/accounting.h" +#include "libc/nt/enum/wsaid.h" +#include "libc/nt/errors.h" +#include "libc/nt/iocp.h" +#include "libc/nt/runtime.h" +#include "libc/nt/struct/overlapped.h" +#include "libc/nt/thunk/msabi.h" +#include "libc/nt/winsock.h" +#include "libc/runtime/runtime.h" +#include "libc/sock/sock.h" +#include "libc/sock/struct/sockaddr.h" +#include "libc/sock/wsaid.internal.h" +#include "libc/stdio/stdio.h" +#include "libc/str/str.h" +#include "libc/sysv/consts/af.h" +#include "libc/sysv/consts/auxv.h" +#include "libc/sysv/consts/ipproto.h" +#include "libc/sysv/consts/sig.h" +#include "libc/sysv/consts/sock.h" +#include "libc/thread/thread.h" +#include "libc/thread/thread2.h" +#include "third_party/getopt/getopt.internal.h" + +__msabi extern typeof(__sys_bind_nt) *const __imp_bind; +__msabi extern typeof(__sys_closesocket_nt) *const __imp_closesocket; + +enum State { SENDING, RECEIVING }; + +struct Client { + struct NtOverlapped overlap; + int64_t handle; + enum State state; + char buf[1500]; + struct NtIovec iov; +}; + +static int64_t iocp; +static char msg[128]; +static uint32_t msglen; +static const char *prog; +static atomic_int a_termsig; +static atomic_long a_errors; +static atomic_long a_requests; +static atomic_bool a_finished; + +static wontreturn void PrintUsage(int fd, int rc) { + tinyprint(fd, "Usage: ", prog, " -H HOST -P PORT\n", NULL); + exit(rc); +} + +static bool32 (*__msabi ConnectEx)(int64_t hSocket, const void *name, + int namelen, const void *opt_lpSendBuffer, + uint32_t dwSendDataLength, + uint32_t *opt_out_lpdwBytesSent, + struct NtOverlapped *lpOverlapped); + +static int64_t CreateNewCompletionPort(uint32_t dwNumberOfConcurrentThreads) { + return CreateIoCompletionPort(-1, 0, 0, dwNumberOfConcurrentThreads); +} + +static bool AssociateDeviceWithCompletionPort(int64_t hCompletionPort, + int64_t hDevice, + uint64_t dwCompletionKey) { + int64_t h = + CreateIoCompletionPort(hDevice, hCompletionPort, dwCompletionKey, 0); + return h == hCompletionPort; +} + +static void NewClient(struct Client *client, const struct sockaddr_in *addr) { + + client->handle = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, + kNtWsaFlagOverlapped); + if (client->handle == -1) { + fprintf(stderr, "WSASocket() failed w/ %d\n", WSAGetLastError()); + exit(1); + } + struct sockaddr_in bindaddr = {AF_INET}; + if (__imp_bind(client->handle, &bindaddr, sizeof(bindaddr))) { + fprintf(stderr, "bind() failed w/ %d\n", WSAGetLastError()); + exit(1); + } + + if (!AssociateDeviceWithCompletionPort(iocp, client->handle, + (intptr_t)client)) { + fprintf(stderr, "AssociateDeviceWithCompletionPort() failed w/ %d\n", + GetLastError()); + exit(1); + } + if (!SetFileCompletionNotificationModes(client->handle, + kNtFileSkipSetEventOnHandle)) { + fprintf(stderr, "SetFileCompletionNotificationModes() failed w/ %d\n", + GetLastError()); + exit(1); + } + + struct NtOverlapped overlap = {0}; + if (!ConnectEx(client->handle, addr, sizeof(*addr), msg, msglen, 0, + &overlap) && + WSAGetLastError() != kNtErrorIoPending) { + fprintf(stderr, "ConnectEx() failed w/ %d\n", WSAGetLastError()); + exit(1); + } +} + +void *Worker(void *arg) { + while (!a_finished) { + bool32 ok; + uint32_t dwFlags; + uint32_t dwBytes; + struct Client *client; + uint64_t CompletionKey; + struct NtOverlapped *lpOverlapped; + if (!(ok = GetQueuedCompletionStatus(iocp, &dwBytes, &CompletionKey, + &lpOverlapped, -1u)) && + !lpOverlapped) { + fprintf(stderr, "GetQueuedCompletionStatus() failed w/ %d\n", + GetLastError()); + exit(1); + } + client = (struct Client *)CompletionKey; + switch (client->state) { + case SENDING: + if (ok) { + dwFlags = 0; + client->state = RECEIVING; + client->iov.buf = client->buf; + client->iov.len = sizeof(client->buf) - 1; + bzero(&client->overlap, sizeof(client->overlap)); + if (WSARecv(client->handle, &client->iov, 1, 0, &dwFlags, + &client->overlap, 0) && + WSAGetLastError() != kNtErrorIoPending) { + fprintf(stderr, "WSARecv() failed w/ %d\n", WSAGetLastError()); + exit(1); + } + } else { + fprintf(stderr, "WSAConnect() or WSASend() failed w/ %d\n", + WSAGetLastError()); + __imp_closesocket(client->handle); + ++a_errors; + } + break; + case RECEIVING: + if (ok) { + if (!dwBytes) { + fprintf(stderr, "got disconnect\n"); + break; + } + client->buf[dwBytes] = 0; + if (!startswith(client->buf, "HTTP/1.1 200")) { + fprintf(stderr, "request failed: %d bytes %`'s\n", dwBytes, + client->buf); + exit(1); + } + ++a_requests; + client->state = SENDING; + client->iov.buf = msg; + client->iov.len = msglen; + bzero(&client->overlap, sizeof(client->overlap)); + if (WSASend(client->handle, &client->iov, 1, 0, 0, &client->overlap, + 0) && + WSAGetLastError() != kNtErrorIoPending) { + fprintf(stderr, "WSASend() failed w/ %d\n", WSAGetLastError()); + exit(1); + } + } else { + fprintf(stderr, "WSARecv() failed w/ %d\n", WSAGetLastError()); + __imp_closesocket(client->handle); + ++a_errors; + } + break; + default: + __builtin_unreachable(); + } + } + return 0; +} + +void OnTerm(int sig) { + a_termsig = sig; +} + +int main(int argc, char *argv[]) { + + prog = argv[0]; + if (!prog) { + prog = "ab"; + } + + int opt; + int nclients = 20; + int nthreads = GetMaximumProcessorCount(0xffff) * 2; + struct sockaddr_in destaddr = {AF_INET, htons(8080), {htonl(0x7f000001)}}; + while ((opt = getopt(argc, argv, "hH:P:")) != -1) { + switch (opt) { + case 'H': + destaddr.sin_addr.s_addr = inet_addr(optarg); + break; + case 'P': + destaddr.sin_port = htons(atoi(optarg)); + break; + case 'h': + PrintUsage(1, 0); + default: + PrintUsage(2, 1); + } + } + + uint32_t ip = ntohl(destaddr.sin_addr.s_addr); + uint16_t port = ntohs(destaddr.sin_port); + msglen = snprintf(msg, sizeof(msg), + "GET / HTTP/1.1\r\n" + "Host: %hhu.%hhu.%hhu.%hhu:%hu\r\n" + "\r\n", + ip >> 24, ip >> 16, ip >> 8, ip, port); + + struct NtWsaData kNtWsaData = {0}; + WSAStartup(0x0202, &kNtWsaData); + + static struct NtGuid ConnectExGuid = WSAID_CONNECTEX; + ConnectEx = __get_wsaid(&ConnectExGuid); + + iocp = CreateNewCompletionPort(0); + if (!iocp) { + fprintf(stderr, "CreateNewCompletionPort() failed w/ %d\n", GetLastError()); + exit(1); + } + + struct sigaction sa = {.sa_handler = OnTerm}; + unassert(!sigaction(SIGINT, &sa, 0)); + unassert(!sigaction(SIGHUP, &sa, 0)); + unassert(!sigaction(SIGTERM, &sa, 0)); + + sigset_t block; + sigfillset(&block); + pthread_attr_t attr; + int pagesz = getauxval(AT_PAGESZ); + pthread_t *threads = calloc(nthreads, sizeof(pthread_t)); + unassert(!pthread_attr_init(&attr)); + unassert(!pthread_attr_setstacksize(&attr, 65536)); + unassert(!pthread_attr_setguardsize(&attr, pagesz)); + unassert(!pthread_attr_setsigmask_np(&attr, &block)); + for (int i = 0; i < nthreads; ++i) { + if ((errno = pthread_create(threads + i, &attr, Worker, 0))) { + perror("pthread_create"); + exit(1); + } + } + unassert(!pthread_attr_destroy(&attr)); + + struct timespec start_time = timespec_real(); + + struct Client *clients = calloc(nclients, sizeof(struct Client)); + for (int i = 0; i < nclients; ++i) { + NewClient(clients + i, &destaddr); + } + + sleep(5); + + a_finished = true; + long request_count = a_requests; + struct timespec end_time = timespec_real(); + + for (int i = 0; i < nthreads; ++i) { + if ((errno = pthread_join(threads[i], 0))) { + perror("pthread_join"); + exit(1); + } + } + + double elapsed_seconds = + timespec_tonanos(timespec_sub(end_time, start_time)) * 1e-9; + double requests_per_second = request_count / elapsed_seconds; + fprintf(stderr, "qps = %g\n", requests_per_second); +}