1 /* $Id: pipebench.c,v 1.12 2003/04/20 16:45:45 marvin Exp $
2 *
3 * Pipebench
4 *
5 * By Thomas Habets <thomas@habets.pp.se>
6 *
7 * Measures the speed of stdin/stdout communication.
8 *
9 * TODO:
10 * - Variable update time (now just updates once a second)
11 */
12 /*
13 * Copyright (C) 2002 Thomas Habets <thomas@habets.pp.se>
14 *
15 * This library is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public
17 * License as published by the Free Software Foundation; either
18 * version 2 of the License, or (at your option) any later version.
19 *
20 * This library is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 * General Public License for more details.
24 *
25 * You should have received a copy of the GNU General Public
26 * License along with this library; if not, write to the Free Software
27 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28 */
29 #include <stdio.h>
30 #include <unistd.h>
31 #include <time.h>
32 #include <signal.h>
33 #include <sys/time.h>
34 #include <sys/types.h>
35 #include <errno.h>
36
37 #ifdef sun
38 #define u_int8_t uint8_t
39 #define u_int16_t uint16_t
40 #define u_int32_t uint32_t
41 #define u_int64_t uint64_t
42 #endif
43
44 static float version = 0.40;
45
46 static volatile int done = 0;
47
sigint(int n)48 static void sigint(int n)
49 {
50 done = 1;
51 }
52
53 /*
54 * Turn a 64 int into SI or pseudo-SI units ("nunit" based).
55 * Two decimal places.
56 *
57 * In: 64 bit int, a storage buffer, size of buffer, and unit base
58 * unit base squared must fit in unsigned long.
59 *
60 * Out: buffer is changed and a pointer is returned to it.
61 *
62 * NOTE: Bits are lost if
63 * 1) Input > (2^64 / 100)
64 * *and*
65 * 2) nunit < 100 (which it should never be)
66 */
unitify(u_int64_t _in,char * buf,int max,unsigned long nunit,int dounit)67 static char *unitify(u_int64_t _in, char *buf, int max, unsigned long nunit,
68 int dounit)
69 {
70 int e = 0;
71 u_int64_t in;
72 double inf;
73 char *unit = "";
74 char *units[] = {
75 "",
76 "k",
77 "M",
78 "G",
79 "T",
80 "P",
81 "E",
82 };
83 int fra = 0;
84
85 inf = in = _in;
86 if (dounit) {
87 if (in > (nunit*nunit)) {
88 e++;
89 in/=nunit;
90 }
91 in *= 100;
92 while (in > (100*nunit)) {
93 e++;
94 in/=nunit;
95 }
96 /* Ha ha ha ha ha ha, oh my god... Yeah I wish I had the
97 * problem this part fixes.
98 */
99 while (e && (e >= (sizeof(units)/sizeof(char*)))) {
100 e--;
101 in*=nunit;
102 }
103 unit = units[e];
104 inf = in / 100.0;
105 fra = 2;
106 }
107 snprintf(buf, max, "%7.*f %s",fra,inf,unit);
108 return buf;
109 }
110
111 /*
112 * Return a string representation of time differance.
113 *
114 * In: Start and end time, a storage buffer and size of it.
115 * Out: buffer is changed and a pointer is returned to it.
116 */
time_diff(struct timeval * start,struct timeval * end,char * buf,int max)117 static char *time_diff(struct timeval *start, struct timeval *end, char *buf,
118 int max)
119 {
120 struct timeval diff;
121 diff.tv_usec = end->tv_usec - start->tv_usec;
122 diff.tv_sec = end->tv_sec - start->tv_sec;
123 if (diff.tv_usec < 0) {
124 diff.tv_usec += 1000000;
125 diff.tv_sec--;
126 }
127 buf[max-1] = 0;
128 snprintf(buf,max,"%.2dh%.2dm%.2d.%.2ds",
129 diff.tv_sec / 3600,
130 (diff.tv_sec / 60) % 60,
131 diff.tv_sec % 60,
132 diff.tv_usec/10000);
133 return buf;
134 }
135
136 /* spoon?
137 *
138 */
usage(void)139 static void usage(void)
140 {
141 printf("Pipebench %1.2f, by Thomas Habets <thomas@habets.pp.se>\n",
142 version);
143 printf("usage: ... | pipebench [ -ehqQIoru ] [ -b <bufsize ] "
144 "[ -s <file> | -S <file> ]\\\n | ...\n");
145 }
146
147 /*
148 * main
149 */
main(int argc,char ** argv)150 int main(int argc, char **argv)
151 {
152 int c;
153 u_int64_t datalen = 0,last_datalen = 0,speed = 0;
154 struct timeval start,tv,tv2;
155 char tdbuf[64];
156 char speedbuf[64];
157 char datalenbuf[64];
158 unsigned int bufsize = 819200;
159 int summary = 1;
160 int errout = 0;
161 int quiet = 0;
162 int fancy = 1;
163 int dounit = 1;
164 FILE *statusf;
165 int statusf_append = 0;
166 const char *statusfn = 0;
167 int unit = 1024;
168 char *buffer;
169
170 statusf = stderr;
171
172 while (EOF != (c = getopt(argc, argv, "ehqQb:ros:S:Iu"))) {
173 switch(c) {
174 case 'e':
175 errout = 1;
176 break;
177 case 'q':
178 quiet = 1;
179 break;
180 case 'Q':
181 quiet = 1;
182 summary = 0;
183 break;
184 case 'o':
185 summary = 0;
186 break;
187 case 'b':
188 bufsize = atoi(optarg);
189 break;
190 case 'h':
191 usage();
192 return 0;
193 case 'r':
194 fancy = 0;
195 summary = 0;
196 break;
197 case 's':
198 statusfn = optarg;
199 statusf_append = 0;
200 break;
201 case 'S':
202 statusfn = optarg;
203 statusf_append = 1;
204 break;
205 case 'I':
206 unit = 1000;
207 break;
208 case 'u':
209 dounit = 0;
210 break;
211 default:
212 usage();
213 return 1;
214 }
215 }
216
217 if (statusfn) {
218 if (!(statusf = fopen(statusfn, statusf_append?"a":"w"))) {
219 perror("pipebench: fopen(status file)");
220 if (errout) {
221 return 1;
222 }
223 }
224 }
225
226 if ((-1 == gettimeofday(&tv, NULL))
227 || (-1 == gettimeofday(&start, NULL))) {
228 perror("pipebench: gettimeofday()");
229 if (errout) {
230 return 1;
231 }
232 }
233
234 if ((SIG_ERR == signal(SIGINT, sigint))) {
235 perror("pipebench: signal()");
236 if (errout) {
237 return 1;
238 }
239 }
240
241 while (!(buffer = malloc(bufsize))) {
242 perror("pipebench: malloc()");
243 bufsize>>=1;
244 }
245
246 while (!feof(stdin) && !done) {
247 int n;
248 char ctimebuf[64];
249
250 if (-1 == (n = fread(buffer, 1, bufsize, stdin))) {
251 perror("pipebench: fread()");
252 if (errout) {
253 return 1;
254 }
255 continue;
256 }
257 datalen += n;
258 while (-1 == fwrite(buffer, n, 1, stdout)) {
259 perror("pipebench: fwrite()");
260 if (errout) {
261 return 1;
262 }
263 }
264 if (0) {
265 fflush(stdout);
266 }
267
268 if (-1 == gettimeofday(&tv2,NULL)) {
269 perror("pipebench(): gettimeofday()");
270 if (errout) {
271 return 1;
272 }
273 }
274 strcpy(ctimebuf,ctime(&tv2.tv_sec));
275 if ((n=strlen(ctimebuf)) && ctimebuf[n-1] == '\n') {
276 ctimebuf[n-1] = 0;
277 }
278 if (fancy && !quiet) {
279 fprintf(statusf, "%s: %sB %sB/second (%s)%c",
280 time_diff(&start,&tv2,tdbuf,sizeof(tdbuf)),
281 unitify(datalen,datalenbuf,sizeof(datalenbuf),
282 unit,dounit),
283 unitify(speed,speedbuf,sizeof(speedbuf),
284 unit,dounit),
285 ctimebuf,
286 statusfn?'\n':'\r');
287 }
288 if (tv.tv_sec != tv2.tv_sec) {
289 speed = (datalen - last_datalen);
290 last_datalen = datalen;
291 if (-1 == gettimeofday(&tv,NULL)) {
292 perror("pipebench(): gettimeofday()");
293 if (errout) {
294 return 1;
295 }
296 }
297 if (!fancy) {
298 fprintf(statusf, "%llu\n",speed);
299 }
300 }
301 }
302 free(buffer);
303 if (summary) {
304 float n;
305
306 if (-1 == gettimeofday(&tv,NULL)) {
307 perror("pipebench(): gettimeofday()");
308 if (errout) {
309 return 1;
310 }
311 }
312
313 n = (tv2.tv_sec - start.tv_sec)
314 + (tv2.tv_usec - start.tv_usec) / 1000000.0;
315 fprintf(statusf," "
316 " "
317 " "
318 "%c"
319 "Summary:\nPiped %sB in %s: %sB/second\n",
320 statusfn?'\n':'\r',
321 unitify(datalen,datalenbuf,sizeof(datalenbuf),
322 unit,dounit),
323 time_diff(&start,&tv2,tdbuf,sizeof(tdbuf)),
324 unitify(n?datalen/n:0,
325 speedbuf,sizeof(speedbuf),unit,dounit));
326 }
327 return 0;
328 }
329