qperf/src/qperf.c

2861 lines
80 KiB
C

/*
* qperf - main.
* Measure socket and RDMA performance.
*
* Copyright (c) 2002-2009 Johann George. All rights reserved.
* Copyright (c) 2006-2009 QLogic Corporation. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
* General Public License (GPL) Version 2, available from the file
* COPYING in the main directory of this source tree, or the
* OpenIB.org BSD license below:
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
*
* - 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.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#define _GNU_SOURCE
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <sched.h>
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <sys/ioctl.h>
#include <sys/times.h>
#include <sys/select.h>
#include <sys/utsname.h>
#include "qperf.h"
/*
* Configurable parameters. If your change makes this version of qperf
* incompatible with previous versions (usually a change to the Req structure),
* increment VER_MIN and set VER_INC to 0. Otherwise, just increment VER_INC.
* VER_MAJ is reserved for major changes.
*/
#define VER_MAJ 0 /* Major version */
#define VER_MIN 4 /* Minor version */
#define VER_INC 5 /* Incremental version */
#define LISTENQ 5 /* Size of listen queue */
#define BUFSIZE 1024 /* Size of buffers */
/*
* Default parameter values.
*/
#define DEF_TIME 2 /* Test duration */
#define DEF_TIMEOUT 5 /* Timeout */
#define DEF_PRECISION 3 /* Precision displayed */
#define DEF_LISTEN_PORT 19765 /* Listen port */
/*
* Option list.
*/
typedef struct OPTION {
char *name; /* Name of option */
char *type; /* Type */
int arg1; /* First argument */
int arg2; /* Second argument */
} OPTION;
/*
* Used to loop through a range of values.
*/
typedef struct LOOP {
struct LOOP *next; /* Pointer to next loop */
OPTION *option; /* Loop variable */
long init; /* Initial value */
long last; /* Last value */
long incr; /* Increment */
int mult; /* If set, multiply, otherwise add */
} LOOP;
/*
* Parameter information.
*/
typedef struct PAR_INFO {
PAR_INDEX index; /* Index into parameter table */
int type; /* Type */
void *ptr; /* Pointer to value */
char *name; /* Option name */
int set; /* Parameter has been set */
int used; /* Parameter has been used */
int inuse; /* Parameter is in use */
} PAR_INFO;
/*
* Parameter name association.
*/
typedef struct PAR_NAME {
char *name; /* Name */
PAR_INDEX loc_i; /* Local index */
PAR_INDEX rem_i; /* Remote index */
} PAR_NAME;
/*
* A simple mapping between two strings.
*/
typedef struct DICT {
char *str1; /* String 1 */
char *str2; /* String 2 */
} DICT;
/*
* Test prototype.
*/
typedef struct TEST {
char *name; /* Test name */
void (*client)(void); /* Client function */
void (*server)(void); /* Server function */
} TEST;
/*
* Used to save output data for formatting.
*/
typedef struct SHOW {
char *pref; /* Name prefix */
char *name; /* Name */
char *data; /* Data */
char *unit; /* Unit */
char *altn; /* Alternative value */
} SHOW;
/*
* Configuration information.
*/
typedef struct CONF {
char node[STRSIZE]; /* Node */
char cpu[STRSIZE]; /* CPU */
char os[STRSIZE]; /* Operating System */
char qperf[STRSIZE]; /* Qperf version */
} CONF;
/*
* Function prototypes.
*/
static void add_ustat(USTAT *l, USTAT *r);
static long arg_long(char ***argvp);
static long arg_size(char ***argvp);
static char *arg_strn(char ***argvp);
static long arg_time(char ***argvp);
static void calc_node(RESN *resn, STAT *stat);
static void calc_results(void);
static void client(TEST *test);
static int cmpsub(char *s2, char *s1);
static char *commify(char *data);
static void dec_req_data(REQ *host);
static void dec_req_version(REQ *host);
static void dec_stat(STAT *host);
static void dec_ustat(USTAT *host);
static void do_args(char *args[]);
static void do_loop(LOOP *loop, TEST *test);
static void do_option(OPTION *option, char ***argvp);
static void enc_req(REQ *host);
static void enc_stat(STAT *host);
static void enc_ustat(USTAT *host);
static TEST *find_test(char *name);
static OPTION *find_option(char *name);
static void get_conf(CONF *conf);
static void get_cpu(CONF *conf);
static void get_times(CLOCK timex[T_N]);
static void initialize(void);
static void init_lstat(void);
static char *loop_arg(char **pp);
static int nice_1024(char *pref, char *name, long long value);
static PAR_INFO *par_info(PAR_INDEX index);
static PAR_INFO *par_set(char *name, PAR_INDEX index);
static int par_isset(PAR_INDEX index);
static void parse_loop(char ***argvp);
static void place_any(char *pref, char *name, char *unit, char *data,
char *altn);
static void place_show(void);
static void place_val(char *pref, char *name, char *unit, double value);
static void remotefd_close(void);
static void remotefd_setup(void);
static void run_client_conf(void);
static void run_client_quit(void);
static void run_server_conf(void);
static void run_server_quit(void);
static void server(void);
static void server_listen(void);
static int server_recv_request(void);
static void set_affinity(void);
static void set_signals(void);
static void show_debug(void);
static void show_info(MEASURE measure);
static void show_rest(void);
static void show_used(void);
static void sig_alrm(int signo, siginfo_t *siginfo, void *ucontext);
static void sig_quit(int signo, siginfo_t *siginfo, void *ucontext);
static void sig_urg(int signo, siginfo_t *siginfo, void *ucontext);
static char *skip_colon(char *s);
static void start_test_timer(int seconds);
static long str_size(char *arg, char *str);
static void strncopy(char *d, char *s, int n);
static char *two_args(char ***argvp);
static int verbose(int type, double value);
static void version_error(void);
static void view_band(int type, char *pref, char *name, double value);
static void view_cost(int type, char *pref, char *name, double value);
static void view_cpus(int type, char *pref, char *name, double value);
static void view_rate(int type, char *pref, char *name, double value);
static void view_long(int type, char *pref, char *name, long long value);
static void view_size(int type, char *pref, char *name, long long value);
static void view_strn(int type, char *pref, char *name, char *value);
static void view_time(int type, char *pref, char *name, double value);
/*
* Configurable variables.
*/
static int ListenPort = DEF_LISTEN_PORT;
static int Precision = DEF_PRECISION;
static int ServerWait = DEF_TIMEOUT;
static int UseBitsPerSec = 0;
/*
* Static variables.
*/
static REQ RReq;
static STAT IStat;
static int ListenFD;
static LOOP *Loops;
static int ProcStatFD;
static STAT RStat;
static int ShowIndex;
static SHOW ShowTable[256];
static int UnifyUnits;
static int UnifyNodes;
static int VerboseConf;
static int VerboseStat;
static int VerboseTime;
static int VerboseUsed;
/*
* Global variables.
*/
RES Res;
REQ Req;
STAT LStat;
char *TestName;
char *ServerName;
SS ServerAddr;
int ServerAddrLen;
int RemoteFD;
int Debug;
volatile int Finished;
/*
* Parameter names. This is used to print out the names of the parameters that
* have been set.
*/
PAR_NAME ParName[] ={
{ "access_recv", L_ACCESS_RECV, R_ACCESS_RECV },
{ "affinity", L_AFFINITY, R_AFFINITY },
{ "alt_port", L_ALT_PORT, R_ALT_PORT },
{ "flip", L_FLIP, R_FLIP },
{ "id", L_ID, R_ID },
{ "msg_size", L_MSG_SIZE, R_MSG_SIZE },
{ "mtu_size", L_MTU_SIZE, R_MTU_SIZE },
{ "no_msgs", L_NO_MSGS, R_NO_MSGS },
{ "poll_mode", L_POLL_MODE, R_POLL_MODE },
{ "port", L_PORT, R_PORT },
{ "rd_atomic", L_RD_ATOMIC, R_RD_ATOMIC },
{ "service_level", L_SL, R_SL },
{ "sock_buf_size", L_SOCK_BUF_SIZE, R_SOCK_BUF_SIZE },
{ "src_path_bits", L_SRC_PATH_BITS, R_SRC_PATH_BITS },
{ "time", L_TIME, R_TIME },
{ "timeout", L_TIMEOUT, R_TIMEOUT },
{ "use_cm", L_USE_CM, R_USE_CM },
};
/*
* Parameters. These must be listed in the same order as the indices are
* defined.
*/
PAR_INFO ParInfo[P_N] ={
{ P_NULL, },
{ L_ACCESS_RECV, 'l', &Req.access_recv },
{ R_ACCESS_RECV, 'l', &RReq.access_recv },
{ L_AFFINITY, 'l', &Req.affinity },
{ R_AFFINITY, 'l', &RReq.affinity },
{ L_ALT_PORT, 'l', &Req.alt_port },
{ R_ALT_PORT, 'l', &RReq.alt_port },
{ L_FLIP, 'l', &Req.flip },
{ R_FLIP, 'l', &RReq.flip },
{ L_ID, 'p', &Req.id },
{ R_ID, 'p', &RReq.id },
{ L_MSG_SIZE, 's', &Req.msg_size },
{ R_MSG_SIZE, 's', &RReq.msg_size },
{ L_MTU_SIZE, 's', &Req.mtu_size },
{ R_MTU_SIZE, 's', &RReq.mtu_size },
{ L_NO_MSGS, 'l', &Req.no_msgs },
{ R_NO_MSGS, 'l', &RReq.no_msgs },
{ L_POLL_MODE, 'l', &Req.poll_mode },
{ R_POLL_MODE, 'l', &RReq.poll_mode },
{ L_PORT, 'l', &Req.port },
{ R_PORT, 'l', &RReq.port },
{ L_RD_ATOMIC, 'l', &Req.rd_atomic },
{ R_RD_ATOMIC, 'l', &RReq.rd_atomic },
{ L_SL, 'l', &Req.sl },
{ R_SL, 'l', &RReq.sl },
{ L_SOCK_BUF_SIZE, 's', &Req.sock_buf_size },
{ R_SOCK_BUF_SIZE, 's', &RReq.sock_buf_size },
{ L_SRC_PATH_BITS, 's', &Req.src_path_bits },
{ R_SRC_PATH_BITS, 's', &RReq.src_path_bits },
{ L_STATIC_RATE, 'p', &Req.static_rate },
{ R_STATIC_RATE, 'p', &RReq.static_rate },
{ L_TIME, 't', &Req.time },
{ R_TIME, 't', &RReq.time },
{ L_TIMEOUT, 't', &Req.timeout },
{ R_TIMEOUT, 't', &RReq.timeout },
{ L_USE_CM, 'l', &Req.use_cm },
{ R_USE_CM, 'l', &RReq.use_cm },
};
/*
* Renamed options. First is old, second is new.
*/
DICT Renamed[] = {
/* -a becomes -ca (--cpu_affinity) */
{ "--affinity", "--cpu_affinity" },
{ "-a", "-ca" },
{ "--loc_affinity", "--loc_cpu_affinity" },
{ "-la", "-lca" },
{ "--rem_affinity", "--rem_cpu_affinity" },
{ "-ra", "-rca" },
/* -r becomes -sr (--static_rate) */
{ "--rate", "--static_rate" },
{ "-r", "-sr" },
{ "--loc_rate", "--loc_static_rate" },
{ "-lr", "-lsr" },
{ "--rem_rate", "--rem_static_rate" },
{ "-rr", "-rsr" },
/* -p becomes -ip (--ip_port) */
{ "--port", "--ip_port" },
{ "-p", "-ip" },
/* -P becomes -cp (--cq_poll) */
{ "--poll", "--cq_poll" },
{ "-P", "-cp" },
{ "--loc_poll", "--loc_cq_poll" },
{ "-lP", "-lcp" },
{ "--rem_poll", "--rem_cq_poll" },
{ "-rP", "-rcp" },
/* -R becomes -nr (--rd_atomic) */
{ "-R", "-nr" },
{ "-lR", "-lnr" },
{ "-rR", "-rnr" },
/* -T becomes -to (--timeout) */
{ "-T", "-to" },
{ "-lT", "-lto" },
{ "-rT", "-rto" },
/* -S becomes -sb (--sock_buf_size) */
{ "-S", "-sb" },
{ "-lS", "-lsb" },
{ "-rS", "-rsb" },
/* -W becomes -ws (--wait_server) */
{ "--wait", "--wait_server" },
{ "-W", "-ws" },
/* verbose options */
{ "-vC", "-vvc", },
{ "-vS", "-vvs", },
{ "-vT", "-vvt", },
{ "-vU", "-vvu", },
/* options that are on */
{ "-aro", "-ar1" },
{ "-cmo", "-cm1" },
{ "-fo", "-f1" },
{ "-cpo", "-cp1" },
{ "-lcpo", "-lcp1" },
{ "-rcpo", "-rcp1" },
/* miscellaneous */
{ "-Ar", "-ar" },
{ "-M", "-mt" },
{ "-u", "-uu", },
};
/*
* Options. The type field (2nd column) is used by do_option. If it begins
* with a S, it is a valid server option. If it begins with a X, it is
* obsolete and will eventually go away.
*/
OPTION Options[] ={
{ "--access_recv", "int", L_ACCESS_RECV, R_ACCESS_RECV },
{ "-ar", "int", L_ACCESS_RECV, R_ACCESS_RECV },
{ "-ar1", "set1", L_ACCESS_RECV, R_ACCESS_RECV },
{ "--alt_port", "int", L_ALT_PORT, R_ALT_PORT },
{ "-ap", "int", L_ALT_PORT, R_ALT_PORT },
{ "--loc_alt_port", "int", L_ALT_PORT, },
{ "-lap", "int", L_ALT_PORT, },
{ "--rem_alt_port", "int", R_ALT_PORT },
{ "-rap", "int", R_ALT_PORT },
{ "--cpu_affinity", "int", L_AFFINITY, R_AFFINITY },
{ "-ca", "int", L_AFFINITY, R_AFFINITY },
{ "--loc_cpu_affinity", "int", L_AFFINITY, },
{ "-lca", "int", L_AFFINITY, },
{ "--rem_cpu_affinity", "int", R_AFFINITY },
{ "-rca", "int", R_AFFINITY },
{ "--debug", "Sdebug", },
{ "-D", "Sdebug", },
{ "--flip", "int", L_FLIP, R_FLIP },
{ "-f", "int", L_FLIP, R_FLIP },
{ "-f1", "set1", L_FLIP, R_FLIP },
{ "--help", "help" },
{ "-h", "help" },
{ "--host", "host", },
{ "-H", "host", },
{ "--id", "str", L_ID, R_ID },
{ "-i", "str", L_ID, R_ID },
{ "--loc_id", "str", L_ID, },
{ "-li", "str", L_ID, },
{ "--rem_id", "str", R_ID },
{ "-ri", "str", R_ID },
{ "--listen_port", "Slp", },
{ "-lp", "Slp", },
{ "--loop", "loop", },
{ "-oo", "loop", },
{ "--msg_size", "size", L_MSG_SIZE, R_MSG_SIZE },
{ "-m", "size", L_MSG_SIZE, R_MSG_SIZE },
{ "--mtu_size", "size", L_MTU_SIZE, R_MTU_SIZE },
{ "-mt", "size", L_MTU_SIZE, R_MTU_SIZE },
{ "--no_msgs", "int", L_NO_MSGS, R_NO_MSGS },
{ "-n", "int", L_NO_MSGS, R_NO_MSGS },
{ "--cq_poll", "int", L_POLL_MODE, R_POLL_MODE },
{ "-cp", "int", L_POLL_MODE, R_POLL_MODE },
{ "-cp1", "set1", L_POLL_MODE, R_POLL_MODE },
{ "--loc_cq_poll", "int", L_POLL_MODE, },
{ "-lcp", "int", L_POLL_MODE, },
{ "-lcp1", "set1", L_POLL_MODE },
{ "--rem_cq_poll", "int", R_POLL_MODE },
{ "-rcp", "int", R_POLL_MODE },
{ "-rcp1", "set1", R_POLL_MODE },
{ "--ip_port", "int", L_PORT, R_PORT },
{ "-ip", "int", L_PORT, R_PORT },
{ "--precision", "precision", },
{ "-e", "precision", },
{ "--rd_atomic", "int", L_RD_ATOMIC, R_RD_ATOMIC },
{ "-nr", "int", L_RD_ATOMIC, R_RD_ATOMIC },
{ "--loc_rd_atomic", "int", L_RD_ATOMIC, },
{ "-lnr", "int", L_RD_ATOMIC, },
{ "--rem_rd_atomic", "int", R_RD_ATOMIC },
{ "-rnr", "int", R_RD_ATOMIC },
{ "--service_level", "sl", L_SL, R_SL },
{ "-sl", "sl", L_SL, R_SL },
{ "--loc_service_level", "sl", L_SL },
{ "-lsl", "sl", L_SL },
{ "--rem_service_level", "sl", R_SL },
{ "-rsl", "sl", R_SL },
{ "--sock_buf_size", "size", L_SOCK_BUF_SIZE, R_SOCK_BUF_SIZE },
{ "-sb", "size", L_SOCK_BUF_SIZE, R_SOCK_BUF_SIZE },
{ "--loc_sock_buf_size", "size", L_SOCK_BUF_SIZE },
{ "-lsb", "size", L_SOCK_BUF_SIZE },
{ "--rem_sock_buf_size", "size", R_SOCK_BUF_SIZE },
{ "-rsb", "size", R_SOCK_BUF_SIZE },
{ "--src_path_bits", "size", L_SRC_PATH_BITS, R_SRC_PATH_BITS },
{ "-sp", "size", L_SRC_PATH_BITS, R_SRC_PATH_BITS },
{ "--loc_src_path_bits", "size", L_SRC_PATH_BITS },
{ "-lsp", "size", L_SRC_PATH_BITS },
{ "--rem_src_path_bits", "size", R_SRC_PATH_BITS },
{ "-rsp", "size", R_SRC_PATH_BITS },
{ "--static_rate", "str", L_STATIC_RATE, R_STATIC_RATE },
{ "-sr", "str", L_STATIC_RATE, R_STATIC_RATE },
{ "--loc_static_rate", "str", L_STATIC_RATE },
{ "-lsr", "str", L_STATIC_RATE },
{ "--rem_static_rate", "str", R_STATIC_RATE },
{ "-rsr", "str", R_STATIC_RATE },
{ "--time", "time", L_TIME, R_TIME },
{ "-t", "time", L_TIME, R_TIME },
{ "--timeout", "time", L_TIMEOUT, R_TIMEOUT },
{ "-to", "time", L_TIMEOUT, R_TIMEOUT },
{ "--loc_timeout", "Stime", L_TIMEOUT },
{ "-lto", "Stime", L_TIMEOUT },
{ "--rem_timeout", "time", R_TIMEOUT },
{ "-rto", "time", R_TIMEOUT },
{ "--unify_nodes", "un", },
{ "-un", "un", },
{ "--unify_units", "uu", },
{ "-uu", "uu", },
{ "--use_bits_per_sec", "ub", },
{ "-ub", "ub", },
{ "--use_cm", "int", L_USE_CM, R_USE_CM },
{ "-cm", "int", L_USE_CM, R_USE_CM },
{ "-cm1", "set1", L_USE_CM, R_USE_CM },
{ "--verbose", "v", },
{ "-v", "v", },
{ "--verbose_conf", "vc", },
{ "-vc", "vc", },
{ "--verbose_stat", "vs", },
{ "-vs", "vs", },
{ "--verbose_time", "vt", },
{ "-vt", "vt", },
{ "--verbose_used", "vu", },
{ "-vu", "vu", },
{ "--verbose_more", "vv", },
{ "-vv", "vv", },
{ "--verbose_more_conf", "vvc", },
{ "-vvc", "vvc", },
{ "--verbose_more_stat", "vvs", },
{ "-vvs", "vvs", },
{ "--verbose_more_time", "vvt", },
{ "-vvt", "vvt", },
{ "--verbose_more_used", "vvu", },
{ "-vvu", "vvu", },
{ "--version", "version", },
{ "-V", "version", },
{ "--wait_server", "wait", },
{ "-ws", "wait", },
};
/*
* Tests.
*/
#define test(n) { #n, run_client_##n, run_server_##n }
TEST Tests[] ={
test(conf),
test(quit),
test(rds_bw),
test(rds_lat),
test(sctp_bw),
test(sctp_lat),
test(sdp_bw),
test(sdp_lat),
test(tcp_bw),
test(tcp_lat),
test(udp_bw),
test(udp_lat),
#ifdef RDMA
test(rc_bi_bw),
test(rc_bw),
test(rc_compare_swap_mr),
test(rc_fetch_add_mr),
test(rc_lat),
test(rc_rdma_read_bw),
test(rc_rdma_read_lat),
test(rc_rdma_write_bw),
test(rc_rdma_write_lat),
test(rc_rdma_write_poll_lat),
test(uc_bi_bw),
test(uc_bw),
test(uc_lat),
test(uc_rdma_write_bw),
test(uc_rdma_write_lat),
test(uc_rdma_write_poll_lat),
test(ud_bi_bw),
test(ud_bw),
test(ud_lat),
test(ver_rc_compare_swap),
test(ver_rc_fetch_add),
test(xrc_bi_bw),
test(xrc_bw),
test(xrc_lat),
#endif
};
int
main(int argc, char *argv[])
{
initialize();
set_signals();
do_args(&argv[1]);
return 0;
}
/*
* Initialize variables.
*/
static void
initialize(void)
{
int i;
RemoteFD = -1;
for (i = 0; i < P_N; ++i)
if (ParInfo[i].index != i)
error(BUG, "initialize: ParInfo: out of order: %d", i);
ProcStatFD = open("/proc/stat", 0);
if (ProcStatFD < 0)
error(SYS, "cannot open /proc/stat");
IStat.no_cpus = sysconf(_SC_NPROCESSORS_ONLN);
IStat.no_ticks = sysconf(_SC_CLK_TCK);
}
/*
* Look for a colon and skip past it and any spaces.
*/
static char *
skip_colon(char *s)
{
for (;;) {
int c = *s++;
if (c == ':')
break;
if (c == '\0')
return 0;
}
while (*s == ' ')
s++;
return s;
}
/*
* A case insensitive string compare. s2 must at least contain all of s1 but
* can be longer.
*/
static int
cmpsub(char *s2, char *s1)
{
for (;;) {
int c1 = *s1++;
int c2 = *s2++;
if (c1 == '\0')
return 1;
if (c2 == '\0')
return 0;
if (tolower(c1) != tolower(c2))
return 0;
}
}
/*
* Set up signal handlers.
*/
static void
set_signals(void)
{
struct sigaction act ={
.sa_sigaction = sig_alrm,
.sa_flags = SA_SIGINFO
};
sigaction(SIGALRM, &act, 0);
sigaction(SIGPIPE, &act, 0);
act.sa_sigaction = sig_quit;
sigaction(SIGQUIT, &act, 0);
act.sa_sigaction = sig_urg;
sigaction(SIGURG, &act, 0);
}
/*
* Note that time is up.
*/
static void
sig_alrm(int signo, siginfo_t *siginfo, void *ucontext)
{
set_finished();
}
/*
* Our child sends us a quit when it wishes us to exit.
*/
static void
sig_quit(int signo, siginfo_t *siginfo, void *ucontext)
{
exit(0);
}
/*
* Called when a TCP/IP out-of-band message is received.
*/
static void
sig_urg(int signo, siginfo_t *siginfo, void *ucontext)
{
urgent();
}
/*
* Parse arguments.
*/
static void
do_args(char *args[])
{
int isClient = 0;
int testSpecified = 0;
while (*args) {
char *arg = *args;
if (arg[0] == '-') {
OPTION *option = find_option(arg);
if (!option)
error(0, "%s: bad option; try: qperf --help options", arg);
if (option->type[0] != 'S')
isClient = 1;
do_option(option, &args);
} else {
isClient = 1;
if (!ServerName)
ServerName = arg;
else {
TEST *test = find_test(arg);
if (!test)
error(0, "%s: bad test; try: qperf --help tests", arg);
do_loop(Loops, test);
testSpecified = 1;
}
++args;
}
}
if (!isClient)
server();
else if (!testSpecified) {
if (!ServerName)
error(0, "you used a client-only option but did not specify the "
"server name.\nDo you want to be a client or server?");
if (find_test(ServerName))
error(0, "must specify host name first; try: qperf --help");
error(0, "must specify a test type; try: qperf --help");
}
}
/*
* Loop through a series of tests.
*/
static void
do_loop(LOOP *loop, TEST *test)
{
if (!loop)
client(test);
else {
long l = loop->init;
while (l <= loop->last) {
char buf[64];
char *args[2] = {loop->option->name, buf};
char **argv = args;
snprintf(buf, sizeof(buf), "%ld", l);
do_option(loop->option, &argv);
do_loop(loop->next, test);
if (loop->mult)
l *= loop->incr;
else
l += loop->incr;
}
}
}
/*
* Given the name of an option, find it.
*/
static OPTION *
find_option(char *name)
{
int n;
DICT *d;
OPTION *p;
n = cardof(Renamed);
d = Renamed;
for (; n--; ++d) {
if (streq(name, d->str1)) {
char *msg = "warning: obsolete option: %s; use %s instead";
error(RET, msg, name, d->str2);
name = d->str2;
break;
}
}
n = cardof(Options);
p = Options;
for (; n--; ++p)
if (streq(name, p->name))
return p;
return 0;
}
/*
* Given the name of a test, find it.
*/
static TEST *
find_test(char *name)
{
int n = cardof(Tests);
TEST *p = Tests;
for (; n--; ++p)
if (streq(name, p->name))
return p;
return 0;
}
/*
* Handle options.
*/
static void
do_option(OPTION *option, char ***argvp)
{
char *t = option->type;
if (*t == 'S')
++t;
if (streq(t, "debug")) {
Debug = 1;
*argvp += 1;
} else if (streq(t, "help")) {
/* Help */
char **usage;
char *category = (*argvp)[1];
if (!category)
category = "main";
for (usage = Usage; *usage; usage += 2)
if (streq(*usage, category))
break;
if (!*usage) {
error(0,
"cannot find help category %s; try: qperf --help categories",
category);
}
printf("%s", usage[1]);
exit(0);
} else if (streq(t, "host")) {
ServerName = arg_strn(argvp);
} else if (streq(t, "int")) {
long v = arg_long(argvp);
setp_u32(option->name, option->arg1, v);
setp_u32(option->name, option->arg2, v);
} else if (streq(t, "loop")) {
parse_loop(argvp);
} else if (streq(t, "lp")) {
ListenPort = arg_long(argvp);
} else if (streq(t, "precision")) {
Precision = arg_long(argvp);
} else if (streq(t, "set1")) {
setp_u32(option->name, option->arg1, 1);
setp_u32(option->name, option->arg2, 1);
*argvp += 1;
} else if (streq(t, "size")) {
long v = arg_size(argvp);
setp_u32(option->name, option->arg1, v);
setp_u32(option->name, option->arg2, v);
} else if (streq(t, "sl")) {
long v = arg_long(argvp);
if (v < 0 || v > 15)
error(0, "service level must be between 0 and 15: %d given", v);
setp_u32(option->name, option->arg1, v);
setp_u32(option->name, option->arg2, v);
} else if (streq(t, "str")) {
char *s = arg_strn(argvp);
setp_str(option->name, option->arg1, s);
setp_str(option->name, option->arg2, s);
} else if (streq(t, "time")) {
long v = arg_time(argvp);
setp_u32(option->name, option->arg1, v);
setp_u32(option->name, option->arg2, v);
} else if (streq(t, "ub")) {
UseBitsPerSec = 1;
*argvp += 1;
} else if (streq(t, "un")) {
UnifyNodes = 1;
*argvp += 1;
} else if (streq(t, "uu")) {
UnifyUnits = 1;
*argvp += 1;
} else if (streq(t, "v")) {
if (VerboseConf < 1)
VerboseConf = 1;
if (VerboseStat < 1)
VerboseStat = 1;
if (VerboseTime < 1)
VerboseTime = 1;
if (VerboseUsed < 1)
VerboseUsed = 1;
*argvp += 1;
} else if (streq(t, "vc")) {
VerboseConf = 1;
*argvp += 1;
} else if (streq(t, "version")) {
printf("qperf %d.%d.%d\n", VER_MAJ, VER_MIN, VER_INC);
exit(0);
} else if (streq(t, "vs")) {
VerboseStat = 1;
*argvp += 1;
} else if (streq(t, "vt")) {
VerboseTime = 1;
*argvp += 1;
} else if (streq(t, "vu")) {
VerboseUsed = 1;
*argvp += 1;
} else if (streq(t, "vv")) {
VerboseConf = 2;
VerboseStat = 2;
VerboseTime = 2;
VerboseUsed = 2;
*argvp += 1;
} else if (streq(t, "vvc")) {
VerboseConf = 2;
*argvp += 1;
} else if (streq(t, "vvs")) {
VerboseStat = 2;
*argvp += 1;
} else if (streq(t, "vvt")) {
VerboseTime = 2;
*argvp += 1;
} else if (streq(t, "vvu")) {
VerboseUsed = 2;
*argvp += 1;
} else if (streq(t, "wait")) {
ServerWait = arg_time(argvp);
} else
error(BUG, "do_option: unknown type: %s", t);
}
/*
* Parse a loop option.
*/
static void
parse_loop(char ***argvp)
{
char *opt = **argvp;
char *s = two_args(argvp);
char *name = loop_arg(&s);
char *init = loop_arg(&s);
char *last = loop_arg(&s);
char *incr = loop_arg(&s);
LOOP *loop = qmalloc(sizeof(LOOP));
memset(loop, 0, sizeof(*loop));
/* Parse variable name */
{
int n = cardof(Options);
OPTION *p = Options;
if (!name)
name = "msg_size";
for (;;) {
char *s = p->name;
if (n-- == 0)
error(0, "%s: %s: no such variable", opt, name);
if (*s++ != '-')
continue;
if (*s == '-')
s++;
if (streq(name, s))
break;
p++;
}
loop->option = p;
}
/* Parse increment */
if (!incr)
loop->incr = 0;
else {
if (incr[0] == '*') {
incr++;
loop->mult = 1;
}
loop->incr = str_size(incr, opt);
if (loop->incr < 1)
error(0, "%s: %s: increment must be positive", opt, incr);
}
/* Parse initial value */
if (init)
loop->init = str_size(init, opt);
else
loop->init = loop->mult ? 1 : 0;
/* Parse last value */
if (!last)
error(0, "%s: must specify limit", opt);
loop->last = str_size(last, opt);
/* Insert into loop list */
if (!Loops)
Loops = loop;
else {
LOOP *l = Loops;
while (l->next)
l = l->next;
l->next = loop;
}
}
/*
* Given a string consisting of arguments separated by colons, return the next
* argument and prepare for scanning the next one.
*/
static char *
loop_arg(char **pp)
{
char *a = *pp;
char *p = a;
while (*p) {
if (*p == ':') {
*p = '\0';
*pp = p + 1;
break;
}
++p;
}
return a[0] ? a : 0;
}
/*
* Ensure that two arguments exist.
*/
static char *
two_args(char ***argvp)
{
char **argv = *argvp;
if (!argv[1])
error(0, "%s: missing argument", argv[0]);
*argvp += 2;
return argv[1];
}
/*
* Return the value of a long argument. It must be non-negative.
*/
static long
arg_long(char ***argvp)
{
char **argv = *argvp;
char *p;
long l;
if (!argv[1])
error(0, "missing argument to %s", argv[0]);
l = strtol(argv[1], &p, 10);
if (p[0] != '\0')
error(0, "bad argument: %s", argv[1]);
if (l < 0)
error(0, "%s requires a non-negative number", argv[0]);
*argvp += 2;
return l;
}
/*
* Return the value of a size argument.
*/
static long
arg_size(char ***argvp)
{
long l;
char **argv = *argvp;
*argvp += 2;
if (!argv[1])
error(0, "missing argument to %s", argv[0]);
l = str_size(argv[1], argv[0]);
if (l < 0)
error(0, "%s requires a non-negative number", argv[0]);
return l;
}
/*
* Scan a size argument from a string.
*/
static long
str_size(char *str, char *arg)
{
char *p;
long m = 1;
long double d = strtold(str, &p);
if (p[0] == '\0')
m = 1;
else if (streq(p, "kb") || streq(p, "k"))
m = 1000;
else if (streq(p, "mb") || streq(p, "m"))
m = 1000 * 1000;
else if (streq(p, "gb") || streq(p, "g"))
m = 1000 * 1000 * 1000;
else if (streq(p, "kib") || streq(p, "K"))
m = 1024;
else if (streq(p, "mib") || streq(p, "M"))
m = 1024 * 1024;
else if (streq(p, "gib") || streq(p, "G"))
m = 1024 * 1024 * 1024;
else
error(0, "%s: bad size: %s", arg, str);
return d * m;
}
/*
* Return the value of a string argument.
*/
static char *
arg_strn(char ***argvp)
{
char **argv = *argvp;
if (!argv[1])
error(0, "missing argument to %s", argv[0]);
*argvp += 2;
return argv[1];
}
/*
* Return the value of a size argument.
*/
static long
arg_time(char ***argvp)
{
char *p;
long double d;
long l = 0;
char **argv = *argvp;
if (!argv[1])
error(0, "missing argument to %s", argv[0]);
d = strtold(argv[1], &p);
if (d < 0)
error(0, "%s requires a non-negative number", argv[0]);
if (p[0] == '\0')
l = (long)d;
else {
int u = *p;
if (p[1] != '\0')
error(0, "bad argument: %s", argv[1]);
if (u == 's' || u == 'S')
l = (long)d;
else if (u == 'm' || u == 'M')
l = (long)(d * (60));
else if (u == 'h' || u == 'H')
l = (long)(d * (60 * 60));
else if (u == 'd' || u == 'D')
l = (long)(d * (60 * 60 * 24));
else
error(0, "bad argument: %s", argv[1]);
}
*argvp += 2;
return l;
}
/*
* Set a value stored in a 32 bit value without letting anyone know we set it.
*/
void
setv_u32(PAR_INDEX index, uint32_t l)
{
PAR_INFO *p = par_info(index);
*((uint32_t *)p->ptr) = l;
}
/*
* Set an option stored in a 32 bit value.
*/
void
setp_u32(char *name, PAR_INDEX index, uint32_t l)
{
PAR_INFO *p = par_set(name, index);
if (!p)
return;
*((uint32_t *)p->ptr) = l;
}
/*
* Set an option stored in a string vector.
*/
void
setp_str(char *name, PAR_INDEX index, char *s)
{
PAR_INFO *p = par_set(name, index);
if (!p)
return;
if (strlen(s) >= STRSIZE)
error(0, "%s: too long", s);
strcpy(p->ptr, s);
}
/*
* Note a parameter as being used.
*/
void
par_use(PAR_INDEX index)
{
PAR_INFO *p = par_info(index);
p->used = 1;
p->inuse = 1;
}
/*
* Set the PAR_INFO.name value.
*/
static PAR_INFO *
par_set(char *name, PAR_INDEX index)
{
PAR_INFO *p = par_info(index);
if (index == P_NULL)
return 0;
if (name) {
p->name = name;
p->set = 1;
} else {
p->used = 1;
p->inuse = 1;
if (p->name)
return 0;
}
return p;
}
/*
* Determine if a parameter is set.
*/
static int
par_isset(PAR_INDEX index)
{
return par_info(index)->name != 0;
}
/*
* Index the ParInfo table.
*/
static PAR_INFO *
par_info(PAR_INDEX index)
{
PAR_INFO *p = &ParInfo[index];
if (index != p->index)
error(BUG, "par_info: table out of order: %d != %d", index, p-index);
return p;
}
/*
* If any options were set but were not used, print out a warning message for
* the user.
*/
void
opt_check(void)
{
PAR_INFO *p;
PAR_INFO *q;
PAR_INFO *r = endof(ParInfo);
for (p = ParInfo; p < r; ++p) {
if (p->used || !p->set)
continue;
error(RET, "warning: %s set but not used in test %s",
p->name, TestName);
for (q = p+1; q < r; ++q)
if (q->set && q->name == p->name)
q->set = 0;
}
}
/*
* Server.
*/
static void
server(void)
{
server_listen();
for (;;) {
REQ req;
pid_t pid;
TEST *test;
int s = offset(REQ, req_index);
debug("ready for requests");
if (!server_recv_request())
continue;
pid = fork();
if (pid < 0) {
error(SYS|RET, "fork failed");
continue;
}
if (pid > 0) {
remotefd_close();
waitpid(pid, 0, 0);
continue;
}
remotefd_setup();
recv_mesg(&req, s, "request version");
dec_init(&req);
dec_req_version(&Req);
if (Req.ver_maj != VER_MAJ || Req.ver_min != VER_MIN)
version_error();
recv_mesg(&req.req_index, sizeof(req)-s, "request data");
dec_req_data(&Req);
if (Req.req_index >= cardof(Tests))
error(0, "bad request index: %d", Req.req_index);
test = &Tests[Req.req_index];
TestName = test->name;
debug("received request: %s", TestName);
init_lstat();
set_affinity();
(test->server)();
exit(0);
}
close(ListenFD);
}
/*
* If there is a version mismatch of qperf between the client and server, tell
* the user which needs to be upgraded.
*/
static void
version_error(void)
{
int hi_maj = Req.ver_maj;
int hi_min = Req.ver_min;
int hi_inc = Req.ver_inc;
int lo_maj = VER_MAJ;
int lo_min = VER_MIN;
int lo_inc = VER_INC;
char *msg = "upgrade qperf on %s from %d.%d.%d to %d.%d.%d";
char *low = "server";
if (lo_maj > hi_maj || (lo_maj == hi_maj && lo_min > hi_min)) {
hi_maj = VER_MAJ;
hi_min = VER_MIN;
hi_inc = VER_INC;
lo_maj = Req.ver_maj;
lo_min = Req.ver_min;
lo_inc = Req.ver_inc;
low = "client";
}
error(0, msg, low, lo_maj, lo_min, lo_inc, hi_maj, hi_min, hi_inc);
}
/*
* Listen for any requests.
*/
static void
server_listen(void)
{
AI *ai;
AI hints ={
.ai_flags = AI_PASSIVE | AI_NUMERICSERV,
.ai_family = AF_UNSPEC,
.ai_socktype = SOCK_STREAM
};
AI *ailist = getaddrinfo_port(0, ListenPort, &hints);
for (ai = ailist; ai; ai = ai->ai_next) {
ListenFD = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
if (ListenFD < 0)
continue;
setsockopt_one(ListenFD, SO_REUSEADDR);
if (bind(ListenFD, ai->ai_addr, ai->ai_addrlen) == SUCCESS0)
break;
close(ListenFD);
}
freeaddrinfo(ailist);
if (!ai)
error(0, "unable to bind to listen port");
if (!Req.timeout)
Req.timeout = DEF_TIMEOUT;
if (listen(ListenFD, LISTENQ) < 0)
error(SYS, "listen failed");
}
/*
* Accept a request from a client.
*/
static int
server_recv_request(void)
{
socklen_t clientLen;
struct sockaddr_in clientAddr;
clientLen = sizeof(clientAddr);
RemoteFD = accept(ListenFD, (struct sockaddr *)&clientAddr, &clientLen);
if (RemoteFD < 0)
return error(SYS|RET, "accept failed");
return 1;
}
/*
* Client.
*/
static void
client(TEST *test)
{
int i;
for (i = 0; i < P_N; ++i)
ParInfo[i].inuse = 0;
if (!par_isset(L_NO_MSGS))
setp_u32(0, L_TIME, DEF_TIME);
if (!par_isset(R_NO_MSGS))
setp_u32(0, R_TIME, DEF_TIME);
setp_u32(0, L_TIMEOUT, DEF_TIMEOUT);
setp_u32(0, R_TIMEOUT, DEF_TIMEOUT);
par_use(L_AFFINITY);
par_use(R_AFFINITY);
par_use(L_TIME);
par_use(R_TIME);
set_affinity();
RReq.ver_maj = VER_MAJ;
RReq.ver_min = VER_MIN;
RReq.ver_inc = VER_INC;
RReq.req_index = test - Tests;
TestName = test->name;
debug("sending request: %s", TestName);
init_lstat();
printf("%s:\n", TestName);
(*test->client)();
remotefd_close();
place_show();
}
/*
* Send a request to the server.
*/
void
client_send_request(void)
{
REQ req;
AI *a;
AI hints ={
.ai_family = AF_UNSPEC,
.ai_socktype = SOCK_STREAM
};
AI *ailist = getaddrinfo_port(ServerName, ListenPort, &hints);
RemoteFD = -1;
if (ServerWait)
start_test_timer(ServerWait);
for (;;) {
for (a = ailist; a; a = a->ai_next) {
if (Finished)
break;
RemoteFD = socket(a->ai_family, a->ai_socktype, a->ai_protocol);
if (RemoteFD < 0)
continue;
if (connect(RemoteFD, a->ai_addr, a->ai_addrlen) != SUCCESS0) {
remotefd_close();
continue;
}
ServerAddrLen = a->ai_addrlen;
memcpy(&ServerAddr, a->ai_addr, ServerAddrLen);
break;
}
if (RemoteFD >= 0 || !ServerWait || Finished)
break;
sleep(1);
}
if (ServerWait)
stop_test_timer();
freeaddrinfo(ailist);
if (RemoteFD < 0)
error(0, "%s: failed to connect", ServerName);
remotefd_setup();
enc_init(&req);
enc_req(&RReq);
send_mesg(&req, sizeof(req), "request data");
}
/*
* Configure the remote file descriptor.
*/
static void
remotefd_setup(void)
{
int one = 1;
if (ioctl(RemoteFD, FIONBIO, &one) < 0)
error(SYS, "ioctl FIONBIO failed");
if (fcntl(RemoteFD, F_SETOWN, getpid()) < 0)
error(SYS, "fcntl F_SETOWN failed");
}
/*
* Close the remote file descriptor.
* Set a file descriptor to non-blocking.
*/
static void
remotefd_close(void)
{
close(RemoteFD);
RemoteFD = -1;
}
/*
* Exchange results. We sync up only to ensure that the client is out of its
* loop so we can close our socket or whatever communication medium we are
* using.
*/
void
exchange_results(void)
{
STAT stat;
if (is_client()) {
recv_mesg(&stat, sizeof(stat), "results");
dec_init(&stat);
dec_stat(&RStat);
send_sync("synchronization after test");
} else {
enc_init(&stat);
enc_stat(&LStat);
send_mesg(&stat, sizeof(stat), "results");
recv_sync("synchronization after test");
}
}
/*
* Initialize local status information.
*/
static void
init_lstat(void)
{
memcpy(&LStat, &IStat, sizeof(LStat));
}
/*
* Show configuration (client side).
*/
static void
run_client_conf(void)
{
CONF lconf;
CONF rconf;
client_send_request();
recv_mesg(&rconf, sizeof(rconf), "configuration");
get_conf(&lconf);
view_strn('a', "", "loc_node", lconf.node);
view_strn('a', "", "loc_cpu", lconf.cpu);
view_strn('a', "", "loc_os", lconf.os);
view_strn('a', "", "loc_qperf", lconf.qperf);
view_strn('a', "", "rem_node", rconf.node);
view_strn('a', "", "rem_cpu", rconf.cpu);
view_strn('a', "", "rem_os", rconf.os);
view_strn('a', "", "rem_qperf", rconf.qperf);
}
/*
* Show configuration (server side).
*/
static void
run_server_conf(void)
{
CONF conf;
get_conf(&conf);
send_mesg(&conf, sizeof(conf), "configuration");
}
/*
* Get configuration.
*/
static void
get_conf(CONF *conf)
{
struct utsname utsname;
uname(&utsname);
strncopy(conf->node, utsname.nodename, sizeof(conf->node));
snprintf(conf->os, sizeof(conf->os), "%s %s", utsname.sysname,
utsname.release);
get_cpu(conf);
snprintf(conf->qperf, sizeof(conf->qperf), "%d.%d.%d",
VER_MAJ, VER_MIN, VER_INC);
}
/*
* Get CPU information.
*/
static void
get_cpu(CONF *conf)
{
char count[STRSIZE];
char speed[STRSIZE];
char buf[BUFSIZE];
char cpu[BUFSIZE];
char mhz[BUFSIZE];
int cpus = 0;
int mixed = 0;
FILE *fp = fopen("/proc/cpuinfo", "r");
if (!fp)
error(0, "cannot open /proc/cpuinfo");
cpu[0] = '\0';
mhz[0] = '\0';
while (fgets(buf, sizeof(buf), fp)) {
int n = strlen(buf);
if (cmpsub(buf, "model name")) {
++cpus;
if (!mixed) {
if (cpu[0] == '\0')
strncopy(cpu, buf, sizeof(cpu));
else if (!streq(buf, cpu))
mixed = 1;
}
} else if (cmpsub(buf, "cpu MHz")) {
if (!mixed) {
if (mhz[0] == '\0')
strncopy(mhz, buf, sizeof(mhz));
else if (!streq(buf, mhz))
mixed = 1;
}
}
while (n && buf[n-1] != '\n') {
if (!fgets(buf, sizeof(buf), fp))
break;
n = strlen(buf);
}
}
fclose(fp);
/* CPU name */
if (mixed)
strncopy(cpu, "Mixed CPUs", sizeof(cpu));
else {
char *p = cpu;
char *q = skip_colon(cpu);
if (!q)
return;
for (;;) {
if (*q == '(' && cmpsub(q, "(r)"))
q += 3;
else if (*q == '(' && cmpsub(q, "(tm)"))
q += 4;
if (tolower(*q) == 'c' && cmpsub(q, "cpu "))
q += 4;
if (tolower(*q) == 'p' && cmpsub(q, "processor "))
q += 10;
else if (q[0] == ' ' && q[1] == ' ')
q += 1;
else if (q[0] == '\n')
q += 1;
else if (!(*p++ = *q++))
break;
}
}
/* CPU speed */
speed[0] = '\0';
if (!mixed) {
int n = strlen(cpu);
if (n < 3 || cpu[n-2] != 'H' || cpu[n-1] != 'z') {
char *q = skip_colon(mhz);
if (q) {
int freq = atoi(q);
if (freq < 1000)
snprintf(speed, sizeof(speed), " %dMHz", freq);
else
snprintf(speed, sizeof(speed), " %.1fGHz", freq/1000.0);
}
}
}
/* Number of CPUs */
if (cpus == 1)
count[0] = '\0';
else
snprintf(count, sizeof(count), "%d Cores: ", cpus);
snprintf(conf->cpu, sizeof(conf->cpu), "%s%s%s", count, cpu, speed);
}
/*
* Quit (client side).
*/
static void
run_client_quit(void)
{
opt_check();
client_send_request();
sync_test();
exit(0);
}
/*
* Quit (server side). The read is to ensure that the client first quits to
* ensure that everything closes down cleanly.
*/
static void
run_server_quit(void)
{
char buf[1];
sync_test();
read(RemoteFD, buf, sizeof(buf));
kill(getppid(), SIGQUIT);
exit(0);
}
/*
* Synchronize the client and server.
*/
void
sync_test(void)
{
synchronize("synchronization before test");
start_test_timer(Req.time);
}
/*
* Start test timer.
*/
static void
start_test_timer(int seconds)
{
struct itimerval itimerval = {{0}};
Finished = 0;
get_times(LStat.time_s);
setitimer(ITIMER_REAL, &itimerval, 0);
if (!seconds)
return;
debug("starting timer for %d seconds", seconds);
itimerval.it_value.tv_sec = seconds;
itimerval.it_interval.tv_usec = 1;
setitimer(ITIMER_REAL, &itimerval, 0);
}
/*
* Stop timing. Note that the end time is obtained by the first call to
* set_finished. In the tests, when SIGALRM goes off, it may be executing a
* system call which gets interrupted. If SIGALRM goes off after Finished is
* checked but before the system call is initiated, the system call will be
* executed and it will take the second SIGALRM call generated by the interval
* timer to wake it up. Hence, we save the end times in sig_alrm. Note that
* if Finished is set, we reject any packets that are sent or arrive in order
* not to cheat. We clear Finished since code assumes that it is the default
* state.
*/
void
stop_test_timer(void)
{
struct itimerval itimerval = {{0}};
set_finished();
setitimer(ITIMER_REAL, &itimerval, 0);
Finished = 0;
debug("stopping timer");
}
/*
* Establish the current test as finished.
*/
void
set_finished(void)
{
if (Finished++ == 0)
get_times(LStat.time_e);
}
/*
* Show results.
*/
void
show_results(MEASURE measure)
{
calc_results();
show_info(measure);
}
/*
* Calculate results.
*/
static void
calc_results(void)
{
double no_msgs;
double locTime;
double remTime;
double midTime;
double gB = 1000 * 1000 * 1000;
add_ustat(&LStat.s, &RStat.rem_s);
add_ustat(&LStat.r, &RStat.rem_r);
add_ustat(&RStat.s, &LStat.rem_s);
add_ustat(&RStat.r, &LStat.rem_r);
memset(&Res, 0, sizeof(Res));
calc_node(&Res.l, &LStat);
calc_node(&Res.r, &RStat);
no_msgs = LStat.r.no_msgs + RStat.r.no_msgs;
if (no_msgs)
Res.latency = Res.l.time_real / no_msgs;
locTime = Res.l.time_real;
remTime = Res.r.time_real;
midTime = (locTime + remTime) / 2;
if (locTime == 0 || remTime == 0)
return;
/* Calculate messaging rate */
if (!RStat.r.no_msgs)
Res.msg_rate = LStat.r.no_msgs / remTime;
else if (!LStat.r.no_msgs)
Res.msg_rate = RStat.r.no_msgs / locTime;
else
Res.msg_rate = (LStat.r.no_msgs + RStat.r.no_msgs) / midTime;
/* Calculate send bandwidth */
if (!RStat.s.no_bytes)
Res.send_bw = LStat.s.no_bytes / locTime;
else if (!LStat.s.no_bytes)
Res.send_bw = RStat.s.no_bytes / remTime;
else
Res.send_bw = (LStat.s.no_bytes + RStat.s.no_bytes) / midTime;
/* Calculate receive bandwidth. */
if (!RStat.r.no_bytes)
Res.recv_bw = LStat.r.no_bytes / locTime;
else if (!LStat.r.no_bytes)
Res.recv_bw = RStat.r.no_bytes / remTime;
else
Res.recv_bw = (LStat.r.no_bytes + RStat.r.no_bytes) / midTime;
/* Calculate costs */
if (LStat.s.no_bytes && !LStat.r.no_bytes && !RStat.s.no_bytes)
Res.send_cost = Res.l.time_cpu*gB / LStat.s.no_bytes;
else if (RStat.s.no_bytes && !RStat.r.no_bytes && !LStat.s.no_bytes)
Res.send_cost = Res.r.time_cpu*gB / RStat.s.no_bytes;
if (RStat.r.no_bytes && !RStat.s.no_bytes && !LStat.r.no_bytes)
Res.recv_cost = Res.r.time_cpu*gB / RStat.r.no_bytes;
else if (LStat.r.no_bytes && !LStat.s.no_bytes && !RStat.r.no_bytes)
Res.recv_cost = Res.l.time_cpu*gB / LStat.r.no_bytes;
}
/*
* Determine the number of packets left to send.
*/
int
left_to_send(long *sentp, int room)
{
int n;
if (!Req.no_msgs)
return room;
n = Req.no_msgs - *sentp;
if (n <= 0)
return 0;
if (n > room)
return room;
return n;
}
/*
* Combine statistics that the remote node kept track of with those that the
* local node kept.
*/
static void
add_ustat(USTAT *l, USTAT *r)
{
l->no_bytes += r->no_bytes;
l->no_msgs += r->no_msgs;
l->no_errs += r->no_errs;
}
/*
* Calculate time values for a node.
*/
static void
calc_node(RESN *resn, STAT *stat)
{
int i;
CLOCK cpu;
double s = stat->time_e[T_REAL] - stat->time_s[T_REAL];
memset(resn, 0, sizeof(*resn));
if (s == 0)
return;
if (stat->no_ticks == 0)
return;
resn->time_real = s / stat->no_ticks;
cpu = 0;
for (i = 0; i < T_N; ++i)
if (i != T_REAL && i != T_IDLE)
cpu += stat->time_e[i] - stat->time_s[i];
resn->time_cpu = (float) cpu / stat->no_ticks;
resn->cpu_user = (stat->time_e[T_USER] - stat->time_s[T_USER]
+ stat->time_e[T_NICE] - stat->time_s[T_NICE]) / s;
resn->cpu_intr = (stat->time_e[T_IRQ] - stat->time_s[T_IRQ]
+ stat->time_e[T_SOFTIRQ] - stat->time_s[T_SOFTIRQ]) / s;
resn->cpu_idle = (stat->time_e[T_IDLE] - stat->time_s[T_IDLE]) / s;
resn->cpu_kernel = (stat->time_e[T_KERNEL] - stat->time_s[T_KERNEL]
+ stat->time_e[T_STEAL] - stat->time_s[T_STEAL]) / s;
resn->cpu_io_wait = (stat->time_e[T_IOWAIT] - stat->time_s[T_IOWAIT]) / s;
resn->cpu_total = resn->cpu_user + resn->cpu_intr
+ resn->cpu_kernel + resn->cpu_io_wait;
}
/*
* Show relevant values.
*/
static void
show_info(MEASURE measure)
{
if (measure == LATENCY) {
view_time('a', "", "latency", Res.latency);
view_rate('s', "", "msg_rate", Res.msg_rate);
} else if (measure == MSG_RATE) {
view_rate('a', "", "msg_rate", Res.msg_rate);
} else if (measure == BANDWIDTH) {
view_band('a', "", "bw", Res.recv_bw);
view_rate('s', "", "msg_rate", Res.msg_rate);
} else if (measure == BANDWIDTH_SR) {
view_band('a', "", "send_bw", Res.send_bw);
view_band('a', "", "recv_bw", Res.recv_bw);
view_rate('s', "", "msg_rate", Res.msg_rate);
}
show_used();
view_cost('t', "", "send_cost", Res.send_cost);
view_cost('t', "", "recv_cost", Res.recv_cost);
show_rest();
if (Debug)
show_debug();
}
/*
* Show parameters the user set.
*/
static void
show_used(void)
{
PAR_NAME *p;
PAR_NAME *q = endof(ParName);
if (!VerboseUsed)
return;
for (p = ParName; p < q; ++p) {
PAR_INFO *l = par_info(p->loc_i);
PAR_INFO *r = par_info(p->rem_i);
if (!l->inuse && !r->inuse)
continue;
if (VerboseUsed < 2 && !l->set & !r->set)
continue;
if (l->type == 'l') {
uint32_t lv = *(uint32_t *)l->ptr;
uint32_t rv = *(uint32_t *)r->ptr;
if (lv == rv)
view_long('u', "", p->name, lv);
else {
view_long('u', "loc_", p->name, lv);
view_long('u', "rem_", p->name, rv);
}
} else if (l->type == 'p') {
if (streq(l->ptr, r->ptr))
view_strn('u', "", p->name, l->ptr);
else {
view_strn('u', "loc_", p->name, l->ptr);
view_strn('u', "rem_", p->name, r->ptr);
}
} else if (l->type == 's') {
uint32_t lv = *(uint32_t *)l->ptr;
uint32_t rv = *(uint32_t *)r->ptr;
if (lv == rv)
view_size('u', "", p->name, lv);
else {
view_size('u', "loc_", p->name, lv);
view_size('u', "rem_", p->name, rv);
}
} else if (l->type == 't') {
uint32_t lv = *(uint32_t *)l->ptr;
uint32_t rv = *(uint32_t *)r->ptr;
if (lv == rv)
view_time('u', "", p->name, lv);
else {
view_time('u', "loc_", p->name, lv);
view_time('u', "rem_", p->name, rv);
}
}
}
}
/*
* Show the remaining parameters.
*/
static void
show_rest(void)
{
RESN *resnS;
RESN *resnR;
STAT *statS;
STAT *statR;
int srmode = 0;
if (!UnifyNodes) {
uint64_t ls = LStat.s.no_bytes;
uint64_t lr = LStat.r.no_bytes;
uint64_t rs = RStat.s.no_bytes;
uint64_t rr = RStat.r.no_bytes;
if (ls && !rs && rr && !lr) {
srmode = 1;
resnS = &Res.l;
resnR = &Res.r;
statS = &LStat;
statR = &RStat;
} else if (rs && !ls && lr && !rr) {
srmode = 1;
resnS = &Res.r;
resnR = &Res.l;
statS = &RStat;
statR = &LStat;
}
}
if (srmode) {
view_cpus('t', "", "send_cpus_used", resnS->cpu_total);
view_cpus('T', "", "send_cpus_user", resnS->cpu_user);
view_cpus('T', "", "send_cpus_intr", resnS->cpu_intr);
view_cpus('T', "", "send_cpus_kernel", resnS->cpu_kernel);
view_cpus('T', "", "send_cpus_iowait", resnS->cpu_io_wait);
view_time('T', "", "send_real_time", resnS->time_real);
view_time('T', "", "send_cpu_time", resnS->time_cpu);
view_long('S', "", "send_errors", statS->s.no_errs);
view_size('S', "", "send_bytes", statS->s.no_bytes);
view_long('S', "", "send_msgs", statS->s.no_msgs);
view_long('S', "", "send_max_cqe", statS->max_cqes);
view_cpus('t', "", "recv_cpus_used", resnR->cpu_total);
view_cpus('T', "", "recv_cpus_user", resnR->cpu_user);
view_cpus('T', "", "recv_cpus_intr", resnR->cpu_intr);
view_cpus('T', "", "recv_cpus_kernel", resnR->cpu_kernel);
view_cpus('T', "", "recv_cpus_iowait", resnR->cpu_io_wait);
view_time('T', "", "recv_real_time", resnR->time_real);
view_time('T', "", "recv_cpu_time", resnR->time_cpu);
view_long('S', "", "recv_errors", statR->r.no_errs);
view_size('S', "", "recv_bytes", statR->r.no_bytes);
view_long('S', "", "recv_msgs", statR->r.no_msgs);
view_long('S', "", "recv_max_cqe", statR->max_cqes);
} else {
view_cpus('t', "", "loc_cpus_used", Res.l.cpu_total);
view_cpus('T', "", "loc_cpus_user", Res.l.cpu_user);
view_cpus('T', "", "loc_cpus_intr", Res.l.cpu_intr);
view_cpus('T', "", "loc_cpus_kernel", Res.l.cpu_kernel);
view_cpus('T', "", "loc_cpus_iowait", Res.l.cpu_io_wait);
view_time('T', "", "loc_real_time", Res.l.time_real);
view_time('T', "", "loc_cpu_time", Res.l.time_cpu);
view_long('S', "", "loc_send_errors", LStat.s.no_errs);
view_long('S', "", "loc_recv_errors", LStat.r.no_errs);
view_size('S', "", "loc_send_bytes", LStat.s.no_bytes);
view_size('S', "", "loc_recv_bytes", LStat.r.no_bytes);
view_long('S', "", "loc_send_msgs", LStat.s.no_msgs);
view_long('S', "", "loc_recv_msgs", LStat.r.no_msgs);
view_long('S', "", "loc_max_cqe", LStat.max_cqes);
view_cpus('t', "", "rem_cpus_used", Res.r.cpu_total);
view_cpus('T', "", "rem_cpus_user", Res.r.cpu_user);
view_cpus('T', "", "rem_cpus_intr", Res.r.cpu_intr);
view_cpus('T', "", "rem_cpus_kernel", Res.r.cpu_kernel);
view_cpus('T', "", "rem_cpus_iowait", Res.r.cpu_io_wait);
view_time('T', "", "rem_real_time", Res.r.time_real);
view_time('T', "", "rem_cpu_time", Res.r.time_cpu);
view_long('S', "", "rem_send_errors", RStat.s.no_errs);
view_long('S', "", "rem_recv_errors", RStat.r.no_errs);
view_size('S', "", "rem_send_bytes", RStat.s.no_bytes);
view_size('S', "", "rem_recv_bytes", RStat.r.no_bytes);
view_long('S', "", "rem_send_msgs", RStat.s.no_msgs);
view_long('S', "", "rem_recv_msgs", RStat.r.no_msgs);
view_long('S', "", "rem_max_cqe", RStat.max_cqes);
}
}
/*
* Show all values.
*/
static void
show_debug(void)
{
/* Local node */
view_long('d', "", "l_no_cpus", LStat.no_cpus);
view_long('d', "", "l_no_ticks", LStat.no_ticks);
view_long('d', "", "l_max_cqes", LStat.max_cqes);
if (LStat.no_ticks) {
double t = LStat.no_ticks;
CLOCK *s = LStat.time_s;
CLOCK *e = LStat.time_e;
double real = (e[T_REAL] - s[T_REAL]) / t;
double user = (e[T_USER] - s[T_USER]) / t;
double nice = (e[T_NICE] - s[T_NICE]) / t;
double system = (e[T_KERNEL] - s[T_KERNEL]) / t;
double idle = (e[T_IDLE] - s[T_IDLE]) / t;
double iowait = (e[T_IOWAIT] - s[T_IOWAIT]) / t;
double irq = (e[T_IRQ] - s[T_IRQ]) / t;
double softirq = (e[T_SOFTIRQ] - s[T_SOFTIRQ]) / t;
double steal = (e[T_STEAL] - s[T_STEAL]) / t;
view_time('d', "", "l_timer_real", real);
view_time('d', "", "l_timer_user", user);
view_time('d', "", "l_timer_nice", nice);
view_time('d', "", "l_timer_system", system);
view_time('d', "", "l_timer_idle", idle);
view_time('d', "", "l_timer_iowait", iowait);
view_time('d', "", "l_timer_irq", irq);
view_time('d', "", "l_timer_softirq", softirq);
view_time('d', "", "l_timer_steal", steal);
}
view_size('d', "", "l_s_no_bytes", LStat.s.no_bytes);
view_long('d', "", "l_s_no_msgs", LStat.s.no_msgs);
view_long('d', "", "l_s_no_errs", LStat.s.no_errs);
view_size('d', "", "l_r_no_bytes", LStat.r.no_bytes);
view_long('d', "", "l_r_no_msgs", LStat.r.no_msgs);
view_long('d', "", "l_r_no_errs", LStat.r.no_errs);
view_size('d', "", "l_rem_s_no_bytes", LStat.rem_s.no_bytes);
view_long('d', "", "l_rem_s_no_msgs", LStat.rem_s.no_msgs);
view_long('d', "", "l_rem_s_no_errs", LStat.rem_s.no_errs);
view_size('d', "", "l_rem_r_no_bytes", LStat.rem_r.no_bytes);
view_long('d', "", "l_rem_r_no_msgs", LStat.rem_r.no_msgs);
view_long('d', "", "l_rem_r_no_errs", LStat.rem_r.no_errs);
/* Remote node */
view_long('d', "", "r_no_cpus", RStat.no_cpus);
view_long('d', "", "r_no_ticks", RStat.no_ticks);
view_long('d', "", "r_max_cqes", RStat.max_cqes);
if (RStat.no_ticks) {
double t = RStat.no_ticks;
CLOCK *s = RStat.time_s;
CLOCK *e = RStat.time_e;
double real = (e[T_REAL] - s[T_REAL]) / t;
double user = (e[T_USER] - s[T_USER]) / t;
double nice = (e[T_NICE] - s[T_NICE]) / t;
double system = (e[T_KERNEL] - s[T_KERNEL]) / t;
double idle = (e[T_IDLE] - s[T_IDLE]) / t;
double iowait = (e[T_IOWAIT] - s[T_IOWAIT]) / t;
double irq = (e[T_IRQ] - s[T_IRQ]) / t;
double softirq = (e[T_SOFTIRQ] - s[T_SOFTIRQ]) / t;
double steal = (e[T_STEAL] - s[T_STEAL]) / t;
view_time('d', "", "r_timer_real", real);
view_time('d', "", "r_timer_user", user);
view_time('d', "", "r_timer_nice", nice);
view_time('d', "", "r_timer_system", system);
view_time('d', "", "r_timer_idle", idle);
view_time('d', "", "r_timer_iowait", iowait);
view_time('d', "", "r_timer_irq", irq);
view_time('d', "", "r_timer_softirq", softirq);
view_time('d', "", "r_timer_steal", steal);
}
view_size('d', "", "r_s_no_bytes", RStat.s.no_bytes);
view_long('d', "", "r_s_no_msgs", RStat.s.no_msgs);
view_long('d', "", "r_s_no_errs", RStat.s.no_errs);
view_size('d', "", "r_r_no_bytes", RStat.r.no_bytes);
view_long('d', "", "r_r_no_msgs", RStat.r.no_msgs);
view_long('d', "", "r_r_no_errs", RStat.r.no_errs);
view_size('d', "", "r_rem_s_no_bytes", RStat.rem_s.no_bytes);
view_long('d', "", "r_rem_s_no_msgs", RStat.rem_s.no_msgs);
view_long('d', "", "r_rem_s_no_errs", RStat.rem_s.no_errs);
view_size('d', "", "r_rem_r_no_bytes", RStat.rem_r.no_bytes);
view_long('d', "", "r_rem_r_no_msgs", RStat.rem_r.no_msgs);
view_long('d', "", "r_rem_r_no_errs", RStat.rem_r.no_errs);
}
/*
* Show a cost in terms of seconds per gigabyte.
*/
static void
view_cost(int type, char *pref, char *name, double value)
{
int n = 0;
char *tab[] ={ "ns/GB", "us/GB", "ms/GB", "sec/GB" };
value *= 1E9;
if (!verbose(type, value))
return;
if (!UnifyUnits) {
while (value >= 1000 && n < (int)cardof(tab)-1) {
value /= 1000;
++n;
}
}
place_val(pref, name, tab[n], value);
}
/*
* Show the number of cpus.
*/
static void
view_cpus(int type, char *pref, char *name, double value)
{
value *= 100;
if (!verbose(type, value))
return;
place_val(pref, name, "% cpus", value);
}
/*
* Show a messaging rate.
*/
static void
view_rate(int type, char *pref, char *name, double value)
{
int n = 0;
char *tab[] ={ "/sec", "K/sec", "M/sec", "G/sec", "T/sec" };
if (!verbose(type, value))
return;
if (!UnifyUnits) {
while (value >= 1000 && n < (int)cardof(tab)-1) {
value /= 1000;
++n;
}
}
place_val(pref, name, tab[n], value);
}
/*
* Show a number.
*/
static void
view_long(int type, char *pref, char *name, long long value)
{
int n = 0;
double val = value;
char *tab[] ={ "", "thousand", "million", "billion", "trillion" };
if (!verbose(type, val))
return;
if (!UnifyUnits && val >= 1000*1000) {
while (val >= 1000 && n < (int)cardof(tab)-1) {
val /= 1000;
++n;
}
}
place_val(pref, name, tab[n], val);
}
/*
* Show a bandwidth value.
*/
static void
view_band(int type, char *pref, char *name, double value)
{
int n, s;
char **tab;
if (!verbose(type, value))
return;
if (UseBitsPerSec) {
char *t[] ={ "bits/sec", "Kb/sec", "Mb/sec", "Gb/sec", "Tb/sec" };
s = cardof(t);
tab = t;
value *= 8;
} else {
char *t[] ={ "bytes/sec", "KB/sec", "MB/sec", "GB/sec", "TB/sec" };
s = cardof(t);
tab = t;
}
n = 0;
if (!UnifyUnits) {
while (value >= 1000 && n < s-1) {
value /= 1000;
++n;
}
}
place_val(pref, name, tab[n], value);
}
/*
* Show a size.
*/
static void
view_size(int type, char *pref, char *name, long long value)
{
int n = 0;
double val = value;
char *tab[] ={ "bytes", "KB", "MB", "GB", "TB" };
if (!verbose(type, val))
return;
if (!UnifyUnits) {
if (nice_1024(pref, name, value))
return;
while (val >= 1000 && n < (int)cardof(tab)-1) {
val /= 1000;
++n;
}
}
place_val(pref, name, tab[n], val);
}
/*
* Show a number if it can be expressed as a nice multiple of a power of 1024.
*/
static int
nice_1024(char *pref, char *name, long long value)
{
char *data;
char *altn;
int n = 0;
long long val = value;
char *tab[] ={ "KiB", "MiB", "GiB", "TiB" };
if (val < 1024 || val % 1024)
return 0;
val /= 1024;
while (val >= 1024 && n < (int)cardof(tab)-1) {
if (val % 1024)
return 0;
val /= 1024;
++n;
}
data = qasprintf("%lld", val);
altn = qasprintf("%lld", value);
place_any(pref, name, tab[n], commify(data), commify(altn));
return 1;
}
/*
* Show a string.
*/
static void
view_strn(int type, char *pref, char *name, char *value)
{
if (!verbose(type, value[0] != '\0'))
return;
place_any(pref, name, 0, strdup(value), 0);
}
/*
* Show a time.
*/
static void
view_time(int type, char *pref, char *name, double value)
{
int n = 0;
char *tab[] ={ "ns", "us", "ms", "sec" };
value *= 1E9;
if (!verbose(type, value))
return;
if (!UnifyUnits) {
while (value >= 1000 && n < (int)cardof(tab)-1) {
value /= 1000;
++n;
}
}
place_val(pref, name, tab[n], value);
}
/*
* Determine if we are verbose enough to show a value.
*/
static int
verbose(int type, double value)
{
if (type == 'a')
return 1;
if (value <= 0)
return 0;
switch (type) {
case 'd': return Debug;
case 'c': return VerboseConf >= 1;
case 's': return VerboseStat >= 1;
case 't': return VerboseTime >= 1;
case 'u': return VerboseUsed >= 1;
case 'C': return VerboseConf >= 2;
case 'S': return VerboseStat >= 2;
case 'T': return VerboseTime >= 2;
case 'U': return VerboseUsed >= 2;
default: error(BUG, "verbose: bad type: %c (%o)", type, type);
}
return 0;
}
/*
* Place a value to be shown later.
*/
static void
place_val(char *pref, char *name, char *unit, double value)
{
char *data = qasprintf("%.0f", value);
char *p = data;
int n = Precision;
if (*p == '-')
++p;
while (isdigit(*p++))
--n;
if (n > 0) {
free(data);
data = qasprintf("%.*f", n, value);
p = &data[strlen(data)];
while (p > data && *--p == '0')
;
if (p > data && *p == '.')
--p;
p[1] = '\0';
}
place_any(pref, name, unit, commify(data), 0);
}
/*
* Place an entry in our show table.
*/
static void
place_any(char *pref, char *name, char *unit, char *data, char *altn)
{
SHOW *show = &ShowTable[ShowIndex++];
if (ShowIndex > cardof(ShowTable))
error(BUG, "need to increase size of ShowTable");
show->pref = pref;
show->name = name;
show->unit = unit;
show->data = data;
show->altn = altn;
}
/*
* Show all saved values.
*/
static void
place_show(void)
{
int i;
int nameLen = 0;
int dataLen = 0;
int unitLen = 0;
/* First compute formating sizes */
for (i = 0; i < ShowIndex; ++i) {
int n;
SHOW *show = &ShowTable[i];
n = (show->pref ? strlen(show->pref) : 0) + strlen(show->name);
if (n > nameLen)
nameLen = n;
n = strlen(show->data);
if (show->unit) {
if (n > dataLen)
dataLen = n;
n = strlen(show->unit);
if (n > unitLen)
unitLen = n;
}
}
/* Then display results */
for (i = 0; i < ShowIndex; ++i) {
int n = 0;
SHOW *show = &ShowTable[i];
printf(" ");
if (show->pref) {
n = strlen(show->pref);
printf("%s", show->pref);
}
printf("%-*s", nameLen-n, show->name);
if (show->unit) {
printf(" = %*s", dataLen, show->data);
printf(" %s", show->unit);
} else
printf(" = %s", show->data);
if (show->altn)
printf(" (%s)", show->altn);
printf("\n");
free(show->data);
free(show->altn);
}
ShowIndex = 0;
}
/*
* Set the processor affinity.
*/
static void
set_affinity(void)
{
cpu_set_t set;
int a = Req.affinity;
if (!a)
return;
CPU_ZERO(&set);
CPU_SET(a-1, &set);
if (sched_setaffinity(0, sizeof(set), &set) < 0)
error(SYS, "cannot set processor affinity (cpu %d)", a-1);
}
/*
* Encode a REQ structure into a data stream.
*/
static void
enc_req(REQ *host)
{
enc_int(host->ver_maj, sizeof(host->ver_maj));
enc_int(host->ver_min, sizeof(host->ver_min));
enc_int(host->ver_inc, sizeof(host->ver_inc));
enc_int(host->req_index, sizeof(host->req_index));
enc_int(host->access_recv, sizeof(host->access_recv));
enc_int(host->affinity, sizeof(host->affinity));
enc_int(host->alt_port, sizeof(host->alt_port));
enc_int(host->flip, sizeof(host->flip));
enc_int(host->msg_size, sizeof(host->msg_size));
enc_int(host->mtu_size, sizeof(host->mtu_size));
enc_int(host->no_msgs, sizeof(host->no_msgs));
enc_int(host->poll_mode, sizeof(host->poll_mode));
enc_int(host->port, sizeof(host->port));
enc_int(host->rd_atomic, sizeof(host->rd_atomic));
enc_int(host->sl, sizeof(host->sl));
enc_int(host->sock_buf_size, sizeof(host->sock_buf_size));
enc_int(host->src_path_bits, sizeof(host->src_path_bits));
enc_int(host->time, sizeof(host->time));
enc_int(host->timeout, sizeof(host->timeout));
enc_int(host->use_cm, sizeof(host->use_cm));
enc_str(host->id, sizeof(host->id));
enc_str(host->static_rate, sizeof(host->static_rate));
}
/*
* Decode the version part of a REQ structure from a data stream. To decode
* the entire REQ structure, call dec_req_version and dec_req_data in
* succession.
*/
static void
dec_req_version(REQ *host)
{
host->ver_maj = dec_int(sizeof(host->ver_maj));
host->ver_min = dec_int(sizeof(host->ver_min));
host->ver_inc = dec_int(sizeof(host->ver_inc));
}
/*
* Decode the data part of a REQ structure from a data stream.
*/
static void
dec_req_data(REQ *host)
{
host->req_index = dec_int(sizeof(host->req_index));
host->access_recv = dec_int(sizeof(host->access_recv));
host->affinity = dec_int(sizeof(host->affinity));
host->alt_port = dec_int(sizeof(host->alt_port));
host->flip = dec_int(sizeof(host->flip));
host->msg_size = dec_int(sizeof(host->msg_size));
host->mtu_size = dec_int(sizeof(host->mtu_size));
host->no_msgs = dec_int(sizeof(host->no_msgs));
host->poll_mode = dec_int(sizeof(host->poll_mode));
host->port = dec_int(sizeof(host->port));
host->rd_atomic = dec_int(sizeof(host->rd_atomic));
host->sl = dec_int(sizeof(host->sl));
host->sock_buf_size = dec_int(sizeof(host->sock_buf_size));
host->src_path_bits = dec_int(sizeof(host->src_path_bits));
host->time = dec_int(sizeof(host->time));
host->timeout = dec_int(sizeof(host->timeout));
host->use_cm = dec_int(sizeof(host->use_cm));
dec_str(host->id, sizeof(host->id));
dec_str(host->static_rate,sizeof(host->static_rate));
}
/*
* Encode a STAT structure into a data stream.
*/
static void
enc_stat(STAT *host)
{
int i;
enc_int(host->no_cpus, sizeof(host->no_cpus));
enc_int(host->no_ticks, sizeof(host->no_ticks));
enc_int(host->max_cqes, sizeof(host->max_cqes));
for (i = 0; i < T_N; ++i)
enc_int(host->time_s[i], sizeof(host->time_s[i]));
for (i = 0; i < T_N; ++i)
enc_int(host->time_e[i], sizeof(host->time_e[i]));
enc_ustat(&host->s);
enc_ustat(&host->r);
enc_ustat(&host->rem_s);
enc_ustat(&host->rem_r);
}
/*
* Decode a STAT structure from a data stream.
*/
static void
dec_stat(STAT *host)
{
int i;
host->no_cpus = dec_int(sizeof(host->no_cpus));
host->no_ticks = dec_int(sizeof(host->no_ticks));
host->max_cqes = dec_int(sizeof(host->max_cqes));
for (i = 0; i < T_N; ++i)
host->time_s[i] = dec_int(sizeof(host->time_s[i]));
for (i = 0; i < T_N; ++i)
host->time_e[i] = dec_int(sizeof(host->time_e[i]));
dec_ustat(&host->s);
dec_ustat(&host->r);
dec_ustat(&host->rem_s);
dec_ustat(&host->rem_r);
}
/*
* Encode a USTAT structure into a data stream.
*/
static void
enc_ustat(USTAT *host)
{
enc_int(host->no_bytes, sizeof(host->no_bytes));
enc_int(host->no_msgs, sizeof(host->no_msgs));
enc_int(host->no_errs, sizeof(host->no_errs));
}
/*
* Decode a USTAT structure from a data stream.
*/
static void
dec_ustat(USTAT *host)
{
host->no_bytes = dec_int(sizeof(host->no_bytes));
host->no_msgs = dec_int(sizeof(host->no_msgs));
host->no_errs = dec_int(sizeof(host->no_errs));
}
/*
* Get various temporal parameters.
*/
static void
get_times(CLOCK timex[T_N])
{
int n;
char *p;
char buf[BUFSIZE];
struct tms tms;
timex[0] = times(&tms);
if (lseek(ProcStatFD, 0, 0) < 0)
error(SYS, "failed to seek /proc/stat");
n = read(ProcStatFD, buf, sizeof(buf)-1);
buf[n] = '\0';
if (strncmp(buf, "cpu ", 4))
error(0, "/proc/stat does not start with 'cpu '");
p = &buf[3];
for (n = 1; n < T_N; ++n) {
while (*p == ' ')
++p;
if (!isdigit(*p)) {
if (*p != '\n' || n < T_N-1)
error(0, "/proc/stat has bad format");
break;
}
timex[n] = strtoll(p, 0, 10);
while (*p != ' ' && *p != '\n' && *p != '\0')
++p;
}
while (n < T_N)
timex[n++] = 0;
}
/*
* Insert commas within a number for readability.
*/
static char *
commify(char *data)
{
int s;
int d;
int seqS;
int seqE;
int dataLen;
int noCommas;
if (!data)
return data;
if (UnifyUnits)
return data;
dataLen = strlen(data);
seqS = seqE = dataLen;
while (--seqS >= 0)
if (!isdigit(data[seqS]))
break;
if (seqS >= 0 && data[seqS] == '.') {
seqE = seqS;
while (--seqS >= 0)
if (!isdigit(data[seqS]))
break;
}
noCommas = (--seqE - ++seqS) / 3;
if (noCommas == 0)
return data;
data = realloc(data, dataLen+noCommas+1);
if (!data)
error(0, "out of space");
s = dataLen;
d = dataLen + noCommas;
for (;;) {
int n;
data[d--] = data[s--];
n = seqE - s;
if (n > 0 && n%3 == 0) {
data[d--] = ',';
if (--noCommas == 0)
break;
}
}
return data;
}
/*
* Like strncpy but ensures the destination is null terminated.
*/
static void
strncopy(char *d, char *s, int n)
{
strncpy(d, s, n);
d[n-1] = '\0';
}