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