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

changeset 0
6474c204b198
     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 +}

mercurial