fix a debug statement to print also the directory name (and not
[tridge/dbench.git] / dbench.c
index 95bcb3d69c6a5316fe39fc03b6900fae47a7b49e..725deb7abfdf246544f5306224d05a8d6c8cfb74 100644 (file)
--- a/dbench.c
+++ b/dbench.c
@@ -1,10 +1,10 @@
 /* 
-   Copyright (C) by Andrew Tridgell <tridge@samba.org> 1999, 2001
+   Copyright (C) by Andrew Tridgell <tridge@samba.org> 1999-2007
    Copyright (C) 2001 by Martin Pool <mbp@samba.org>
    
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
@@ -13,8 +13,7 @@
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
 */
 
 /* TODO: We could try allowing for different flavours of synchronous
    can choose what kind it wants for each OPEN operation. */
 
 #include "dbench.h"
+#include "popt.h"
+#include <sys/sem.h>
+
+struct options options = {
+       .backend             = "fileio",
+       .timelimit           = 600,
+       .loadfile            = DATADIR "/client.txt",
+       .directory           = ".",
+       .tcp_options         = TCP_OPTIONS,
+       .nprocs              = 10,
+       .sync_open           = 0,
+       .sync_dirs           = 0,
+       .do_fsync            = 0,
+       .fsync_frequency     = 0,
+       .warmup              = -1,
+       .targetrate          = 0.0,
+       .ea_enable           = 0,
+       .clients_per_process = 1,
+       .server              = "localhost",
+       .export              = "/tmp",
+       .protocol            = "tcp",
+       .run_once            = 0,
+       .trunc_io            = 0,
+};
 
-int sync_open = 0, sync_dirs = 0;
-char *tcp_options = TCP_OPTIONS;
-static int timelimit = 600, warmup;
-const char *directory = ".";
-static char *loadfile = DATADIR "/client.txt";
 static struct timeval tv_start;
 static struct timeval tv_end;
-
-#if HAVE_XATTR_SUPPORT
-int xattr_enable=0;
-#endif
+static int barrier=-1;
+static double throughput;
+struct nb_operations *nb_ops;
 
 static FILE *open_loadfile(void)
 {
        FILE            *f;
 
-       if ((f = fopen(loadfile, "rt")) != NULL)
+       if ((f = fopen(options.loadfile, "rt")) != NULL)
                return f;
 
        fprintf(stderr,
-               "dbench: error opening '%s': %s\n", loadfile,
+               "dbench: error opening '%s': %s\n", options.loadfile,
                strerror(errno));
 
        return NULL;
@@ -54,9 +71,9 @@ static FILE *open_loadfile(void)
 
 static struct child_struct *children;
 
-static void sigcont(int sig)
-{
-       (void)sig;
+static void sem_cleanup() {
+       if (!(barrier==-1)) 
+               semctl(barrier,0,IPC_RMID);
 }
 
 static void sig_alarm(int sig)
@@ -64,55 +81,79 @@ static void sig_alarm(int sig)
        double total_bytes = 0;
        int total_lines = 0;
        int i;
-       int nprocs = children[0].nprocs;
-       int in_warmup = 0, in_cleanup = 0;
+       int nclients = options.nprocs * options.clients_per_process;
+       int in_warmup = 0;
        double t;
-
+       static int in_cleanup;
+       double latency;
+       struct timeval tnow;
+       int num_active = 0;
+       int num_finished = 0;
        (void)sig;
 
-       for (i=0;i<nprocs;i++) {
+       tnow = timeval_current();
+
+       for (i=0;i<nclients;i++) {
                total_bytes += children[i].bytes - children[i].bytes_done_warmup;
                if (children[i].bytes == 0) {
                        in_warmup = 1;
+               } else {
+                       num_active++;
                }
                total_lines += children[i].line;
-               if (children[i].cleanup) in_cleanup = 1;
+               if (children[i].cleanup_finished) {
+                       num_finished++;
+               }
        }
 
        t = timeval_elapsed(&tv_start);
 
-       if (!in_warmup && warmup>0 && t > warmup) {
-               tv_start = timeval_current();
-               warmup = 0;
-               for (i=0;i<nprocs;i++) {
+       if (!in_warmup && options.warmup>0 && t > options.warmup) {
+               tv_start = tnow;
+               options.warmup = 0;
+               for (i=0;i<nclients;i++) {
                        children[i].bytes_done_warmup = children[i].bytes;
+                       children[i].worst_latency = 0;
+                       memset(&children[i].ops, 0, sizeof(children[i].ops));
                }
                goto next;
        }
-       if (t < warmup) {
+       if (t < options.warmup) {
                in_warmup = 1;
-       } else if (!in_warmup && t > timelimit) {
-               for (i=0;i<nprocs;i++) {
+       } else if (!in_warmup && !in_cleanup && t > options.timelimit) {
+               for (i=0;i<nclients;i++) {
                        children[i].done = 1;
                }
-               tv_end = timeval_current();
+               tv_end = tnow;
+               in_cleanup = 1;
        }
        if (t < 1) {
                goto next;
        }
 
+       latency = 0;
+       if (!in_cleanup) {
+               for (i=0;i<nclients;i++) {
+                       latency = MAX(children[i].max_latency, latency);
+                       latency = MAX(latency, timeval_elapsed2(&children[i].lasttime, &tnow));
+                       children[i].max_latency = 0;
+                       if (latency > children[i].worst_latency) {
+                               children[i].worst_latency = latency;
+                       }
+               }
+       }
+
         if (in_warmup) {
-                printf("%4d  %8d  %7.2f MB/sec  warmup %3.0f sec   \n", 
-                       nprocs, total_lines/nprocs, 
-                       1.0e-6 * total_bytes / t, t);
+                printf("%4d  %8d  %7.2f MB/sec  warmup %3.0f sec  latency %.03f ms\n", 
+                       num_active, total_lines/nclients, 
+                       1.0e-6 * total_bytes / t, t, latency*1000);
         } else if (in_cleanup) {
-                printf("%4d  %8d  %7.2f MB/sec  cleanup %3.0f sec   \n", 
-                       nprocs, total_lines/nprocs, 
-                       1.0e-6 * total_bytes / t, t);
+                printf("%4d  cleanup %3.0f sec\n", nclients - num_finished, t);
         } else {
-                printf("%4d  %8d  %7.2f MB/sec  execute %3.0f sec   \n", 
-                       nprocs, total_lines/nprocs, 
-                       1.0e-6 * total_bytes / t, t);
+                printf("%4d  %8d  %7.2f MB/sec  execute %3.0f sec  latency %.03f ms\n", 
+                       nclients, total_lines/nclients, 
+                       1.0e-6 * total_bytes / t, t, latency*1000);
+               throughput = 1.0e-6 * total_bytes / t;
         }
 
        fflush(stdout);
@@ -121,24 +162,75 @@ next:
        alarm(PRINT_FREQ);
 }
 
+
+static void show_one_latency(struct op *ops, struct op *ops_all)
+{
+       int i;
+       printf(" Operation                Count    AvgLat    MaxLat\n");
+       printf(" --------------------------------------------------\n");
+       for (i=0;nb_ops->ops[i].name;i++) {
+               struct op *op1, *op_all;
+               op1    = &ops[i];
+               op_all = &ops_all[i];
+               if (op_all->count == 0) continue;
+               printf(" %-22s %7u %9.03f %9.03f\n",
+                      nb_ops->ops[i].name, op1->count, 
+                      1000*op1->total_time/op1->count,
+                      op1->max_latency*1000);
+       }
+       printf("\n");
+}
+
+static void report_latencies(void)
+{
+       struct op sum[MAX_OPS];
+       int i, j;
+       struct op *op1, *op2;
+       struct child_struct *child;
+
+       memset(sum, 0, sizeof(sum));
+       for (i=0;nb_ops->ops[i].name;i++) {
+               op1 = &sum[i];
+               for (j=0;j<options.nprocs * options.clients_per_process;j++) {
+                       child = &children[j];
+                       op2 = &child->ops[i];
+                       op1->count += op2->count;
+                       op1->total_time += op2->total_time;
+                       op1->max_latency = MAX(op1->max_latency, op2->max_latency);
+               }
+       }
+       show_one_latency(sum, sum);
+
+       if (!options.per_client_results) {
+               return;
+       }
+
+       printf("Per client results:\n");
+       for (i=0;i<options.nprocs * options.clients_per_process;i++) {
+               child = &children[i];
+               printf("Client %u did %u lines and %.0f bytes\n", 
+                      i, child->line, child->bytes - child->bytes_done_warmup);
+               show_one_latency(child->ops, sum);              
+       }
+}
+
 /* this creates the specified number of child processes and runs fn()
    in all of them */
 static void create_procs(int nprocs, void (*fn)(struct child_struct *, const char *))
 {
+       int nclients = nprocs * options.clients_per_process;
        int i, status;
        int synccount;
        struct timeval tv;
        FILE *load;
+       struct sembuf sbuf;
+       double t;
 
        load = open_loadfile();
        if (load == NULL) {
                exit(1);
        }
 
-       signal(SIGCONT, sigcont);
-
-       synccount = 0;
-
        if (nprocs < 1) {
                fprintf(stderr,
                        "create %d procs?  you must be kidding.\n",
@@ -146,40 +238,69 @@ static void create_procs(int nprocs, void (*fn)(struct child_struct *, const cha
                return;
        }
 
-       children = shm_setup(sizeof(struct child_struct)*nprocs);
+       children = shm_setup(sizeof(struct child_struct)*nclients);
        if (!children) {
                printf("Failed to setup shared memory\n");
                return;
        }
 
-       memset(children, 0, sizeof(*children)*nprocs);
+       memset(children, 0, sizeof(*children)*nclients);
 
-       for (i=0;i<nprocs;i++) {
+       for (i=0;i<nclients;i++) {
                children[i].id = i;
-               children[i].nprocs = nprocs;
                children[i].cleanup = 0;
-               children[i].directory = directory;
+               children[i].directory = options.directory;
+               children[i].starttime = timeval_current();
+               children[i].lasttime = timeval_current();
+       }
+
+       if (atexit(sem_cleanup) != 0) {
+               printf("can't register cleanup function on exit\n");
+               exit(1);
+       }
+       sbuf.sem_num =  0;
+       if ( !(barrier = semget(IPC_PRIVATE,1,IPC_CREAT | S_IRUSR | S_IWUSR)) ) {
+               printf("failed to create barrier semaphore \n");
+       }
+       sbuf.sem_flg =  SEM_UNDO;
+       sbuf.sem_op  =  1;
+       if (semop(barrier, &sbuf, 1) == -1) {
+               printf("failed to initialize the barrier semaphore\n");
+               exit(1);
        }
+       sbuf.sem_flg =  0;
 
        for (i=0;i<nprocs;i++) {
                if (fork() == 0) {
+                       int j;
+
                        setlinebuf(stdout);
-                       nb_setup(&children[i]);
-                       children[i].status = getpid();
-                       pause();
-                       fn(&children[i], loadfile);
+
+                       for (j=0;j<options.clients_per_process;j++) {
+                               nb_ops->setup(&children[i*options.clients_per_process + j]);
+                       }
+
+                       sbuf.sem_op = 0;
+                       if (semop(barrier, &sbuf, 1) == -1) {
+                               printf("failed to use the barrier semaphore in child %d\n",getpid());
+                               exit(1);
+                       }
+
+                       semctl(barrier,0,IPC_RMID);
+
+                       fn(&children[i*options.clients_per_process], options.loadfile);
                        _exit(0);
                }
        }
 
+       synccount = 0;
        tv = timeval_current();
        do {
-               synccount = 0;
-               for (i=0;i<nprocs;i++) {
-                       if (children[i].status) synccount++;
-               }
+               synccount = semctl(barrier,0,GETZCNT);
+               t = timeval_elapsed(&tv);
+               printf("%d of %d processes prepared for launch %3.0f sec\n", synccount, nprocs, t);
                if (synccount == nprocs) break;
-               usleep(100000);
+               usleep(100*1000);
        } while (timeval_elapsed(&tv) < 30);
 
        if (synccount != nprocs) {
@@ -187,11 +308,15 @@ static void create_procs(int nprocs, void (*fn)(struct child_struct *, const cha
                return;
        }
 
-       printf("%d clients started\n", nprocs);
-
-       kill(0, SIGCONT);
-
+       printf("releasing clients\n");
        tv_start = timeval_current();
+       sbuf.sem_op  =  -1;
+       if (semop(barrier, &sbuf, 1) == -1) {
+               printf("failed to release barrier\n");
+               exit(1);
+       }
+
+       semctl(barrier,0,IPC_RMID);
 
        signal(SIGALRM, sig_alarm);
        alarm(PRINT_FREQ);
@@ -210,118 +335,174 @@ static void create_procs(int nprocs, void (*fn)(struct child_struct *, const cha
        sig_alarm(SIGALRM);
 
        printf("\n");
-}
 
-
-static void show_usage(void)
-{
-       printf("usage: dbench [OPTIONS] nprocs\n" \
-              "usage: tbench [OPTIONS] nprocs <server>\n" \
-              "options:\n" \
-              "  -v               show version\n" \
-              "  -t timelimit     run time in seconds (default 600)\n" \
-              "  -D directory     base directory to run in\n" \
-              "  -c loadfile      set location of the loadfile\n" \
-              "  -s               synchronous file IO\n" \
-              "  -S               synchronous directories (mkdir, unlink...)\n" \
-              "  -x               enable xattr support\n" \
-              "  -T options       set socket options for tbench\n");
-       exit(1);
+       report_latencies();
 }
 
 
 
-static int process_opts(int argc, char **argv,
-                       int *nprocs)
+static void process_opts(int argc, const char **argv)
 {
-       int c;
-       extern int sync_open;
-       extern char *server;
-
-       while ((c = getopt(argc, argv, "vc:sST:t:xD:")) != -1) 
-               switch (c) {
-               case 'c':
-                       loadfile = optarg;
-                       break;
-               case 's':
-                       sync_open = 1;
-                       break;
-               case 'S':
-                       sync_dirs = 1;
-                       break;
-               case 'T':
-                       tcp_options = optarg;
-                       break;
-               case 't':
-                       timelimit = atoi(optarg);
-                       break;
-               case 'D':
-                       directory = optarg;
-                       break;
-               case 'v':
-                       exit(0);
-                       break;
-               case 'x':
-#if HAVE_XATTR_SUPPORT
-                       xattr_enable = 1;
-#else
-                       printf("xattr suppport not compiled in\n");
+       const char **extra_argv;
+       int extra_argc = 0;
+       struct poptOption popt_options[] = {
+               POPT_AUTOHELP
+               { "backend", 'B', POPT_ARG_STRING, &options.backend, 0, 
+                 "dbench backend (fileio, sockio, nfs)", "string" },
+               { "timelimit", 't', POPT_ARG_INT, &options.timelimit, 0, 
+                 "timelimit", "integer" },
+               { "loadfile",  'c', POPT_ARG_STRING, &options.loadfile, 0, 
+                 "loadfile", "filename" },
+               { "directory", 'D', POPT_ARG_STRING, &options.directory, 0, 
+                 "working directory", NULL },
+               { "tcp-options", 'T', POPT_ARG_STRING, &options.tcp_options, 0, 
+                 "TCP socket options", NULL },
+               { "target-rate", 'R', POPT_ARG_DOUBLE, &options.targetrate, 0, 
+                 "target throughput (MB/sec)", NULL },
+               { "sync", 's', POPT_ARG_NONE, &options.sync_open, 0, 
+                 "use O_SYNC", NULL },
+               { "sync-dir", 'S', POPT_ARG_NONE, &options.sync_dirs, 0, 
+                 "sync directory changes", NULL },
+               { "fsync", 'F', POPT_ARG_NONE, &options.do_fsync, 0, 
+                 "fsync on write", NULL },
+               { "xattr", 'x', POPT_ARG_NONE, &options.ea_enable, 0, 
+                 "use xattrs", NULL },
+               { "no-resolve", 0, POPT_ARG_NONE, &options.no_resolve, 0, 
+                 "disable name resolution simulation", NULL },
+               { "clients-per-process", 0, POPT_ARG_INT, &options.clients_per_process, 0, 
+                 "number of clients per process", NULL },
+               { "trunc-io", 0, POPT_ARG_INT, &options.trunc_io, 0, 
+                 "truncate all io to this size", NULL },
+               { "one-byte-write-fix", 0, POPT_ARG_NONE, &options.one_byte_write_fix, 0, 
+                 "try to fix 1 byte writes", NULL },
+               { "stat-check", 0, POPT_ARG_NONE, &options.stat_check, 0, 
+                 "check for pointless calls with stat", NULL },
+               { "fake-io", 0, POPT_ARG_NONE, &options.fake_io, 0, 
+                 "fake up read/write calls", NULL },
+               { "skip-cleanup", 0, POPT_ARG_NONE, &options.skip_cleanup, 0, 
+                 "skip cleanup operations", NULL },
+               { "per-client-results", 0, POPT_ARG_NONE, &options.per_client_results, 0, 
+                 "show results per client", NULL },
+               { "server",  0, POPT_ARG_STRING, &options.server, 0, 
+                 "server", NULL },
+               { "export",  0, POPT_ARG_STRING, &options.export, 0, 
+                 "export", NULL },
+               { "protocol",  0, POPT_ARG_STRING, &options.protocol, 0, 
+                 "protocol", NULL },
+               { "run-once", 0, POPT_ARG_NONE, &options.run_once, 0,
+                 "Stop once reaching the end of the loadfile", NULL},
+               { "scsi",  0, POPT_ARG_STRING, &options.scsi_dev, 0, 
+                 "scsi device", NULL },
+               { "warmup", 0, POPT_ARG_INT, &options.warmup, 0, 
+                 "How meny seconds of warmup to run", NULL },
+               POPT_TABLEEND
+       };
+       poptContext pc;
+       int opt;
+
+       pc = poptGetContext(argv[0], argc, argv, popt_options, POPT_CONTEXT_KEEP_FIRST);
+
+       while ((opt = poptGetNextOpt(pc)) != -1) {
+               if (strcmp(poptBadOption(pc, 0), "-h") == 0) {
+                       poptPrintHelp(pc, stdout, 0);
                        exit(1);
-#endif
-                       break;
-               case '?':
-                       if (isprint (optopt))
-                               fprintf (stderr, "Unknown option `-%c'.\n", optopt);
-                       else
-                               fprintf (stderr,
-                                        "Unknown option character `\\x%x'.\n",
-                                        optopt);
-                       return 0;
-               default:
-                       abort ();
                }
-       
-       if (!argv[optind])
-               return 0;
-       
-       *nprocs = atoi(argv[optind++]);
+               fprintf(stderr, "Invalid option %s: %s\n", 
+                       poptBadOption(pc, 0), poptStrerror(opt));
+               exit(1);
+       }
+
+       /* setup the remaining options for the main program to use */
+       extra_argv = poptGetArgs(pc);
+       if (extra_argv) {
+               extra_argv++;
+               while (extra_argv[extra_argc]) extra_argc++;
+       }
+
+       if (extra_argc < 1) {
+               printf("You need to specify NPROCS\n");
+               poptPrintHelp(pc, stdout, 0);
+               exit(1);
+       }
 
-       if (argv[optind])
-               server = argv[optind++];
+#ifndef HAVE_EA_SUPPORT
+       if (options.ea_enable) {
+               printf("EA suppport not compiled in\n");
+               exit(1);
+       }
+#endif
+       
+       options.nprocs = atoi(extra_argv[0]);
 
-       return 1;
+       if (extra_argc >= 2) {
+               options.server = extra_argv[1];
+       }
 }
 
 
 
- int main(int argc, char *argv[])
+ int main(int argc, const char *argv[])
 {
-       int nprocs;
        double total_bytes = 0;
-       double t;
+       double t, latency=0;
        int i;
 
+       setlinebuf(stdout);
+
        printf("dbench version %s - Copyright Andrew Tridgell 1999-2004\n\n", VERSION);
 
-       if (!process_opts(argc, argv, &nprocs))
-               show_usage();
+       if (strstr(argv[0], "dbench")) {
+               options.backend = "fileio";
+       } else if (strstr(argv[0], "tbench")) {
+               options.backend = "sockio";
+       } else if (strstr(argv[0], "nfsbench")) {
+               options.backend = "nfs";
+       } else if (strstr(argv[0], "scsibench")) {
+               options.backend = "scsi";
+       }
 
-       warmup = timelimit / 5;
+       process_opts(argc, argv);
+
+       if (strcmp(options.backend, "fileio") == 0) {
+               extern struct nb_operations fileio_ops;
+               nb_ops = &fileio_ops;
+       } else if (strcmp(options.backend, "sockio") == 0) {
+               extern struct nb_operations sockio_ops;
+               nb_ops = &sockio_ops;
+       } else if (strcmp(options.backend, "nfs") == 0) {
+               extern struct nb_operations nfs_ops;
+               nb_ops = &nfs_ops;
+#ifdef HAVE_LINUX_SCSI_SG
+       } else if (strcmp(options.backend, "scsi") == 0) {
+               extern struct nb_operations scsi_ops;
+               nb_ops = &scsi_ops;
+#endif /* HAVE_LINUX_SCSI_SG */
+       } else {
+               printf("Unknown backend '%s'\n", options.backend);
+               exit(1);
+       }
+
+       if (options.warmup == -1) {
+               options.warmup = options.timelimit / 5;
+       }
 
         printf("Running for %d seconds with load '%s' and minimum warmup %d secs\n", 
-               timelimit, loadfile, warmup);
+               options.timelimit, options.loadfile, options.warmup);
 
-       create_procs(nprocs, child_run);
+       create_procs(options.nprocs, child_run);
 
-       for (i=0;i<nprocs;i++) {
+       for (i=0;i<options.nprocs*options.clients_per_process;i++) {
                total_bytes += children[i].bytes - children[i].bytes_done_warmup;
+               latency = MAX(latency, children[i].worst_latency);
        }
 
        t = timeval_elapsed2(&tv_start, &tv_end);
 
-       printf("Throughput %g MB/sec%s%s %d procs\n", 
-              1.0e-6 * total_bytes / t,
-              sync_open ? " (sync open)" : "",
-              sync_dirs ? " (sync dirs)" : "", nprocs);
+       printf("Throughput %g MB/sec%s%s  %d clients  %d procs  max_latency=%.03f ms\n", 
+              throughput,
+              options.sync_open ? " (sync open)" : "",
+              options.sync_dirs ? " (sync dirs)" : "", 
+              options.nprocs*options.clients_per_process,
+              options.nprocs, latency*1000);
        return 0;
 }