1 #include <sys/types.h> 2 #include <sys/stat.h> 3 #include <sys/wait.h> 4 5 #include <errno.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 fprintf(stderr, "calloc failed\n"); 94 exit(1); 95 } 96 97 null_fd = open("/dev/null", O_RDWR); 98 if (null_fd < 0) { 99 fprintf(stderr, "open null failed: %d\n", errno); 100 exit(1); 101 } 102 103 for (i = 0; i < ninst; ++i) { 104 if (pipe(instance[i].pipes) < 0) { 105 fprintf(stderr, "pipe %dth failed: %d\n", i, errno); 106 exit(1); 107 } 108 } 109 110 for (i = 0; i < ninst; ++i) { 111 pid_t pid; 112 113 pid = vfork(); 114 if (pid == 0) { 115 int ret; 116 117 dup2(instance[i].pipes[1], STDOUT_FILENO); 118 dup2(null_fd, STDERR_FILENO); 119 ret = execv(NETPERF_PATH, args); 120 if (ret < 0) { 121 fprintf(stderr, "execv %d failed: %d\n", 122 i, errno); 123 _exit(1); 124 } 125 /* Never reached */ 126 abort(); 127 } else if (pid < 0) { 128 fprintf(stderr, "vfork %d failed: %d\n", i, errno); 129 exit(1); 130 } 131 close(instance[i].pipes[1]); 132 instance[i].pipes[1] = -1; 133 } 134 135 ninst_done = 0; 136 while (ninst_done < ninst) { 137 pid_t pid; 138 139 pid = waitpid(-1, NULL, 0); 140 if (pid < 0) { 141 fprintf(stderr, "waitpid failed: %d\n", errno); 142 exit(1); 143 } 144 ++ninst_done; 145 } 146 147 res_max = 0.0; 148 res_min = 0.0; 149 jain = 0.0; 150 result = 0.0; 151 for (i = 0; i < ninst; ++i) { 152 char line[128]; 153 FILE *fp; 154 155 fp = fdopen(instance[i].pipes[0], "r"); 156 if (fp == NULL) { 157 fprintf(stderr, "fdopen %dth failed\n", i); 158 exit(1); 159 } 160 161 while (fgets(line, sizeof(line), fp) != NULL) { 162 int n, arg1, arg2, arg3; 163 double res, arg4; 164 165 n = sscanf(line, "%d%d%d%lf%lf", 166 &arg1, &arg2, &arg3, &arg4, &res); 167 if (n == 5) { 168 if (!set_minmax) { 169 res_max = res; 170 res_min = res; 171 set_minmax = 1; 172 } else { 173 if (res > res_max) 174 res_max = res; 175 if (res < res_min) 176 res_min = res; 177 } 178 jain += (res * res); 179 result += res; 180 break; 181 } 182 } 183 fclose(fp); 184 } 185 186 jain *= ninst; 187 jain = (result * result) / jain; 188 189 printf("%s %.2f Mbps\n", reverse ? "TCP_MAERTS" : "TCP_STREAM", result); 190 printf("min/max (jain) %.2f Mbps/%.2f Mbps (%f)\n", 191 res_min, res_max, jain); 192 193 exit(0); 194 } 195