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