1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/nsprpub/pr/tests/thruput.c Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,379 @@ 1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +/* 1.10 +** File: thruput.c 1.11 +** Description: Test server's throughput capability comparing various 1.12 +** implmentation strategies. 1.13 +** 1.14 +** Note: Requires a server machine and an aribitrary number of 1.15 +** clients to bang on it. Trust the numbers on the server 1.16 +** more than those being displayed by the various clients. 1.17 +*/ 1.18 + 1.19 +#include "prerror.h" 1.20 +#include "prinrval.h" 1.21 +#include "prinit.h" 1.22 +#include "prio.h" 1.23 +#include "prlock.h" 1.24 +#include "prmem.h" 1.25 +#include "prnetdb.h" 1.26 +#include "prprf.h" 1.27 +#include "prthread.h" 1.28 +#include "pprio.h" 1.29 +#include "plerror.h" 1.30 +#include "plgetopt.h" 1.31 + 1.32 +#define ADDR_BUFFER 100 1.33 +#define PORT_NUMBER 51877 1.34 +#define SAMPLING_INTERVAL 10 1.35 +#define BUFFER_SIZE (32 * 1024) 1.36 + 1.37 +static PRInt32 domain = PR_AF_INET; 1.38 +static PRInt32 protocol = 6; /* TCP */ 1.39 +static PRFileDesc *err = NULL; 1.40 +static PRIntn concurrency = 1; 1.41 +static PRInt32 xport_buffer = -1; 1.42 +static PRUint32 initial_streams = 1; 1.43 +static PRInt32 buffer_size = BUFFER_SIZE; 1.44 +static PRThreadScope thread_scope = PR_LOCAL_THREAD; 1.45 + 1.46 +typedef struct Shared 1.47 +{ 1.48 + PRLock *ml; 1.49 + PRUint32 sampled; 1.50 + PRUint32 threads; 1.51 + PRIntervalTime timein; 1.52 + PRNetAddr server_address; 1.53 +} Shared; 1.54 + 1.55 +static Shared *shared = NULL; 1.56 + 1.57 +static PRStatus PrintAddress(const PRNetAddr* address) 1.58 +{ 1.59 + char buffer[ADDR_BUFFER]; 1.60 + PRStatus rv = PR_NetAddrToString(address, buffer, sizeof(buffer)); 1.61 + if (PR_SUCCESS == rv) 1.62 + PR_fprintf(err, "%s:%u\n", buffer, PR_ntohs(address->inet.port)); 1.63 + else PL_FPrintError(err, "PR_NetAddrToString"); 1.64 + return rv; 1.65 +} /* PrintAddress */ 1.66 + 1.67 + 1.68 +static void PR_CALLBACK Clientel(void *arg) 1.69 +{ 1.70 + PRStatus rv; 1.71 + PRFileDesc *xport; 1.72 + PRInt32 bytes, sampled; 1.73 + PRIntervalTime now, interval; 1.74 + PRBool do_display = PR_FALSE; 1.75 + Shared *shared = (Shared*)arg; 1.76 + char *buffer = (char*)PR_Malloc(buffer_size); 1.77 + PRNetAddr *server_address = &shared->server_address; 1.78 + PRIntervalTime connect_timeout = PR_SecondsToInterval(5); 1.79 + PRIntervalTime sampling_interval = PR_SecondsToInterval(SAMPLING_INTERVAL); 1.80 + 1.81 + PR_fprintf(err, "Client connecting to "); 1.82 + (void)PrintAddress(server_address); 1.83 + 1.84 + do 1.85 + { 1.86 + xport = PR_Socket(domain, PR_SOCK_STREAM, protocol); 1.87 + if (NULL == xport) 1.88 + { 1.89 + PL_FPrintError(err, "PR_Socket"); 1.90 + return; 1.91 + } 1.92 + 1.93 + if (xport_buffer != -1) 1.94 + { 1.95 + PRSocketOptionData data; 1.96 + data.option = PR_SockOpt_RecvBufferSize; 1.97 + data.value.recv_buffer_size = (PRSize)xport_buffer; 1.98 + rv = PR_SetSocketOption(xport, &data); 1.99 + if (PR_FAILURE == rv) 1.100 + PL_FPrintError(err, "PR_SetSocketOption - ignored"); 1.101 + data.option = PR_SockOpt_SendBufferSize; 1.102 + data.value.send_buffer_size = (PRSize)xport_buffer; 1.103 + rv = PR_SetSocketOption(xport, &data); 1.104 + if (PR_FAILURE == rv) 1.105 + PL_FPrintError(err, "PR_SetSocketOption - ignored"); 1.106 + } 1.107 + 1.108 + rv = PR_Connect(xport, server_address, connect_timeout); 1.109 + if (PR_FAILURE == rv) 1.110 + { 1.111 + PL_FPrintError(err, "PR_Connect"); 1.112 + if (PR_IO_TIMEOUT_ERROR != PR_GetError()) 1.113 + PR_Sleep(connect_timeout); 1.114 + PR_Close(xport); /* delete it and start over */ 1.115 + } 1.116 + } while (PR_FAILURE == rv); 1.117 + 1.118 + do 1.119 + { 1.120 + bytes = PR_Recv( 1.121 + xport, buffer, buffer_size, 0, PR_INTERVAL_NO_TIMEOUT); 1.122 + PR_Lock(shared->ml); 1.123 + now = PR_IntervalNow(); 1.124 + shared->sampled += bytes; 1.125 + interval = now - shared->timein; 1.126 + if (interval > sampling_interval) 1.127 + { 1.128 + sampled = shared->sampled; 1.129 + shared->timein = now; 1.130 + shared->sampled = 0; 1.131 + do_display = PR_TRUE; 1.132 + } 1.133 + PR_Unlock(shared->ml); 1.134 + 1.135 + if (do_display) 1.136 + { 1.137 + PRUint32 rate = sampled / PR_IntervalToMilliseconds(interval); 1.138 + PR_fprintf(err, "%u streams @ %u Kbytes/sec\n", shared->threads, rate); 1.139 + do_display = PR_FALSE; 1.140 + } 1.141 + 1.142 + } while (bytes > 0); 1.143 +} /* Clientel */ 1.144 + 1.145 +static void Client(const char *server_name) 1.146 +{ 1.147 + PRStatus rv; 1.148 + PRHostEnt host; 1.149 + char buffer[PR_NETDB_BUF_SIZE]; 1.150 + PRIntervalTime dally = PR_SecondsToInterval(60); 1.151 + PR_fprintf(err, "Translating the name %s\n", server_name); 1.152 + rv = PR_GetHostByName(server_name, buffer, sizeof(buffer), &host); 1.153 + if (PR_FAILURE == rv) 1.154 + PL_FPrintError(err, "PR_GetHostByName"); 1.155 + else 1.156 + { 1.157 + if (PR_EnumerateHostEnt( 1.158 + 0, &host, PORT_NUMBER, &shared->server_address) < 0) 1.159 + PL_FPrintError(err, "PR_EnumerateHostEnt"); 1.160 + else 1.161 + { 1.162 + do 1.163 + { 1.164 + shared->threads += 1; 1.165 + (void)PR_CreateThread( 1.166 + PR_USER_THREAD, Clientel, shared, 1.167 + PR_PRIORITY_NORMAL, thread_scope, 1.168 + PR_UNJOINABLE_THREAD, 8 * 1024); 1.169 + if (shared->threads == initial_streams) 1.170 + { 1.171 + PR_Sleep(dally); 1.172 + initial_streams += 1; 1.173 + } 1.174 + } while (PR_TRUE); 1.175 + } 1.176 + } 1.177 +} 1.178 + 1.179 +static void PR_CALLBACK Servette(void *arg) 1.180 +{ 1.181 + PRInt32 bytes, sampled; 1.182 + PRIntervalTime now, interval; 1.183 + PRBool do_display = PR_FALSE; 1.184 + PRFileDesc *client = (PRFileDesc*)arg; 1.185 + char *buffer = (char*)PR_Malloc(buffer_size); 1.186 + PRIntervalTime sampling_interval = PR_SecondsToInterval(SAMPLING_INTERVAL); 1.187 + 1.188 + if (xport_buffer != -1) 1.189 + { 1.190 + PRStatus rv; 1.191 + PRSocketOptionData data; 1.192 + data.option = PR_SockOpt_RecvBufferSize; 1.193 + data.value.recv_buffer_size = (PRSize)xport_buffer; 1.194 + rv = PR_SetSocketOption(client, &data); 1.195 + if (PR_FAILURE == rv) 1.196 + PL_FPrintError(err, "PR_SetSocketOption - ignored"); 1.197 + data.option = PR_SockOpt_SendBufferSize; 1.198 + data.value.send_buffer_size = (PRSize)xport_buffer; 1.199 + rv = PR_SetSocketOption(client, &data); 1.200 + if (PR_FAILURE == rv) 1.201 + PL_FPrintError(err, "PR_SetSocketOption - ignored"); 1.202 + } 1.203 + 1.204 + do 1.205 + { 1.206 + bytes = PR_Send( 1.207 + client, buffer, buffer_size, 0, PR_INTERVAL_NO_TIMEOUT); 1.208 + 1.209 + PR_Lock(shared->ml); 1.210 + now = PR_IntervalNow(); 1.211 + shared->sampled += bytes; 1.212 + interval = now - shared->timein; 1.213 + if (interval > sampling_interval) 1.214 + { 1.215 + sampled = shared->sampled; 1.216 + shared->timein = now; 1.217 + shared->sampled = 0; 1.218 + do_display = PR_TRUE; 1.219 + } 1.220 + PR_Unlock(shared->ml); 1.221 + 1.222 + if (do_display) 1.223 + { 1.224 + PRUint32 rate = sampled / PR_IntervalToMilliseconds(interval); 1.225 + PR_fprintf(err, "%u streams @ %u Kbytes/sec\n", shared->threads, rate); 1.226 + do_display = PR_FALSE; 1.227 + } 1.228 + } while (bytes > 0); 1.229 +} /* Servette */ 1.230 + 1.231 +static void Server(void) 1.232 +{ 1.233 + PRStatus rv; 1.234 + PRNetAddr server_address, client_address; 1.235 + PRFileDesc *xport = PR_Socket(domain, PR_SOCK_STREAM, protocol); 1.236 + 1.237 + if (NULL == xport) 1.238 + { 1.239 + PL_FPrintError(err, "PR_Socket"); 1.240 + return; 1.241 + } 1.242 + 1.243 + rv = PR_InitializeNetAddr(PR_IpAddrAny, PORT_NUMBER, &server_address); 1.244 + if (PR_FAILURE == rv) PL_FPrintError(err, "PR_InitializeNetAddr"); 1.245 + else 1.246 + { 1.247 + rv = PR_Bind(xport, &server_address); 1.248 + if (PR_FAILURE == rv) PL_FPrintError(err, "PR_Bind"); 1.249 + else 1.250 + { 1.251 + PRFileDesc *client; 1.252 + rv = PR_Listen(xport, 10); 1.253 + PR_fprintf(err, "Server listening on "); 1.254 + (void)PrintAddress(&server_address); 1.255 + do 1.256 + { 1.257 + client = PR_Accept( 1.258 + xport, &client_address, PR_INTERVAL_NO_TIMEOUT); 1.259 + if (NULL == client) PL_FPrintError(err, "PR_Accept"); 1.260 + else 1.261 + { 1.262 + PR_fprintf(err, "Server accepting from "); 1.263 + (void)PrintAddress(&client_address); 1.264 + shared->threads += 1; 1.265 + (void)PR_CreateThread( 1.266 + PR_USER_THREAD, Servette, client, 1.267 + PR_PRIORITY_NORMAL, thread_scope, 1.268 + PR_UNJOINABLE_THREAD, 8 * 1024); 1.269 + } 1.270 + } while (PR_TRUE); 1.271 + 1.272 + } 1.273 + } 1.274 +} /* Server */ 1.275 + 1.276 +static void Help(void) 1.277 +{ 1.278 + PR_fprintf(err, "Usage: [-h] [<server>]\n"); 1.279 + PR_fprintf(err, "\t-s <n> Initial # of connections (default: 1)\n"); 1.280 + PR_fprintf(err, "\t-C <n> Set 'concurrency' (default: 1)\n"); 1.281 + PR_fprintf(err, "\t-b <nK> Client buffer size (default: 32k)\n"); 1.282 + PR_fprintf(err, "\t-B <nK> Transport recv/send buffer size (default: sys)\n"); 1.283 + PR_fprintf(err, "\t-G Use GLOBAL threads (default: LOCAL)\n"); 1.284 + PR_fprintf(err, "\t-X Use XTP transport (default: TCP)\n"); 1.285 + PR_fprintf(err, "\t-6 Use IPv6 (default: IPv4)\n"); 1.286 + PR_fprintf(err, "\t-h This message and nothing else\n"); 1.287 + PR_fprintf(err, "\t<server> DNS name of server\n"); 1.288 + PR_fprintf(err, "\t\tIf <server> is not specified, this host will be\n"); 1.289 + PR_fprintf(err, "\t\tthe server and not act as a client.\n"); 1.290 +} /* Help */ 1.291 + 1.292 +int main(int argc, char **argv) 1.293 +{ 1.294 + PLOptStatus os; 1.295 + const char *server_name = NULL; 1.296 + PLOptState *opt = PL_CreateOptState(argc, argv, "hGX6C:b:s:B:"); 1.297 + 1.298 + err = PR_GetSpecialFD(PR_StandardError); 1.299 + 1.300 + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) 1.301 + { 1.302 + if (PL_OPT_BAD == os) continue; 1.303 + switch (opt->option) 1.304 + { 1.305 + case 0: /* Name of server */ 1.306 + server_name = opt->value; 1.307 + break; 1.308 + case 'G': /* Globular threads */ 1.309 + thread_scope = PR_GLOBAL_THREAD; 1.310 + break; 1.311 + case 'X': /* Use XTP as the transport */ 1.312 + protocol = 36; 1.313 + break; 1.314 + case '6': /* Use IPv6 */ 1.315 + domain = PR_AF_INET6; 1.316 + break; 1.317 + case 's': /* initial_streams */ 1.318 + initial_streams = atoi(opt->value); 1.319 + break; 1.320 + case 'C': /* concurrency */ 1.321 + concurrency = atoi(opt->value); 1.322 + break; 1.323 + case 'b': /* buffer size */ 1.324 + buffer_size = 1024 * atoi(opt->value); 1.325 + break; 1.326 + case 'B': /* buffer size */ 1.327 + xport_buffer = 1024 * atoi(opt->value); 1.328 + break; 1.329 + case 'h': /* user wants some guidance */ 1.330 + default: 1.331 + Help(); /* so give him an earful */ 1.332 + return 2; /* but not a lot else */ 1.333 + } 1.334 + } 1.335 + PL_DestroyOptState(opt); 1.336 + 1.337 + shared = PR_NEWZAP(Shared); 1.338 + shared->ml = PR_NewLock(); 1.339 + 1.340 + PR_fprintf(err, 1.341 + "This machine is %s\n", 1.342 + (NULL == server_name) ? "the SERVER" : "a CLIENT"); 1.343 + 1.344 + PR_fprintf(err, 1.345 + "Transport being used is %s\n", 1.346 + (6 == protocol) ? "TCP" : "XTP"); 1.347 + 1.348 + if (PR_GLOBAL_THREAD == thread_scope) 1.349 + { 1.350 + if (1 != concurrency) 1.351 + { 1.352 + PR_fprintf(err, " **Concurrency > 1 and GLOBAL threads!?!?\n"); 1.353 + PR_fprintf(err, " **Ignoring concurrency\n"); 1.354 + concurrency = 1; 1.355 + } 1.356 + } 1.357 + 1.358 + if (1 != concurrency) 1.359 + { 1.360 + PR_SetConcurrency(concurrency); 1.361 + PR_fprintf(err, "Concurrency set to %u\n", concurrency); 1.362 + } 1.363 + 1.364 + PR_fprintf(err, 1.365 + "All threads will be %s\n", 1.366 + (PR_GLOBAL_THREAD == thread_scope) ? "GLOBAL" : "LOCAL"); 1.367 + 1.368 + PR_fprintf(err, "Client buffer size will be %u\n", buffer_size); 1.369 + 1.370 + if (-1 != xport_buffer) 1.371 + PR_fprintf( 1.372 + err, "Transport send & receive buffer size will be %u\n", xport_buffer); 1.373 + 1.374 + 1.375 + if (NULL == server_name) Server(); 1.376 + else Client(server_name); 1.377 + 1.378 + return 0; 1.379 +} /* main */ 1.380 + 1.381 +/* thruput.c */ 1.382 +