1 #include <sys/types.h> 2 #include <sys/stat.h> 3 #include <sys/wait.h> 4 5 #include <err.h> 6 #include <fcntl.h> 7 #include <signal.h> 8 #include <stdio.h> 9 #include <stdint.h> 10 #include <stdlib.h> 11 #include <string.h> 12 #include <unistd.h> 13 14 #define NETPERF_CMD "netperf" 15 #define NETPERF_PATH "/usr/local/bin/" NETPERF_CMD 16 17 #ifndef __DECONST 18 #define __DECONST(type, var) ((type)(uintptr_t)(const void *)(var)) 19 #endif 20 21 #ifndef __unused 22 #define __unused __attribute__((__unused__)) 23 #endif 24 25 struct netperf_child { 26 int pipes[2]; 27 pid_t pid; 28 }; 29 30 static struct netperf_child *instance; 31 static int ninstance; 32 33 static void 34 usage(const char *cmd) 35 { 36 fprintf(stderr, "%s -H host [-H host1] [-l len_s] [-i instances] " 37 "[-m msgsz] [-S sockbuf] [-r|-s] [-x]\n", cmd); 38 exit(1); 39 } 40 41 static void 42 sigint_handler(int sig __unused) 43 { 44 int i; 45 46 for (i = 0; i < ninstance; ++i) { 47 if (instance[i].pid != -1) 48 kill(instance[i].pid, SIGKILL); 49 } 50 kill(getpid(), SIGKILL); 51 } 52 53 int 54 main(int argc, char *argv[]) 55 { 56 char len_str[32], sockbuf_str[64], msgsz_str[64]; 57 char *args[32]; 58 const char *msgsz, *sockbuf, *name1, *name2; 59 const char ** volatile host; 60 volatile int set_minmax = 0, nhost, dual; 61 int len, ninst, ninst_done, host_idx, host_arg_idx, test_arg_idx; 62 int opt, i, null_fd; 63 volatile int reverse = 0, sfile = 0; 64 double result, res_max, res_min, jain; 65 sigset_t nset, oset; 66 67 dual = 0; 68 ninst = 2; 69 len = 10; 70 msgsz = NULL; 71 sockbuf = NULL; 72 73 host_idx = 0; 74 nhost = 8; 75 host = malloc(sizeof(const char *) * nhost); 76 if (host == NULL) 77 err(1, "malloc failed"); 78 79 while ((opt = getopt(argc, argv, "H:S:i:l:m:rsx")) != -1) { 80 switch (opt) { 81 case 'H': 82 if (host_idx == nhost) { 83 const char **new_host; 84 85 nhost *= 2; 86 new_host = malloc(sizeof(const char *) * nhost); 87 if (new_host == NULL) 88 err(1, "malloc failed"); 89 memcpy(new_host, host, 90 host_idx * sizeof(const char *)); 91 free(host); 92 host = new_host; 93 } 94 host[host_idx++] = optarg; 95 break; 96 97 case 'S': 98 sockbuf = optarg; 99 break; 100 101 case 'i': 102 ninst = strtoul(optarg, NULL, 10); 103 break; 104 105 case 'l': 106 len = strtoul(optarg, NULL, 10); 107 break; 108 109 case 'm': 110 msgsz = optarg; 111 break; 112 113 case 'r': 114 reverse = 1; 115 sfile = 0; 116 break; 117 118 case 's': 119 reverse = 0; 120 sfile = 1; 121 break; 122 123 case 'x': 124 dual = 1; 125 break; 126 127 default: 128 usage(argv[0]); 129 } 130 } 131 nhost = host_idx; 132 133 if (ninst <= 0 || nhost == 0 || len <= 0) 134 usage(argv[0]); 135 136 snprintf(len_str, sizeof(len_str), "%d", len); 137 138 i = 0; 139 args[i++] = __DECONST(char *, NETPERF_CMD); 140 args[i++] = __DECONST(char *, "-P0"); 141 args[i++] = __DECONST(char *, "-H"); 142 host_arg_idx = i; 143 args[i++] = __DECONST(char *, NULL); 144 args[i++] = __DECONST(char *, "-l"); 145 args[i++] = __DECONST(char *, len_str); 146 args[i++] = __DECONST(char *, "-t"); 147 test_arg_idx = i; 148 if (reverse) 149 args[i++] = __DECONST(char *, "TCP_MAERTS"); 150 else if (sfile) 151 args[i++] = __DECONST(char *, "TCP_SENDFILE"); 152 else 153 args[i++] = __DECONST(char *, "TCP_STREAM"); 154 if (msgsz != NULL || sockbuf != NULL) { 155 args[i++] = __DECONST(char *, "--"); 156 if (msgsz != NULL) { 157 snprintf(msgsz_str, sizeof(msgsz_str), "%s,%s", 158 msgsz, msgsz); 159 args[i++] = __DECONST(char *, "-m"); 160 args[i++] = __DECONST(char *, msgsz_str); 161 args[i++] = __DECONST(char *, "-M"); 162 args[i++] = __DECONST(char *, msgsz_str); 163 } 164 if (sockbuf != NULL) { 165 snprintf(sockbuf_str, sizeof(sockbuf_str), "%s,%s", 166 sockbuf, sockbuf); 167 args[i++] = __DECONST(char *, "-s"); 168 args[i++] = __DECONST(char *, sockbuf_str); 169 args[i++] = __DECONST(char *, "-S"); 170 args[i++] = __DECONST(char *, sockbuf_str); 171 } 172 } 173 args[i] = NULL; 174 175 ninstance = ninst * nhost * (dual + 1); 176 instance = calloc(ninstance, sizeof(struct netperf_child)); 177 if (instance == NULL) 178 err(1, "calloc failed"); 179 180 null_fd = open("/dev/null", O_RDWR); 181 if (null_fd < 0) 182 err(1, "open null failed"); 183 184 for (i = 0; i < ninstance; ++i) { 185 if (pipe(instance[i].pipes) < 0) 186 err(1, "pipe %dth failed", i); 187 instance[i].pid = -1; 188 } 189 190 sigemptyset(&nset); 191 sigaddset(&nset, SIGINT); 192 sigprocmask(SIG_BLOCK, &nset, &oset); 193 signal(SIGINT, sigint_handler); 194 195 for (i = 0; i < ninstance; ++i) { 196 pid_t pid; 197 198 pid = vfork(); 199 if (pid == 0) { 200 int ret; 201 202 dup2(instance[i].pipes[1], STDOUT_FILENO); 203 dup2(null_fd, STDERR_FILENO); 204 205 args[host_arg_idx] = __DECONST(char *, 206 host[i % nhost]); 207 if (dual) { 208 const char *test_type; 209 210 if ((i / nhost) & dual) { 211 test_type = sfile ? 212 "TCP_SENDFILE" : "TCP_STREAM"; 213 } else { 214 test_type = "TCP_MAERTS"; 215 } 216 args[test_arg_idx] = __DECONST(char *, 217 test_type); 218 } 219 ret = execv(NETPERF_PATH, args); 220 if (ret < 0) { 221 warn("execv %d failed", i); 222 _exit(1); 223 } 224 /* Never reached */ 225 abort(); 226 } else if (pid < 0) { 227 err(1, "vfork %d failed", i); 228 } 229 close(instance[i].pipes[1]); 230 instance[i].pipes[1] = -1; 231 instance[i].pid = pid; 232 } 233 234 sigprocmask(SIG_SETMASK, &oset, NULL); 235 236 ninst_done = 0; 237 while (ninst_done < ninstance) { 238 pid_t pid; 239 240 pid = waitpid(-1, NULL, 0); 241 if (pid < 0) 242 err(1, "waitpid failed"); 243 ++ninst_done; 244 } 245 246 res_max = 0.0; 247 res_min = 0.0; 248 jain = 0.0; 249 result = 0.0; 250 for (i = 0; i < ninstance; ++i) { 251 char line[128]; 252 FILE *fp; 253 254 fp = fdopen(instance[i].pipes[0], "r"); 255 if (fp == NULL) 256 err(1, "fdopen %dth failed", i); 257 258 while (fgets(line, sizeof(line), fp) != NULL) { 259 int n, arg1, arg2, arg3; 260 double res, arg4; 261 262 n = sscanf(line, "%d%d%d%lf%lf", 263 &arg1, &arg2, &arg3, &arg4, &res); 264 if (n == 5) { 265 if (!set_minmax) { 266 res_max = res; 267 res_min = res; 268 set_minmax = 1; 269 } else { 270 if (res > res_max) 271 res_max = res; 272 if (res < res_min) 273 res_min = res; 274 } 275 jain += (res * res); 276 result += res; 277 break; 278 } 279 } 280 fclose(fp); 281 } 282 283 jain *= ninstance; 284 jain = (result * result) / jain; 285 286 name1 = "TCP_STREAM"; 287 name2 = ""; 288 if (dual) { 289 name1 = "TCP_MAERTS"; 290 if (sfile) 291 name2 = "/TCP_SENDFILE"; 292 else 293 name2 = "/TCP_STREAM"; 294 } else if (reverse) { 295 name1 = "TCP_MAERTS"; 296 } else if (sfile) { 297 name1 = "TCP_SENDFILE"; 298 } 299 300 printf("%s%s %.2f Mbps\n", name1, name2, result); 301 printf("min/max (jain) %.2f Mbps/%.2f Mbps (%f)\n", 302 res_min, res_max, jain); 303 304 exit(0); 305 } 306