1 /* $NetBSD: test-ratelim.c,v 1.8 2024/08/18 20:47:23 christos Exp $ */ 2 3 /* 4 * Copyright (c) 2009-2012 Niels Provos and Nick Mathewson 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. The name of the author may not be used to endorse or promote products 15 * derived from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 #include "../util-internal.h" 29 30 #include <stdio.h> 31 #include <stdlib.h> 32 #include <string.h> 33 #include <assert.h> 34 #include <math.h> 35 36 #ifdef _WIN32 37 #include <winsock2.h> 38 #include <ws2tcpip.h> 39 #else 40 #include <sys/socket.h> 41 #include <netinet/in.h> 42 # ifdef _XOPEN_SOURCE_EXTENDED 43 # include <arpa/inet.h> 44 # endif 45 #endif 46 #include <signal.h> 47 48 #include "event2/bufferevent.h" 49 #include "event2/buffer.h" 50 #include "event2/event.h" 51 #include "event2/util.h" 52 #include "event2/listener.h" 53 #include "event2/thread.h" 54 55 #ifndef MIN 56 #define MIN(a,b) (((a)<(b))?(a):(b)) 57 #endif 58 59 static struct evutil_weakrand_state weakrand_state; 60 61 static int cfg_verbose = 0; 62 static int cfg_help = 0; 63 64 static int cfg_n_connections = 30; 65 static int cfg_duration = 5; 66 static int cfg_connlimit = 0; 67 static int cfg_grouplimit = 0; 68 static int cfg_tick_msec = 1000; 69 static int cfg_min_share = -1; 70 static int cfg_group_drain = 0; 71 72 static int cfg_connlimit_tolerance = -1; 73 static int cfg_grouplimit_tolerance = -1; 74 static int cfg_stddev_tolerance = -1; 75 76 #ifdef _WIN32 77 static int cfg_enable_iocp = 0; 78 #endif 79 80 static struct timeval cfg_tick = { 0, 500*1000 }; 81 82 static struct ev_token_bucket_cfg *conn_bucket_cfg = NULL; 83 static struct ev_token_bucket_cfg *group_bucket_cfg = NULL; 84 struct bufferevent_rate_limit_group *ratelim_group = NULL; 85 static double seconds_per_tick = 0.0; 86 87 struct client_state { 88 size_t queued; 89 ev_uint64_t received; 90 91 }; 92 static const struct timeval *ms100_common=NULL; 93 94 /* Timers bias for slow CPUs, affects: 95 * - cfg_connlimit_tolerance (--check-connlimit) 96 * - cfg_grouplimit_tolerance (--check-grouplimit) 97 * - cfg_stddev_tolerance (--check-stddev) 98 */ 99 static int timer_bias_events; 100 static struct timeval timer_bias_start; 101 double timer_bias_spend; 102 /* Real cost is less (approximately ~5 usec), 103 * this macros adjusted to make the bias less */ 104 #define TIMER_MAX_COST_USEC 10 105 106 /* info from check_bucket_levels_cb */ 107 static int total_n_bev_checks = 0; 108 static ev_int64_t total_rbucket_level=0; 109 static ev_int64_t total_wbucket_level=0; 110 static ev_int64_t total_max_to_read=0; 111 static ev_int64_t total_max_to_write=0; 112 static ev_int64_t max_bucket_level=EV_INT64_MIN; 113 static ev_int64_t min_bucket_level=EV_INT64_MAX; 114 115 /* from check_group_bucket_levels_cb */ 116 static int total_n_group_bev_checks = 0; 117 static ev_int64_t total_group_rbucket_level = 0; 118 static ev_int64_t total_group_wbucket_level = 0; 119 120 static int n_echo_conns_open = 0; 121 122 /* Info on the open connections */ 123 struct bufferevent **bevs; 124 struct client_state *states; 125 struct bufferevent_rate_limit_group *group = NULL; 126 127 static void check_bucket_levels_cb(evutil_socket_t fd, short events, void *arg); 128 129 static void 130 loud_writecb(struct bufferevent *bev, void *ctx) 131 { 132 struct client_state *cs = ctx; 133 struct evbuffer *output = bufferevent_get_output(bev); 134 char buf[1024]; 135 int r = evutil_weakrand_(&weakrand_state); 136 memset(buf, r, sizeof(buf)); 137 while (evbuffer_get_length(output) < 8192) { 138 evbuffer_add(output, buf, sizeof(buf)); 139 cs->queued += sizeof(buf); 140 } 141 } 142 143 static void 144 discard_readcb(struct bufferevent *bev, void *ctx) 145 { 146 struct client_state *cs = ctx; 147 struct evbuffer *input = bufferevent_get_input(bev); 148 size_t len = evbuffer_get_length(input); 149 evbuffer_drain(input, len); 150 cs->received += len; 151 } 152 153 static void 154 write_on_connectedcb(struct bufferevent *bev, short what, void *ctx) 155 { 156 if (what & BEV_EVENT_CONNECTED) { 157 loud_writecb(bev, ctx); 158 /* XXXX this shouldn't be needed. */ 159 bufferevent_enable(bev, EV_READ|EV_WRITE); 160 } 161 } 162 163 static void 164 echo_readcb(struct bufferevent *bev, void *ctx) 165 { 166 struct evbuffer *input = bufferevent_get_input(bev); 167 struct evbuffer *output = bufferevent_get_output(bev); 168 169 evbuffer_add_buffer(output, input); 170 if (evbuffer_get_length(output) > 1024000) 171 bufferevent_disable(bev, EV_READ); 172 } 173 174 static void 175 echo_writecb(struct bufferevent *bev, void *ctx) 176 { 177 struct evbuffer *output = bufferevent_get_output(bev); 178 if (evbuffer_get_length(output) < 512000) 179 bufferevent_enable(bev, EV_READ); 180 } 181 182 static void 183 echo_eventcb(struct bufferevent *bev, short what, void *ctx) 184 { 185 if (what & (BEV_EVENT_EOF|BEV_EVENT_ERROR)) { 186 --n_echo_conns_open; 187 bufferevent_free(bev); 188 } 189 } 190 191 static void 192 echo_listenercb(struct evconnlistener *listener, evutil_socket_t newsock, 193 struct sockaddr *sourceaddr, int socklen, void *ctx) 194 { 195 struct event_base *base = ctx; 196 int flags = BEV_OPT_CLOSE_ON_FREE|BEV_OPT_THREADSAFE; 197 struct bufferevent *bev; 198 199 bev = bufferevent_socket_new(base, newsock, flags); 200 bufferevent_setcb(bev, echo_readcb, echo_writecb, echo_eventcb, NULL); 201 if (conn_bucket_cfg) { 202 struct event *check_event = 203 event_new(base, -1, EV_PERSIST, check_bucket_levels_cb, bev); 204 bufferevent_set_rate_limit(bev, conn_bucket_cfg); 205 206 assert(bufferevent_get_token_bucket_cfg(bev) != NULL); 207 event_add(check_event, ms100_common); 208 } 209 if (ratelim_group) 210 bufferevent_add_to_rate_limit_group(bev, ratelim_group); 211 ++n_echo_conns_open; 212 bufferevent_enable(bev, EV_READ|EV_WRITE); 213 } 214 215 /* Called periodically to check up on how full the buckets are */ 216 static void 217 check_bucket_levels_cb(evutil_socket_t fd, short events, void *arg) 218 { 219 struct bufferevent *bev = arg; 220 221 ev_ssize_t r = bufferevent_get_read_limit(bev); 222 ev_ssize_t w = bufferevent_get_write_limit(bev); 223 ev_ssize_t rm = bufferevent_get_max_to_read(bev); 224 ev_ssize_t wm = bufferevent_get_max_to_write(bev); 225 /* XXXX check that no value is above the cofigured burst 226 * limit */ 227 total_rbucket_level += r; 228 total_wbucket_level += w; 229 total_max_to_read += rm; 230 total_max_to_write += wm; 231 #define B(x) \ 232 if ((x) > max_bucket_level) \ 233 max_bucket_level = (x); \ 234 if ((x) < min_bucket_level) \ 235 min_bucket_level = (x) 236 B(r); 237 B(w); 238 #undef B 239 240 total_n_bev_checks++; 241 if (total_n_bev_checks >= .8 * ((double)cfg_duration / cfg_tick_msec) * cfg_n_connections) { 242 event_free(event_base_get_running_event(bufferevent_get_base(bev))); 243 } 244 } 245 246 static void 247 check_group_bucket_levels_cb(evutil_socket_t fd, short events, void *arg) 248 { 249 if (ratelim_group) { 250 ev_ssize_t r = bufferevent_rate_limit_group_get_read_limit(ratelim_group); 251 ev_ssize_t w = bufferevent_rate_limit_group_get_write_limit(ratelim_group); 252 total_group_rbucket_level += r; 253 total_group_wbucket_level += w; 254 } 255 ++total_n_group_bev_checks; 256 } 257 258 static void 259 group_drain_cb(evutil_socket_t fd, short events, void *arg) 260 { 261 bufferevent_rate_limit_group_decrement_read(ratelim_group, cfg_group_drain); 262 bufferevent_rate_limit_group_decrement_write(ratelim_group, cfg_group_drain); 263 } 264 265 static void 266 timer_bias_cb(evutil_socket_t fd, short events, void *arg) 267 { 268 struct event *event = arg; 269 struct timeval end; 270 struct timeval diff; 271 272 /** XXX: use rdtsc? (portability issues?) */ 273 evutil_gettimeofday(&end, NULL); 274 evutil_timersub(&end, &timer_bias_start, &diff); 275 timer_bias_spend += diff.tv_sec + diff.tv_usec * 1e6; 276 timer_bias_start = end; 277 278 if (++timer_bias_events == 100) 279 event_del(event); 280 } 281 static double 282 timer_bias_calculate(void) 283 { 284 struct event_config *cfg = NULL; 285 struct event_base *base = NULL; 286 struct event *timer = NULL; 287 struct timeval tv = { 0, 1 }; 288 int done = 0; 289 290 cfg = event_config_new(); 291 if (!cfg) 292 goto err; 293 if (event_config_set_flag(cfg, EVENT_BASE_FLAG_PRECISE_TIMER)) 294 goto err; 295 base = event_base_new_with_config(cfg); 296 if (!base) 297 goto err; 298 299 timer = event_new(base, -1, EV_PERSIST, timer_bias_cb, event_self_cbarg()); 300 if (!timer || event_add(timer, &tv)) { 301 goto err; 302 } 303 304 evutil_gettimeofday(&timer_bias_start, NULL); 305 event_base_dispatch(base); 306 done = 1; 307 308 err: 309 if (cfg) 310 event_config_free(cfg); 311 if (timer) 312 event_free(timer); 313 if (base) 314 event_base_free(base); 315 316 if (done) 317 return MIN(timer_bias_spend / 1e6 / timer_bias_events / TIMER_MAX_COST_USEC, 5); 318 319 fprintf(stderr, "Couldn't create event for CPU cycle counter bias\n"); 320 return -1; 321 } 322 323 static int 324 test_ratelimiting(void) 325 { 326 struct event_base *base; 327 struct sockaddr_in sin; 328 struct evconnlistener *listener; 329 330 struct sockaddr_storage ss; 331 ev_socklen_t slen; 332 333 int i; 334 335 struct timeval tv; 336 337 ev_uint64_t total_received; 338 double total_sq_persec, total_persec; 339 double variance; 340 double expected_total_persec = -1.0, expected_avg_persec = -1.0; 341 int ok = 1; 342 struct event_config *base_cfg; 343 struct event *periodic_level_check; 344 struct event *group_drain_event=NULL; 345 double timer_bias; 346 347 memset(&sin, 0, sizeof(sin)); 348 sin.sin_family = AF_INET; 349 sin.sin_addr.s_addr = htonl(0x7f000001); /* 127.0.0.1 */ 350 sin.sin_port = 0; /* unspecified port */ 351 352 if (0) 353 event_enable_debug_mode(); 354 355 timer_bias = timer_bias_calculate(); 356 if (timer_bias > 1) { 357 fprintf(stderr, "CPU is slow, timers bias is %f\n", timer_bias); 358 cfg_connlimit_tolerance *= timer_bias; 359 cfg_grouplimit_tolerance *= timer_bias; 360 cfg_stddev_tolerance *= timer_bias; 361 } else { 362 printf("CPU is fast enough, timers bias is %f\n", timer_bias); 363 } 364 365 base_cfg = event_config_new(); 366 367 #ifdef _WIN32 368 if (cfg_enable_iocp) { 369 #ifdef EVTHREAD_USE_WINDOWS_THREADS_IMPLEMENTED 370 evthread_use_windows_threads(); 371 #endif 372 event_config_set_flag(base_cfg, EVENT_BASE_FLAG_STARTUP_IOCP); 373 } 374 #endif 375 376 base = event_base_new_with_config(base_cfg); 377 event_config_free(base_cfg); 378 if (! base) { 379 fprintf(stderr, "Couldn't create event_base"); 380 return 1; 381 } 382 383 listener = evconnlistener_new_bind(base, echo_listenercb, base, 384 LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE, -1, 385 (struct sockaddr *)&sin, sizeof(sin)); 386 if (! listener) { 387 fprintf(stderr, "Couldn't create listener"); 388 return 1; 389 } 390 391 slen = sizeof(ss); 392 if (getsockname(evconnlistener_get_fd(listener), (struct sockaddr *)&ss, 393 &slen) < 0) { 394 perror("getsockname"); 395 return 1; 396 } 397 398 if (cfg_connlimit > 0) { 399 conn_bucket_cfg = ev_token_bucket_cfg_new( 400 cfg_connlimit, cfg_connlimit * 4, 401 cfg_connlimit, cfg_connlimit * 4, 402 &cfg_tick); 403 assert(conn_bucket_cfg); 404 } 405 406 if (cfg_grouplimit > 0) { 407 group_bucket_cfg = ev_token_bucket_cfg_new( 408 cfg_grouplimit, cfg_grouplimit * 4, 409 cfg_grouplimit, cfg_grouplimit * 4, 410 &cfg_tick); 411 group = ratelim_group = bufferevent_rate_limit_group_new( 412 base, group_bucket_cfg); 413 expected_total_persec = cfg_grouplimit - (cfg_group_drain / seconds_per_tick); 414 expected_avg_persec = cfg_grouplimit / cfg_n_connections; 415 if (cfg_connlimit > 0 && expected_avg_persec > cfg_connlimit) 416 expected_avg_persec = cfg_connlimit; 417 if (cfg_min_share >= 0) 418 bufferevent_rate_limit_group_set_min_share( 419 ratelim_group, cfg_min_share); 420 } 421 422 if (expected_avg_persec < 0 && cfg_connlimit > 0) 423 expected_avg_persec = cfg_connlimit; 424 425 if (expected_avg_persec > 0) 426 expected_avg_persec /= seconds_per_tick; 427 if (expected_total_persec > 0) 428 expected_total_persec /= seconds_per_tick; 429 430 bevs = calloc(cfg_n_connections, sizeof(struct bufferevent *)); 431 states = calloc(cfg_n_connections, sizeof(struct client_state)); 432 433 for (i = 0; i < cfg_n_connections; ++i) { 434 bevs[i] = bufferevent_socket_new(base, -1, 435 BEV_OPT_CLOSE_ON_FREE|BEV_OPT_THREADSAFE); 436 assert(bevs[i]); 437 bufferevent_setcb(bevs[i], discard_readcb, loud_writecb, 438 write_on_connectedcb, &states[i]); 439 bufferevent_enable(bevs[i], EV_READ|EV_WRITE); 440 bufferevent_socket_connect(bevs[i], (struct sockaddr *)&ss, 441 slen); 442 } 443 444 tv.tv_sec = cfg_duration - 1; 445 tv.tv_usec = 995000; 446 447 event_base_loopexit(base, &tv); 448 449 tv.tv_sec = 0; 450 tv.tv_usec = 100*1000; 451 ms100_common = event_base_init_common_timeout(base, &tv); 452 453 periodic_level_check = event_new(base, -1, EV_PERSIST, check_group_bucket_levels_cb, NULL); 454 event_add(periodic_level_check, ms100_common); 455 456 if (cfg_group_drain && ratelim_group) { 457 group_drain_event = event_new(base, -1, EV_PERSIST, group_drain_cb, NULL); 458 event_add(group_drain_event, &cfg_tick); 459 } 460 461 event_base_dispatch(base); 462 463 ratelim_group = NULL; /* So no more responders get added */ 464 event_free(periodic_level_check); 465 if (group_drain_event) 466 event_free(group_drain_event); 467 468 for (i = 0; i < cfg_n_connections; ++i) { 469 bufferevent_free(bevs[i]); 470 } 471 evconnlistener_free(listener); 472 473 /* Make sure no new echo_conns get added to the group. */ 474 ratelim_group = NULL; 475 476 /* This should get _everybody_ freed */ 477 while (n_echo_conns_open) { 478 printf("waiting for %d conns\n", n_echo_conns_open); 479 tv.tv_sec = 0; 480 tv.tv_usec = 300000; 481 event_base_loopexit(base, &tv); 482 event_base_dispatch(base); 483 } 484 485 if (group) 486 bufferevent_rate_limit_group_free(group); 487 488 if (total_n_bev_checks) { 489 printf("Average read bucket level: %f\n", 490 (double)total_rbucket_level/total_n_bev_checks); 491 printf("Average write bucket level: %f\n", 492 (double)total_wbucket_level/total_n_bev_checks); 493 printf("Highest read bucket level: %f\n", 494 (double)max_bucket_level); 495 printf("Highest write bucket level: %f\n", 496 (double)min_bucket_level); 497 printf("Average max-to-read: %f\n", 498 ((double)total_max_to_read)/total_n_bev_checks); 499 printf("Average max-to-write: %f\n", 500 ((double)total_max_to_write)/total_n_bev_checks); 501 } 502 if (total_n_group_bev_checks) { 503 printf("Average group read bucket level: %f\n", 504 ((double)total_group_rbucket_level)/total_n_group_bev_checks); 505 printf("Average group write bucket level: %f\n", 506 ((double)total_group_wbucket_level)/total_n_group_bev_checks); 507 } 508 509 total_received = 0; 510 total_persec = 0.0; 511 total_sq_persec = 0.0; 512 for (i=0; i < cfg_n_connections; ++i) { 513 double persec = states[i].received; 514 persec /= cfg_duration; 515 total_received += states[i].received; 516 total_persec += persec; 517 total_sq_persec += persec*persec; 518 printf("%d: %f per second\n", i+1, persec); 519 } 520 printf(" total: %f per second\n", 521 ((double)total_received)/cfg_duration); 522 if (expected_total_persec > 0) { 523 double diff = expected_total_persec - 524 ((double)total_received/cfg_duration); 525 printf(" [Off by %lf]\n", diff); 526 if (cfg_grouplimit_tolerance > 0 && 527 fabs(diff) > cfg_grouplimit_tolerance) { 528 fprintf(stderr, "Group bandwidth out of bounds\n"); 529 ok = 0; 530 } 531 } 532 533 printf(" average: %f per second\n", 534 (((double)total_received)/cfg_duration)/cfg_n_connections); 535 if (expected_avg_persec > 0) { 536 double diff = expected_avg_persec - (((double)total_received)/cfg_duration)/cfg_n_connections; 537 printf(" [Off by %lf]\n", diff); 538 if (cfg_connlimit_tolerance > 0 && 539 fabs(diff) > cfg_connlimit_tolerance) { 540 fprintf(stderr, "Connection bandwidth out of bounds\n"); 541 ok = 0; 542 } 543 } 544 545 variance = total_sq_persec/cfg_n_connections - total_persec*total_persec/(cfg_n_connections*cfg_n_connections); 546 547 printf(" stddev: %f per second\n", sqrt(variance)); 548 if (cfg_stddev_tolerance > 0 && 549 sqrt(variance) > cfg_stddev_tolerance) { 550 fprintf(stderr, "Connection variance out of bounds\n"); 551 ok = 0; 552 } 553 554 event_base_free(base); 555 free(bevs); 556 free(states); 557 558 return ok ? 0 : 1; 559 } 560 561 static struct option { 562 const char *name; int *ptr; int min; int isbool; 563 } options[] = { 564 { "-v", &cfg_verbose, 0, 1 }, 565 { "-h", &cfg_help, 0, 1 }, 566 { "-n", &cfg_n_connections, 1, 0 }, 567 { "-d", &cfg_duration, 1, 0 }, 568 { "-c", &cfg_connlimit, 0, 0 }, 569 { "-g", &cfg_grouplimit, 0, 0 }, 570 { "-G", &cfg_group_drain, -100000, 0 }, 571 { "-t", &cfg_tick_msec, 10, 0 }, 572 { "--min-share", &cfg_min_share, 0, 0 }, 573 { "--check-connlimit", &cfg_connlimit_tolerance, 0, 0 }, 574 { "--check-grouplimit", &cfg_grouplimit_tolerance, 0, 0 }, 575 { "--check-stddev", &cfg_stddev_tolerance, 0, 0 }, 576 #ifdef _WIN32 577 { "--iocp", &cfg_enable_iocp, 0, 1 }, 578 #endif 579 { NULL, NULL, -1, 0 }, 580 }; 581 582 static int 583 handle_option(int argc, char **argv, int *i, const struct option *opt) 584 { 585 long val; 586 char *endptr = NULL; 587 if (opt->isbool) { 588 *opt->ptr = 1; 589 return 0; 590 } 591 if (*i + 1 == argc) { 592 fprintf(stderr, "Too few arguments to '%s'\n",argv[*i]); 593 return -1; 594 } 595 val = strtol(argv[*i+1], &endptr, 10); 596 if (*argv[*i+1] == '\0' || !endptr || *endptr != '\0') { 597 fprintf(stderr, "Couldn't parse numeric value '%s'\n", 598 argv[*i+1]); 599 return -1; 600 } 601 if (val < opt->min || val > 0x7fffffff) { 602 fprintf(stderr, "Value '%s' is out-of-range'\n", 603 argv[*i+1]); 604 return -1; 605 } 606 *opt->ptr = (int)val; 607 ++*i; 608 return 0; 609 } 610 611 static void 612 usage(void) 613 { 614 fprintf(stderr, 615 "test-ratelim [-v] [-n INT] [-d INT] [-c INT] [-g INT] [-t INT]\n\n" 616 "Pushes bytes through a number of possibly rate-limited connections, and\n" 617 "displays average throughput.\n\n" 618 " -n INT: Number of connections to open (default: 30)\n" 619 " -d INT: Duration of the test in seconds (default: 5 sec)\n"); 620 fprintf(stderr, 621 " -c INT: Connection-rate limit applied to each connection in bytes per second\n" 622 " (default: None.)\n" 623 " -g INT: Group-rate limit applied to sum of all usage in bytes per second\n" 624 " (default: None.)\n" 625 " -G INT: drain INT bytes from the group limit every tick. (default: 0)\n" 626 " -t INT: Granularity of timing, in milliseconds (default: 1000 msec)\n"); 627 } 628 629 int 630 main(int argc, char **argv) 631 { 632 int i,j; 633 double ratio; 634 635 #ifdef _WIN32 636 WORD wVersionRequested = MAKEWORD(2,2); 637 WSADATA wsaData; 638 639 (void) WSAStartup(wVersionRequested, &wsaData); 640 #endif 641 642 evutil_weakrand_seed_(&weakrand_state, 0); 643 644 #ifndef _WIN32 645 if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) 646 return 1; 647 #endif 648 for (i = 1; i < argc; ++i) { 649 for (j = 0; options[j].name; ++j) { 650 if (!strcmp(argv[i],options[j].name)) { 651 if (handle_option(argc,argv,&i,&options[j])<0) 652 return 1; 653 goto again; 654 } 655 } 656 fprintf(stderr, "Unknown option '%s'\n", argv[i]); 657 usage(); 658 return 1; 659 again: 660 ; 661 } 662 if (cfg_help) { 663 usage(); 664 return 0; 665 } 666 667 cfg_tick.tv_sec = cfg_tick_msec / 1000; 668 cfg_tick.tv_usec = (cfg_tick_msec % 1000)*1000; 669 670 seconds_per_tick = ratio = cfg_tick_msec / 1000.0; 671 672 cfg_connlimit *= ratio; 673 cfg_grouplimit *= ratio; 674 675 { 676 struct timeval tv; 677 evutil_gettimeofday(&tv, NULL); 678 #ifdef _WIN32 679 srand(tv.tv_usec); 680 #else 681 srandom(tv.tv_usec); 682 #endif 683 } 684 685 #ifndef EVENT__DISABLE_THREAD_SUPPORT 686 evthread_enable_lock_debugging(); 687 #endif 688 689 return test_ratelimiting(); 690 } 691