cosmopolitan/tool/net/winbench.c
Justine Tunney 43fe5956ad
Use DNS implementation from Musl Libc
Now that our socket system call polyfills are good enough to support
Musl's DNS library we should be using that rather than the barebones
domain name system implementation we rolled on our own. There's many
benefits to making this change. So many, that I myself wouldn't feel
qualified to enumerate them all. The Musl DNS code had to be changed
in order to support Windows of course, which looks very solid so far
2023-12-28 23:04:35 -08:00

285 lines
9.3 KiB
C

#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/dce.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_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);
}
}
static void *Worker(void *arg) {
while (!a_finished) {
uint32_t i;
uint32_t dwFlags;
uint32_t dwRecordCount;
struct NtOverlappedEntry records[8];
if (!GetQueuedCompletionStatusEx(iocp, records, ARRAYLEN(records),
&dwRecordCount, -1u, false)) {
fprintf(stderr, "GetQueuedCompletionStatus() failed w/ %d\n",
GetLastError());
exit(1);
}
for (i = 0; i < dwRecordCount; ++i) {
uint32_t dwBytes = records[i].dwNumberOfBytesTransferred;
struct Client *client = (struct Client *)records[i].lpCompletionKey;
switch (client->state) {
case SENDING:
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);
}
break;
case RECEIVING:
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);
}
break;
default:
__builtin_unreachable();
}
}
}
return 0;
}
static void OnTerm(int sig) {
a_termsig = sig;
}
int main(int argc, char *argv[]) {
if (!IsWindows()) {
tinyprint(2, "error: this program is intended for windows\n", NULL);
return 1;
}
prog = argv[0];
if (!prog) {
prog = "winbench";
}
int opt;
int nclients = 1000;
int nthreads = GetMaximumProcessorCount(0xffff);
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(10);
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);
}