converted all backends to new common format
[tridge/dbench.git] / dbench.c
1 /* 
2    Copyright (C) by Andrew Tridgell <tridge@samba.org> 1999-2007
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 3 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, see <http://www.gnu.org/licenses/>.
17 */
18
19 /* TODO: We could try allowing for different flavours of synchronous
20    operation: data sync and so on.  Linux apparently doesn't make any
21    distinction, however, and for practical purposes it probably
22    doesn't matter.  On NFSv4 it might be interesting, since the client
23    can choose what kind it wants for each OPEN operation. */
24
25 #include "dbench.h"
26 #include "popt.h"
27 #include <sys/sem.h>
28
29 struct options options = {
30         .timelimit           = 600,
31         .loadfile            = DATADIR "/client.txt",
32         .directory           = ".",
33         .tcp_options         = TCP_OPTIONS,
34         .nprocs              = 10,
35         .sync_open           = 0,
36         .sync_dirs           = 0,
37         .do_fsync            = 0,
38         .fsync_frequency     = 0,
39         .warmup              = -1,
40         .targetrate          = 0.0,
41         .ea_enable           = 0,
42         .clients_per_process = 1,
43         .server              = "localhost",
44         .export              = "/tmp",
45         .protocol            = "tcp",
46 };
47
48 static struct timeval tv_start;
49 static struct timeval tv_end;
50 static int barrier=-1;
51 static double throughput;
52
53 static FILE *open_loadfile(void)
54 {
55         FILE            *f;
56
57         if ((f = fopen(options.loadfile, "rt")) != NULL)
58                 return f;
59
60         fprintf(stderr,
61                 "dbench: error opening '%s': %s\n", options.loadfile,
62                 strerror(errno));
63
64         return NULL;
65 }
66
67
68 static struct child_struct *children;
69
70 static void sem_cleanup() {
71         if (!(barrier==-1)) 
72                 semctl(barrier,0,IPC_RMID);
73 }
74
75 static void sig_alarm(int sig)
76 {
77         double total_bytes = 0;
78         int total_lines = 0;
79         int i;
80         int nclients = options.nprocs * options.clients_per_process;
81         int in_warmup = 0;
82         double t;
83         static int in_cleanup;
84         double latency;
85         struct timeval tnow;
86         int num_active = 0;
87         int num_finished = 0;
88         (void)sig;
89
90         tnow = timeval_current();
91
92         for (i=0;i<nclients;i++) {
93                 total_bytes += children[i].bytes - children[i].bytes_done_warmup;
94                 if (children[i].bytes == 0) {
95                         in_warmup = 1;
96                 } else {
97                         num_active++;
98                 }
99                 total_lines += children[i].line;
100                 if (children[i].cleanup_finished) {
101                         num_finished++;
102                 }
103         }
104
105         t = timeval_elapsed(&tv_start);
106
107         if (!in_warmup && options.warmup>0 && t > options.warmup) {
108                 tv_start = tnow;
109                 options.warmup = 0;
110                 for (i=0;i<nclients;i++) {
111                         children[i].bytes_done_warmup = children[i].bytes;
112                         children[i].worst_latency = 0;
113                         memset(&children[i].ops, 0, sizeof(children[i].ops));
114                 }
115                 goto next;
116         }
117         if (t < options.warmup) {
118                 in_warmup = 1;
119         } else if (!in_warmup && !in_cleanup && t > options.timelimit) {
120                 for (i=0;i<nclients;i++) {
121                         children[i].done = 1;
122                 }
123                 tv_end = tnow;
124                 in_cleanup = 1;
125         }
126         if (t < 1) {
127                 goto next;
128         }
129
130         latency = 0;
131         if (!in_cleanup) {
132                 for (i=0;i<nclients;i++) {
133                         latency = MAX(children[i].max_latency, latency);
134                         latency = MAX(latency, timeval_elapsed2(&children[i].lasttime, &tnow));
135                         children[i].max_latency = 0;
136                         if (latency > children[i].worst_latency) {
137                                 children[i].worst_latency = latency;
138                         }
139                 }
140         }
141
142         if (in_warmup) {
143                 printf("%4d  %8d  %7.2f MB/sec  warmup %3.0f sec  latency %.03f ms\n", 
144                        num_active, total_lines/nclients, 
145                        1.0e-6 * total_bytes / t, t, latency*1000);
146         } else if (in_cleanup) {
147                 printf("%4d  cleanup %3.0f sec\n", nclients - num_finished, t);
148         } else {
149                 printf("%4d  %8d  %7.2f MB/sec  execute %3.0f sec  latency %.03f ms\n", 
150                        nclients, total_lines/nclients, 
151                        1.0e-6 * total_bytes / t, t, latency*1000);
152                 throughput = 1.0e-6 * total_bytes / t;
153         }
154
155         fflush(stdout);
156 next:
157         signal(SIGALRM, sig_alarm);
158         alarm(PRINT_FREQ);
159 }
160
161
162 static void show_one_latency(struct op *ops, struct op *ops_all)
163 {
164         int i;
165         printf(" Operation                Count    AvgLat    MaxLat\n");
166         printf(" --------------------------------------------------\n");
167         for (i=0;nb_ops.ops[i].name;i++) {
168                 struct op *op1, *op_all;
169                 op1    = &ops[i];
170                 op_all = &ops_all[i];
171                 if (op_all->count == 0) continue;
172                 printf(" %-22s %7u %9.03f %9.03f\n",
173                        nb_ops.ops[i].name, op1->count, 
174                        1000*op1->total_time/op1->count,
175                        op1->max_latency*1000);
176         }
177         printf("\n");
178 }
179
180 static void report_latencies(void)
181 {
182         struct op sum[MAX_OPS];
183         int i, j;
184         struct op *op1, *op2;
185         struct child_struct *child;
186
187         memset(sum, 0, sizeof(sum));
188         for (i=0;nb_ops.ops[i].name;i++) {
189                 op1 = &sum[i];
190                 for (j=0;j<options.nprocs * options.clients_per_process;j++) {
191                         child = &children[j];
192                         op2 = &child->ops[i];
193                         op1->count += op2->count;
194                         op1->total_time += op2->total_time;
195                         op1->max_latency = MAX(op1->max_latency, op2->max_latency);
196                 }
197         }
198         show_one_latency(sum, sum);
199
200         if (!options.per_client_results) {
201                 return;
202         }
203
204         printf("Per client results:\n");
205         for (i=0;i<options.nprocs * options.clients_per_process;i++) {
206                 child = &children[i];
207                 printf("Client %u did %u lines and %.0f bytes\n", 
208                        i, child->line, child->bytes - child->bytes_done_warmup);
209                 show_one_latency(child->ops, sum);              
210         }
211 }
212
213 /* this creates the specified number of child processes and runs fn()
214    in all of them */
215 static void create_procs(int nprocs, void (*fn)(struct child_struct *, const char *))
216 {
217         int nclients = nprocs * options.clients_per_process;
218         int i, status;
219         int synccount;
220         struct timeval tv;
221         FILE *load;
222         struct sembuf sbuf;
223         double t;
224
225         load = open_loadfile();
226         if (load == NULL) {
227                 exit(1);
228         }
229
230         if (nprocs < 1) {
231                 fprintf(stderr,
232                         "create %d procs?  you must be kidding.\n",
233                         nprocs);
234                 return;
235         }
236
237         children = shm_setup(sizeof(struct child_struct)*nclients);
238         if (!children) {
239                 printf("Failed to setup shared memory\n");
240                 return;
241         }
242
243         memset(children, 0, sizeof(*children)*nclients);
244
245         for (i=0;i<nclients;i++) {
246                 children[i].id = i;
247                 children[i].cleanup = 0;
248                 children[i].directory = options.directory;
249                 children[i].starttime = timeval_current();
250                 children[i].lasttime = timeval_current();
251         }
252
253         if (atexit(sem_cleanup) != 0) {
254                 printf("can't register cleanup function on exit\n");
255                 exit(1);
256         }
257         sbuf.sem_num =  0;
258         if ( !(barrier = semget(IPC_PRIVATE,1,IPC_CREAT | S_IRUSR | S_IWUSR)) ) {
259                 printf("failed to create barrier semaphore \n");
260         }
261         sbuf.sem_flg =  SEM_UNDO;
262         sbuf.sem_op  =  1;
263         if (semop(barrier, &sbuf, 1) == -1) {
264                 printf("failed to initialize the barrier semaphore\n");
265                 exit(1);
266         }
267         sbuf.sem_flg =  0;
268
269         for (i=0;i<nprocs;i++) {
270                 if (fork() == 0) {
271                         int j;
272
273                         setlinebuf(stdout);
274
275                         for (j=0;j<options.clients_per_process;j++) {
276                                 nb_ops.setup(&children[i*options.clients_per_process + j]);
277                         }
278
279                         sbuf.sem_op = 0;
280                         if (semop(barrier, &sbuf, 1) == -1) {
281                                 printf("failed to use the barrier semaphore in child %d\n",getpid());
282                                 exit(1);
283                         }
284
285                         semctl(barrier,0,IPC_RMID);
286
287                         fn(&children[i*options.clients_per_process], options.loadfile);
288                         _exit(0);
289                 }
290         }
291
292         synccount = 0;
293         tv = timeval_current();
294         do {
295                 synccount = semctl(barrier,0,GETZCNT);
296                 t = timeval_elapsed(&tv);
297                 printf("%d of %d processes prepared for launch %3.0f sec\n", synccount, nprocs, t);
298                 if (synccount == nprocs) break;
299                 usleep(100*1000);
300         } while (timeval_elapsed(&tv) < 30);
301
302         if (synccount != nprocs) {
303                 printf("FAILED TO START %d CLIENTS (started %d)\n", nprocs, synccount);
304                 return;
305         }
306
307         printf("releasing clients\n");
308         tv_start = timeval_current();
309         sbuf.sem_op  =  -1;
310         if (semop(barrier, &sbuf, 1) == -1) {
311                 printf("failed to release barrier\n");
312                 exit(1);
313         }
314
315         semctl(barrier,0,IPC_RMID);
316
317         signal(SIGALRM, sig_alarm);
318         alarm(PRINT_FREQ);
319
320         for (i=0;i<nprocs;) {
321                 if (waitpid(0, &status, 0) == -1) continue;
322                 if (WEXITSTATUS(status) != 0) {
323                         printf("Child failed with status %d\n",
324                                WEXITSTATUS(status));
325                         exit(1);
326                 }
327                 i++;
328         }
329
330         alarm(0);
331         sig_alarm(SIGALRM);
332
333         printf("\n");
334
335         report_latencies();
336 }
337
338
339 static void show_usage(void)
340 {
341         printf("usage: dbench [OPTIONS] nprocs\n" \
342                "usage: tbench [OPTIONS] nprocs <server>\n" \
343                "options:\n" \
344                "  -v               show version\n" \
345                "  -t timelimit     run time in seconds (default 600)\n" \
346                "  -D directory     base directory to run in\n" \
347                "  -c loadfile      set location of the loadfile\n" \
348                "  -R               target rate (MByte/sec)\n" \
349                "  -s               synchronous file IO\n" \
350                "  -F               fsync on write\n" \
351                "  -S               synchronous directories (mkdir, unlink...)\n" \
352                "  -x               enable EA support\n" \
353                "  -T options       set socket options for tbench\n");
354         exit(1);
355 }
356
357
358
359 static int process_opts(int argc, const char **argv)
360 {
361         const char **extra_argv;
362         int extra_argc = 0;
363         struct poptOption popt_options[] = {
364                 POPT_AUTOHELP
365                 { "timelimit", 't', POPT_ARG_INT, &options.timelimit, 0, 
366                   "timelimit", "integer" },
367                 { "loadfile",  'c', POPT_ARG_STRING, &options.loadfile, 0, 
368                   "loadfile", "filename" },
369                 { "directory", 'D', POPT_ARG_STRING, &options.directory, 0, 
370                   "working directory", NULL },
371                 { "tcp-options", 'T', POPT_ARG_STRING, &options.tcp_options, 0, 
372                   "TCP socket options", NULL },
373                 { "target-rate", 'R', POPT_ARG_DOUBLE, &options.targetrate, 0, 
374                   "target throughput (MB/sec)", NULL },
375                 { "sync", 's', POPT_ARG_NONE, &options.sync_open, 0, 
376                   "use O_SYNC", NULL },
377                 { "sync-dir", 'S', POPT_ARG_NONE, &options.sync_dirs, 0, 
378                   "sync directory changes", NULL },
379                 { "fsync", 'F', POPT_ARG_NONE, &options.do_fsync, 0, 
380                   "fsync on write", NULL },
381                 { "xattr", 'x', POPT_ARG_NONE, &options.ea_enable, 0, 
382                   "use xattrs", NULL },
383                 { "no-resolve", 0, POPT_ARG_NONE, &options.no_resolve, 0, 
384                   "disable name resolution simulation", NULL },
385                 { "clients-per-process", 0, POPT_ARG_INT, &options.clients_per_process, 0, 
386                   "number of clients per process", NULL },
387                 { "one-byte-write-fix", 0, POPT_ARG_NONE, &options.one_byte_write_fix, 0, 
388                   "try to fix 1 byte writes", NULL },
389                 { "stat-check", 0, POPT_ARG_NONE, &options.stat_check, 0, 
390                   "check for pointless calls with stat", NULL },
391                 { "fake-io", 0, POPT_ARG_NONE, &options.fake_io, 0, 
392                   "fake up read/write calls", NULL },
393                 { "skip-cleanup", 0, POPT_ARG_NONE, &options.skip_cleanup, 0, 
394                   "skip cleanup operations", NULL },
395                 { "per-client-results", 0, POPT_ARG_NONE, &options.per_client_results, 0, 
396                   "show results per client", NULL },
397                 { "server",  'S', POPT_ARG_STRING, &options.server, 0, 
398                   "server", NULL },
399                 { "export",  'E', POPT_ARG_STRING, &options.export, 0, 
400                   "export", NULL },
401                 { "protocol",  'P', POPT_ARG_STRING, &options.protocol, 0, 
402                   "protocol", NULL },
403                 POPT_TABLEEND
404         };
405         poptContext pc;
406         int opt;
407
408         pc = poptGetContext(argv[0], argc, argv, popt_options, POPT_CONTEXT_KEEP_FIRST);
409
410         while ((opt = poptGetNextOpt(pc)) != -1) {
411                 switch (opt) {
412                 default:
413                         fprintf(stderr, "Invalid option %s: %s\n", 
414                                 poptBadOption(pc, 0), poptStrerror(opt));
415                         exit(1);
416                 }
417         }
418
419         /* setup the remaining options for the main program to use */
420         extra_argv = poptGetArgs(pc);
421         if (extra_argv) {
422                 extra_argv++;
423                 while (extra_argv[extra_argc]) extra_argc++;
424         }
425
426         if (extra_argc < 1) {
427                 printf("You need to specify NPROCS\n");
428                 poptPrintHelp(pc, stdout, 0);
429                 exit(1);
430         }
431
432 #ifndef HAVE_EA_SUPPORT
433         if (options.ea_enable) {
434                 printf("EA suppport not compiled in\n");
435                 exit(1);
436         }
437 #endif
438         
439         options.nprocs = atoi(extra_argv[0]);
440
441         if (extra_argc >= 2) {
442                 options.server = extra_argv[1];
443         }
444
445         return 1;
446 }
447
448
449
450  int main(int argc, const char *argv[])
451 {
452         double total_bytes = 0;
453         double t, latency=0;
454         int i;
455
456         setlinebuf(stdout);
457
458         printf("dbench version %s - Copyright Andrew Tridgell 1999-2004\n\n", VERSION);
459
460         if (!process_opts(argc, argv))
461                 show_usage();
462
463         if (options.warmup == -1) {
464                 options.warmup = options.timelimit / 5;
465         }
466
467         printf("Running for %d seconds with load '%s' and minimum warmup %d secs\n", 
468                options.timelimit, options.loadfile, options.warmup);
469
470         create_procs(options.nprocs, child_run);
471
472         for (i=0;i<options.nprocs*options.clients_per_process;i++) {
473                 total_bytes += children[i].bytes - children[i].bytes_done_warmup;
474                 latency = MAX(latency, children[i].worst_latency);
475         }
476
477         t = timeval_elapsed2(&tv_start, &tv_end);
478
479         printf("Throughput %g MB/sec%s%s  %d clients  %d procs  max_latency=%.03f ms\n", 
480                throughput,
481                options.sync_open ? " (sync open)" : "",
482                options.sync_dirs ? " (sync dirs)" : "", 
483                options.nprocs*options.clients_per_process,
484                options.nprocs, latency*1000);
485         return 0;
486 }