Back to index

lightning-sunbird  0.9+nobinonly
thruput.c
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is the Netscape Portable Runtime (NSPR).
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998-2000
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 /*
00039 ** File:        thruput.c
00040 ** Description: Test server's throughput capability comparing various
00041 **              implmentation strategies.
00042 **
00043 ** Note:        Requires a server machine and an aribitrary number of
00044 **              clients to bang on it. Trust the numbers on the server
00045 **              more than those being displayed by the various clients.
00046 */
00047 
00048 #include "prerror.h"
00049 #include "prinrval.h"
00050 #include "prinit.h"
00051 #include "prio.h"
00052 #include "prlock.h"
00053 #include "prmem.h"
00054 #include "prnetdb.h"
00055 #include "prprf.h"
00056 #include "prthread.h"
00057 
00058 #include "pprio.h"
00059 
00060 #include "plerror.h"
00061 #include "plgetopt.h"
00062 
00063 #define ADDR_BUFFER 100
00064 #define PORT_NUMBER 51877
00065 #define SAMPLING_INTERVAL 10
00066 #define BUFFER_SIZE (32 * 1024)
00067 
00068 static PRInt32 domain = PR_AF_INET;
00069 static PRInt32 protocol = 6;  /* TCP */
00070 static PRFileDesc *err = NULL;
00071 static PRIntn concurrency = 1;
00072 static PRInt32 xport_buffer = -1;
00073 static PRUint32 initial_streams = 1;
00074 static PRInt32 buffer_size = BUFFER_SIZE;
00075 static PRThreadScope thread_scope = PR_LOCAL_THREAD;
00076 
00077 typedef struct Shared
00078 {
00079     PRLock *ml;
00080     PRUint32 sampled;
00081     PRUint32 threads;
00082     PRIntervalTime timein;
00083     PRNetAddr server_address;
00084 } Shared;
00085 
00086 static Shared *shared = NULL;
00087 
00088 static PRStatus PrintAddress(const PRNetAddr* address)
00089 {
00090     char buffer[ADDR_BUFFER];
00091     PRStatus rv = PR_NetAddrToString(address, buffer, sizeof(buffer));
00092     if (PR_SUCCESS == rv)
00093         PR_fprintf(err, "%s:%u\n", buffer, PR_ntohs(address->inet.port));
00094     else PL_FPrintError(err, "PR_NetAddrToString");
00095     return rv;
00096 }  /* PrintAddress */
00097 
00098 
00099 static void PR_CALLBACK Clientel(void *arg)
00100 {
00101     PRStatus rv;
00102     PRFileDesc *xport;
00103     PRInt32 bytes, sampled;
00104     PRIntervalTime now, interval;
00105     PRBool do_display = PR_FALSE;
00106     Shared *shared = (Shared*)arg;
00107     char *buffer = (char*)PR_Malloc(buffer_size);
00108     PRNetAddr *server_address = &shared->server_address;
00109     PRIntervalTime connect_timeout = PR_SecondsToInterval(5);
00110     PRIntervalTime sampling_interval = PR_SecondsToInterval(SAMPLING_INTERVAL);
00111 
00112     PR_fprintf(err, "Client connecting to ");
00113     (void)PrintAddress(server_address);
00114 
00115     do
00116     {
00117         xport = PR_Socket(domain, PR_SOCK_STREAM, protocol);
00118         if (NULL == xport)
00119         {
00120             PL_FPrintError(err, "PR_Socket");
00121             return;
00122         }
00123 
00124         if (xport_buffer != -1)
00125         {
00126             PRSocketOptionData data;
00127             data.option = PR_SockOpt_RecvBufferSize;
00128             data.value.recv_buffer_size = (PRSize)xport_buffer;
00129             rv = PR_SetSocketOption(xport, &data);
00130             if (PR_FAILURE == rv)
00131                 PL_FPrintError(err, "PR_SetSocketOption - ignored");
00132             data.option = PR_SockOpt_SendBufferSize;
00133             data.value.send_buffer_size = (PRSize)xport_buffer;
00134             rv = PR_SetSocketOption(xport, &data);
00135             if (PR_FAILURE == rv)
00136                 PL_FPrintError(err, "PR_SetSocketOption - ignored");
00137         }
00138 
00139         rv = PR_Connect(xport, server_address, connect_timeout);
00140         if (PR_FAILURE == rv)
00141         {
00142             PL_FPrintError(err, "PR_Connect");
00143             if (PR_IO_TIMEOUT_ERROR != PR_GetError())
00144                 PR_Sleep(connect_timeout);
00145             PR_Close(xport);  /* delete it and start over */
00146         }
00147     } while (PR_FAILURE == rv);
00148 
00149     do
00150     {
00151         bytes = PR_Recv(
00152             xport, buffer, buffer_size, 0, PR_INTERVAL_NO_TIMEOUT);
00153         PR_Lock(shared->ml);
00154         now = PR_IntervalNow();
00155         shared->sampled += bytes;
00156         interval = now - shared->timein;
00157         if (interval > sampling_interval)
00158         {
00159             sampled = shared->sampled;
00160             shared->timein = now;
00161             shared->sampled = 0;
00162             do_display = PR_TRUE;
00163         }
00164         PR_Unlock(shared->ml);
00165 
00166         if (do_display)
00167         {
00168             PRUint32 rate = sampled / PR_IntervalToMilliseconds(interval);
00169             PR_fprintf(err, "%u streams @ %u Kbytes/sec\n", shared->threads, rate);
00170             do_display = PR_FALSE;
00171         }
00172 
00173     } while (bytes > 0);
00174 }  /* Clientel */
00175 
00176 static void Client(const char *server_name)
00177 {
00178     PRStatus rv;
00179     PRHostEnt host;
00180     char buffer[PR_NETDB_BUF_SIZE];
00181     PRIntervalTime dally = PR_SecondsToInterval(60);
00182     PR_fprintf(err, "Translating the name %s\n", server_name);
00183     rv = PR_GetHostByName(server_name, buffer, sizeof(buffer), &host);
00184     if (PR_FAILURE == rv)
00185         PL_FPrintError(err, "PR_GetHostByName");
00186     else
00187     {
00188         if (PR_EnumerateHostEnt(
00189             0, &host, PORT_NUMBER, &shared->server_address) < 0)
00190             PL_FPrintError(err, "PR_EnumerateHostEnt");
00191         else
00192         {
00193             do
00194             {
00195                 shared->threads += 1;
00196                 (void)PR_CreateThread(
00197                     PR_USER_THREAD, Clientel, shared,
00198                     PR_PRIORITY_NORMAL, thread_scope,
00199                     PR_UNJOINABLE_THREAD, 8 * 1024);
00200                 if (shared->threads == initial_streams)
00201                 {
00202                     PR_Sleep(dally);
00203                     initial_streams += 1;
00204                 }
00205             } while (PR_TRUE);
00206         }
00207     }
00208 }
00209 
00210 static void PR_CALLBACK Servette(void *arg)
00211 {
00212     PRInt32 bytes, sampled;
00213     PRIntervalTime now, interval;
00214     PRBool do_display = PR_FALSE;
00215     PRFileDesc *client = (PRFileDesc*)arg;
00216     char *buffer = (char*)PR_Malloc(buffer_size);
00217     PRIntervalTime sampling_interval = PR_SecondsToInterval(SAMPLING_INTERVAL);
00218 
00219     if (xport_buffer != -1)
00220     {
00221         PRStatus rv;
00222         PRSocketOptionData data;
00223         data.option = PR_SockOpt_RecvBufferSize;
00224         data.value.recv_buffer_size = (PRSize)xport_buffer;
00225         rv = PR_SetSocketOption(client, &data);
00226         if (PR_FAILURE == rv)
00227             PL_FPrintError(err, "PR_SetSocketOption - ignored");
00228         data.option = PR_SockOpt_SendBufferSize;
00229         data.value.send_buffer_size = (PRSize)xport_buffer;
00230         rv = PR_SetSocketOption(client, &data);
00231         if (PR_FAILURE == rv)
00232             PL_FPrintError(err, "PR_SetSocketOption - ignored");
00233     }
00234 
00235     do
00236     {
00237         bytes = PR_Send(
00238             client, buffer, buffer_size, 0, PR_INTERVAL_NO_TIMEOUT);
00239 
00240         PR_Lock(shared->ml);
00241         now = PR_IntervalNow();
00242         shared->sampled += bytes;
00243         interval = now - shared->timein;
00244         if (interval > sampling_interval)
00245         {
00246             sampled = shared->sampled;
00247             shared->timein = now;
00248             shared->sampled = 0;
00249             do_display = PR_TRUE;
00250         }
00251         PR_Unlock(shared->ml);
00252 
00253         if (do_display)
00254         {
00255             PRUint32 rate = sampled / PR_IntervalToMilliseconds(interval);
00256             PR_fprintf(err, "%u streams @ %u Kbytes/sec\n", shared->threads, rate);
00257             do_display = PR_FALSE;
00258         }
00259     } while (bytes > 0);
00260 }  /* Servette */
00261 
00262 static void Server(void)
00263 {
00264     PRStatus rv;
00265     PRNetAddr server_address, client_address;
00266     PRFileDesc *xport = PR_Socket(domain, PR_SOCK_STREAM, protocol);
00267 
00268     if (NULL == xport)
00269     {
00270         PL_FPrintError(err, "PR_Socket");
00271         return;
00272     }
00273 
00274     rv = PR_InitializeNetAddr(PR_IpAddrAny, PORT_NUMBER, &server_address);
00275     if (PR_FAILURE == rv) PL_FPrintError(err, "PR_InitializeNetAddr");
00276     else
00277     {
00278         rv = PR_Bind(xport, &server_address);
00279         if (PR_FAILURE == rv) PL_FPrintError(err, "PR_Bind");
00280         else
00281         {
00282             PRFileDesc *client;
00283             rv = PR_Listen(xport, 10);
00284             PR_fprintf(err, "Server listening on ");
00285             (void)PrintAddress(&server_address);
00286             do
00287             {
00288                 client = PR_Accept(
00289                     xport, &client_address, PR_INTERVAL_NO_TIMEOUT);
00290                 if (NULL == client) PL_FPrintError(err, "PR_Accept");
00291                 else
00292                 {
00293                     PR_fprintf(err, "Server accepting from ");
00294                     (void)PrintAddress(&client_address);
00295                     shared->threads += 1;
00296                     (void)PR_CreateThread(
00297                         PR_USER_THREAD, Servette, client,
00298                         PR_PRIORITY_NORMAL, thread_scope,
00299                         PR_UNJOINABLE_THREAD, 8 * 1024);
00300                 }
00301             } while (PR_TRUE);
00302 
00303         }
00304     }
00305 }  /* Server */
00306 
00307 static void Help(void)
00308 {
00309     PR_fprintf(err, "Usage: [-h] [<server>]\n");
00310     PR_fprintf(err, "\t-s <n>   Initial # of connections        (default: 1)\n");
00311     PR_fprintf(err, "\t-C <n>   Set 'concurrency'               (default: 1)\n");
00312     PR_fprintf(err, "\t-b <nK>  Client buffer size              (default: 32k)\n");
00313     PR_fprintf(err, "\t-B <nK>  Transport recv/send buffer size (default: sys)\n");
00314     PR_fprintf(err, "\t-G       Use GLOBAL threads              (default: LOCAL)\n");
00315     PR_fprintf(err, "\t-X       Use XTP transport               (default: TCP)\n");
00316     PR_fprintf(err, "\t-6       Use IPv6                        (default: IPv4)\n");
00317     PR_fprintf(err, "\t-h       This message and nothing else\n");
00318     PR_fprintf(err, "\t<server> DNS name of server\n");
00319     PR_fprintf(err, "\t\tIf <server> is not specified, this host will be\n");
00320     PR_fprintf(err, "\t\tthe server and not act as a client.\n");
00321 }  /* Help */
00322 
00323 PRIntn main(PRIntn argc, char **argv)
00324 {
00325     PLOptStatus os;
00326     const char *server_name = NULL;
00327     PLOptState *opt = PL_CreateOptState(argc, argv, "hGX6C:b:s:B:");
00328 
00329     err = PR_GetSpecialFD(PR_StandardError);
00330 
00331     while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
00332     {
00333         if (PL_OPT_BAD == os) continue;
00334         switch (opt->option)
00335         {
00336         case 0:  /* Name of server */
00337             server_name = opt->value;
00338             break;
00339         case 'G':  /* Globular threads */
00340             thread_scope = PR_GLOBAL_THREAD;
00341             break;
00342         case 'X':  /* Use XTP as the transport */
00343             protocol = 36;
00344             break;
00345         case '6':  /* Use IPv6 */
00346             domain = PR_AF_INET6;
00347             break;
00348         case 's':  /* initial_streams */
00349             initial_streams = atoi(opt->value);
00350             break;
00351         case 'C':  /* concurrency */
00352             concurrency = atoi(opt->value);
00353             break;
00354         case 'b':  /* buffer size */
00355             buffer_size = 1024 * atoi(opt->value);
00356             break;
00357         case 'B':  /* buffer size */
00358             xport_buffer = 1024 * atoi(opt->value);
00359             break;
00360         case 'h':  /* user wants some guidance */
00361         default:
00362             Help();  /* so give him an earful */
00363             return 2;  /* but not a lot else */
00364         }
00365     }
00366     PL_DestroyOptState(opt);
00367 
00368     shared = PR_NEWZAP(Shared);
00369     shared->ml = PR_NewLock();
00370 
00371     PR_fprintf(err,
00372         "This machine is %s\n",
00373         (NULL == server_name) ? "the SERVER" : "a CLIENT");
00374 
00375     PR_fprintf(err,
00376         "Transport being used is %s\n",
00377         (6 == protocol) ? "TCP" : "XTP");
00378 
00379     if (PR_GLOBAL_THREAD == thread_scope)
00380     {
00381         if (1 != concurrency)
00382         {
00383             PR_fprintf(err, "  **Concurrency > 1 and GLOBAL threads!?!?\n");
00384             PR_fprintf(err, "  **Ignoring concurrency\n");
00385             concurrency = 1;
00386         }
00387     }
00388 
00389     if (1 != concurrency)
00390     {
00391         PR_SetConcurrency(concurrency);
00392         PR_fprintf(err, "Concurrency set to %u\n", concurrency);
00393     }
00394 
00395     PR_fprintf(err,
00396         "All threads will be %s\n",
00397         (PR_GLOBAL_THREAD == thread_scope) ? "GLOBAL" : "LOCAL");
00398 
00399     PR_fprintf(err, "Client buffer size will be %u\n", buffer_size);
00400    
00401     if (-1 != xport_buffer)
00402     PR_fprintf(
00403         err, "Transport send & receive buffer size will be %u\n", xport_buffer);
00404     
00405 
00406     if (NULL == server_name) Server();
00407     else Client(server_name);
00408 
00409 }  /* main */
00410 
00411 /* thruput.c */
00412