1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/ipc/chromium/src/third_party/libevent/test/test-ratelim.c Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,478 @@ 1.4 +/* 1.5 + * Copyright (c) 2009-2012 Niels Provos and Nick Mathewson 1.6 + * 1.7 + * Redistribution and use in source and binary forms, with or without 1.8 + * modification, are permitted provided that the following conditions 1.9 + * are met: 1.10 + * 1. Redistributions of source code must retain the above copyright 1.11 + * notice, this list of conditions and the following disclaimer. 1.12 + * 2. Redistributions in binary form must reproduce the above copyright 1.13 + * notice, this list of conditions and the following disclaimer in the 1.14 + * documentation and/or other materials provided with the distribution. 1.15 + * 3. The name of the author may not be used to endorse or promote products 1.16 + * derived from this software without specific prior written permission. 1.17 + * 1.18 + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1.19 + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1.20 + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 1.21 + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 1.22 + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 1.23 + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 1.24 + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 1.25 + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 1.26 + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 1.27 + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 1.28 + */ 1.29 + 1.30 +#include <stdio.h> 1.31 +#include <stdlib.h> 1.32 +#include <string.h> 1.33 +#include <assert.h> 1.34 +#include <math.h> 1.35 + 1.36 +#ifdef WIN32 1.37 +#include <winsock2.h> 1.38 +#include <ws2tcpip.h> 1.39 +#else 1.40 +#include <sys/socket.h> 1.41 +#include <netinet/in.h> 1.42 +# ifdef _XOPEN_SOURCE_EXTENDED 1.43 +# include <arpa/inet.h> 1.44 +# endif 1.45 +#endif 1.46 +#include <signal.h> 1.47 + 1.48 +#include "event2/bufferevent.h" 1.49 +#include "event2/buffer.h" 1.50 +#include "event2/event.h" 1.51 +#include "event2/util.h" 1.52 +#include "event2/listener.h" 1.53 +#include "event2/thread.h" 1.54 + 1.55 +#include "../util-internal.h" 1.56 + 1.57 +static int cfg_verbose = 0; 1.58 +static int cfg_help = 0; 1.59 + 1.60 +static int cfg_n_connections = 30; 1.61 +static int cfg_duration = 5; 1.62 +static int cfg_connlimit = 0; 1.63 +static int cfg_grouplimit = 0; 1.64 +static int cfg_tick_msec = 1000; 1.65 +static int cfg_min_share = -1; 1.66 + 1.67 +static int cfg_connlimit_tolerance = -1; 1.68 +static int cfg_grouplimit_tolerance = -1; 1.69 +static int cfg_stddev_tolerance = -1; 1.70 + 1.71 +#ifdef _WIN32 1.72 +static int cfg_enable_iocp = 0; 1.73 +#endif 1.74 + 1.75 +static struct timeval cfg_tick = { 0, 500*1000 }; 1.76 + 1.77 +static struct ev_token_bucket_cfg *conn_bucket_cfg = NULL; 1.78 +static struct ev_token_bucket_cfg *group_bucket_cfg = NULL; 1.79 +struct bufferevent_rate_limit_group *ratelim_group = NULL; 1.80 +static double seconds_per_tick = 0.0; 1.81 + 1.82 +struct client_state { 1.83 + size_t queued; 1.84 + ev_uint64_t received; 1.85 +}; 1.86 + 1.87 +static int n_echo_conns_open = 0; 1.88 + 1.89 +static void 1.90 +loud_writecb(struct bufferevent *bev, void *ctx) 1.91 +{ 1.92 + struct client_state *cs = ctx; 1.93 + struct evbuffer *output = bufferevent_get_output(bev); 1.94 + char buf[1024]; 1.95 +#ifdef WIN32 1.96 + int r = rand() % 256; 1.97 +#else 1.98 + int r = random() % 256; 1.99 +#endif 1.100 + memset(buf, r, sizeof(buf)); 1.101 + while (evbuffer_get_length(output) < 8192) { 1.102 + evbuffer_add(output, buf, sizeof(buf)); 1.103 + cs->queued += sizeof(buf); 1.104 + } 1.105 +} 1.106 + 1.107 +static void 1.108 +discard_readcb(struct bufferevent *bev, void *ctx) 1.109 +{ 1.110 + struct client_state *cs = ctx; 1.111 + struct evbuffer *input = bufferevent_get_input(bev); 1.112 + size_t len = evbuffer_get_length(input); 1.113 + evbuffer_drain(input, len); 1.114 + cs->received += len; 1.115 +} 1.116 + 1.117 +static void 1.118 +write_on_connectedcb(struct bufferevent *bev, short what, void *ctx) 1.119 +{ 1.120 + if (what & BEV_EVENT_CONNECTED) { 1.121 + loud_writecb(bev, ctx); 1.122 + /* XXXX this shouldn't be needed. */ 1.123 + bufferevent_enable(bev, EV_READ|EV_WRITE); 1.124 + } 1.125 +} 1.126 + 1.127 +static void 1.128 +echo_readcb(struct bufferevent *bev, void *ctx) 1.129 +{ 1.130 + struct evbuffer *input = bufferevent_get_input(bev); 1.131 + struct evbuffer *output = bufferevent_get_output(bev); 1.132 + 1.133 + evbuffer_add_buffer(output, input); 1.134 + if (evbuffer_get_length(output) > 1024000) 1.135 + bufferevent_disable(bev, EV_READ); 1.136 +} 1.137 + 1.138 +static void 1.139 +echo_writecb(struct bufferevent *bev, void *ctx) 1.140 +{ 1.141 + struct evbuffer *output = bufferevent_get_output(bev); 1.142 + if (evbuffer_get_length(output) < 512000) 1.143 + bufferevent_enable(bev, EV_READ); 1.144 +} 1.145 + 1.146 +static void 1.147 +echo_eventcb(struct bufferevent *bev, short what, void *ctx) 1.148 +{ 1.149 + if (what & (BEV_EVENT_EOF|BEV_EVENT_ERROR)) { 1.150 + --n_echo_conns_open; 1.151 + bufferevent_free(bev); 1.152 + } 1.153 +} 1.154 + 1.155 +static void 1.156 +echo_listenercb(struct evconnlistener *listener, evutil_socket_t newsock, 1.157 + struct sockaddr *sourceaddr, int socklen, void *ctx) 1.158 +{ 1.159 + struct event_base *base = ctx; 1.160 + int flags = BEV_OPT_CLOSE_ON_FREE|BEV_OPT_THREADSAFE; 1.161 + struct bufferevent *bev; 1.162 + 1.163 + bev = bufferevent_socket_new(base, newsock, flags); 1.164 + bufferevent_setcb(bev, echo_readcb, echo_writecb, echo_eventcb, NULL); 1.165 + if (conn_bucket_cfg) 1.166 + bufferevent_set_rate_limit(bev, conn_bucket_cfg); 1.167 + if (ratelim_group) 1.168 + bufferevent_add_to_rate_limit_group(bev, ratelim_group); 1.169 + ++n_echo_conns_open; 1.170 + bufferevent_enable(bev, EV_READ|EV_WRITE); 1.171 +} 1.172 + 1.173 +static int 1.174 +test_ratelimiting(void) 1.175 +{ 1.176 + struct event_base *base; 1.177 + struct sockaddr_in sin; 1.178 + struct evconnlistener *listener; 1.179 + 1.180 + struct sockaddr_storage ss; 1.181 + ev_socklen_t slen; 1.182 + 1.183 + struct bufferevent **bevs; 1.184 + struct client_state *states; 1.185 + struct bufferevent_rate_limit_group *group = NULL; 1.186 + 1.187 + int i; 1.188 + 1.189 + struct timeval tv; 1.190 + 1.191 + ev_uint64_t total_received; 1.192 + double total_sq_persec, total_persec; 1.193 + double variance; 1.194 + double expected_total_persec = -1.0, expected_avg_persec = -1.0; 1.195 + int ok = 1; 1.196 + struct event_config *base_cfg; 1.197 + 1.198 + memset(&sin, 0, sizeof(sin)); 1.199 + sin.sin_family = AF_INET; 1.200 + sin.sin_addr.s_addr = htonl(0x7f000001); /* 127.0.0.1 */ 1.201 + sin.sin_port = 0; /* unspecified port */ 1.202 + 1.203 + if (0) 1.204 + event_enable_debug_mode(); 1.205 + 1.206 + base_cfg = event_config_new(); 1.207 + 1.208 +#ifdef _WIN32 1.209 + if (cfg_enable_iocp) { 1.210 + evthread_use_windows_threads(); 1.211 + event_config_set_flag(base_cfg, EVENT_BASE_FLAG_STARTUP_IOCP); 1.212 + } 1.213 +#endif 1.214 + 1.215 + base = event_base_new_with_config(base_cfg); 1.216 + event_config_free(base_cfg); 1.217 + 1.218 + listener = evconnlistener_new_bind(base, echo_listenercb, base, 1.219 + LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE, -1, 1.220 + (struct sockaddr *)&sin, sizeof(sin)); 1.221 + 1.222 + slen = sizeof(ss); 1.223 + if (getsockname(evconnlistener_get_fd(listener), (struct sockaddr *)&ss, 1.224 + &slen) < 0) { 1.225 + perror("getsockname"); 1.226 + return 1; 1.227 + } 1.228 + 1.229 + if (cfg_connlimit > 0) { 1.230 + conn_bucket_cfg = ev_token_bucket_cfg_new( 1.231 + cfg_connlimit, cfg_connlimit * 4, 1.232 + cfg_connlimit, cfg_connlimit * 4, 1.233 + &cfg_tick); 1.234 + assert(conn_bucket_cfg); 1.235 + } 1.236 + 1.237 + if (cfg_grouplimit > 0) { 1.238 + group_bucket_cfg = ev_token_bucket_cfg_new( 1.239 + cfg_grouplimit, cfg_grouplimit * 4, 1.240 + cfg_grouplimit, cfg_grouplimit * 4, 1.241 + &cfg_tick); 1.242 + group = ratelim_group = bufferevent_rate_limit_group_new( 1.243 + base, group_bucket_cfg); 1.244 + expected_total_persec = cfg_grouplimit; 1.245 + expected_avg_persec = cfg_grouplimit / cfg_n_connections; 1.246 + if (cfg_connlimit > 0 && expected_avg_persec > cfg_connlimit) 1.247 + expected_avg_persec = cfg_connlimit; 1.248 + if (cfg_min_share >= 0) 1.249 + bufferevent_rate_limit_group_set_min_share( 1.250 + ratelim_group, cfg_min_share); 1.251 + } 1.252 + 1.253 + if (expected_avg_persec < 0 && cfg_connlimit > 0) 1.254 + expected_avg_persec = cfg_connlimit; 1.255 + 1.256 + if (expected_avg_persec > 0) 1.257 + expected_avg_persec /= seconds_per_tick; 1.258 + if (expected_total_persec > 0) 1.259 + expected_total_persec /= seconds_per_tick; 1.260 + 1.261 + bevs = calloc(cfg_n_connections, sizeof(struct bufferevent *)); 1.262 + states = calloc(cfg_n_connections, sizeof(struct client_state)); 1.263 + 1.264 + for (i = 0; i < cfg_n_connections; ++i) { 1.265 + bevs[i] = bufferevent_socket_new(base, -1, 1.266 + BEV_OPT_CLOSE_ON_FREE|BEV_OPT_THREADSAFE); 1.267 + assert(bevs[i]); 1.268 + bufferevent_setcb(bevs[i], discard_readcb, loud_writecb, 1.269 + write_on_connectedcb, &states[i]); 1.270 + bufferevent_enable(bevs[i], EV_READ|EV_WRITE); 1.271 + bufferevent_socket_connect(bevs[i], (struct sockaddr *)&ss, 1.272 + slen); 1.273 + } 1.274 + 1.275 + tv.tv_sec = cfg_duration - 1; 1.276 + tv.tv_usec = 995000; 1.277 + 1.278 + event_base_loopexit(base, &tv); 1.279 + 1.280 + event_base_dispatch(base); 1.281 + 1.282 + ratelim_group = NULL; /* So no more responders get added */ 1.283 + 1.284 + for (i = 0; i < cfg_n_connections; ++i) { 1.285 + bufferevent_free(bevs[i]); 1.286 + } 1.287 + evconnlistener_free(listener); 1.288 + 1.289 + /* Make sure no new echo_conns get added to the group. */ 1.290 + ratelim_group = NULL; 1.291 + 1.292 + /* This should get _everybody_ freed */ 1.293 + while (n_echo_conns_open) { 1.294 + printf("waiting for %d conns\n", n_echo_conns_open); 1.295 + tv.tv_sec = 0; 1.296 + tv.tv_usec = 300000; 1.297 + event_base_loopexit(base, &tv); 1.298 + event_base_dispatch(base); 1.299 + } 1.300 + 1.301 + if (group) 1.302 + bufferevent_rate_limit_group_free(group); 1.303 + 1.304 + total_received = 0; 1.305 + total_persec = 0.0; 1.306 + total_sq_persec = 0.0; 1.307 + for (i=0; i < cfg_n_connections; ++i) { 1.308 + double persec = states[i].received; 1.309 + persec /= cfg_duration; 1.310 + total_received += states[i].received; 1.311 + total_persec += persec; 1.312 + total_sq_persec += persec*persec; 1.313 + printf("%d: %f per second\n", i+1, persec); 1.314 + } 1.315 + printf(" total: %f per second\n", 1.316 + ((double)total_received)/cfg_duration); 1.317 + if (expected_total_persec > 0) { 1.318 + double diff = expected_total_persec - 1.319 + ((double)total_received/cfg_duration); 1.320 + printf(" [Off by %lf]\n", diff); 1.321 + if (cfg_grouplimit_tolerance > 0 && 1.322 + fabs(diff) > cfg_grouplimit_tolerance) { 1.323 + fprintf(stderr, "Group bandwidth out of bounds\n"); 1.324 + ok = 0; 1.325 + } 1.326 + } 1.327 + 1.328 + printf(" average: %f per second\n", 1.329 + (((double)total_received)/cfg_duration)/cfg_n_connections); 1.330 + if (expected_avg_persec > 0) { 1.331 + double diff = expected_avg_persec - (((double)total_received)/cfg_duration)/cfg_n_connections; 1.332 + printf(" [Off by %lf]\n", diff); 1.333 + if (cfg_connlimit_tolerance > 0 && 1.334 + fabs(diff) > cfg_connlimit_tolerance) { 1.335 + fprintf(stderr, "Connection bandwidth out of bounds\n"); 1.336 + ok = 0; 1.337 + } 1.338 + } 1.339 + 1.340 + variance = total_sq_persec/cfg_n_connections - total_persec*total_persec/(cfg_n_connections*cfg_n_connections); 1.341 + 1.342 + printf(" stddev: %f per second\n", sqrt(variance)); 1.343 + if (cfg_stddev_tolerance > 0 && 1.344 + sqrt(variance) > cfg_stddev_tolerance) { 1.345 + fprintf(stderr, "Connection variance out of bounds\n"); 1.346 + ok = 0; 1.347 + } 1.348 + 1.349 + event_base_free(base); 1.350 + free(bevs); 1.351 + free(states); 1.352 + 1.353 + return ok ? 0 : 1; 1.354 +} 1.355 + 1.356 +static struct option { 1.357 + const char *name; int *ptr; int min; int isbool; 1.358 +} options[] = { 1.359 + { "-v", &cfg_verbose, 0, 1 }, 1.360 + { "-h", &cfg_help, 0, 1 }, 1.361 + { "-n", &cfg_n_connections, 1, 0 }, 1.362 + { "-d", &cfg_duration, 1, 0 }, 1.363 + { "-c", &cfg_connlimit, 0, 0 }, 1.364 + { "-g", &cfg_grouplimit, 0, 0 }, 1.365 + { "-t", &cfg_tick_msec, 10, 0 }, 1.366 + { "--min-share", &cfg_min_share, 0, 0 }, 1.367 + { "--check-connlimit", &cfg_connlimit_tolerance, 0, 0 }, 1.368 + { "--check-grouplimit", &cfg_grouplimit_tolerance, 0, 0 }, 1.369 + { "--check-stddev", &cfg_stddev_tolerance, 0, 0 }, 1.370 +#ifdef _WIN32 1.371 + { "--iocp", &cfg_enable_iocp, 0, 1 }, 1.372 +#endif 1.373 + { NULL, NULL, -1, 0 }, 1.374 +}; 1.375 + 1.376 +static int 1.377 +handle_option(int argc, char **argv, int *i, const struct option *opt) 1.378 +{ 1.379 + long val; 1.380 + char *endptr = NULL; 1.381 + if (opt->isbool) { 1.382 + *opt->ptr = 1; 1.383 + return 0; 1.384 + } 1.385 + if (*i + 1 == argc) { 1.386 + fprintf(stderr, "Too few arguments to '%s'\n",argv[*i]); 1.387 + return -1; 1.388 + } 1.389 + val = strtol(argv[*i+1], &endptr, 10); 1.390 + if (*argv[*i+1] == '\0' || !endptr || *endptr != '\0') { 1.391 + fprintf(stderr, "Couldn't parse numeric value '%s'\n", 1.392 + argv[*i+1]); 1.393 + return -1; 1.394 + } 1.395 + if (val < opt->min || val > 0x7fffffff) { 1.396 + fprintf(stderr, "Value '%s' is out-of-range'\n", 1.397 + argv[*i+1]); 1.398 + return -1; 1.399 + } 1.400 + *opt->ptr = (int)val; 1.401 + ++*i; 1.402 + return 0; 1.403 +} 1.404 + 1.405 +static void 1.406 +usage(void) 1.407 +{ 1.408 + fprintf(stderr, 1.409 +"test-ratelim [-v] [-n INT] [-d INT] [-c INT] [-g INT] [-t INT]\n\n" 1.410 +"Pushes bytes through a number of possibly rate-limited connections, and\n" 1.411 +"displays average throughput.\n\n" 1.412 +" -n INT: Number of connections to open (default: 30)\n" 1.413 +" -d INT: Duration of the test in seconds (default: 5 sec)\n"); 1.414 + fprintf(stderr, 1.415 +" -c INT: Connection-rate limit applied to each connection in bytes per second\n" 1.416 +" (default: None.)\n" 1.417 +" -g INT: Group-rate limit applied to sum of all usage in bytes per second\n" 1.418 +" (default: None.)\n" 1.419 +" -t INT: Granularity of timing, in milliseconds (default: 1000 msec)\n"); 1.420 +} 1.421 + 1.422 +int 1.423 +main(int argc, char **argv) 1.424 +{ 1.425 + int i,j; 1.426 + double ratio; 1.427 + 1.428 +#ifdef WIN32 1.429 + WORD wVersionRequested = MAKEWORD(2,2); 1.430 + WSADATA wsaData; 1.431 + 1.432 + (void) WSAStartup(wVersionRequested, &wsaData); 1.433 +#endif 1.434 + 1.435 +#ifndef WIN32 1.436 + if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) 1.437 + return 1; 1.438 +#endif 1.439 + for (i = 1; i < argc; ++i) { 1.440 + for (j = 0; options[j].name; ++j) { 1.441 + if (!strcmp(argv[i],options[j].name)) { 1.442 + if (handle_option(argc,argv,&i,&options[j])<0) 1.443 + return 1; 1.444 + goto again; 1.445 + } 1.446 + } 1.447 + fprintf(stderr, "Unknown option '%s'\n", argv[i]); 1.448 + usage(); 1.449 + return 1; 1.450 + again: 1.451 + ; 1.452 + } 1.453 + if (cfg_help) { 1.454 + usage(); 1.455 + return 0; 1.456 + } 1.457 + 1.458 + cfg_tick.tv_sec = cfg_tick_msec / 1000; 1.459 + cfg_tick.tv_usec = (cfg_tick_msec % 1000)*1000; 1.460 + 1.461 + seconds_per_tick = ratio = cfg_tick_msec / 1000.0; 1.462 + 1.463 + cfg_connlimit *= ratio; 1.464 + cfg_grouplimit *= ratio; 1.465 + 1.466 + { 1.467 + struct timeval tv; 1.468 + evutil_gettimeofday(&tv, NULL); 1.469 +#ifdef WIN32 1.470 + srand(tv.tv_usec); 1.471 +#else 1.472 + srandom(tv.tv_usec); 1.473 +#endif 1.474 + } 1.475 + 1.476 +#ifndef _EVENT_DISABLE_THREAD_SUPPORT 1.477 + evthread_enable_lock_debuging(); 1.478 +#endif 1.479 + 1.480 + return test_ratelimiting(); 1.481 +}