2914 lines
76 KiB
C
2914 lines
76 KiB
C
/*
|
|
* qperf - main.
|
|
* Run performance tests over TCP/IP and RDMA.
|
|
*
|
|
* Copyright (c) 2002-2007 Johann George. All rights reserved.
|
|
* Copyright (c) 2006-2007 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 <stdarg.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 2 /* Minor version */
|
|
#define VER_INC 0 /* Incremental version */
|
|
#define LISTENQ 5 /* Size of listen queue */
|
|
#define BUFSIZE 1024 /* Size of buffers */
|
|
#define SYNCMESG "SyN" /* Synchronize message */
|
|
#define SYNCSIZE sizeof(SYNCMESG) /* Size of synchronize message */
|
|
|
|
|
|
/*
|
|
* For convenience.
|
|
*/
|
|
#define with(c) |(c<<8)
|
|
|
|
|
|
/*
|
|
* Option list.
|
|
*/
|
|
typedef struct OPTION {
|
|
char *name; /* Name of option */
|
|
short server_valid; /* Option valid on server */
|
|
void (*func)(); /* Function to call */
|
|
int arg1; /* First argument */
|
|
int arg2; /* Second argument */
|
|
} OPTION;
|
|
|
|
|
|
/*
|
|
* 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;
|
|
|
|
|
|
/*
|
|
* 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 bug_die(char *fmt, ...);
|
|
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(REQ *host);
|
|
static void dec_stat(STAT *host);
|
|
static void dec_ustat(USTAT *host);
|
|
static void do_args(char *args[]);
|
|
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 double get_seconds(void);
|
|
static void get_times(CLOCK timex[T_N]);
|
|
static void initialize(void);
|
|
static void init_lstat(void);
|
|
static void init_vars(void);
|
|
static int nice_1024(char *pref, char *name, long long value);
|
|
static void opt_help(OPTION *option, char ***argvp);
|
|
static void opt_misc(OPTION *option, char ***argvp);
|
|
static void opt_strn(OPTION *option, char ***argvp);
|
|
static void opt_long(OPTION *option, char ***argvp);
|
|
static void opt_size(OPTION *option, char ***argvp);
|
|
static void opt_time(OPTION *option, char ***argvp);
|
|
static void opt_vers(OPTION *option, char ***argvp);
|
|
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 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 char *qasprintf(char *fmt, ...);
|
|
static int recv_sync(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 int send_recv_mesg(int sr, char *item, int fd, char *buf, int len);
|
|
static int send_sync(void);
|
|
static void server(void);
|
|
static void server_listen(void);
|
|
static int server_recv_request(void);
|
|
static void set_affinity(void);
|
|
static int set_nonblock(int fd);
|
|
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 char *skip_colon(char *s);
|
|
static void start_timing(int seconds);
|
|
static void strncopy(char *d, char *s, int n);
|
|
static int verbose(int type, double value);
|
|
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 = 19765;
|
|
static int Precision = 3;
|
|
static int ServerTimeout = 5;
|
|
|
|
|
|
/*
|
|
* Static variables.
|
|
*/
|
|
static REQ RReq;
|
|
static int Debug;
|
|
static uint8_t *DecodePtr;
|
|
static int ExitStatus;
|
|
static uint8_t *EncodePtr;
|
|
static STAT IStat;
|
|
static int ListenFD;
|
|
static int ProcStatFD;
|
|
static int RemoteFD;
|
|
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;
|
|
static int Wait;
|
|
|
|
|
|
/*
|
|
* Global variables.
|
|
*/
|
|
RES Res;
|
|
REQ Req;
|
|
STAT LStat;
|
|
char *TestName;
|
|
char *ServerName;
|
|
int Successful;
|
|
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 },
|
|
{ "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 },
|
|
{ "sock_buf_size", L_SOCK_BUF_SIZE, R_SOCK_BUF_SIZE },
|
|
{ "time", L_TIME, R_TIME },
|
|
{ "timeout", L_TIMEOUT, R_TIMEOUT },
|
|
};
|
|
|
|
|
|
/*
|
|
* 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_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_RATE, 'p', &Req.rate },
|
|
{ R_RATE, 'p', &RReq.rate },
|
|
{ L_RD_ATOMIC, 'l', &Req.rd_atomic },
|
|
{ R_RD_ATOMIC, 'l', &RReq.rd_atomic },
|
|
{ L_SOCK_BUF_SIZE, 's', &Req.sock_buf_size },
|
|
{ R_SOCK_BUF_SIZE, 's', &RReq.sock_buf_size },
|
|
{ L_TIME, 't', &Req.time },
|
|
{ R_TIME, 't', &RReq.time },
|
|
{ L_TIMEOUT, 't', &Req.timeout },
|
|
{ R_TIMEOUT, 't', &RReq.timeout },
|
|
};
|
|
|
|
|
|
/*
|
|
* Options.
|
|
*/
|
|
OPTION Options[] ={
|
|
{ "--access_recv", 0, &opt_long, L_ACCESS_RECV, R_ACCESS_RECV },
|
|
{ "-Ar", 0, &opt_long, L_ACCESS_RECV, R_ACCESS_RECV },
|
|
{ "--affinity", 0, &opt_long, L_AFFINITY, R_AFFINITY },
|
|
{ "-a", 0, &opt_long, L_AFFINITY, R_AFFINITY },
|
|
{ "--loc_affinity", 0, &opt_long, L_AFFINITY, },
|
|
{ "-la", 0, &opt_long, L_AFFINITY, },
|
|
{ "--rem_affinity", 0, &opt_long, R_AFFINITY },
|
|
{ "-ra", 0, &opt_long, R_AFFINITY },
|
|
{ "--debug", 1, &opt_misc, 'D', },
|
|
{ "-D", 1, &opt_misc, 'D', },
|
|
{ "--flip", 0, &opt_long, L_FLIP, R_FLIP },
|
|
{ "-f", 0, &opt_long, L_FLIP, R_FLIP },
|
|
{ "--help", 0, &opt_help },
|
|
{ "-h", 0, &opt_help },
|
|
{ "--host", 0, &opt_misc, 'H', },
|
|
{ "-H", 0, &opt_misc, 'H', },
|
|
{ "--id", 0, &opt_strn, L_ID, R_ID },
|
|
{ "-i", 0, &opt_strn, L_ID, R_ID },
|
|
{ "--loc_id", 0, &opt_strn, L_ID, },
|
|
{ "-li", 0, &opt_strn, L_ID, },
|
|
{ "--rem_id", 0, &opt_strn, R_ID },
|
|
{ "-ri", 0, &opt_strn, R_ID },
|
|
{ "--listen_port", 1, &opt_misc, 'l','p' },
|
|
{ "-lp", 1, &opt_misc, 'l','p' },
|
|
{ "--msg_size", 0, &opt_size, L_MSG_SIZE, R_MSG_SIZE },
|
|
{ "-m", 0, &opt_size, L_MSG_SIZE, R_MSG_SIZE },
|
|
{ "--mtu_size", 0, &opt_size, L_MTU_SIZE, R_MTU_SIZE },
|
|
{ "-M", 0, &opt_size, L_MTU_SIZE, R_MTU_SIZE },
|
|
{ "--no_msgs", 0, &opt_long, L_NO_MSGS, R_NO_MSGS },
|
|
{ "-n", 0, &opt_long, L_NO_MSGS, R_NO_MSGS },
|
|
{ "--poll", 0, &opt_long, L_POLL_MODE, R_POLL_MODE },
|
|
{ "-P", 0, &opt_long, L_POLL_MODE, R_POLL_MODE },
|
|
{ "--loc_poll", 0, &opt_long, L_POLL_MODE, },
|
|
{ "-lP", 0, &opt_long, L_POLL_MODE, },
|
|
{ "--rem_poll", 0, &opt_long, R_POLL_MODE },
|
|
{ "-rP", 0, &opt_long, R_POLL_MODE },
|
|
{ "--port", 0, &opt_long, L_PORT, R_PORT },
|
|
{ "-p", 0, &opt_long, L_PORT, R_PORT },
|
|
{ "--precision", 0, &opt_misc, 'e', },
|
|
{ "-e", 0, &opt_misc, 'e', },
|
|
{ "--rate", 0, &opt_strn, L_RATE, R_RATE },
|
|
{ "-r", 0, &opt_strn, L_RATE, R_RATE },
|
|
{ "--loc_rate", 0, &opt_strn, L_RATE },
|
|
{ "-lr", 0, &opt_strn, L_RATE },
|
|
{ "--rem_rate", 0, &opt_strn, R_RATE },
|
|
{ "-rr", 0, &opt_strn, R_RATE },
|
|
{ "-rd_atomic", 0, &opt_long, L_RD_ATOMIC, R_RD_ATOMIC },
|
|
{ "-R", 0, &opt_long, L_RD_ATOMIC, R_RD_ATOMIC },
|
|
{ "--loc_rd_atomic", 0, &opt_long, L_RD_ATOMIC, },
|
|
{ "-lR", 0, &opt_long, L_RD_ATOMIC, },
|
|
{ "--rem_rd_atomic", 0, &opt_long, R_RD_ATOMIC },
|
|
{ "-rR", 0, &opt_long, R_RD_ATOMIC },
|
|
{ "--sock_buf_size", 0, &opt_size, L_SOCK_BUF_SIZE, R_SOCK_BUF_SIZE },
|
|
{ "-S", 0, &opt_size, L_SOCK_BUF_SIZE, R_SOCK_BUF_SIZE },
|
|
{ "--loc_sock_buf_size", 0, &opt_size, L_SOCK_BUF_SIZE },
|
|
{ "-lS", 0, &opt_size, L_SOCK_BUF_SIZE },
|
|
{ "--rem_sock_buf_size", 0, &opt_size, R_SOCK_BUF_SIZE },
|
|
{ "-rS", 0, &opt_size, R_SOCK_BUF_SIZE },
|
|
{ "--time", 0, &opt_time, L_TIME, R_TIME },
|
|
{ "-t", 0, &opt_time, L_TIME, R_TIME },
|
|
{ "--timeout", 0, &opt_time, L_TIMEOUT, R_TIMEOUT },
|
|
{ "-T", 0, &opt_time, L_TIMEOUT, R_TIMEOUT },
|
|
{ "--loc_timeout", 0, &opt_time, L_TIMEOUT },
|
|
{ "-lT", 0, &opt_time, L_TIMEOUT },
|
|
{ "--rem_timeout", 0, &opt_time, R_TIMEOUT },
|
|
{ "-rT", 0, &opt_time, R_TIMEOUT },
|
|
{ "--server_timeout", 0, &opt_misc, 's', 't' },
|
|
{ "-st", 0, &opt_misc, 's', 't' },
|
|
{ "--unify_nodes", 0, &opt_misc, 'U' },
|
|
{ "-U", 0, &opt_misc, 'U' },
|
|
{ "--unify_units", 0, &opt_misc, 'u' },
|
|
{ "-u", 0, &opt_misc, 'u' },
|
|
{ "--verbose", 0, &opt_misc, 'v' },
|
|
{ "-v", 0, &opt_misc, 'v' },
|
|
{ "--verbose_conf", 0, &opt_misc, 'v', 'c' },
|
|
{ "-vc", 0, &opt_misc, 'v', 'c' },
|
|
{ "--verbose_stat", 0, &opt_misc, 'v', 's' },
|
|
{ "-vs", 0, &opt_misc, 'v', 's' },
|
|
{ "--verbose_time", 0, &opt_misc, 'v', 't' },
|
|
{ "-vt", 0, &opt_misc, 'v', 't' },
|
|
{ "--verbose_used", 0, &opt_misc, 'v', 'u' },
|
|
{ "-vu", 0, &opt_misc, 'v', 'u' },
|
|
{ "--verbose_more", 0, &opt_misc, 'v', 'v' },
|
|
{ "-vv", 0, &opt_misc, 'v', 'v' },
|
|
{ "--verbose_more_conf", 0, &opt_misc, 'v', 'c' },
|
|
{ "-vC", 0, &opt_misc, 'v', 'C' },
|
|
{ "--verbose_more_stat", 0, &opt_misc, 'v', 's' },
|
|
{ "-vS", 0, &opt_misc, 'v', 'S' },
|
|
{ "--verbose_more_time", 0, &opt_misc, 'v', 't' },
|
|
{ "-vT", 0, &opt_misc, 'v', 'T' },
|
|
{ "--verbose_more_used", 0, &opt_misc, 'v', 'u' },
|
|
{ "-vU", 0, &opt_misc, 'v', 'U' },
|
|
{ "--version", 0, &opt_vers, },
|
|
{ "-V", 0, &opt_vers, },
|
|
{ "--wait", 0, &opt_misc, 'W', },
|
|
{ "-W", 0, &opt_misc, 'W', },
|
|
};
|
|
|
|
|
|
/*
|
|
* Tests.
|
|
*/
|
|
#define test(n) { #n, run_client_##n, run_server_##n }
|
|
TEST Tests[] ={
|
|
test(conf),
|
|
test(quit),
|
|
test(rds_bw),
|
|
test(rds_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),
|
|
#endif
|
|
};
|
|
|
|
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
initialize();
|
|
set_signals();
|
|
do_args(&argv[1]);
|
|
return ExitStatus;
|
|
}
|
|
|
|
|
|
/*
|
|
* Initialize.
|
|
*/
|
|
static void
|
|
initialize(void)
|
|
{
|
|
init_vars();
|
|
}
|
|
|
|
|
|
/*
|
|
* Initialize variables.
|
|
*/
|
|
static void
|
|
init_vars(void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < P_N; ++i)
|
|
if (ParInfo[i].index != i)
|
|
bug_die("initialize: ParInfo: out of order: %d", i);
|
|
ProcStatFD = open("/proc/stat", 0);
|
|
if (ProcStatFD < 0)
|
|
syserror_die("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 alrm ={ .sa_sigaction = sig_alrm };
|
|
sigaction(SIGALRM, &alrm, 0);
|
|
sigaction(SIGPIPE, &alrm, 0);
|
|
}
|
|
|
|
|
|
/*
|
|
* Note that time is up.
|
|
*/
|
|
static void
|
|
sig_alrm(int signo, siginfo_t *siginfo, void *ucontext)
|
|
{
|
|
set_finished();
|
|
}
|
|
|
|
|
|
/*
|
|
* 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_die("%s: bad option; try qperf --help", arg);
|
|
if (!option->server_valid)
|
|
isClient = 1;
|
|
option->func(option, &args);
|
|
} else {
|
|
isClient = 1;
|
|
if (!ServerName)
|
|
ServerName = arg;
|
|
else {
|
|
TEST *p = find_test(arg);
|
|
if (!p)
|
|
error_die("%s: bad test; try qperf --help", arg);
|
|
client(p);
|
|
testSpecified = 1;
|
|
}
|
|
++args;
|
|
}
|
|
}
|
|
if (!isClient)
|
|
server();
|
|
else if (!testSpecified) {
|
|
if (!ServerName)
|
|
error_die("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_die("Must specify host name first; try qperf --help");
|
|
error_die("Must specify a test type; try qperf --help");
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Given the name of an option, find it.
|
|
*/
|
|
static OPTION *
|
|
find_option(char *name)
|
|
{
|
|
int n = cardof(Options);
|
|
OPTION *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;
|
|
}
|
|
|
|
|
|
/*
|
|
* Print out a help message.
|
|
*/
|
|
static void
|
|
opt_help(OPTION *option, char ***argvp)
|
|
{
|
|
char **usage;
|
|
char *category = (*argvp)[1];
|
|
|
|
if (!category)
|
|
category = "main";
|
|
for (usage = Usage; *usage; usage += 2)
|
|
if (streq(*usage, category))
|
|
break;
|
|
if (!*usage)
|
|
error_die("Cannot find help category %s; try: qperf --help");
|
|
printf("%s", usage[1]);
|
|
exit(0);
|
|
}
|
|
|
|
|
|
/*
|
|
* Handle options requiring a long argument.
|
|
*/
|
|
static void
|
|
opt_long(OPTION *option, char ***argvp)
|
|
{
|
|
long l = arg_long(argvp);
|
|
setp_u32(option->name, option->arg1, l);
|
|
setp_u32(option->name, option->arg2, l);
|
|
}
|
|
|
|
|
|
/*
|
|
* Handle miscellaneous options.
|
|
*/
|
|
static void
|
|
opt_misc(OPTION *option, char ***argvp)
|
|
{
|
|
switch (option->arg1 with (option->arg2)) {
|
|
case 'e':
|
|
Precision = arg_long(argvp);
|
|
return;
|
|
case 'u':
|
|
UnifyUnits = 1;
|
|
break;
|
|
case 'v':
|
|
VerboseConf = 1;
|
|
VerboseStat = 1;
|
|
VerboseTime = 1;
|
|
VerboseUsed = 1;
|
|
break;
|
|
case 'D':
|
|
Debug = 1;
|
|
break;
|
|
case 'H':
|
|
ServerName = arg_strn(argvp);
|
|
return;
|
|
case 'U':
|
|
UnifyNodes = 1;
|
|
break;
|
|
case 'W':
|
|
Wait = arg_time(argvp);
|
|
return;
|
|
case ('l') with ('p'):
|
|
ListenPort = arg_long(argvp);
|
|
return;
|
|
case ('s') with ('t'):
|
|
ServerTimeout = arg_time(argvp);
|
|
return;
|
|
case ('v') with ('c'):
|
|
VerboseConf = 1;
|
|
break;
|
|
case ('v') with ('s'):
|
|
VerboseStat = 1;
|
|
break;
|
|
case ('v') with ('t'):
|
|
VerboseTime = 1;
|
|
break;
|
|
case ('v') with ('u'):
|
|
VerboseUsed = 1;
|
|
break;
|
|
case ('v') with ('v'):
|
|
VerboseConf = 2;
|
|
VerboseStat = 2;
|
|
VerboseTime = 2;
|
|
VerboseUsed = 2;
|
|
break;
|
|
case ('v') with ('C'):
|
|
VerboseConf = 2;
|
|
break;
|
|
case ('v') with ('S'):
|
|
VerboseStat = 2;
|
|
break;
|
|
case ('v') with ('T'):
|
|
VerboseTime = 2;
|
|
break;
|
|
case ('v') with ('U'):
|
|
VerboseUsed = 2;
|
|
break;
|
|
default:
|
|
bug_die("opt_misc: unknown argument: %s", option->name);
|
|
}
|
|
*argvp += 1;
|
|
}
|
|
|
|
|
|
/*
|
|
* Handle options requiring a size argument.
|
|
*/
|
|
static void
|
|
opt_size(OPTION *option, char ***argvp)
|
|
{
|
|
long l = arg_size(argvp);
|
|
setp_u32(option->name, option->arg1, l);
|
|
setp_u32(option->name, option->arg2, l);
|
|
}
|
|
|
|
|
|
/*
|
|
* Handle options requiring a string argument.
|
|
*/
|
|
static void
|
|
opt_strn(OPTION *option, char ***argvp)
|
|
{
|
|
char *s = arg_strn(argvp);
|
|
setp_str(option->name, option->arg1, s);
|
|
setp_str(option->name, option->arg2, s);
|
|
}
|
|
|
|
|
|
/*
|
|
* Handle options requiring a time argument.
|
|
*/
|
|
static void
|
|
opt_time(OPTION *option, char ***argvp)
|
|
{
|
|
long l = arg_time(argvp);
|
|
setp_u32(option->name, option->arg1, l);
|
|
setp_u32(option->name, option->arg2, l);
|
|
}
|
|
|
|
|
|
/*
|
|
* Print out our current version.
|
|
*/
|
|
static void
|
|
opt_vers(OPTION *option, char ***argvp)
|
|
{
|
|
printf("qperf %d.%d.%d\n", VER_MAJ, VER_MIN, VER_INC);
|
|
exit(0);
|
|
}
|
|
|
|
|
|
/*
|
|
* 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("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;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* 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_die("Missing argument to %s", argv[0]);
|
|
l = strtol(argv[1], &p, 10);
|
|
if (p[0] != '\0')
|
|
error_die("Bad argument: %s", argv[1]);
|
|
if (l < 0)
|
|
error_die("%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)
|
|
{
|
|
char *p;
|
|
long double d;
|
|
long l = 0;
|
|
char **argv = *argvp;
|
|
|
|
if (!argv[1])
|
|
error_die("Missing argument to %s", argv[0]);
|
|
d = strtold(argv[1], &p);
|
|
if (d < 0)
|
|
error_die("%s requires a non-negative number", argv[0]);
|
|
|
|
if (p[0] == '\0')
|
|
l = d;
|
|
else {
|
|
if (streq(p, "kb") || streq(p, "k"))
|
|
l = (long)(d * (1000));
|
|
else if (streq(p, "mb") || streq(p, "m"))
|
|
l = (long)(d * (1000 * 1000));
|
|
else if (streq(p, "gb") || streq(p, "g"))
|
|
l = (long)(d * (1000 * 1000 * 1000));
|
|
else if (streq(p, "kib") || streq(p, "K"))
|
|
l = (long)(d * (1024));
|
|
else if (streq(p, "mib") || streq(p, "M"))
|
|
l = (long)(d * (1024 * 1024));
|
|
else if (streq(p, "gib") || streq(p, "G"))
|
|
l = (long)(d * (1024 * 1024 * 1024));
|
|
else
|
|
error_die("Bad argument: %s", argv[1]);
|
|
}
|
|
*argvp += 2;
|
|
return l;
|
|
}
|
|
|
|
|
|
/*
|
|
* Return the value of a string argument.
|
|
*/
|
|
static char *
|
|
arg_strn(char ***argvp)
|
|
{
|
|
char **argv = *argvp;
|
|
if (!argv[1])
|
|
error_die("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_die("Missing argument to %s", argv[0]);
|
|
d = strtold(argv[1], &p);
|
|
if (d < 0)
|
|
error_die("%s requires a non-negative number", argv[0]);
|
|
|
|
if (p[0] == '\0')
|
|
l = (long)d;
|
|
else {
|
|
int u = *p;
|
|
if (p[1] != '\0')
|
|
error_die("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_die("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_die("%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)
|
|
bug_die("par_info: table out of order: %d != %d", index, p-index);
|
|
return p;
|
|
}
|
|
|
|
|
|
/*
|
|
* Server.
|
|
*/
|
|
static void
|
|
server(void)
|
|
{
|
|
pid_t pid;
|
|
|
|
server_listen();
|
|
for (;;) {
|
|
TEST *test;
|
|
|
|
debug("waiting for request");
|
|
if (!server_recv_request())
|
|
continue;
|
|
if (Req.ver_maj != VER_MAJ || Req.ver_min != VER_MIN) {
|
|
int h_maj = Req.ver_maj;
|
|
int h_min = Req.ver_min;
|
|
int h_inc = Req.ver_inc;
|
|
int l_maj = VER_MAJ;
|
|
int l_min = VER_MIN;
|
|
int l_inc = VER_INC;
|
|
char *msg = "upgrade %s from %d.%d.%d to %d.%d.%d";
|
|
char *low = "client";
|
|
|
|
if (l_maj > h_maj || (l_maj == h_maj && l_min > h_min)) {
|
|
h_maj = VER_MAJ;
|
|
h_min = VER_MIN;
|
|
h_inc = VER_INC;
|
|
l_maj = Req.ver_maj;
|
|
l_min = Req.ver_min;
|
|
l_inc = Req.ver_inc;
|
|
low = "server";
|
|
}
|
|
error(msg, low, l_maj, l_min, l_inc, h_maj, h_min, h_inc);
|
|
continue;
|
|
}
|
|
if (Req.req_index >= cardof(Tests)) {
|
|
error("server: bad request index: %d", Req.req_index);
|
|
continue;
|
|
}
|
|
test = &Tests[Req.req_index];
|
|
TestName = test->name;
|
|
debug("request is %s", TestName);
|
|
pid = fork();
|
|
if (pid == 0) {
|
|
init_lstat();
|
|
Finished = 0;
|
|
Successful = 0;
|
|
set_affinity();
|
|
(test->server)();
|
|
stop_timing();
|
|
exit(0);
|
|
} else
|
|
waitpid(pid, 0, 0);
|
|
close(RemoteFD);
|
|
}
|
|
close(ListenFD);
|
|
}
|
|
|
|
|
|
/*
|
|
* Listen for any requests.
|
|
*/
|
|
static void
|
|
server_listen(void)
|
|
{
|
|
int stat;
|
|
char *service;
|
|
struct addrinfo *r;
|
|
struct addrinfo *res;
|
|
struct addrinfo hints ={
|
|
.ai_flags = AI_PASSIVE,
|
|
.ai_family = AF_UNSPEC,
|
|
.ai_socktype = SOCK_STREAM
|
|
};
|
|
|
|
service = qasprintf("%d", ListenPort);
|
|
stat = getaddrinfo(0, service, &hints, &res);
|
|
if (stat != SUCCESS0)
|
|
error_die("getaddrinfo failed: %s", gai_strerror(stat));
|
|
free(service);
|
|
|
|
ListenFD = -1;
|
|
for (r = res; r; r = r->ai_next) {
|
|
ListenFD = socket(r->ai_family, r->ai_socktype, r->ai_protocol);
|
|
if (ListenFD >= 0) {
|
|
int one = 1;
|
|
stat = setsockopt(ListenFD, SOL_SOCKET, SO_REUSEADDR,
|
|
&one, sizeof(one));
|
|
if (stat < 0)
|
|
syserror_die("setsockopt failed");
|
|
if (bind(ListenFD, r->ai_addr, r->ai_addrlen) == SUCCESS0)
|
|
break;
|
|
close(ListenFD);
|
|
ListenFD = -1;
|
|
}
|
|
}
|
|
freeaddrinfo(res);
|
|
if (ListenFD < 0)
|
|
error_die("Unable to bind to listen port");
|
|
|
|
Req.timeout = ServerTimeout;
|
|
if (listen(ListenFD, LISTENQ) < 0)
|
|
syserror_die("listen failed");
|
|
}
|
|
|
|
|
|
/*
|
|
* Accept a request from a client.
|
|
*/
|
|
static int
|
|
server_recv_request(void)
|
|
{
|
|
REQ req;
|
|
socklen_t clientLen;
|
|
struct sockaddr_in clientAddr;
|
|
|
|
clientLen = sizeof(clientAddr);
|
|
RemoteFD = accept(ListenFD, (struct sockaddr *)&clientAddr, &clientLen);
|
|
if (RemoteFD < 0)
|
|
return syserror("accept failed");
|
|
if (!set_nonblock(RemoteFD))
|
|
goto err;
|
|
if (!recv_mesg(&req, sizeof(req), "request data"))
|
|
goto err;
|
|
dec_init(&req);
|
|
dec_req(&Req);
|
|
return 1;
|
|
|
|
err:
|
|
close(RemoteFD);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* 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, 2);
|
|
if (!par_isset(R_NO_MSGS))
|
|
setp_u32(0, R_TIME, 2);
|
|
setp_u32(0, L_TIMEOUT, 5);
|
|
setp_u32(0, R_TIMEOUT, 5);
|
|
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);
|
|
Finished = 0;
|
|
Successful = 0;
|
|
(*test->client)();
|
|
close(RemoteFD);
|
|
if (!Successful)
|
|
ExitStatus = 1;
|
|
place_show();
|
|
}
|
|
|
|
|
|
/*
|
|
* Send a request to the server.
|
|
*/
|
|
void
|
|
client_send_request(void)
|
|
{
|
|
REQ req;
|
|
int stat;
|
|
char *service;
|
|
struct addrinfo *r;
|
|
struct addrinfo *res;
|
|
struct addrinfo hints ={
|
|
.ai_family = AF_UNSPEC,
|
|
.ai_socktype = SOCK_STREAM
|
|
};
|
|
|
|
service = qasprintf("%d", ListenPort);
|
|
stat = getaddrinfo(ServerName, service, &hints, &res);
|
|
if (stat != SUCCESS0)
|
|
error_die("getaddrinfo failed: %s", gai_strerror(stat));
|
|
free(service);
|
|
|
|
RemoteFD = -1;
|
|
if (Wait)
|
|
start_timing(Wait);
|
|
for (;;) {
|
|
for (r = res; r; r = r->ai_next) {
|
|
RemoteFD = socket(r->ai_family, r->ai_socktype, r->ai_protocol);
|
|
if (RemoteFD >= 0) {
|
|
if (connect(RemoteFD, r->ai_addr, r->ai_addrlen) == SUCCESS0)
|
|
break;
|
|
close(RemoteFD);
|
|
RemoteFD = -1;
|
|
}
|
|
}
|
|
if (RemoteFD >= 0 || !Wait || Finished)
|
|
break;
|
|
sleep(1);
|
|
}
|
|
if (Wait)
|
|
stop_timing();
|
|
freeaddrinfo(res);
|
|
if (RemoteFD < 0)
|
|
error_die("Failed to connect");
|
|
if (!set_nonblock(RemoteFD))
|
|
die();
|
|
enc_init(&req);
|
|
enc_req(&RReq);
|
|
if (!send_mesg(&req, sizeof(req), "request data"))
|
|
die();
|
|
}
|
|
|
|
|
|
/*
|
|
* Set a file descriptor to non-blocking.
|
|
*/
|
|
static int
|
|
set_nonblock(int fd)
|
|
{
|
|
int one = 1;
|
|
if (ioctl(fd, FIONBIO, &one) < 0)
|
|
return syserror("failed to set to non-blocking");
|
|
return 1;
|
|
}
|
|
|
|
|
|
/*
|
|
* Synchronize the client and server.
|
|
*/
|
|
int
|
|
synchronize(void)
|
|
{
|
|
if (is_client()) {
|
|
if (!send_sync())
|
|
return 0;
|
|
if (!recv_sync())
|
|
return 0;
|
|
} else {
|
|
if (!recv_sync())
|
|
return 0;
|
|
if (!send_sync())
|
|
return 0;
|
|
}
|
|
debug("sync completed");
|
|
start_timing(Req.time);
|
|
return 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 (!Successful)
|
|
return;
|
|
Successful = 0;
|
|
if (is_client()) {
|
|
if (!recv_mesg(&stat, sizeof(stat), "results"))
|
|
return;
|
|
dec_init(&stat);
|
|
dec_stat(&RStat);
|
|
if (!send_sync())
|
|
return;
|
|
} else {
|
|
enc_init(&stat);
|
|
enc_stat(&LStat);
|
|
if (!send_mesg(&stat, sizeof(stat), "results"))
|
|
return;
|
|
if (!recv_sync())
|
|
return;
|
|
}
|
|
Successful = 1;
|
|
}
|
|
|
|
|
|
/*
|
|
* Send a synchronize message.
|
|
*/
|
|
static int
|
|
send_sync(void)
|
|
{
|
|
return send_mesg(SYNCMESG, SYNCSIZE, "sync");
|
|
}
|
|
|
|
|
|
/*
|
|
* Receive a synchronize message.
|
|
*/
|
|
static int
|
|
recv_sync(void)
|
|
{
|
|
char data[SYNCSIZE];
|
|
|
|
if (!recv_mesg(data, sizeof(data), "sync"))
|
|
return 0;
|
|
if (memcmp(data, SYNCMESG, SYNCSIZE) != SUCCESS0)
|
|
return error("sync failure: data does not match");
|
|
return 1;
|
|
}
|
|
|
|
|
|
/*
|
|
* Send a message to the client.
|
|
*/
|
|
int
|
|
send_mesg(void *ptr, int len, char *item)
|
|
{
|
|
debug("sending %s", item);
|
|
return send_recv_mesg('s', item, RemoteFD, ptr, len);
|
|
}
|
|
|
|
|
|
/*
|
|
* Receive a response from the server.
|
|
*/
|
|
int
|
|
recv_mesg(void *ptr, int len, char *item)
|
|
{
|
|
debug("waiting for %s", item);
|
|
return send_recv_mesg('r', item, RemoteFD, ptr, len);
|
|
}
|
|
|
|
|
|
/*
|
|
* Send or receive a message to a file descriptor timing out after a certain
|
|
* amount of time.
|
|
*/
|
|
static int
|
|
send_recv_mesg(int sr, char *item, int fd, char *buf, int len)
|
|
{
|
|
typedef ssize_t (IO)(int fd, void *buf, size_t count);
|
|
double etime;
|
|
fd_set *fdset;
|
|
fd_set rfdset;
|
|
fd_set wfdset;
|
|
char *action;
|
|
IO *func;
|
|
|
|
if (sr == 'r') {
|
|
func = (IO *)read;
|
|
fdset = &rfdset;
|
|
action = "receive";
|
|
} else {
|
|
func = (IO *)write;
|
|
fdset = &wfdset;
|
|
action = "send";
|
|
}
|
|
|
|
etime = get_seconds() + Req.timeout;
|
|
while (len) {
|
|
int n;
|
|
double time;
|
|
struct timeval timeval;
|
|
|
|
errno = 0;
|
|
time = etime - get_seconds();
|
|
if (time <= 0)
|
|
return error("failed to %s %s: timed out", action, item);
|
|
n = time += 1.0 / (1000*1000);
|
|
timeval.tv_sec = n;
|
|
timeval.tv_usec = (time-n) * 1000*1000;
|
|
|
|
FD_ZERO(&rfdset);
|
|
FD_ZERO(&wfdset);
|
|
FD_SET(fd, fdset);
|
|
if (select(fd+1, &rfdset, &wfdset, 0, &timeval) < 0)
|
|
return syserror("failed to %s %s: select failed", action, item);
|
|
if (!FD_ISSET(fd, fdset))
|
|
continue;
|
|
n = func(fd, buf, len);
|
|
if (n < 0)
|
|
return syserror("failed to %s %s", action, item);
|
|
if (n == 0) {
|
|
char *side = is_client() ? "server" : "client";
|
|
return syserror("failed to %s %s: %s not responding",
|
|
action, item, side);
|
|
}
|
|
len -= n;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
|
|
/*
|
|
* 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();
|
|
if (!recv_mesg(&rconf, sizeof(rconf), "configuration"))
|
|
return;
|
|
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_die("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 if (cpus == 2)
|
|
snprintf(count, sizeof(count), "Dual-Core ");
|
|
else if (cpus == 4)
|
|
snprintf(count, sizeof(count), "Quad-Core ");
|
|
else
|
|
snprintf(count, sizeof(count), "%d-Core ", 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();
|
|
synchronize();
|
|
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];
|
|
|
|
synchronize();
|
|
read(RemoteFD, buf, sizeof(buf));
|
|
exit(0);
|
|
}
|
|
|
|
|
|
/*
|
|
* Start timing.
|
|
*/
|
|
static void
|
|
start_timing(int seconds)
|
|
{
|
|
struct itimerval itimerval = {{0}};
|
|
|
|
get_times(LStat.time_s);
|
|
setitimer(ITIMER_REAL, &itimerval, 0);
|
|
if (!seconds)
|
|
return;
|
|
|
|
debug("starting timer");
|
|
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, usually, when SIGALRM goes off, it is executing
|
|
* a read or write system call which gets interrupted. If SIGALRM goes off
|
|
* after Finished is checked but before the system call is performed, 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.
|
|
*/
|
|
void
|
|
stop_timing(void)
|
|
{
|
|
struct itimerval itimerval = {{0}};
|
|
|
|
set_finished();
|
|
setitimer(ITIMER_REAL, &itimerval, 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;
|
|
|
|
if (!Successful)
|
|
return;
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
/*
|
|
* Touch data.
|
|
*/
|
|
void
|
|
touch_data(void *p, int n)
|
|
{
|
|
uint64_t a;
|
|
volatile uint64_t *p64 = p;
|
|
|
|
while (n >= sizeof(*p64)) {
|
|
a = *p64++;
|
|
n -= sizeof(*p64);
|
|
}
|
|
if (n) {
|
|
volatile uint8_t *p8 = (uint8_t *)p64;
|
|
while (n >= sizeof(*p8)) {
|
|
a = *p8++;
|
|
n -= sizeof(*p8);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* 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 (!Successful)
|
|
return;
|
|
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;
|
|
static 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;
|
|
static 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;
|
|
static 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 = 0;
|
|
static char *tab[] ={
|
|
"bytes/sec", "KB/sec", "MB/sec", "GB/sec", "TB/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 size.
|
|
*/
|
|
static void
|
|
view_size(int type, char *pref, char *name, long long value)
|
|
{
|
|
int n = 0;
|
|
double val = value;
|
|
static 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;
|
|
static 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;
|
|
static 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: bug_die("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))
|
|
bug_die("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)
|
|
syserror_die("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->flip, sizeof(host->flip));
|
|
enc_int(host->access_recv, sizeof(host->access_recv));
|
|
enc_int(host->affinity, sizeof(host->affinity));
|
|
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->timeout, sizeof(host->timeout));
|
|
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->sock_buf_size, sizeof(host->sock_buf_size));
|
|
enc_int(host->time, sizeof(host->time));
|
|
enc_str(host->id, sizeof(host->id));
|
|
}
|
|
|
|
|
|
/*
|
|
* Decode a REQ structure from a data stream.
|
|
*/
|
|
static void
|
|
dec_req(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));
|
|
host->req_index = dec_int(sizeof(host->req_index));
|
|
host->flip = dec_int(sizeof(host->flip));
|
|
host->access_recv = dec_int(sizeof(host->access_recv));
|
|
host->affinity = dec_int(sizeof(host->affinity));
|
|
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->timeout = dec_int(sizeof(host->timeout));
|
|
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->sock_buf_size = dec_int(sizeof(host->sock_buf_size));
|
|
host->time = dec_int(sizeof(host->time));
|
|
dec_str(host->id, sizeof(host->id));
|
|
}
|
|
|
|
|
|
/*
|
|
* 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));
|
|
}
|
|
|
|
|
|
/*
|
|
* Initialize encode pointer.
|
|
*/
|
|
void
|
|
enc_init(void *p)
|
|
{
|
|
EncodePtr = p;
|
|
}
|
|
|
|
|
|
/*
|
|
* Initialize decode pointer.
|
|
*/
|
|
void
|
|
dec_init(void *p)
|
|
{
|
|
DecodePtr = p;
|
|
}
|
|
|
|
|
|
/*
|
|
* Encode a string.
|
|
*/
|
|
void
|
|
enc_str(char *s, int n)
|
|
{
|
|
memcpy(EncodePtr, s, n);
|
|
EncodePtr += n;
|
|
}
|
|
|
|
|
|
/*
|
|
* Decode a string.
|
|
*/
|
|
void
|
|
dec_str(char *s, int n)
|
|
{
|
|
memcpy(s, DecodePtr, n);
|
|
DecodePtr += n;
|
|
}
|
|
|
|
|
|
/*
|
|
* Encode an integer.
|
|
*/
|
|
void
|
|
enc_int(int64_t l, int n)
|
|
{
|
|
while (n--) {
|
|
*EncodePtr++ = l;
|
|
l >>= 8;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Decode an integer.
|
|
*/
|
|
int64_t
|
|
dec_int(int n)
|
|
{
|
|
uint64_t l = 0;
|
|
uint8_t *p = (DecodePtr += n);
|
|
while (n--)
|
|
l = (l << 8) | (*--p & 0xFF);
|
|
return l;
|
|
}
|
|
|
|
|
|
/*
|
|
* 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)
|
|
syserror_die("Failed to seek /proc/stat");
|
|
n = read(ProcStatFD, buf, sizeof(buf)-1);
|
|
buf[n] = '\0';
|
|
if (strncmp(buf, "cpu ", 4))
|
|
error_die("/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_die("/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;
|
|
}
|
|
|
|
|
|
/*
|
|
* Get the time of day in seconds as a floating point number.
|
|
*/
|
|
static double
|
|
get_seconds(void)
|
|
{
|
|
struct timeval timeval;
|
|
|
|
if (gettimeofday(&timeval, 0) < 0)
|
|
syserror_die("gettimeofday failed");
|
|
return timeval.tv_sec + timeval.tv_usec/(1000.0*1000.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_die("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';
|
|
}
|
|
|
|
|
|
/*
|
|
* Call malloc and panic on error.
|
|
*/
|
|
void *
|
|
qmalloc(long n)
|
|
{
|
|
void *p = malloc(n);
|
|
if (!p)
|
|
error_die("Out of space");
|
|
return p;
|
|
}
|
|
|
|
|
|
/*
|
|
* Print out an error message and exit.
|
|
*/
|
|
static char *
|
|
qasprintf(char *fmt, ...)
|
|
{
|
|
int stat;
|
|
char *str;
|
|
va_list alist;
|
|
|
|
va_start(alist, fmt);
|
|
stat = vasprintf(&str, fmt, alist);
|
|
va_end(alist);
|
|
if (stat < 0)
|
|
error_die("Out of space");
|
|
return str;
|
|
}
|
|
|
|
|
|
/*
|
|
* Print out a debug message.
|
|
*/
|
|
void
|
|
debug(char *fmt, ...)
|
|
{
|
|
va_list alist;
|
|
|
|
if (!Debug)
|
|
return;
|
|
va_start(alist, fmt);
|
|
vfprintf(stderr, fmt, alist);
|
|
va_end(alist);
|
|
fprintf(stderr, "\n");
|
|
}
|
|
|
|
|
|
/*
|
|
* Print out an error message.
|
|
*/
|
|
int
|
|
error(char *fmt, ...)
|
|
{
|
|
va_list alist;
|
|
|
|
va_start(alist, fmt);
|
|
vfprintf(stderr, fmt, alist);
|
|
va_end(alist);
|
|
fprintf(stderr, "\n");
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* Print out an error message and exit.
|
|
*/
|
|
void
|
|
error_die(char *fmt, ...)
|
|
{
|
|
va_list alist;
|
|
|
|
va_start(alist, fmt);
|
|
vfprintf(stderr, fmt, alist);
|
|
va_end(alist);
|
|
fprintf(stderr, "\n");
|
|
die();
|
|
}
|
|
|
|
|
|
/*
|
|
* Print out a system error message.
|
|
*/
|
|
int
|
|
syserror(char *fmt, ...)
|
|
{
|
|
va_list alist;
|
|
|
|
va_start(alist, fmt);
|
|
vfprintf(stderr, fmt, alist);
|
|
va_end(alist);
|
|
if (errno)
|
|
fprintf(stderr, ": %s", strerror(errno));
|
|
fprintf(stderr, "\n");
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Print out a system error message and exit.
|
|
*/
|
|
void
|
|
syserror_die(char *fmt, ...)
|
|
{
|
|
va_list alist;
|
|
|
|
va_start(alist, fmt);
|
|
vfprintf(stderr, fmt, alist);
|
|
va_end(alist);
|
|
if (errno)
|
|
fprintf(stderr, ": %s", strerror(errno));
|
|
fprintf(stderr, "\n");
|
|
die();
|
|
}
|
|
|
|
|
|
/*
|
|
* Print out an internal error and exit.
|
|
*/
|
|
static void
|
|
bug_die(char *fmt, ...)
|
|
{
|
|
va_list alist;
|
|
|
|
fprintf(stderr, "internal error: ");
|
|
va_start(alist, fmt);
|
|
vfprintf(stderr, fmt, alist);
|
|
va_end(alist);
|
|
fprintf(stderr, "\n");
|
|
die();
|
|
}
|
|
|
|
|
|
/*
|
|
* Exit unsuccessfully.
|
|
*/
|
|
void
|
|
die(void)
|
|
{
|
|
exit(1);
|
|
}
|