ipc/chromium/src/third_party/libevent/test/test-ratelim.c

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /*
michael@0 2 * Copyright (c) 2009-2012 Niels Provos and Nick Mathewson
michael@0 3 *
michael@0 4 * Redistribution and use in source and binary forms, with or without
michael@0 5 * modification, are permitted provided that the following conditions
michael@0 6 * are met:
michael@0 7 * 1. Redistributions of source code must retain the above copyright
michael@0 8 * notice, this list of conditions and the following disclaimer.
michael@0 9 * 2. Redistributions in binary form must reproduce the above copyright
michael@0 10 * notice, this list of conditions and the following disclaimer in the
michael@0 11 * documentation and/or other materials provided with the distribution.
michael@0 12 * 3. The name of the author may not be used to endorse or promote products
michael@0 13 * derived from this software without specific prior written permission.
michael@0 14 *
michael@0 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
michael@0 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
michael@0 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
michael@0 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
michael@0 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
michael@0 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
michael@0 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
michael@0 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
michael@0 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
michael@0 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
michael@0 25 */
michael@0 26
michael@0 27 #include <stdio.h>
michael@0 28 #include <stdlib.h>
michael@0 29 #include <string.h>
michael@0 30 #include <assert.h>
michael@0 31 #include <math.h>
michael@0 32
michael@0 33 #ifdef WIN32
michael@0 34 #include <winsock2.h>
michael@0 35 #include <ws2tcpip.h>
michael@0 36 #else
michael@0 37 #include <sys/socket.h>
michael@0 38 #include <netinet/in.h>
michael@0 39 # ifdef _XOPEN_SOURCE_EXTENDED
michael@0 40 # include <arpa/inet.h>
michael@0 41 # endif
michael@0 42 #endif
michael@0 43 #include <signal.h>
michael@0 44
michael@0 45 #include "event2/bufferevent.h"
michael@0 46 #include "event2/buffer.h"
michael@0 47 #include "event2/event.h"
michael@0 48 #include "event2/util.h"
michael@0 49 #include "event2/listener.h"
michael@0 50 #include "event2/thread.h"
michael@0 51
michael@0 52 #include "../util-internal.h"
michael@0 53
michael@0 54 static int cfg_verbose = 0;
michael@0 55 static int cfg_help = 0;
michael@0 56
michael@0 57 static int cfg_n_connections = 30;
michael@0 58 static int cfg_duration = 5;
michael@0 59 static int cfg_connlimit = 0;
michael@0 60 static int cfg_grouplimit = 0;
michael@0 61 static int cfg_tick_msec = 1000;
michael@0 62 static int cfg_min_share = -1;
michael@0 63
michael@0 64 static int cfg_connlimit_tolerance = -1;
michael@0 65 static int cfg_grouplimit_tolerance = -1;
michael@0 66 static int cfg_stddev_tolerance = -1;
michael@0 67
michael@0 68 #ifdef _WIN32
michael@0 69 static int cfg_enable_iocp = 0;
michael@0 70 #endif
michael@0 71
michael@0 72 static struct timeval cfg_tick = { 0, 500*1000 };
michael@0 73
michael@0 74 static struct ev_token_bucket_cfg *conn_bucket_cfg = NULL;
michael@0 75 static struct ev_token_bucket_cfg *group_bucket_cfg = NULL;
michael@0 76 struct bufferevent_rate_limit_group *ratelim_group = NULL;
michael@0 77 static double seconds_per_tick = 0.0;
michael@0 78
michael@0 79 struct client_state {
michael@0 80 size_t queued;
michael@0 81 ev_uint64_t received;
michael@0 82 };
michael@0 83
michael@0 84 static int n_echo_conns_open = 0;
michael@0 85
michael@0 86 static void
michael@0 87 loud_writecb(struct bufferevent *bev, void *ctx)
michael@0 88 {
michael@0 89 struct client_state *cs = ctx;
michael@0 90 struct evbuffer *output = bufferevent_get_output(bev);
michael@0 91 char buf[1024];
michael@0 92 #ifdef WIN32
michael@0 93 int r = rand() % 256;
michael@0 94 #else
michael@0 95 int r = random() % 256;
michael@0 96 #endif
michael@0 97 memset(buf, r, sizeof(buf));
michael@0 98 while (evbuffer_get_length(output) < 8192) {
michael@0 99 evbuffer_add(output, buf, sizeof(buf));
michael@0 100 cs->queued += sizeof(buf);
michael@0 101 }
michael@0 102 }
michael@0 103
michael@0 104 static void
michael@0 105 discard_readcb(struct bufferevent *bev, void *ctx)
michael@0 106 {
michael@0 107 struct client_state *cs = ctx;
michael@0 108 struct evbuffer *input = bufferevent_get_input(bev);
michael@0 109 size_t len = evbuffer_get_length(input);
michael@0 110 evbuffer_drain(input, len);
michael@0 111 cs->received += len;
michael@0 112 }
michael@0 113
michael@0 114 static void
michael@0 115 write_on_connectedcb(struct bufferevent *bev, short what, void *ctx)
michael@0 116 {
michael@0 117 if (what & BEV_EVENT_CONNECTED) {
michael@0 118 loud_writecb(bev, ctx);
michael@0 119 /* XXXX this shouldn't be needed. */
michael@0 120 bufferevent_enable(bev, EV_READ|EV_WRITE);
michael@0 121 }
michael@0 122 }
michael@0 123
michael@0 124 static void
michael@0 125 echo_readcb(struct bufferevent *bev, void *ctx)
michael@0 126 {
michael@0 127 struct evbuffer *input = bufferevent_get_input(bev);
michael@0 128 struct evbuffer *output = bufferevent_get_output(bev);
michael@0 129
michael@0 130 evbuffer_add_buffer(output, input);
michael@0 131 if (evbuffer_get_length(output) > 1024000)
michael@0 132 bufferevent_disable(bev, EV_READ);
michael@0 133 }
michael@0 134
michael@0 135 static void
michael@0 136 echo_writecb(struct bufferevent *bev, void *ctx)
michael@0 137 {
michael@0 138 struct evbuffer *output = bufferevent_get_output(bev);
michael@0 139 if (evbuffer_get_length(output) < 512000)
michael@0 140 bufferevent_enable(bev, EV_READ);
michael@0 141 }
michael@0 142
michael@0 143 static void
michael@0 144 echo_eventcb(struct bufferevent *bev, short what, void *ctx)
michael@0 145 {
michael@0 146 if (what & (BEV_EVENT_EOF|BEV_EVENT_ERROR)) {
michael@0 147 --n_echo_conns_open;
michael@0 148 bufferevent_free(bev);
michael@0 149 }
michael@0 150 }
michael@0 151
michael@0 152 static void
michael@0 153 echo_listenercb(struct evconnlistener *listener, evutil_socket_t newsock,
michael@0 154 struct sockaddr *sourceaddr, int socklen, void *ctx)
michael@0 155 {
michael@0 156 struct event_base *base = ctx;
michael@0 157 int flags = BEV_OPT_CLOSE_ON_FREE|BEV_OPT_THREADSAFE;
michael@0 158 struct bufferevent *bev;
michael@0 159
michael@0 160 bev = bufferevent_socket_new(base, newsock, flags);
michael@0 161 bufferevent_setcb(bev, echo_readcb, echo_writecb, echo_eventcb, NULL);
michael@0 162 if (conn_bucket_cfg)
michael@0 163 bufferevent_set_rate_limit(bev, conn_bucket_cfg);
michael@0 164 if (ratelim_group)
michael@0 165 bufferevent_add_to_rate_limit_group(bev, ratelim_group);
michael@0 166 ++n_echo_conns_open;
michael@0 167 bufferevent_enable(bev, EV_READ|EV_WRITE);
michael@0 168 }
michael@0 169
michael@0 170 static int
michael@0 171 test_ratelimiting(void)
michael@0 172 {
michael@0 173 struct event_base *base;
michael@0 174 struct sockaddr_in sin;
michael@0 175 struct evconnlistener *listener;
michael@0 176
michael@0 177 struct sockaddr_storage ss;
michael@0 178 ev_socklen_t slen;
michael@0 179
michael@0 180 struct bufferevent **bevs;
michael@0 181 struct client_state *states;
michael@0 182 struct bufferevent_rate_limit_group *group = NULL;
michael@0 183
michael@0 184 int i;
michael@0 185
michael@0 186 struct timeval tv;
michael@0 187
michael@0 188 ev_uint64_t total_received;
michael@0 189 double total_sq_persec, total_persec;
michael@0 190 double variance;
michael@0 191 double expected_total_persec = -1.0, expected_avg_persec = -1.0;
michael@0 192 int ok = 1;
michael@0 193 struct event_config *base_cfg;
michael@0 194
michael@0 195 memset(&sin, 0, sizeof(sin));
michael@0 196 sin.sin_family = AF_INET;
michael@0 197 sin.sin_addr.s_addr = htonl(0x7f000001); /* 127.0.0.1 */
michael@0 198 sin.sin_port = 0; /* unspecified port */
michael@0 199
michael@0 200 if (0)
michael@0 201 event_enable_debug_mode();
michael@0 202
michael@0 203 base_cfg = event_config_new();
michael@0 204
michael@0 205 #ifdef _WIN32
michael@0 206 if (cfg_enable_iocp) {
michael@0 207 evthread_use_windows_threads();
michael@0 208 event_config_set_flag(base_cfg, EVENT_BASE_FLAG_STARTUP_IOCP);
michael@0 209 }
michael@0 210 #endif
michael@0 211
michael@0 212 base = event_base_new_with_config(base_cfg);
michael@0 213 event_config_free(base_cfg);
michael@0 214
michael@0 215 listener = evconnlistener_new_bind(base, echo_listenercb, base,
michael@0 216 LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE, -1,
michael@0 217 (struct sockaddr *)&sin, sizeof(sin));
michael@0 218
michael@0 219 slen = sizeof(ss);
michael@0 220 if (getsockname(evconnlistener_get_fd(listener), (struct sockaddr *)&ss,
michael@0 221 &slen) < 0) {
michael@0 222 perror("getsockname");
michael@0 223 return 1;
michael@0 224 }
michael@0 225
michael@0 226 if (cfg_connlimit > 0) {
michael@0 227 conn_bucket_cfg = ev_token_bucket_cfg_new(
michael@0 228 cfg_connlimit, cfg_connlimit * 4,
michael@0 229 cfg_connlimit, cfg_connlimit * 4,
michael@0 230 &cfg_tick);
michael@0 231 assert(conn_bucket_cfg);
michael@0 232 }
michael@0 233
michael@0 234 if (cfg_grouplimit > 0) {
michael@0 235 group_bucket_cfg = ev_token_bucket_cfg_new(
michael@0 236 cfg_grouplimit, cfg_grouplimit * 4,
michael@0 237 cfg_grouplimit, cfg_grouplimit * 4,
michael@0 238 &cfg_tick);
michael@0 239 group = ratelim_group = bufferevent_rate_limit_group_new(
michael@0 240 base, group_bucket_cfg);
michael@0 241 expected_total_persec = cfg_grouplimit;
michael@0 242 expected_avg_persec = cfg_grouplimit / cfg_n_connections;
michael@0 243 if (cfg_connlimit > 0 && expected_avg_persec > cfg_connlimit)
michael@0 244 expected_avg_persec = cfg_connlimit;
michael@0 245 if (cfg_min_share >= 0)
michael@0 246 bufferevent_rate_limit_group_set_min_share(
michael@0 247 ratelim_group, cfg_min_share);
michael@0 248 }
michael@0 249
michael@0 250 if (expected_avg_persec < 0 && cfg_connlimit > 0)
michael@0 251 expected_avg_persec = cfg_connlimit;
michael@0 252
michael@0 253 if (expected_avg_persec > 0)
michael@0 254 expected_avg_persec /= seconds_per_tick;
michael@0 255 if (expected_total_persec > 0)
michael@0 256 expected_total_persec /= seconds_per_tick;
michael@0 257
michael@0 258 bevs = calloc(cfg_n_connections, sizeof(struct bufferevent *));
michael@0 259 states = calloc(cfg_n_connections, sizeof(struct client_state));
michael@0 260
michael@0 261 for (i = 0; i < cfg_n_connections; ++i) {
michael@0 262 bevs[i] = bufferevent_socket_new(base, -1,
michael@0 263 BEV_OPT_CLOSE_ON_FREE|BEV_OPT_THREADSAFE);
michael@0 264 assert(bevs[i]);
michael@0 265 bufferevent_setcb(bevs[i], discard_readcb, loud_writecb,
michael@0 266 write_on_connectedcb, &states[i]);
michael@0 267 bufferevent_enable(bevs[i], EV_READ|EV_WRITE);
michael@0 268 bufferevent_socket_connect(bevs[i], (struct sockaddr *)&ss,
michael@0 269 slen);
michael@0 270 }
michael@0 271
michael@0 272 tv.tv_sec = cfg_duration - 1;
michael@0 273 tv.tv_usec = 995000;
michael@0 274
michael@0 275 event_base_loopexit(base, &tv);
michael@0 276
michael@0 277 event_base_dispatch(base);
michael@0 278
michael@0 279 ratelim_group = NULL; /* So no more responders get added */
michael@0 280
michael@0 281 for (i = 0; i < cfg_n_connections; ++i) {
michael@0 282 bufferevent_free(bevs[i]);
michael@0 283 }
michael@0 284 evconnlistener_free(listener);
michael@0 285
michael@0 286 /* Make sure no new echo_conns get added to the group. */
michael@0 287 ratelim_group = NULL;
michael@0 288
michael@0 289 /* This should get _everybody_ freed */
michael@0 290 while (n_echo_conns_open) {
michael@0 291 printf("waiting for %d conns\n", n_echo_conns_open);
michael@0 292 tv.tv_sec = 0;
michael@0 293 tv.tv_usec = 300000;
michael@0 294 event_base_loopexit(base, &tv);
michael@0 295 event_base_dispatch(base);
michael@0 296 }
michael@0 297
michael@0 298 if (group)
michael@0 299 bufferevent_rate_limit_group_free(group);
michael@0 300
michael@0 301 total_received = 0;
michael@0 302 total_persec = 0.0;
michael@0 303 total_sq_persec = 0.0;
michael@0 304 for (i=0; i < cfg_n_connections; ++i) {
michael@0 305 double persec = states[i].received;
michael@0 306 persec /= cfg_duration;
michael@0 307 total_received += states[i].received;
michael@0 308 total_persec += persec;
michael@0 309 total_sq_persec += persec*persec;
michael@0 310 printf("%d: %f per second\n", i+1, persec);
michael@0 311 }
michael@0 312 printf(" total: %f per second\n",
michael@0 313 ((double)total_received)/cfg_duration);
michael@0 314 if (expected_total_persec > 0) {
michael@0 315 double diff = expected_total_persec -
michael@0 316 ((double)total_received/cfg_duration);
michael@0 317 printf(" [Off by %lf]\n", diff);
michael@0 318 if (cfg_grouplimit_tolerance > 0 &&
michael@0 319 fabs(diff) > cfg_grouplimit_tolerance) {
michael@0 320 fprintf(stderr, "Group bandwidth out of bounds\n");
michael@0 321 ok = 0;
michael@0 322 }
michael@0 323 }
michael@0 324
michael@0 325 printf(" average: %f per second\n",
michael@0 326 (((double)total_received)/cfg_duration)/cfg_n_connections);
michael@0 327 if (expected_avg_persec > 0) {
michael@0 328 double diff = expected_avg_persec - (((double)total_received)/cfg_duration)/cfg_n_connections;
michael@0 329 printf(" [Off by %lf]\n", diff);
michael@0 330 if (cfg_connlimit_tolerance > 0 &&
michael@0 331 fabs(diff) > cfg_connlimit_tolerance) {
michael@0 332 fprintf(stderr, "Connection bandwidth out of bounds\n");
michael@0 333 ok = 0;
michael@0 334 }
michael@0 335 }
michael@0 336
michael@0 337 variance = total_sq_persec/cfg_n_connections - total_persec*total_persec/(cfg_n_connections*cfg_n_connections);
michael@0 338
michael@0 339 printf(" stddev: %f per second\n", sqrt(variance));
michael@0 340 if (cfg_stddev_tolerance > 0 &&
michael@0 341 sqrt(variance) > cfg_stddev_tolerance) {
michael@0 342 fprintf(stderr, "Connection variance out of bounds\n");
michael@0 343 ok = 0;
michael@0 344 }
michael@0 345
michael@0 346 event_base_free(base);
michael@0 347 free(bevs);
michael@0 348 free(states);
michael@0 349
michael@0 350 return ok ? 0 : 1;
michael@0 351 }
michael@0 352
michael@0 353 static struct option {
michael@0 354 const char *name; int *ptr; int min; int isbool;
michael@0 355 } options[] = {
michael@0 356 { "-v", &cfg_verbose, 0, 1 },
michael@0 357 { "-h", &cfg_help, 0, 1 },
michael@0 358 { "-n", &cfg_n_connections, 1, 0 },
michael@0 359 { "-d", &cfg_duration, 1, 0 },
michael@0 360 { "-c", &cfg_connlimit, 0, 0 },
michael@0 361 { "-g", &cfg_grouplimit, 0, 0 },
michael@0 362 { "-t", &cfg_tick_msec, 10, 0 },
michael@0 363 { "--min-share", &cfg_min_share, 0, 0 },
michael@0 364 { "--check-connlimit", &cfg_connlimit_tolerance, 0, 0 },
michael@0 365 { "--check-grouplimit", &cfg_grouplimit_tolerance, 0, 0 },
michael@0 366 { "--check-stddev", &cfg_stddev_tolerance, 0, 0 },
michael@0 367 #ifdef _WIN32
michael@0 368 { "--iocp", &cfg_enable_iocp, 0, 1 },
michael@0 369 #endif
michael@0 370 { NULL, NULL, -1, 0 },
michael@0 371 };
michael@0 372
michael@0 373 static int
michael@0 374 handle_option(int argc, char **argv, int *i, const struct option *opt)
michael@0 375 {
michael@0 376 long val;
michael@0 377 char *endptr = NULL;
michael@0 378 if (opt->isbool) {
michael@0 379 *opt->ptr = 1;
michael@0 380 return 0;
michael@0 381 }
michael@0 382 if (*i + 1 == argc) {
michael@0 383 fprintf(stderr, "Too few arguments to '%s'\n",argv[*i]);
michael@0 384 return -1;
michael@0 385 }
michael@0 386 val = strtol(argv[*i+1], &endptr, 10);
michael@0 387 if (*argv[*i+1] == '\0' || !endptr || *endptr != '\0') {
michael@0 388 fprintf(stderr, "Couldn't parse numeric value '%s'\n",
michael@0 389 argv[*i+1]);
michael@0 390 return -1;
michael@0 391 }
michael@0 392 if (val < opt->min || val > 0x7fffffff) {
michael@0 393 fprintf(stderr, "Value '%s' is out-of-range'\n",
michael@0 394 argv[*i+1]);
michael@0 395 return -1;
michael@0 396 }
michael@0 397 *opt->ptr = (int)val;
michael@0 398 ++*i;
michael@0 399 return 0;
michael@0 400 }
michael@0 401
michael@0 402 static void
michael@0 403 usage(void)
michael@0 404 {
michael@0 405 fprintf(stderr,
michael@0 406 "test-ratelim [-v] [-n INT] [-d INT] [-c INT] [-g INT] [-t INT]\n\n"
michael@0 407 "Pushes bytes through a number of possibly rate-limited connections, and\n"
michael@0 408 "displays average throughput.\n\n"
michael@0 409 " -n INT: Number of connections to open (default: 30)\n"
michael@0 410 " -d INT: Duration of the test in seconds (default: 5 sec)\n");
michael@0 411 fprintf(stderr,
michael@0 412 " -c INT: Connection-rate limit applied to each connection in bytes per second\n"
michael@0 413 " (default: None.)\n"
michael@0 414 " -g INT: Group-rate limit applied to sum of all usage in bytes per second\n"
michael@0 415 " (default: None.)\n"
michael@0 416 " -t INT: Granularity of timing, in milliseconds (default: 1000 msec)\n");
michael@0 417 }
michael@0 418
michael@0 419 int
michael@0 420 main(int argc, char **argv)
michael@0 421 {
michael@0 422 int i,j;
michael@0 423 double ratio;
michael@0 424
michael@0 425 #ifdef WIN32
michael@0 426 WORD wVersionRequested = MAKEWORD(2,2);
michael@0 427 WSADATA wsaData;
michael@0 428
michael@0 429 (void) WSAStartup(wVersionRequested, &wsaData);
michael@0 430 #endif
michael@0 431
michael@0 432 #ifndef WIN32
michael@0 433 if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
michael@0 434 return 1;
michael@0 435 #endif
michael@0 436 for (i = 1; i < argc; ++i) {
michael@0 437 for (j = 0; options[j].name; ++j) {
michael@0 438 if (!strcmp(argv[i],options[j].name)) {
michael@0 439 if (handle_option(argc,argv,&i,&options[j])<0)
michael@0 440 return 1;
michael@0 441 goto again;
michael@0 442 }
michael@0 443 }
michael@0 444 fprintf(stderr, "Unknown option '%s'\n", argv[i]);
michael@0 445 usage();
michael@0 446 return 1;
michael@0 447 again:
michael@0 448 ;
michael@0 449 }
michael@0 450 if (cfg_help) {
michael@0 451 usage();
michael@0 452 return 0;
michael@0 453 }
michael@0 454
michael@0 455 cfg_tick.tv_sec = cfg_tick_msec / 1000;
michael@0 456 cfg_tick.tv_usec = (cfg_tick_msec % 1000)*1000;
michael@0 457
michael@0 458 seconds_per_tick = ratio = cfg_tick_msec / 1000.0;
michael@0 459
michael@0 460 cfg_connlimit *= ratio;
michael@0 461 cfg_grouplimit *= ratio;
michael@0 462
michael@0 463 {
michael@0 464 struct timeval tv;
michael@0 465 evutil_gettimeofday(&tv, NULL);
michael@0 466 #ifdef WIN32
michael@0 467 srand(tv.tv_usec);
michael@0 468 #else
michael@0 469 srandom(tv.tv_usec);
michael@0 470 #endif
michael@0 471 }
michael@0 472
michael@0 473 #ifndef _EVENT_DISABLE_THREAD_SUPPORT
michael@0 474 evthread_enable_lock_debuging();
michael@0 475 #endif
michael@0 476
michael@0 477 return test_ratelimiting();
michael@0 478 }

mercurial