#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void mainloop(const struct sockaddr_in *, int, int, long, u_long *, int, int); static int bindcpu; static int cpucnt; static void usage(const char *cmd) { fprintf(stderr, "%s -4 inet4 [-4 inet4_1] -p port " "[-i n_instance] [-c conn_max] [-l duration] [-u] [-B]\n", cmd); exit(1); } int main(int argc, char *argv[]) { struct sockaddr_in *in, *tmp; int opt, ninst, nconn, i, in_max, in_cnt, do_udp; long dur; u_long *result, sum; u_short port; size_t prm_len; prm_len = sizeof(cpucnt); if (sysctlbyname("hw.ncpu", &cpucnt, &prm_len, NULL, 0) != 0) err(2, "sysctl hw.ncpu failed"); ninst = cpucnt; nconn = 8; dur = 10; port = 0; do_udp = 0; in_max = 8; in = calloc(in_max, sizeof(struct sockaddr_in)); if (in == NULL) err(1, "calloc failed"); in_cnt = 0; while ((opt = getopt(argc, argv, "4:Bc:i:l:p:u")) != -1) { switch (opt) { case '4': if (in_cnt >= in_max) { struct sockaddr_in *old_in = in; int old_in_max = in_max; in_max <<= 1; in = calloc(in_max, sizeof(struct sockaddr_in)); if (in == NULL) err(1, "calloc failed"); memcpy(in, old_in, old_in_max * sizeof(struct sockaddr_in)); free(old_in); } tmp = &in[in_cnt]; if (inet_pton(AF_INET, optarg, &tmp->sin_addr) <= 0) { fprintf(stderr, "invalid inet address %s\n", optarg); usage(argv[0]); } ++in_cnt; break; case 'B': bindcpu = 1; break; case 'c': nconn = atoi(optarg); break; case 'i': ninst = atoi(optarg); break; case 'l': dur = strtol(optarg, NULL, 10); break; case 'p': port = htons(atoi(optarg)); break; case 'u': do_udp = 1; break; default: usage(argv[0]); } } if (ninst < 1 || dur < 1 || nconn < 1 || port == 0 || in_cnt == 0) usage(argv[0]); for (i = 0; i < in_cnt; ++i) { tmp = &in[i]; tmp->sin_family = AF_INET; tmp->sin_port = port; } result = mmap(NULL, ninst * sizeof(u_long), PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0); if (result == MAP_FAILED) err(1, "mmap failed"); memset(result, 0, ninst * sizeof(u_long)); for (i = 0; i < ninst; ++i) { pid_t pid; pid = fork(); if (pid == 0) { mainloop(in, in_cnt, nconn, dur, &result[i], do_udp, i); exit(0); } else if (pid < 0) { err(1, "fork failed"); } } for (i = 0; i < ninst; ++i) { pid_t pid; pid = waitpid(-1, NULL, 0); if (pid < 0) err(1, "waitpid failed"); } sum = 0; for (i = 0; i < ninst; ++i) sum += result[i]; printf("%lu\n", sum); exit(0); } static void udp_send(const struct sockaddr_in *in) { uint8_t d[18]; int s; s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) { warn("socket DGRAM failed"); return; } if (connect(s, (const struct sockaddr *)in, sizeof(*in)) < 0) { warn("connect DGRAM failed"); goto done; } write(s, d, sizeof(d)); done: close(s); } static void mainloop(const struct sockaddr_in *in, int in_cnt, int nconn_max, long dur, u_long *res, int do_udp, int inst) { struct timespec start, end; struct kevent *evt_change0, *evt; int kq, nchange = 0, nconn = 0, nevt_max; u_long count = 0; double time_us; u_int in_idx = 0; #ifndef SOCK_NONBLOCK int nblock = 1; #endif if (bindcpu) { int cpu = inst % cpucnt; usched_set(getpid(), USCHED_SET_CPU, &cpu, sizeof(cpu)); } kq = kqueue(); if (kq < 0) err(1, "kqueue failed"); nevt_max = nconn_max + 1; /* timer */ evt_change0 = malloc(nevt_max * sizeof(struct kevent)); if (evt_change0 == NULL) err(1, "malloc evt_change failed"); evt = malloc(nevt_max * sizeof(struct kevent)); if (evt == NULL) err(1, "malloc evt failed"); EV_SET(&evt_change0[0], 0, EVFILT_TIMER, EV_ADD | EV_ONESHOT, 0, dur * 1000L, NULL); nchange = 1; clock_gettime(CLOCK_MONOTONIC_PRECISE, &start); for (;;) { struct kevent *evt_change = NULL; int n, i, done = 0; while (nconn < nconn_max) { const struct sockaddr_in *tmp; int s; tmp = &in[in_idx % in_cnt]; ++in_idx; if (do_udp) udp_send(tmp); #ifndef SOCK_NONBLOCK s = socket(AF_INET, SOCK_STREAM, 0); if (s < 0) err(1, "socket failed"); if (ioctl(s, FIONBIO, &nblock, sizeof(nblock)) < 0) err(1, "ioctl failed"); #else s = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0); if (s < 0) err(1, "socket failed"); #endif n = connect(s, (const struct sockaddr *)tmp, sizeof(*tmp)); if (n == 0) { ++count; close(s); continue; } else { int error = errno; if (error != EINPROGRESS) errc(1, error, "connect failed"); } ++nconn; if (nchange >= nevt_max) { fprintf(stderr, "invalid nchange %d, max %d\n", nchange, nevt_max); abort(); } EV_SET(&evt_change0[nchange], s, EVFILT_WRITE, EV_ADD, 0, 0, NULL); ++nchange; } if (nchange) evt_change = evt_change0; n = kevent(kq, evt_change, nchange, evt, nevt_max, NULL); if (n < 0) err(1, "kevent failed"); nchange = 0; for (i = 0; i < n; ++i) { struct kevent *e = &evt[i]; if (e->filter == EVFILT_TIMER) { done = 1; continue; } if ((e->flags & EV_EOF) && e->fflags) { /* Error, don't count */ } else { ++count; } close(e->ident); --nconn; } if (done) break; } clock_gettime(CLOCK_MONOTONIC_PRECISE, &end); timespecsub(&end, &start, &end); time_us = ((double)end.tv_sec * 1000000.0) + ((double)end.tv_nsec / 1000.0); *res = ((double)count * 1000000.0) / time_us; }