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
usage(const char * cmd)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
sigint_handler(int sig __unused)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
main(int argc,char * argv[])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