d23a61d0757b2a9b485ddc129d480143af7591fb
[tridge/dbench.git] / dbench.c
1 /* 
2    Copyright (C) by Andrew Tridgell <tridge@samba.org> 1999, 2001
3    Copyright (C) 2001 by Martin Pool <mbp@samba.org>
4    
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2 of the License, or
8    (at your option) any later version.
9    
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14    
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20 /* TODO: We could try allowing for different flavours of synchronous
21    operation: data sync and so on.  Linux apparently doesn't make any
22    distinction, however, and for practical purposes it probably
23    doesn't matter.  On NFSv4 it might be interesting, since the client
24    can choose what kind it wants for each OPEN operation. */
25
26 #include "dbench.h"
27 #include <sys/sem.h>
28
29 int sync_open = 0, do_fsync = 0, sync_dirs = 0;
30 char *tcp_options = TCP_OPTIONS;
31 static int timelimit = 600, warmup;
32 static const char *directory = ".";
33 static char *loadfile = DATADIR "/client.txt";
34 static struct timeval tv_start;
35 static struct timeval tv_end;
36 static int barrier=-1;
37 double targetrate;
38
39 #if HAVE_EA_SUPPORT
40 int ea_enable=0;
41 #endif
42
43 static FILE *open_loadfile(void)
44 {
45         FILE            *f;
46
47         if ((f = fopen(loadfile, "rt")) != NULL)
48                 return f;
49
50         fprintf(stderr,
51                 "dbench: error opening '%s': %s\n", loadfile,
52                 strerror(errno));
53
54         return NULL;
55 }
56
57
58 static struct child_struct *children;
59
60 static void sem_cleanup() {
61         if (!(barrier==-1)) 
62                 semctl(barrier,0,IPC_RMID);
63 }
64
65 static void sig_alarm(int sig)
66 {
67         double total_bytes = 0;
68         int total_lines = 0;
69         int i;
70         int nprocs = children[0].nprocs;
71         int in_warmup = 0;
72         double t;
73         static int in_cleanup;
74         double latency;
75         struct timeval tnow;
76
77         (void)sig;
78
79         tnow = timeval_current();
80
81         for (i=0;i<nprocs;i++) {
82                 total_bytes += children[i].bytes - children[i].bytes_done_warmup;
83                 if (children[i].bytes == 0) {
84                         in_warmup = 1;
85                 }
86                 total_lines += children[i].line;
87         }
88
89         t = timeval_elapsed(&tv_start);
90
91         if (!in_warmup && warmup>0 && t > warmup) {
92                 tv_start = tnow;
93                 warmup = 0;
94                 for (i=0;i<nprocs;i++) {
95                         children[i].bytes_done_warmup = children[i].bytes;
96                 }
97                 goto next;
98         }
99         if (t < warmup) {
100                 in_warmup = 1;
101         } else if (!in_warmup && !in_cleanup && t > timelimit) {
102                 for (i=0;i<nprocs;i++) {
103                         children[i].done = 1;
104                 }
105                 tv_end = tnow;
106                 in_cleanup = 1;
107         }
108         if (t < 1) {
109                 goto next;
110         }
111
112         latency = 0;
113         for (i=0;i<nprocs;i++) {
114                 latency = MAX(children[i].max_latency, latency);
115                 latency = MAX(latency, timeval_elapsed2(&children[i].lasttime, &tnow));
116                 children[i].max_latency = 0;
117         }
118
119         if (in_warmup) {
120                 printf("%4d  %8d  %7.2f MB/sec  warmup %3.0f sec  latency %.03f ms \n", 
121                        nprocs, total_lines/nprocs, 
122                        1.0e-6 * total_bytes / t, t, latency*1000);
123         } else if (in_cleanup) {
124                 printf("%4d  %8d  %7.2f MB/sec  cleanup %3.0f sec  latency %.03f ms\n", 
125                        nprocs, total_lines/nprocs, 
126                        1.0e-6 * total_bytes / t, t, latency*1000);
127         } else {
128                 printf("%4d  %8d  %7.2f MB/sec  execute %3.0f sec  latency %.03f ms\n", 
129                        nprocs, total_lines/nprocs, 
130                        1.0e-6 * total_bytes / t, t, latency*1000);
131         }
132
133         fflush(stdout);
134 next:
135         signal(SIGALRM, sig_alarm);
136         alarm(PRINT_FREQ);
137 }
138
139 /* this creates the specified number of child processes and runs fn()
140    in all of them */
141 static void create_procs(int nprocs, void (*fn)(struct child_struct *, const char *))
142 {
143         int i, status;
144         int synccount;
145         struct timeval tv;
146         FILE *load;
147         struct sembuf sbuf;
148         double t;
149
150         load = open_loadfile();
151         if (load == NULL) {
152                 exit(1);
153         }
154
155         if (nprocs < 1) {
156                 fprintf(stderr,
157                         "create %d procs?  you must be kidding.\n",
158                         nprocs);
159                 return;
160         }
161
162         children = shm_setup(sizeof(struct child_struct)*nprocs);
163         if (!children) {
164                 printf("Failed to setup shared memory\n");
165                 return;
166         }
167
168         memset(children, 0, sizeof(*children)*nprocs);
169
170         for (i=0;i<nprocs;i++) {
171                 children[i].id = i;
172                 children[i].nprocs = nprocs;
173                 children[i].cleanup = 0;
174                 children[i].directory = directory;
175                 children[i].starttime = timeval_current();
176         }
177
178         if (atexit(sem_cleanup) != 0) {
179                 printf("can't register cleanup function on exit\n");
180                 exit(1);
181         }
182         sbuf.sem_num =  0;
183         if ( !(barrier = semget(IPC_PRIVATE,1,IPC_CREAT | S_IRUSR | S_IWUSR)) ) {
184                 printf("failed to create barrier semaphore \n");
185         }
186         sbuf.sem_flg =  SEM_UNDO;
187         sbuf.sem_op  =  1;
188         if (semop(barrier, &sbuf, 1) == -1) {
189                 printf("failed to initialize the barrier semaphore\n");
190                 exit(1);
191         }
192         sbuf.sem_flg =  0;
193
194         for (i=0;i<nprocs;i++) {
195                 if (fork() == 0) {
196                         setlinebuf(stdout);
197                         nb_setup(&children[i]);
198
199                         sbuf.sem_op  =  0;
200                         if (semop(barrier, &sbuf, 1) == -1) {
201                                 printf("failed to use the barrier semaphore in child %d\n",getpid());
202                                 exit(1);
203                         }
204
205                         semctl(barrier,0,IPC_RMID);
206
207                         fn(&children[i], loadfile);
208                         _exit(0);
209                 }
210         }
211
212         synccount = 0;
213         tv = timeval_current();
214         do {
215                 synccount = semctl(barrier,0,GETZCNT);
216                 t = timeval_elapsed(&tv);
217                 printf("%d of %d clients prepared for launch %3.0f sec\n", synccount, nprocs, t);
218                 if (synccount == nprocs) break;
219                 usleep(100*1000);
220         } while (timeval_elapsed(&tv) < 30);
221
222         if (synccount != nprocs) {
223                 printf("FAILED TO START %d CLIENTS (started %d)\n", nprocs, synccount);
224                 return;
225         }
226
227         printf("releasing clients\n");
228         tv_start = timeval_current();
229         sbuf.sem_op  =  -1;
230         if (semop(barrier, &sbuf, 1) == -1) {
231                 printf("failed to release barrier\n");
232                 exit(1);
233         }
234
235         semctl(barrier,0,IPC_RMID);
236
237         signal(SIGALRM, sig_alarm);
238         alarm(PRINT_FREQ);
239
240         for (i=0;i<nprocs;) {
241                 if (waitpid(0, &status, 0) == -1) continue;
242                 if (WEXITSTATUS(status) != 0) {
243                         printf("Child failed with status %d\n",
244                                WEXITSTATUS(status));
245                         exit(1);
246                 }
247                 i++;
248         }
249
250         alarm(0);
251         sig_alarm(SIGALRM);
252
253         printf("\n");
254 }
255
256
257 static void show_usage(void)
258 {
259         printf("usage: dbench [OPTIONS] nprocs\n" \
260                "usage: tbench [OPTIONS] nprocs <server>\n" \
261                "options:\n" \
262                "  -v               show version\n" \
263                "  -t timelimit     run time in seconds (default 600)\n" \
264                "  -D directory     base directory to run in\n" \
265                "  -c loadfile      set location of the loadfile\n" \
266                "  -R               target rate (MByte/sec)\n" \
267                "  -s               synchronous file IO\n" \
268                "  -F               fsync on write\n" \
269                "  -S               synchronous directories (mkdir, unlink...)\n" \
270                "  -x               enable EA support\n" \
271                "  -T options       set socket options for tbench\n");
272         exit(1);
273 }
274
275
276
277 static int process_opts(int argc, char **argv,
278                         int *nprocs)
279 {
280         int c;
281         extern int sync_open;
282         extern char *server;
283         
284         targetrate = 0;
285
286         while ((c = getopt(argc, argv, "vc:sST:t:xD:R:F")) != -1) 
287                 switch (c) {
288                 case 'c':
289                         loadfile = optarg;
290                         break;
291                 case 's':
292                         sync_open = 1;
293                         break;
294                 case 'F':
295                         do_fsync = 1;
296                         break;
297                 case 'S':
298                         sync_dirs = 1;
299                         break;
300                 case 'T':
301                         tcp_options = optarg;
302                         break;
303                 case 't':
304                         timelimit = atoi(optarg);
305                         break;
306                 case 'D':
307                         directory = optarg;
308                         break;
309                 case 'v':
310                         exit(0);
311                         break;
312                 case 'R':
313                         targetrate = strtod(optarg, NULL);
314                         break;
315                 case 'x':
316
317 #if HAVE_EA_SUPPORT
318                         ea_enable = 1;
319 #else
320                         printf("EA suppport not compiled in\n");
321                         exit(1);
322 #endif
323                         break;
324                 case '?':
325                         if (isprint (optopt))
326                                 fprintf (stderr, "Unknown option `-%c'.\n", optopt);
327                         else
328                                 fprintf (stderr,
329                                          "Unknown option character `\\x%x'.\n",
330                                          optopt);
331                         return 0;
332                 default:
333                         abort ();
334                 }
335         
336         if (!argv[optind])
337                 return 0;
338         
339         *nprocs = atoi(argv[optind++]);
340
341         if (argv[optind])
342                 server = argv[optind++];
343
344         return 1;
345 }
346
347
348
349  int main(int argc, char *argv[])
350 {
351         int nprocs;
352         double total_bytes = 0;
353         double t;
354         int i;
355
356         printf("dbench version %s - Copyright Andrew Tridgell 1999-2004\n\n", VERSION);
357
358         if (!process_opts(argc, argv, &nprocs))
359                 show_usage();
360
361         warmup = timelimit / 5;
362
363         printf("Running for %d seconds with load '%s' and minimum warmup %d secs\n", 
364                timelimit, loadfile, warmup);
365
366         create_procs(nprocs, child_run);
367
368         for (i=0;i<nprocs;i++) {
369                 total_bytes += children[i].bytes - children[i].bytes_done_warmup;
370         }
371
372         t = timeval_elapsed2(&tv_start, &tv_end);
373
374         printf("Throughput %g MB/sec%s%s %d procs\n", 
375                1.0e-6 * total_bytes / t,
376                sync_open ? " (sync open)" : "",
377                sync_dirs ? " (sync dirs)" : "", nprocs);
378         return 0;
379 }