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