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