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 <stdio.h> 8 #include <stdlib.h> 9 #include <unistd.h> 10 11 #define NETPERF_CMD "netperf" 12 #define NETPERF_PATH "/usr/local/bin/" NETPERF_CMD 13 14 struct netperf_child { 15 int pipes[2]; 16 }; 17 18 static void 19 usage(const char *cmd) 20 { 21 fprintf(stderr, "%s -H host [-l len_s] [-i instances] [-m msgsz] " 22 "[-r|-s]\n", cmd); 23 exit(1); 24 } 25 26 int 27 main(int argc, char *argv[]) 28 { 29 struct netperf_child *instance; 30 char len_str[32]; 31 char *args[32]; 32 const char *host, *msgsz; 33 volatile int ninst, set_minmax = 0; 34 int len, ninst_done; 35 int opt, i, null_fd; 36 volatile int reverse = 0, sfile = 0; 37 double result, res_max, res_min, jain; 38 39 host = NULL; 40 ninst = 2; 41 len = 10; 42 msgsz = NULL; 43 44 while ((opt = getopt(argc, argv, "H:i:l:m:rs")) != -1) { 45 switch (opt) { 46 case 'H': 47 host = optarg; 48 break; 49 50 case 'i': 51 ninst = strtoul(optarg, NULL, 10); 52 break; 53 54 case 'l': 55 len = strtoul(optarg, NULL, 10); 56 break; 57 58 case 'm': 59 msgsz = optarg; 60 break; 61 62 case 'r': 63 reverse = 1; 64 sfile = 0; 65 break; 66 67 case 's': 68 reverse = 0; 69 sfile = 1; 70 break; 71 72 default: 73 usage(argv[0]); 74 } 75 } 76 if (ninst <= 0 || host == NULL || len <= 0) 77 usage(argv[0]); 78 79 snprintf(len_str, sizeof(len_str), "%d", len); 80 81 i = 0; 82 args[i++] = __DECONST(char *, NETPERF_CMD); 83 args[i++] = __DECONST(char *, "-P0"); 84 args[i++] = __DECONST(char *, "-H"); 85 args[i++] = __DECONST(char *, host); 86 args[i++] = __DECONST(char *, "-l"); 87 args[i++] = __DECONST(char *, len_str); 88 args[i++] = __DECONST(char *, "-t"); 89 if (reverse) 90 args[i++] = __DECONST(char *, "TCP_MAERTS"); 91 else if (sfile) 92 args[i++] = __DECONST(char *, "TCP_SENDFILE"); 93 else 94 args[i++] = __DECONST(char *, "TCP_STREAM"); 95 if (msgsz != NULL) { 96 args[i++] = __DECONST(char *, "--"); 97 args[i++] = __DECONST(char *, "-m"); 98 args[i++] = __DECONST(char *, msgsz); 99 } 100 args[i] = NULL; 101 102 instance = calloc(ninst, sizeof(struct netperf_child)); 103 if (instance == NULL) 104 err(1, "calloc failed"); 105 106 null_fd = open("/dev/null", O_RDWR); 107 if (null_fd < 0) 108 err(1, "open null failed"); 109 110 for (i = 0; i < ninst; ++i) { 111 if (pipe(instance[i].pipes) < 0) 112 err(1, "pipe %dth failed", i); 113 } 114 115 for (i = 0; i < ninst; ++i) { 116 pid_t pid; 117 118 pid = vfork(); 119 if (pid == 0) { 120 int ret; 121 122 dup2(instance[i].pipes[1], STDOUT_FILENO); 123 dup2(null_fd, STDERR_FILENO); 124 ret = execv(NETPERF_PATH, args); 125 if (ret < 0) { 126 warn("execv %d failed", i); 127 _exit(1); 128 } 129 /* Never reached */ 130 abort(); 131 } else if (pid < 0) { 132 err(1, "vfork %d failed", i); 133 } 134 close(instance[i].pipes[1]); 135 instance[i].pipes[1] = -1; 136 } 137 138 ninst_done = 0; 139 while (ninst_done < ninst) { 140 pid_t pid; 141 142 pid = waitpid(-1, NULL, 0); 143 if (pid < 0) 144 err(1, "waitpid failed"); 145 ++ninst_done; 146 } 147 148 res_max = 0.0; 149 res_min = 0.0; 150 jain = 0.0; 151 result = 0.0; 152 for (i = 0; i < ninst; ++i) { 153 char line[128]; 154 FILE *fp; 155 156 fp = fdopen(instance[i].pipes[0], "r"); 157 if (fp == NULL) 158 err(1, "fdopen %dth failed", i); 159 160 while (fgets(line, sizeof(line), fp) != NULL) { 161 int n, arg1, arg2, arg3; 162 double res, arg4; 163 164 n = sscanf(line, "%d%d%d%lf%lf", 165 &arg1, &arg2, &arg3, &arg4, &res); 166 if (n == 5) { 167 if (!set_minmax) { 168 res_max = res; 169 res_min = res; 170 set_minmax = 1; 171 } else { 172 if (res > res_max) 173 res_max = res; 174 if (res < res_min) 175 res_min = res; 176 } 177 jain += (res * res); 178 result += res; 179 break; 180 } 181 } 182 fclose(fp); 183 } 184 185 jain *= ninst; 186 jain = (result * result) / jain; 187 188 printf("%s %.2f Mbps\n", reverse ? "TCP_MAERTS" : "TCP_STREAM", result); 189 printf("min/max (jain) %.2f Mbps/%.2f Mbps (%f)\n", 190 res_min, res_max, jain); 191 192 exit(0); 193 } 194