1 /*******************************************************************************
2  *  The BYTE UNIX Benchmarks - Release 3
3  *      Module: fstime.c   SID: 3.5 5/15/91 19:30:19
4  *
5  *******************************************************************************
6  * Bug reports, patches, comments, suggestions should be sent to:
7  *
8  *      Ben Smith, Rick Grehan or Tom Yager
9  *      ben@bytepb.byte.com   rick_g@bytepb.byte.com   tyager@bytepb.byte.com
10  *
11  *******************************************************************************
12  *  Modification Log:
13  * $Header: fstime.c,v 3.4 87/06/22 14:23:05 kjmcdonell Beta $
14  * 10/19/89 - rewrote timing calcs and added clock check (Ben Smith)
15  * 10/26/90 - simplify timing, change defaults (Tom Yager)
16  * 11/16/90 - added better error handling and changed output format (Ben Smith)
17  * 11/17/90 - changed the whole thing around (Ben Smith)
18  * 2/22/91 - change a few style elements and improved error handling (Ben Smith)
19  * 4/17/91 - incorporated suggestions from Seckin Unlu (seckin@sumac.intel.com)
20  * 4/17/91 - limited size of file, will rewind when reaches end of file
21  * 7/95    - fixed mishandling of read() and write() return codes
22  *           Carl Emilio Prelz <fluido@telepac.pt>
23  * 12/95   - Massive changes.  Made sleep time proportional increase with run
24  *           time; added fsbuffer and fsdisk variants; added partial counting
25  *           of partial reads/writes (was *full* credit); added dual syncs.
26  *           David C Niemi <niemi@tux.org>
27  * 10/22/97 - code cleanup to remove ANSI C compiler warnings
28  *           Andy Kahn <kahn@zk3.dec.com>
29  *  9/24/07 - Separate out the read and write tests;
30  *           output the actual time used in the results.
31  *           Ian Smith <johantheghost at yahoo period com>
32  ******************************************************************************/
33 char SCCSid[] = "@(#) @(#)fstime.c:3.5 -- 5/15/91 19:30:19";
34 
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <signal.h>
38 #include <errno.h>
39 #include <fcntl.h>
40 #include <unistd.h>
41 #include <sys/time.h>
42 
43 #define SECONDS 10
44 
45 #define MAX_BUFSIZE 8192
46 
47 /* This must be set to the smallest BUFSIZE or 1024, whichever is smaller */
48 #define COUNTSIZE 256
49 #define HALFCOUNT (COUNTSIZE/2)         /* Half of COUNTSIZE */
50 
51 #define FNAME0  "dummy0"
52 #define FNAME1  "dummy1"
53 
54 int w_test(int timeSecs);
55 int r_test(int timeSecs);
56 int c_test(int timeSecs);
57 
58 long read_score = 1, write_score = 1, copy_score = 1;
59 
60 /****************** GLOBALS ***************************/
61 
62 /* The buffer size for the tests. */
63 int bufsize = 1024;
64 
65 /*
66  * The max number of 1024-byte blocks in the file.
67  * Don't limit it much, so that memory buffering
68  * can be overcome.
69  */
70 int max_blocks = 2000;
71 
72 /* The max number of BUFSIZE blocks in the file. */
73 int max_buffs = 2000;
74 
75 /* Countable units per 1024 bytes */
76 int count_per_k;
77 
78 /* Countable units per bufsize */
79 int count_per_buf;
80 
81 /* The actual buffer. */
82 /* char *buf = 0; */
83 /* Let's carry on using a static buffer for this, like older versions
84  * of the code did.  It turns out that if you use a malloc buffer,
85  * it goes 50% slower on reads, when using a 4k buffer -- at least on
86  * my OpenSUSE 10.2 system.
87  * What up wit dat?
88  */
89 char buf[MAX_BUFSIZE];
90 
91 int                     f;
92 int                     g;
93 int                     i;
94 void                    stop_count();
95 void                    clean_up();
96 int                     sigalarm = 0;
97 
98 /******************** MAIN ****************************/
99 
main(argc,argv)100 int main(argc, argv)
101 int     argc;
102 char    *argv[];
103 {
104     /* The number of seconds to run for. */
105     int                     seconds = SECONDS;
106 
107     /* The type of test to run. */
108     char test = 'c';
109 
110     int status;
111     int i;
112 
113     for (i = 1; i < argc; ++i) {
114         if (argv[i][0] == '-') {
115             switch (argv[i][1]) {
116                 case 'c':
117                 case 'r':
118                 case 'w':
119                     test = argv[i][1];
120                     break;
121                 case 'b':
122                     bufsize = atoi(argv[++i]);
123                     break;
124                 case 'm':
125                     max_blocks = atoi(argv[++i]);
126                     break;
127                 case 't':
128                     seconds = atoi(argv[++i]);
129                     break;
130                 case 'd':
131                     if (chdir(argv[++i]) < 0) {
132                         perror("fstime: chdir");
133                         exit(1);
134                     }
135                     break;
136                 default:
137                     fprintf(stderr, "Usage: fstime [-c|-r|-w] [-b <bufsize>] [-m <max_blocks>] [-t <seconds>]\n");
138                     exit(2);
139             }
140         } else {
141             fprintf(stderr, "Usage: fstime [-c|-r|-w] [-b <bufsize>] [-m <max_blocks>] [-t <seconds>]\n");
142             exit(2);
143         }
144     }
145 
146     if (bufsize < COUNTSIZE || bufsize > MAX_BUFSIZE) {
147         fprintf(stderr, "fstime: buffer size must be in range %d-%d\n",
148                 COUNTSIZE, 1024*1024);
149         exit(3);
150     }
151     if (max_blocks < 1 || max_blocks > 1024*1024) {
152         fprintf(stderr, "fstime: max blocks must be in range %d-%d\n",
153                 1, 1024*1024);
154         exit(3);
155     }
156     if (seconds < 1 || seconds > 3600) {
157         fprintf(stderr, "fstime: time must be in range %d-%d seconds\n",
158                 1, 3600);
159         exit(3);
160     }
161 
162     max_buffs = max_blocks * 1024 / bufsize;
163     count_per_k = 1024 / COUNTSIZE;
164     count_per_buf = bufsize / COUNTSIZE;
165 
166     /*
167     if ((buf = malloc(bufsize)) == 0) {
168         fprintf(stderr, "fstime: failed to malloc %d bytes\n", bufsize);
169         exit(4);
170     }
171     */
172 
173     if((f = creat(FNAME0, 0600)) == -1) {
174             perror("fstime: creat");
175             exit(1);
176     }
177     close(f);
178 
179     if((g = creat(FNAME1, 0600)) == -1) {
180             perror("fstime: creat");
181             exit(1);
182     }
183     close(g);
184 
185     if( (f = open(FNAME0, 2)) == -1) {
186             perror("fstime: open");
187             exit(1);
188     }
189     if( ( g = open(FNAME1, 2)) == -1 ) {
190             perror("fstime: open");
191             exit(1);
192     }
193 
194     /* fill buffer */
195     for (i=0; i < bufsize; ++i)
196             buf[i] = i & 0xff;
197 
198     signal(SIGKILL,clean_up);
199 
200     /*
201      * Run the selected test.
202      * When I got here, this program ran full 30-second tests for
203      * write, read, and copy, outputting the results for each.  BUT
204      * only the copy results are actually used in the benchmark index.
205      * With multiple iterations and three sets of FS tests, that amounted
206      * to about 10 minutes of wasted time per run.
207      *
208      * So, I've made the test selectable.  Except that the read and write
209      * passes are used to create the test file and calibrate the rates used
210      * to tweak the results of the copy test.  So, for copy tests, we do
211      * a few seconds of write and read to prime the pump.
212      *
213      * Note that this will also pull the file into the FS cache on any
214      * modern system prior to the copy test.  Whether this is good or
215      * bad is a matter of perspective, but it's how it was when I got
216      * here.
217      *
218      * Ian Smith <johantheghost at yahoo period com> 21 Sep 2007
219      */
220     switch (test) {
221     case 'w':
222         status = w_test(seconds);
223         break;
224     case 'r':
225         w_test(2);
226         status = r_test(seconds);
227         break;
228     case 'c':
229         w_test(2);
230         r_test(2);
231         status = c_test(seconds);
232         break;
233     default:
234         fprintf(stderr, "fstime: unknown test \'%c\'\n", test);
235         exit(6);
236     }
237     if (status) {
238         clean_up();
239         exit(1);
240     }
241 
242     clean_up();
243     exit(0);
244 }
245 
246 
getFloatTime()247 static double getFloatTime()
248 {
249         struct timeval t;
250 
251         gettimeofday(&t, 0);
252         return (double) t.tv_sec + (double) t.tv_usec / 1000000.0;
253 }
254 
255 
256 /*
257  * Run the write test for the time given in seconds.
258  */
w_test(int timeSecs)259 int w_test(int timeSecs)
260 {
261         unsigned long counted = 0L;
262         unsigned long tmp;
263         long f_blocks;
264         double start, end;
265         extern int sigalarm;
266 
267         /* Sync and let it settle */
268         sync();
269         sleep(2);
270         sync();
271         sleep(2);
272 
273         /* Set an alarm. */
274         sigalarm = 0;
275         signal(SIGALRM, stop_count);
276         alarm(timeSecs);
277 
278         start = getFloatTime();
279 
280         while (!sigalarm) {
281                 for(f_blocks=0; f_blocks < max_buffs; ++f_blocks) {
282                         if ((tmp=write(f, buf, bufsize)) != bufsize) {
283                                 if (errno != EINTR) {
284                                         perror("fstime: write");
285                                         return(-1);
286                                 }
287                                 stop_count();
288                                 counted += ((tmp+HALFCOUNT)/COUNTSIZE);
289                         } else
290                                 counted += count_per_buf;
291                 }
292                 lseek(f, 0L, 0); /* rewind */
293         }
294 
295         /* stop clock */
296         end = getFloatTime();
297         write_score = (long) ((double) counted / ((end - start) * count_per_k));
298         printf("Write done: %ld in %.4f, score %ld\n",
299                             counted, end - start, write_score);
300 
301         /*
302          * Output the test results. Use the true time.
303          */
304         fprintf(stderr, "COUNT|%ld|0|KBps\n", write_score);
305         fprintf(stderr, "TIME|%.1f\n", end - start);
306 
307         return(0);
308 }
309 
310 /*
311  * Run the read test for the time given in seconds.
312  */
r_test(int timeSecs)313 int r_test(int timeSecs)
314 {
315         unsigned long counted = 0L;
316         unsigned long tmp;
317         double start, end;
318         extern int sigalarm;
319 
320         /* Sync and let it settle */
321         sync();
322         sleep(2);
323         sync();
324         sleep(2);
325 
326         /* rewind */
327         errno = 0;
328         lseek(f, 0L, 0);
329 
330         /* Set an alarm. */
331         sigalarm = 0;
332         signal(SIGALRM, stop_count);
333         alarm(timeSecs);
334 
335         start = getFloatTime();
336 
337         while (!sigalarm) {
338                 /* read while checking for an error */
339                 if ((tmp=read(f, buf, bufsize)) != bufsize) {
340                         switch(errno) {
341                         case 0:
342                         case EINVAL:
343                                 lseek(f, 0L, 0);  /* rewind at end of file */
344                                 counted += (tmp+HALFCOUNT)/COUNTSIZE;
345                                 continue;
346                         case EINTR:
347                                 stop_count();
348                                 counted += (tmp+HALFCOUNT)/COUNTSIZE;
349                                 break;
350                         default:
351                                 perror("fstime: read");
352                                 return(-1);
353                                 break;
354                         }
355                 } else
356                         counted += count_per_buf;
357         }
358 
359         /* stop clock */
360         end = getFloatTime();
361         read_score = (long) ((double) counted / ((end - start) * count_per_k));
362         printf("Read done: %ld in %.4f, score %ld\n",
363                             counted, end - start, read_score);
364 
365         /*
366          * Output the test results. Use the true time.
367          */
368         fprintf(stderr, "COUNT|%ld|0|KBps\n", read_score);
369         fprintf(stderr, "TIME|%.1f\n", end - start);
370 
371         return(0);
372 }
373 
374 
375 /*
376  * Run the copy test for the time given in seconds.
377  */
c_test(int timeSecs)378 int c_test(int timeSecs)
379 {
380         unsigned long counted = 0L;
381         unsigned long tmp;
382         double start, end;
383         extern int sigalarm;
384 
385         sync();
386         sleep(2);
387         sync();
388         sleep(1);
389 
390         /* rewind */
391         errno = 0;
392         lseek(f, 0L, 0);
393 
394         /* Set an alarm. */
395         sigalarm = 0;
396         signal(SIGALRM, stop_count);
397         alarm(timeSecs);
398 
399         start = getFloatTime();
400 
401         while (!sigalarm) {
402                 if ((tmp=read(f, buf, bufsize)) != bufsize) {
403                         switch(errno) {
404                         case 0:
405                         case EINVAL:
406                                 lseek(f, 0L, 0);  /* rewind at end of file */
407                                 lseek(g, 0L, 0);  /* rewind the output too */
408                                 continue;
409                         case EINTR:
410                                 /* part credit for leftover bytes read */
411                                 counted += ( (tmp * write_score) /
412                                         (read_score + write_score)
413                                         + HALFCOUNT) / COUNTSIZE;
414                                 stop_count();
415                                 break;
416                         default:
417                                 perror("fstime: copy read");
418                                 return(-1);
419                                 break;
420                         }
421                 } else  {
422                         if ((tmp=write(g, buf, bufsize)) != bufsize) {
423                                 if (errno != EINTR) {
424                                         perror("fstime: copy write");
425                                         return(-1);
426                                 }
427                                 counted += (
428                                  /* Full credit for part of buffer written */
429                                         tmp +
430 
431                                  /* Plus part credit having read full buffer */
432                                         ( ((bufsize - tmp) * write_score) /
433                                         (read_score + write_score) )
434                                         + HALFCOUNT) / COUNTSIZE;
435                                 stop_count();
436                         } else
437                                 counted += count_per_buf;
438                 }
439         }
440 
441         /* stop clock */
442         end = getFloatTime();
443         copy_score = (long) ((double) counted / ((end - start) * count_per_k));
444         printf("Copy done: %ld in %.4f, score %ld\n",
445                             counted, end - start, copy_score);
446 
447         /*
448          * Output the test results. Use the true time.
449          */
450         fprintf(stderr, "COUNT|%ld|0|KBps\n", copy_score);
451         fprintf(stderr, "TIME|%.1f\n", end - start);
452 
453         return(0);
454 }
455 
stop_count(void)456 void stop_count(void)
457 {
458         extern int sigalarm;
459         sigalarm = 1;
460 }
461 
clean_up(void)462 void clean_up(void)
463 {
464         unlink(FNAME0);
465         unlink(FNAME1);
466 }
467