2008-06-06 05:36:51 -07:00
|
|
|
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/* ***** BEGIN LICENSE BLOCK *****
|
|
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
|
|
*
|
|
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
|
|
* the License. You may obtain a copy of the License at
|
|
|
|
* http://www.mozilla.org/MPL/
|
|
|
|
*
|
|
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
|
|
* for the specific language governing rights and limitations under the
|
|
|
|
* License.
|
|
|
|
*
|
|
|
|
* The Original Code is the Netscape Portable Runtime (NSPR).
|
|
|
|
*
|
|
|
|
* The Initial Developer of the Original Code is
|
|
|
|
* Netscape Communications Corporation.
|
|
|
|
* Portions created by the Initial Developer are Copyright (C) 1998-2000
|
|
|
|
* the Initial Developer. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Contributor(s):
|
|
|
|
*
|
|
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
|
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
|
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
|
|
* the provisions above, a recipient may use your version of this file under
|
|
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
|
|
*
|
|
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
|
|
|
|
/*
|
|
|
|
** File: thruput.c
|
|
|
|
** Description: Test server's throughput capability comparing various
|
|
|
|
** implmentation strategies.
|
|
|
|
**
|
|
|
|
** Note: Requires a server machine and an aribitrary number of
|
|
|
|
** clients to bang on it. Trust the numbers on the server
|
|
|
|
** more than those being displayed by the various clients.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "prerror.h"
|
|
|
|
#include "prinrval.h"
|
|
|
|
#include "prinit.h"
|
|
|
|
#include "prio.h"
|
|
|
|
#include "prlock.h"
|
|
|
|
#include "prmem.h"
|
|
|
|
#include "prnetdb.h"
|
|
|
|
#include "prprf.h"
|
|
|
|
#include "prthread.h"
|
|
|
|
#include "pprio.h"
|
|
|
|
#include "plerror.h"
|
|
|
|
#include "plgetopt.h"
|
|
|
|
|
|
|
|
#define ADDR_BUFFER 100
|
|
|
|
#define PORT_NUMBER 51877
|
|
|
|
#define SAMPLING_INTERVAL 10
|
|
|
|
#define BUFFER_SIZE (32 * 1024)
|
|
|
|
|
|
|
|
static PRInt32 domain = PR_AF_INET;
|
|
|
|
static PRInt32 protocol = 6; /* TCP */
|
|
|
|
static PRFileDesc *err = NULL;
|
|
|
|
static PRIntn concurrency = 1;
|
|
|
|
static PRInt32 xport_buffer = -1;
|
|
|
|
static PRUint32 initial_streams = 1;
|
|
|
|
static PRInt32 buffer_size = BUFFER_SIZE;
|
|
|
|
static PRThreadScope thread_scope = PR_LOCAL_THREAD;
|
|
|
|
|
|
|
|
typedef struct Shared
|
|
|
|
{
|
|
|
|
PRLock *ml;
|
|
|
|
PRUint32 sampled;
|
|
|
|
PRUint32 threads;
|
|
|
|
PRIntervalTime timein;
|
|
|
|
PRNetAddr server_address;
|
|
|
|
} Shared;
|
|
|
|
|
|
|
|
static Shared *shared = NULL;
|
|
|
|
|
|
|
|
static PRStatus PrintAddress(const PRNetAddr* address)
|
|
|
|
{
|
|
|
|
char buffer[ADDR_BUFFER];
|
|
|
|
PRStatus rv = PR_NetAddrToString(address, buffer, sizeof(buffer));
|
|
|
|
if (PR_SUCCESS == rv)
|
|
|
|
PR_fprintf(err, "%s:%u\n", buffer, PR_ntohs(address->inet.port));
|
|
|
|
else PL_FPrintError(err, "PR_NetAddrToString");
|
|
|
|
return rv;
|
|
|
|
} /* PrintAddress */
|
|
|
|
|
|
|
|
|
|
|
|
static void PR_CALLBACK Clientel(void *arg)
|
|
|
|
{
|
|
|
|
PRStatus rv;
|
|
|
|
PRFileDesc *xport;
|
|
|
|
PRInt32 bytes, sampled;
|
|
|
|
PRIntervalTime now, interval;
|
|
|
|
PRBool do_display = PR_FALSE;
|
|
|
|
Shared *shared = (Shared*)arg;
|
|
|
|
char *buffer = (char*)PR_Malloc(buffer_size);
|
|
|
|
PRNetAddr *server_address = &shared->server_address;
|
|
|
|
PRIntervalTime connect_timeout = PR_SecondsToInterval(5);
|
|
|
|
PRIntervalTime sampling_interval = PR_SecondsToInterval(SAMPLING_INTERVAL);
|
|
|
|
|
|
|
|
PR_fprintf(err, "Client connecting to ");
|
|
|
|
(void)PrintAddress(server_address);
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
xport = PR_Socket(domain, PR_SOCK_STREAM, protocol);
|
|
|
|
if (NULL == xport)
|
|
|
|
{
|
|
|
|
PL_FPrintError(err, "PR_Socket");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (xport_buffer != -1)
|
|
|
|
{
|
|
|
|
PRSocketOptionData data;
|
|
|
|
data.option = PR_SockOpt_RecvBufferSize;
|
|
|
|
data.value.recv_buffer_size = (PRSize)xport_buffer;
|
|
|
|
rv = PR_SetSocketOption(xport, &data);
|
|
|
|
if (PR_FAILURE == rv)
|
|
|
|
PL_FPrintError(err, "PR_SetSocketOption - ignored");
|
|
|
|
data.option = PR_SockOpt_SendBufferSize;
|
|
|
|
data.value.send_buffer_size = (PRSize)xport_buffer;
|
|
|
|
rv = PR_SetSocketOption(xport, &data);
|
|
|
|
if (PR_FAILURE == rv)
|
|
|
|
PL_FPrintError(err, "PR_SetSocketOption - ignored");
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = PR_Connect(xport, server_address, connect_timeout);
|
|
|
|
if (PR_FAILURE == rv)
|
|
|
|
{
|
|
|
|
PL_FPrintError(err, "PR_Connect");
|
|
|
|
if (PR_IO_TIMEOUT_ERROR != PR_GetError())
|
|
|
|
PR_Sleep(connect_timeout);
|
|
|
|
PR_Close(xport); /* delete it and start over */
|
|
|
|
}
|
|
|
|
} while (PR_FAILURE == rv);
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
bytes = PR_Recv(
|
|
|
|
xport, buffer, buffer_size, 0, PR_INTERVAL_NO_TIMEOUT);
|
|
|
|
PR_Lock(shared->ml);
|
|
|
|
now = PR_IntervalNow();
|
|
|
|
shared->sampled += bytes;
|
|
|
|
interval = now - shared->timein;
|
|
|
|
if (interval > sampling_interval)
|
|
|
|
{
|
|
|
|
sampled = shared->sampled;
|
|
|
|
shared->timein = now;
|
|
|
|
shared->sampled = 0;
|
|
|
|
do_display = PR_TRUE;
|
|
|
|
}
|
|
|
|
PR_Unlock(shared->ml);
|
|
|
|
|
|
|
|
if (do_display)
|
|
|
|
{
|
|
|
|
PRUint32 rate = sampled / PR_IntervalToMilliseconds(interval);
|
|
|
|
PR_fprintf(err, "%u streams @ %u Kbytes/sec\n", shared->threads, rate);
|
|
|
|
do_display = PR_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
} while (bytes > 0);
|
|
|
|
} /* Clientel */
|
|
|
|
|
|
|
|
static void Client(const char *server_name)
|
|
|
|
{
|
|
|
|
PRStatus rv;
|
|
|
|
PRHostEnt host;
|
|
|
|
char buffer[PR_NETDB_BUF_SIZE];
|
|
|
|
PRIntervalTime dally = PR_SecondsToInterval(60);
|
|
|
|
PR_fprintf(err, "Translating the name %s\n", server_name);
|
|
|
|
rv = PR_GetHostByName(server_name, buffer, sizeof(buffer), &host);
|
|
|
|
if (PR_FAILURE == rv)
|
|
|
|
PL_FPrintError(err, "PR_GetHostByName");
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (PR_EnumerateHostEnt(
|
|
|
|
0, &host, PORT_NUMBER, &shared->server_address) < 0)
|
|
|
|
PL_FPrintError(err, "PR_EnumerateHostEnt");
|
|
|
|
else
|
|
|
|
{
|
|
|
|
do
|
|
|
|
{
|
|
|
|
shared->threads += 1;
|
|
|
|
(void)PR_CreateThread(
|
|
|
|
PR_USER_THREAD, Clientel, shared,
|
|
|
|
PR_PRIORITY_NORMAL, thread_scope,
|
|
|
|
PR_UNJOINABLE_THREAD, 8 * 1024);
|
|
|
|
if (shared->threads == initial_streams)
|
|
|
|
{
|
|
|
|
PR_Sleep(dally);
|
|
|
|
initial_streams += 1;
|
|
|
|
}
|
|
|
|
} while (PR_TRUE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void PR_CALLBACK Servette(void *arg)
|
|
|
|
{
|
|
|
|
PRInt32 bytes, sampled;
|
|
|
|
PRIntervalTime now, interval;
|
|
|
|
PRBool do_display = PR_FALSE;
|
|
|
|
PRFileDesc *client = (PRFileDesc*)arg;
|
|
|
|
char *buffer = (char*)PR_Malloc(buffer_size);
|
|
|
|
PRIntervalTime sampling_interval = PR_SecondsToInterval(SAMPLING_INTERVAL);
|
|
|
|
|
|
|
|
if (xport_buffer != -1)
|
|
|
|
{
|
|
|
|
PRStatus rv;
|
|
|
|
PRSocketOptionData data;
|
|
|
|
data.option = PR_SockOpt_RecvBufferSize;
|
|
|
|
data.value.recv_buffer_size = (PRSize)xport_buffer;
|
|
|
|
rv = PR_SetSocketOption(client, &data);
|
|
|
|
if (PR_FAILURE == rv)
|
|
|
|
PL_FPrintError(err, "PR_SetSocketOption - ignored");
|
|
|
|
data.option = PR_SockOpt_SendBufferSize;
|
|
|
|
data.value.send_buffer_size = (PRSize)xport_buffer;
|
|
|
|
rv = PR_SetSocketOption(client, &data);
|
|
|
|
if (PR_FAILURE == rv)
|
|
|
|
PL_FPrintError(err, "PR_SetSocketOption - ignored");
|
|
|
|
}
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
bytes = PR_Send(
|
|
|
|
client, buffer, buffer_size, 0, PR_INTERVAL_NO_TIMEOUT);
|
|
|
|
|
|
|
|
PR_Lock(shared->ml);
|
|
|
|
now = PR_IntervalNow();
|
|
|
|
shared->sampled += bytes;
|
|
|
|
interval = now - shared->timein;
|
|
|
|
if (interval > sampling_interval)
|
|
|
|
{
|
|
|
|
sampled = shared->sampled;
|
|
|
|
shared->timein = now;
|
|
|
|
shared->sampled = 0;
|
|
|
|
do_display = PR_TRUE;
|
|
|
|
}
|
|
|
|
PR_Unlock(shared->ml);
|
|
|
|
|
|
|
|
if (do_display)
|
|
|
|
{
|
|
|
|
PRUint32 rate = sampled / PR_IntervalToMilliseconds(interval);
|
|
|
|
PR_fprintf(err, "%u streams @ %u Kbytes/sec\n", shared->threads, rate);
|
|
|
|
do_display = PR_FALSE;
|
|
|
|
}
|
|
|
|
} while (bytes > 0);
|
|
|
|
} /* Servette */
|
|
|
|
|
|
|
|
static void Server(void)
|
|
|
|
{
|
|
|
|
PRStatus rv;
|
|
|
|
PRNetAddr server_address, client_address;
|
|
|
|
PRFileDesc *xport = PR_Socket(domain, PR_SOCK_STREAM, protocol);
|
|
|
|
|
|
|
|
if (NULL == xport)
|
|
|
|
{
|
|
|
|
PL_FPrintError(err, "PR_Socket");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = PR_InitializeNetAddr(PR_IpAddrAny, PORT_NUMBER, &server_address);
|
|
|
|
if (PR_FAILURE == rv) PL_FPrintError(err, "PR_InitializeNetAddr");
|
|
|
|
else
|
|
|
|
{
|
|
|
|
rv = PR_Bind(xport, &server_address);
|
|
|
|
if (PR_FAILURE == rv) PL_FPrintError(err, "PR_Bind");
|
|
|
|
else
|
|
|
|
{
|
|
|
|
PRFileDesc *client;
|
|
|
|
rv = PR_Listen(xport, 10);
|
|
|
|
PR_fprintf(err, "Server listening on ");
|
|
|
|
(void)PrintAddress(&server_address);
|
|
|
|
do
|
|
|
|
{
|
|
|
|
client = PR_Accept(
|
|
|
|
xport, &client_address, PR_INTERVAL_NO_TIMEOUT);
|
|
|
|
if (NULL == client) PL_FPrintError(err, "PR_Accept");
|
|
|
|
else
|
|
|
|
{
|
|
|
|
PR_fprintf(err, "Server accepting from ");
|
|
|
|
(void)PrintAddress(&client_address);
|
|
|
|
shared->threads += 1;
|
|
|
|
(void)PR_CreateThread(
|
|
|
|
PR_USER_THREAD, Servette, client,
|
|
|
|
PR_PRIORITY_NORMAL, thread_scope,
|
|
|
|
PR_UNJOINABLE_THREAD, 8 * 1024);
|
|
|
|
}
|
|
|
|
} while (PR_TRUE);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} /* Server */
|
|
|
|
|
|
|
|
static void Help(void)
|
|
|
|
{
|
|
|
|
PR_fprintf(err, "Usage: [-h] [<server>]\n");
|
|
|
|
PR_fprintf(err, "\t-s <n> Initial # of connections (default: 1)\n");
|
|
|
|
PR_fprintf(err, "\t-C <n> Set 'concurrency' (default: 1)\n");
|
|
|
|
PR_fprintf(err, "\t-b <nK> Client buffer size (default: 32k)\n");
|
|
|
|
PR_fprintf(err, "\t-B <nK> Transport recv/send buffer size (default: sys)\n");
|
|
|
|
PR_fprintf(err, "\t-G Use GLOBAL threads (default: LOCAL)\n");
|
|
|
|
PR_fprintf(err, "\t-X Use XTP transport (default: TCP)\n");
|
|
|
|
PR_fprintf(err, "\t-6 Use IPv6 (default: IPv4)\n");
|
|
|
|
PR_fprintf(err, "\t-h This message and nothing else\n");
|
|
|
|
PR_fprintf(err, "\t<server> DNS name of server\n");
|
|
|
|
PR_fprintf(err, "\t\tIf <server> is not specified, this host will be\n");
|
|
|
|
PR_fprintf(err, "\t\tthe server and not act as a client.\n");
|
|
|
|
} /* Help */
|
|
|
|
|
2009-03-22 09:44:55 -07:00
|
|
|
int main(int argc, char **argv)
|
2008-06-06 05:36:51 -07:00
|
|
|
{
|
|
|
|
PLOptStatus os;
|
|
|
|
const char *server_name = NULL;
|
|
|
|
PLOptState *opt = PL_CreateOptState(argc, argv, "hGX6C:b:s:B:");
|
|
|
|
|
|
|
|
err = PR_GetSpecialFD(PR_StandardError);
|
|
|
|
|
|
|
|
while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
|
|
|
|
{
|
|
|
|
if (PL_OPT_BAD == os) continue;
|
|
|
|
switch (opt->option)
|
|
|
|
{
|
|
|
|
case 0: /* Name of server */
|
|
|
|
server_name = opt->value;
|
|
|
|
break;
|
|
|
|
case 'G': /* Globular threads */
|
|
|
|
thread_scope = PR_GLOBAL_THREAD;
|
|
|
|
break;
|
|
|
|
case 'X': /* Use XTP as the transport */
|
|
|
|
protocol = 36;
|
|
|
|
break;
|
|
|
|
case '6': /* Use IPv6 */
|
|
|
|
domain = PR_AF_INET6;
|
|
|
|
break;
|
|
|
|
case 's': /* initial_streams */
|
|
|
|
initial_streams = atoi(opt->value);
|
|
|
|
break;
|
|
|
|
case 'C': /* concurrency */
|
|
|
|
concurrency = atoi(opt->value);
|
|
|
|
break;
|
|
|
|
case 'b': /* buffer size */
|
|
|
|
buffer_size = 1024 * atoi(opt->value);
|
|
|
|
break;
|
|
|
|
case 'B': /* buffer size */
|
|
|
|
xport_buffer = 1024 * atoi(opt->value);
|
|
|
|
break;
|
|
|
|
case 'h': /* user wants some guidance */
|
|
|
|
default:
|
|
|
|
Help(); /* so give him an earful */
|
|
|
|
return 2; /* but not a lot else */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
PL_DestroyOptState(opt);
|
|
|
|
|
|
|
|
shared = PR_NEWZAP(Shared);
|
|
|
|
shared->ml = PR_NewLock();
|
|
|
|
|
|
|
|
PR_fprintf(err,
|
|
|
|
"This machine is %s\n",
|
|
|
|
(NULL == server_name) ? "the SERVER" : "a CLIENT");
|
|
|
|
|
|
|
|
PR_fprintf(err,
|
|
|
|
"Transport being used is %s\n",
|
|
|
|
(6 == protocol) ? "TCP" : "XTP");
|
|
|
|
|
|
|
|
if (PR_GLOBAL_THREAD == thread_scope)
|
|
|
|
{
|
|
|
|
if (1 != concurrency)
|
|
|
|
{
|
|
|
|
PR_fprintf(err, " **Concurrency > 1 and GLOBAL threads!?!?\n");
|
|
|
|
PR_fprintf(err, " **Ignoring concurrency\n");
|
|
|
|
concurrency = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (1 != concurrency)
|
|
|
|
{
|
|
|
|
PR_SetConcurrency(concurrency);
|
|
|
|
PR_fprintf(err, "Concurrency set to %u\n", concurrency);
|
|
|
|
}
|
|
|
|
|
|
|
|
PR_fprintf(err,
|
|
|
|
"All threads will be %s\n",
|
|
|
|
(PR_GLOBAL_THREAD == thread_scope) ? "GLOBAL" : "LOCAL");
|
|
|
|
|
|
|
|
PR_fprintf(err, "Client buffer size will be %u\n", buffer_size);
|
|
|
|
|
|
|
|
if (-1 != xport_buffer)
|
|
|
|
PR_fprintf(
|
|
|
|
err, "Transport send & receive buffer size will be %u\n", xport_buffer);
|
|
|
|
|
|
|
|
|
|
|
|
if (NULL == server_name) Server();
|
|
|
|
else Client(server_name);
|
|
|
|
|
|
|
|
} /* main */
|
|
|
|
|
|
|
|
/* thruput.c */
|
|
|
|
|