/* * Copyright (c) 2000 - 2001 Kungliga Tekniska Högskolan * (Royal Institute of Technology, Stockholm, Sweden). * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution * at such time that OpenAFS documentation is written. * * 3. Neither the name of the Institute nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include #endif /* nn * We are using getopt since we want it to be possible to link to * transarc libs. */ #ifdef RCSID RCSID("$Id$"); #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_ERRX #include /* not stricly right, but if we have a errx() there * is hopefully a err.h */ #endif #include "rx.h" #include "rx_null.h" #include "rx_globals.h" #if defined(u_int32) #define u_int32_t u_int32 #elif defined(hget32) #define u_int32_t afs_uint32 #endif static const char *__progname; #ifndef HAVE_WARNX static void warnx(const char *fmt, ...) { va_list args; va_start(args, fmt); fprintf(stderr, "%s: ", __progname); vfprintf(stderr, fmt, args); fprintf(stderr, "\n"); va_end(args); } #endif /* !HAVE_WARNX */ #ifndef HAVE_ERRX static void errx(int eval, const char *fmt, ...) { va_list args; va_start(args, fmt); fprintf(stderr, "%s: ", __progname); vfprintf(stderr, fmt, args); fprintf(stderr, "\n"); va_end(args); exit(eval); } #endif /* !HAVE_ERRX */ #ifndef HAVE_WARN static void warn(const char *fmt, ...) { va_list args; char *errstr; va_start(args, fmt); fprintf(stderr, "%s: ", __progname); vfprintf(stderr, fmt, args); errstr = strerror(errno); fprintf(stderr, ": %s\n", errstr ? errstr : "unknown error"); va_end(args); } #endif /* !HAVE_WARN */ #ifndef HAVE_ERR static void err(int eval, const char *fmt, ...) { va_list args; char *errstr; va_start(args, fmt); fprintf(stderr, "%s: ", __progname); vfprintf(stderr, fmt, args); errstr = strerror(errno); fprintf(stderr, ": %s\n", errstr ? errstr : "unknown error"); va_end(args); exit(eval); } #endif /* !HAVE_ERR */ #define DEFAULT_PORT 7009 /* To match tcpdump */ #define DEFAULT_HOST "127.0.0.1" #define DEFAULT_BYTES 1000000 #define RXPERF_BUFSIZE 10000 enum { RX_PERF_VERSION = 3 }; enum { RX_SERVER_ID = 147 }; enum { RX_PERF_UNKNOWN = -1, RX_PERF_SEND = 0, RX_PERF_RECV = 1, RX_PERF_RPC = 3, RX_PERF_FILE = 4 }; enum { RXPERF_MAGIC_COOKIE = 0x4711 }; /* * */ #if DEBUG #define DBFPRINT(x) do { printf x ; } while(0) #else #define DBFPRINT(x) #endif static void sigusr1(int foo) { exit(2); /* XXX profiler */ } static void sigint(int foo) { rx_Finalize(); exit(2); /* XXX profiler */ } /* * */ static struct timeval timer_start; static struct timeval timer_stop; static int timer_check = 0; static void start_timer(void) { timer_check++; gettimeofday(&timer_start, NULL); } /* * */ static void end_and_print_timer(char *str) { long long start_l, stop_l; timer_check--; assert(timer_check == 0); gettimeofday(&timer_stop, NULL); start_l = timer_start.tv_sec * 1000000 + timer_start.tv_usec; stop_l = timer_stop.tv_sec * 1000000 + timer_stop.tv_usec; printf("%s:\t%8llu msec\n", str, (stop_l - start_l) / 1000); } /* * */ static u_long str2addr(const char *s) { struct in_addr server; struct hostent *h; #ifndef INADDR_NONE #define INADDR_NONE 0xffffffff #endif if (inet_addr(s) != INADDR_NONE) return inet_addr(s); h = gethostbyname(s); if (h != NULL) { memcpy(&server, h->h_addr_list[0], sizeof(server)); return server.s_addr; } return 0; } /* * */ static void get_sec(int serverp, struct rx_securityClass **sec, int *secureindex) { if (serverp) { *sec = rxnull_NewServerSecurityObject(); *secureindex = 1; } else { *sec = rxnull_NewClientSecurityObject(); *secureindex = 0; } } /* * process the "RPC" and return the results */ char somebuf[RXPERF_BUFSIZE]; int32_t rxwrite_size = sizeof(somebuf); int32_t rxread_size = sizeof(somebuf); static int readbytes(struct rx_call *call, int32_t bytes) { int32_t size; while (bytes > 0) { size = rxread_size; if (size > bytes) size = bytes; if (rx_Read(call, somebuf, size) != size) return 1; bytes -= size; } return 0; } static int sendbytes(struct rx_call *call, int32_t bytes) { int32_t size; while (bytes > 0) { size = rxwrite_size; if (size > bytes) size = bytes; if (rx_Write(call, somebuf, size) != size) return 1; bytes -= size; } return 0; } static int32_t rxperf_ExecuteRequest(struct rx_call *call) { int32_t version; int32_t command; u_int32_t bytes; u_int32_t recvb; u_int32_t sendb; u_int32_t data; u_int32_t num; u_int32_t *readwrite; int i; int readp = TRUE; DBFPRINT(("got a request\n")); if (rx_Read(call, &version, 4) != 4) { warn("rx_Read failed to read version"); return -1; } if (htonl(RX_PERF_VERSION) != version) { warnx("client has wrong version"); return -1; } if (rx_Read(call, &command, 4) != 4) { warnx("rx_Read failed to read command"); return -1; } command = ntohl(command); if (rx_Read(call, &data, 4) != 4) { warnx("rx_Read failed to read size"); return -1; } rxread_size = ntohl(data); if (rxread_size > sizeof(somebuf)) { warnx("rxread_size too large %d", rxread_size); return -1; } if (rx_Read(call, &data, 4) != 4) { warnx("rx_Read failed to write size"); return -1; } rxwrite_size = ntohl(data); if (rxwrite_size > sizeof(somebuf)) { warnx("rxwrite_size too large %d", rxwrite_size); return -1; } switch (command) { case RX_PERF_SEND: DBFPRINT(("got a send request\n")); if (rx_Read(call, &bytes, 4) != 4) { warnx("rx_Read failed to read bytes"); return -1; } bytes = ntohl(bytes); DBFPRINT(("reading(%d) ", bytes)); readbytes(call, bytes); data = htonl(RXPERF_MAGIC_COOKIE); if (rx_Write(call, &data, 4) != 4) { warnx("rx_Write failed when sending back result"); return -1; } DBFPRINT(("done\n")); break; case RX_PERF_RPC: DBFPRINT(("got a rpc request, reading commands\n")); if (rx_Read(call, &recvb, 4) != 4) { warnx("rx_Read failed to read recvbytes"); return -1; } recvb = ntohl(recvb); if (rx_Read(call, &sendb, 4) != 4) { warnx("rx_Read failed to read sendbytes"); return -1; } sendb = ntohl(sendb); DBFPRINT(("read(%d) ", recvb)); if (readbytes(call, recvb)) { warnx("readbytes failed"); return -1; } DBFPRINT(("send(%d) ", sendb)); if (sendbytes(call, sendb)) { warnx("sendbytes failed"); return -1; } DBFPRINT(("done\n")); data = htonl(RXPERF_MAGIC_COOKIE); if (rx_Write(call, &data, 4) != 4) { warnx("rx_Write failed when sending back magic cookie"); return -1; } break; case RX_PERF_FILE: if (rx_Read(call, &data, 4) != 4) errx(1, "failed to read num from client"); num = ntohl(data); readwrite = malloc(num * sizeof(u_int32_t)); if (readwrite == NULL) err(1, "malloc"); if (rx_Read(call, readwrite, num * sizeof(u_int32_t)) != num * sizeof(u_int32_t)) errx(1, "failed to read recvlist from client"); for (i = 0; i < num; i++) { if (readwrite[i] == 0) { DBFPRINT(("readp %d", readwrite[i])); readp = !readp; } bytes = ntohl(readwrite[i]) * sizeof(u_int32_t); if (readp) { DBFPRINT(("read\n")); readbytes(call, bytes); } else { sendbytes(call, bytes); DBFPRINT(("send\n")); } } break; case RX_PERF_RECV: DBFPRINT(("got a recv request\n")); if (rx_Read(call, &bytes, 4) != 4) { warnx("rx_Read failed to read bytes"); return -1; } bytes = ntohl(bytes); DBFPRINT(("sending(%d) ", bytes)); sendbytes(call, bytes); data = htonl(RXPERF_MAGIC_COOKIE); if (rx_Write(call, &data, 4) != 4) { warnx("rx_Write failed when sending back result"); return -1; } DBFPRINT(("done\n")); break; default: warnx("client sent a unsupported command"); return -1; } DBFPRINT(("done with command\n")); return 0; } /* * */ static void do_server(int port) { struct rx_service *service; struct rx_securityClass *secureobj; int secureindex; int ret; ret = rx_Init(port); if (ret) errx(1, "rx_Init failed"); get_sec(1, &secureobj, &secureindex); service = rx_NewService(0, RX_SERVER_ID, "rxperf", &secureobj, secureindex, rxperf_ExecuteRequest); if (service == NULL) errx(1, "Cant create server"); rx_StartServer(1); abort(); } /* * */ static void readfile(const char *filename, u_int32_t ** readwrite, u_int32_t * size) { FILE *f; u_int32_t len = 16; u_int32_t num = 0; u_int32_t data; char *ptr; char buf[RXPERF_BUFSIZE]; *readwrite = malloc(sizeof(u_int32_t) * len); if (*readwrite == NULL) err(1, "malloc"); f = fopen(filename, "r"); if (f == NULL) err(1, "fopen"); while (fgets(buf, sizeof(buf), f) != NULL) { if (num >= len) { len = len * 2; *readwrite = realloc(*readwrite, len * sizeof(u_int32_t)); if (*readwrite == NULL) err(1, "realloc"); } if (*buf != '\n') { data = htonl(strtol(buf, &ptr, 0)); if (ptr && ptr == buf) errx(1, "can't resolve number of bytes to transfer"); } else { data = 0; } (*readwrite)[num] = data; num++; } *size = num; if (fclose(f) == -1) err(1, "fclose"); } /* * */ static void do_client(const char *server, int port, char *filename, int32_t command, int32_t times, int32_t bytes, int32_t sendtimes, int32_t recvtimes) { struct rx_connection *conn; struct rx_call *call; u_int32_t addr = str2addr(server); struct rx_securityClass *secureobj; int secureindex; int32_t data; int32_t num; int ret; int i; int readp = FALSE; char stamp[1024]; u_int32_t size; u_int32_t *readwrite; ret = rx_Init(0); if (ret) errx(1, "rx_Init failed"); get_sec(0, &secureobj, &secureindex); conn = rx_NewConnection(addr, port, RX_SERVER_ID, secureobj, secureindex); if (conn == NULL) errx(1, "failed to contact server"); sprintf(stamp, "send\t%d times\t%d writes\t%d reads", times, sendtimes, recvtimes); start_timer(); for (i = 0; i < times; i++) { DBFPRINT(("starting command ")); call = rx_NewCall(conn); if (call == NULL) errx(1, "rx_NewCall failed"); data = htonl(RX_PERF_VERSION); if (rx_Write(call, &data, 4) != 4) errx(1, "rx_Write failed to send version"); data = htonl(command); if (rx_Write(call, &data, 4) != 4) errx(1, "rx_Write failed to send command"); data = htonl(rxread_size); if (rx_Write(call, &data, 4) != 4) errx(1, "rx_Write failed to send read size"); data = htonl(rxwrite_size); if (rx_Write(call, &data, 4) != 4) errx(1, "rx_Write failed to send write read"); switch (command) { case RX_PERF_RECV: DBFPRINT(("command ")); data = htonl(bytes); if (rx_Write(call, &data, 4) != 4) errx(1, "rx_Write failed to send size"); DBFPRINT(("sending(%d) ", bytes)); if (readbytes(call, bytes)) errx(1, "sendbytes"); if (rx_Read(call, &data, 4) != 4) errx(1, "failed to read result from server"); if (data != htonl(RXPERF_MAGIC_COOKIE)) warn("server send wrong magic cookie in responce"); DBFPRINT(("done\n")); break; case RX_PERF_SEND: DBFPRINT(("command ")); data = htonl(bytes); if (rx_Write(call, &data, 4) != 4) errx(1, "rx_Write failed to send size"); DBFPRINT(("sending(%d) ", bytes)); if (sendbytes(call, bytes)) errx(1, "sendbytes"); if (rx_Read(call, &data, 4) != 4) errx(1, "failed to read result from server"); if (data != htonl(RXPERF_MAGIC_COOKIE)) warn("server send wrong magic cookie in responce"); DBFPRINT(("done\n")); break; case RX_PERF_RPC: DBFPRINT(("commands ")); data = htonl(sendtimes); if (rx_Write(call, &data, 4) != 4) errx(1, "rx_Write failed to send command"); data = htonl(recvtimes); if (rx_Write(call, &data, 4) != 4) errx(1, "rx_Write failed to send command"); DBFPRINT(("send(%d) ", sendtimes)); sendbytes(call, sendtimes); DBFPRINT(("recv(%d) ", recvtimes)); readbytes(call, recvtimes); if (rx_Read(call, &bytes, 4) != 4) errx(1, "failed to read result from server"); if (bytes != htonl(RXPERF_MAGIC_COOKIE)) warn("server send wrong magic cookie in responce"); DBFPRINT(("done\n")); break; case RX_PERF_FILE: readfile(filename, &readwrite, &num); data = htonl(num); if (rx_Write(call, &data, sizeof(data)) != 4) errx(1, "rx_Write failed to send size"); if (rx_Write(call, readwrite, num * sizeof(u_int32_t)) != num * sizeof(u_int32_t)) errx(1, "rx_Write failed to send list"); for (i = 0; i < num; i++) { if (readwrite[i] == 0) readp = !readp; size = ntohl(readwrite[i]) * sizeof(u_int32_t); if (readp) { readbytes(call, size); DBFPRINT(("read\n")); } else { sendbytes(call, size); DBFPRINT(("send\n")); } } break; default: abort(); } rx_EndCall(call, 0); } end_and_print_timer(stamp); DBFPRINT(("done for good\n")); rx_Finalize(); } static void usage() { #define COMMON "" fprintf(stderr, "usage: %s client -c send -b \n", __progname); fprintf(stderr, "usage: %s client -c recv -b \n", __progname); fprintf(stderr, "usage: %s client -c rpc -S -R \n", __progname); fprintf(stderr, "usage: %s client -c file -f filename\n", __progname); fprintf(stderr, "%s: usage: common option to the client " "-w -r -T times -p port -s server\n", __progname); fprintf(stderr, "usage: %s server -p port\n", __progname); #undef COMMMON exit(1); } /* * do argument processing and call networking functions */ static int rxperf_server(int argc, char **argv) { int port = DEFAULT_PORT; char *ptr; int ch; while ((ch = getopt(argc, argv, "r:d:p:w:")) != -1) { switch (ch) { case 'd': #ifdef RXDEBUG rx_debugFile = fopen(optarg, "w"); if (rx_debugFile == NULL) err(1, "fopen %s", optarg); #else errx(1, "compiled without RXDEBUG"); #endif break; case 'r': rxread_size = strtol(optarg, &ptr, 0); if (ptr != 0 && ptr[0] != '\0') errx(1, "can't resolve readsize"); if (rxread_size > sizeof(somebuf)) errx(1, "%d > sizeof(somebuf) (%d)", rxread_size, sizeof(somebuf)); break; case 'p': port = strtol(optarg, &ptr, 0); if (ptr != 0 && ptr[0] != '\0') errx(1, "can't resolve portname"); break; case 'w': rxwrite_size = strtol(optarg, &ptr, 0); if (ptr != 0 && ptr[0] != '\0') errx(1, "can't resolve writesize"); if (rxwrite_size > sizeof(somebuf)) errx(1, "%d > sizeof(somebuf) (%d)", rxwrite_size, sizeof(somebuf)); break; default: usage(); } } if (optind != argc) usage(); do_server(htons(port)); return 0; } /* * do argument processing and call networking functions */ static int rxperf_client(int argc, char **argv) { char *host = DEFAULT_HOST; int bytes = DEFAULT_BYTES; int port = DEFAULT_PORT; char *filename = NULL; int32_t cmd; int sendtimes = 3; int recvtimes = 30; int times = 100; char *ptr; int ch; cmd = RX_PERF_UNKNOWN; while ((ch = getopt(argc, argv, "T:S:R:b:c:d:p:r:s:w:f:")) != -1) { switch (ch) { case 'b': bytes = strtol(optarg, &ptr, 0); if (ptr && *ptr != '\0') errx(1, "can't resolve number of bytes to transfer"); break; case 'c': if (strcasecmp(optarg, "send") == 0) cmd = RX_PERF_SEND; else if (strcasecmp(optarg, "recv") == 0) cmd = RX_PERF_RECV; else if (strcasecmp(optarg, "rpc") == 0) cmd = RX_PERF_RPC; else if (strcasecmp(optarg, "file") == 0) cmd = RX_PERF_FILE; else errx(1, "unknown command %s", optarg); break; case 'd': #ifdef RXDEBUG rx_debugFile = fopen(optarg, "w"); if (rx_debugFile == NULL) err(1, "fopen %s", optarg); #else errx(1, "compiled without RXDEBUG"); #endif break; case 'p': port = strtol(optarg, &ptr, 0); if (ptr != 0 && ptr[0] != '\0') errx(1, "can't resolve portname"); break; case 'r': rxread_size = strtol(optarg, &ptr, 0); if (ptr != 0 && ptr[0] != '\0') errx(1, "can't resolve readsize"); if (rxread_size > sizeof(somebuf)) errx(1, "%d > sizeof(somebuf) (%d)", rxread_size, sizeof(somebuf)); break; case 's': host = strdup(optarg); if (host == NULL) err(1, "strdup"); break; case 'w': rxwrite_size = strtol(optarg, &ptr, 0); if (ptr != 0 && ptr[0] != '\0') errx(1, "can't resolve writesize"); if (rxwrite_size > sizeof(somebuf)) errx(1, "%d > sizeof(somebuf) (%d)", rxwrite_size, sizeof(somebuf)); break; case 'T': times = strtol(optarg, &ptr, 0); if (ptr && *ptr != '\0') errx(1, "can't resolve number of bytes to transfer"); break; case 'S': sendtimes = strtol(optarg, &ptr, 0); if (ptr && *ptr != '\0') errx(1, "can't resolve number of bytes to transfer"); break; case 'R': recvtimes = strtol(optarg, &ptr, 0); if (ptr && *ptr != '\0') errx(1, "can't resolve number of bytes to transfer"); break; case 'f': filename = optarg; break; default: usage(); } } if (optind != argc) usage(); if (cmd == RX_PERF_UNKNOWN) errx(1, "no command given to the client"); do_client(host, htons(port), filename, cmd, times, bytes, sendtimes, recvtimes); return 0; } /* * setup world and call cmd */ int main(int argc, char **argv) { PROCESS pid; __progname = strrchr(argv[0], '/'); if (__progname == 0) __progname = argv[0]; signal(SIGUSR1, sigusr1); signal(SIGINT, sigint); LWP_InitializeProcessSupport(LWP_NORMAL_PRIORITY, &pid); memset(somebuf, 0, sizeof(somebuf)); if (argc >= 2 && strcmp(argv[1], "server") == 0) rxperf_server(argc - 1, argv + 1); else if (argc >= 2 && strcmp(argv[1], "client") == 0) rxperf_client(argc - 1, argv + 1); else usage(); return 0; }