2 Copyright (C) by Andrew Tridgell <tridge@samba.org> 1999-2007
3 Copyright (C) 2001 by Martin Pool <mbp@samba.org>
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.
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.
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/>.
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. */
30 struct options options = {
33 .loadfile = DATADIR "/client.txt",
35 .tcp_options = TCP_OPTIONS,
44 .clients_per_process = 1,
45 .server = "localhost",
49 .allow_scsi_writes = 0,
53 .machine_readable = 0,
56 static struct timeval tv_start;
57 static struct timeval tv_end;
58 static int barrier=-1;
59 static double throughput;
60 struct nb_operations *nb_ops;
63 static gzFile *open_loadfile(void)
67 if ((f = gzopen(options.loadfile, "rt")) != NULL)
71 "dbench: error opening '%s': %s\n", options.loadfile,
78 static struct child_struct *children;
80 static void sem_cleanup() {
82 semctl(barrier,0,IPC_RMID);
85 static void sig_alarm(int sig)
87 double total_bytes = 0;
90 int nclients = options.nprocs * options.clients_per_process;
93 static int in_cleanup;
100 tnow = timeval_current();
102 for (i=0;i<nclients;i++) {
103 total_bytes += children[i].bytes - children[i].bytes_done_warmup;
104 if (children[i].bytes == 0 && options.warmup == -1) {
109 total_lines += children[i].line;
110 if (children[i].cleanup_finished) {
115 t = timeval_elapsed(&tv_start);
117 if (!in_warmup && options.warmup>0 && t > options.warmup) {
120 for (i=0;i<nclients;i++) {
121 children[i].bytes_done_warmup = children[i].bytes;
122 children[i].worst_latency = 0;
123 memset(&children[i].ops, 0, sizeof(children[i].ops));
127 if (t < options.warmup) {
129 } else if (!in_warmup && !in_cleanup && t > options.timelimit) {
130 for (i=0;i<nclients;i++) {
131 children[i].done = 1;
142 for (i=0;i<nclients;i++) {
143 latency = MAX(children[i].max_latency, latency);
144 latency = MAX(latency, timeval_elapsed2(&children[i].lasttime, &tnow));
145 children[i].max_latency = 0;
146 if (latency > children[i].worst_latency) {
147 children[i].worst_latency = latency;
153 if (options.machine_readable) {
154 printf("@W@%d@%d@%.2f@%u@%.03f@\n",
155 num_active, total_lines/nclients,
156 1.0e-6 * total_bytes / t, (int)t, latency*1000);
158 printf("%4d %8d %7.2f MB/sec warmup %3.0f sec latency %.03f ms\n",
159 num_active, total_lines/nclients,
160 1.0e-6 * total_bytes / t, t, latency*1000);
162 } else if (in_cleanup) {
163 if (options.machine_readable) {
164 printf("@C@%d@%d@%.2f@%u@%.03f@\n",
165 num_active, total_lines/nclients,
166 1.0e-6 * total_bytes / t, (int)t, latency*1000);
168 printf("%4d cleanup %3.0f sec\n", nclients - num_finished, t);
171 if (options.machine_readable) {
172 printf("@R@%d@%d@%.2f@%u@%.03f@\n",
173 num_active, total_lines/nclients,
174 1.0e-6 * total_bytes / t, (int)t, latency*1000);
176 printf("%4d %8d %7.2f MB/sec execute %3.0f sec latency %.03f ms\n",
177 nclients, total_lines/nclients,
178 1.0e-6 * total_bytes / t, t, latency*1000);
179 throughput = 1.0e-6 * total_bytes / t;
185 signal(SIGALRM, sig_alarm);
190 static void show_one_latency(struct op *ops, struct op *ops_all)
193 printf(" Operation Count AvgLat MaxLat\n");
194 printf(" --------------------------------------------------\n");
195 for (i=0;nb_ops->ops[i].name;i++) {
196 struct op *op1, *op_all;
198 op_all = &ops_all[i];
199 if (op_all->count == 0) continue;
200 if (options.machine_readable) {
201 printf(":%s:%u:%.03f:%.03f:\n",
202 nb_ops->ops[i].name, op1->count,
203 1000*op1->total_time/op1->count,
204 op1->max_latency*1000);
206 printf(" %-22s %7u %9.03f %9.03f\n",
207 nb_ops->ops[i].name, op1->count,
208 1000*op1->total_time/op1->count,
209 op1->max_latency*1000);
215 static void report_latencies(void)
217 struct op sum[MAX_OPS];
219 struct op *op1, *op2;
220 struct child_struct *child;
222 memset(sum, 0, sizeof(sum));
223 for (i=0;nb_ops->ops[i].name;i++) {
225 for (j=0;j<options.nprocs * options.clients_per_process;j++) {
226 child = &children[j];
227 op2 = &child->ops[i];
228 op1->count += op2->count;
229 op1->total_time += op2->total_time;
230 op1->max_latency = MAX(op1->max_latency, op2->max_latency);
233 show_one_latency(sum, sum);
235 if (!options.per_client_results) {
239 printf("Per client results:\n");
240 for (i=0;i<options.nprocs * options.clients_per_process;i++) {
241 child = &children[i];
242 printf("Client %u did %u lines and %.0f bytes\n",
243 i, child->line, child->bytes - child->bytes_done_warmup);
244 show_one_latency(child->ops, sum);
248 /* this creates the specified number of child processes and runs fn()
250 static void create_procs(int nprocs, void (*fn)(struct child_struct *, const char *))
252 int nclients = nprocs * options.clients_per_process;
260 load = open_loadfile();
267 "create %d procs? you must be kidding.\n",
272 children = shm_setup(sizeof(struct child_struct)*nclients);
274 printf("Failed to setup shared memory\n");
278 memset(children, 0, sizeof(*children)*nclients);
280 for (i=0;i<nclients;i++) {
282 children[i].num_clients = nclients;
283 children[i].cleanup = 0;
284 children[i].directory = options.directory;
285 children[i].starttime = timeval_current();
286 children[i].lasttime = timeval_current();
289 if (atexit(sem_cleanup) != 0) {
290 printf("can't register cleanup function on exit\n");
294 if ( !(barrier = semget(IPC_PRIVATE,1,IPC_CREAT | S_IRUSR | S_IWUSR)) ) {
295 printf("failed to create barrier semaphore \n");
297 sbuf.sem_flg = SEM_UNDO;
299 if (semop(barrier, &sbuf, 1) == -1) {
300 printf("failed to initialize the barrier semaphore\n");
305 for (i=0;i<nprocs;i++) {
310 srandom(getpid() ^ time(NULL));
312 for (j=0;j<options.clients_per_process;j++) {
313 nb_ops->setup(&children[i*options.clients_per_process + j]);
317 if (semop(barrier, &sbuf, 1) == -1) {
318 printf("failed to use the barrier semaphore in child %d\n",getpid());
322 semctl(barrier,0,IPC_RMID);
324 fn(&children[i*options.clients_per_process], options.loadfile);
330 tv = timeval_current();
332 synccount = semctl(barrier,0,GETZCNT);
333 t = timeval_elapsed(&tv);
334 printf("%d of %d processes prepared for launch %3.0f sec\n", synccount, nprocs, t);
335 if (synccount == nprocs) break;
337 } while (timeval_elapsed(&tv) < 30);
339 if (synccount != nprocs) {
340 printf("FAILED TO START %d CLIENTS (started %d)\n", nprocs, synccount);
344 printf("releasing clients\n");
345 tv_start = timeval_current();
347 if (semop(barrier, &sbuf, 1) == -1) {
348 printf("failed to release barrier\n");
352 semctl(barrier,0,IPC_RMID);
354 signal(SIGALRM, sig_alarm);
357 for (i=0;i<nprocs;) {
358 if (waitpid(0, &status, 0) == -1) continue;
359 if (WEXITSTATUS(status) != 0) {
360 printf("Child failed with status %d\n",
361 WEXITSTATUS(status));
377 static void process_opts(int argc, const char **argv)
379 const char **extra_argv;
381 struct poptOption popt_options[] = {
383 { "backend", 'B', POPT_ARG_STRING, &options.backend, 0,
384 "dbench backend (fileio, sockio, nfs, scsi, iscsi, smb)", "string" },
385 { "timelimit", 't', POPT_ARG_INT, &options.timelimit, 0,
386 "timelimit", "integer" },
387 { "loadfile", 'c', POPT_ARG_STRING, &options.loadfile, 0,
388 "loadfile", "filename" },
389 { "directory", 'D', POPT_ARG_STRING, &options.directory, 0,
390 "working directory", NULL },
391 { "tcp-options", 'T', POPT_ARG_STRING, &options.tcp_options, 0,
392 "TCP socket options", NULL },
393 { "target-rate", 'R', POPT_ARG_DOUBLE, &options.targetrate, 0,
394 "target throughput (MB/sec)", NULL },
395 { "sync", 's', POPT_ARG_NONE, &options.sync_open, 0,
396 "use O_SYNC", NULL },
397 { "sync-dir", 'S', POPT_ARG_NONE, &options.sync_dirs, 0,
398 "sync directory changes", NULL },
399 { "fsync", 'F', POPT_ARG_NONE, &options.do_fsync, 0,
400 "fsync on write", NULL },
401 { "xattr", 'x', POPT_ARG_NONE, &options.ea_enable, 0,
402 "use xattrs", NULL },
403 { "no-resolve", 0, POPT_ARG_NONE, &options.no_resolve, 0,
404 "disable name resolution simulation", NULL },
405 { "clients-per-process", 0, POPT_ARG_INT, &options.clients_per_process, 0,
406 "number of clients per process", NULL },
407 { "trunc-io", 0, POPT_ARG_INT, &options.trunc_io, 0,
408 "truncate all io to this size", NULL },
409 { "one-byte-write-fix", 0, POPT_ARG_NONE, &options.one_byte_write_fix, 0,
410 "try to fix 1 byte writes", NULL },
411 { "stat-check", 0, POPT_ARG_NONE, &options.stat_check, 0,
412 "check for pointless calls with stat", NULL },
413 { "fake-io", 0, POPT_ARG_NONE, &options.fake_io, 0,
414 "fake up read/write calls", NULL },
415 { "skip-cleanup", 0, POPT_ARG_NONE, &options.skip_cleanup, 0,
416 "skip cleanup operations", NULL },
417 { "per-client-results", 0, POPT_ARG_NONE, &options.per_client_results, 0,
418 "show results per client", NULL },
419 { "server", 0, POPT_ARG_STRING, &options.server, 0,
421 { "export", 0, POPT_ARG_STRING, &options.export, 0,
423 { "protocol", 0, POPT_ARG_STRING, &options.protocol, 0,
425 { "run-once", 0, POPT_ARG_NONE, &options.run_once, 0,
426 "Stop once reaching the end of the loadfile", NULL},
427 { "scsi", 0, POPT_ARG_STRING, &options.scsi_dev, 0,
428 "scsi device", NULL },
429 { "allow-scsi-writes", 0, POPT_ARG_NONE, &options.allow_scsi_writes, 0,
430 "Allow SCSI write command to the device", NULL},
431 { "iscsi-lun", 0, POPT_ARG_INT, &options.iscsi_lun, 0,
432 "iSCSI LUN to send I/O to", NULL },
433 { "iscsi-portal", 0, POPT_ARG_STRING, &options.iscsi_portal, 0,
434 "ip address of iscsi target", NULL },
435 { "iscsi-port", 0, POPT_ARG_INT, &options.iscsi_port, 0,
436 "iSCSI tcp port to connect to", NULL },
437 { "iscsi-target", 0, POPT_ARG_STRING, &options.iscsi_target, 0,
438 "iscsi IQN name of target", NULL },
439 { "warmup", 0, POPT_ARG_INT, &options.warmup, 0,
440 "How many seconds of warmup to run", NULL },
441 { "machine-readable", 0, POPT_ARG_NONE, &options.machine_readable, 0,
442 "Print data in more machine-readable friendly format", NULL},
443 #ifdef HAVE_LIBSMBCLIENT
444 { "smb-share", 0, POPT_ARG_STRING, &options.smb_share, 0,
445 "//SERVER/SHARE to use", NULL },
446 { "smb-user", 0, POPT_ARG_STRING, &options.smb_user, 0,
447 "User to authenticate as : [<domain>/]<user>%<password>", NULL },
454 pc = poptGetContext(argv[0], argc, argv, popt_options, POPT_CONTEXT_KEEP_FIRST);
456 while ((opt = poptGetNextOpt(pc)) != -1) {
457 if (strcmp(poptBadOption(pc, 0), "-h") == 0) {
458 poptPrintHelp(pc, stdout, 0);
461 fprintf(stderr, "Invalid option %s: %s\n",
462 poptBadOption(pc, 0), poptStrerror(opt));
466 /* setup the remaining options for the main program to use */
467 extra_argv = poptGetArgs(pc);
470 while (extra_argv[extra_argc]) extra_argc++;
473 if (extra_argc < 1) {
474 printf("You need to specify NPROCS\n");
475 poptPrintHelp(pc, stdout, 0);
479 #ifndef HAVE_EA_SUPPORT
480 if (options.ea_enable) {
481 printf("EA suppport not compiled in\n");
486 options.nprocs = atoi(extra_argv[0]);
488 if (extra_argc >= 2) {
489 options.server = extra_argv[1];
495 int main(int argc, const char *argv[])
497 double total_bytes = 0;
503 printf("dbench version %s - Copyright Andrew Tridgell 1999-2004\n\n", VERSION);
505 if (strstr(argv[0], "dbench")) {
506 options.backend = "fileio";
507 } else if (strstr(argv[0], "tbench")) {
508 options.backend = "sockio";
509 } else if (strstr(argv[0], "nfsbench")) {
510 options.backend = "nfs";
511 } else if (strstr(argv[0], "scsibench")) {
512 options.backend = "scsi";
513 } else if (strstr(argv[0], "iscsibench")) {
514 options.backend = "iscsi";
517 srandom(getpid() ^ time(NULL));
518 global_random = random();
520 process_opts(argc, argv);
522 if (strcmp(options.backend, "fileio") == 0) {
523 extern struct nb_operations fileio_ops;
524 nb_ops = &fileio_ops;
525 } else if (strcmp(options.backend, "sockio") == 0) {
526 extern struct nb_operations sockio_ops;
527 nb_ops = &sockio_ops;
528 } else if (strcmp(options.backend, "nfs") == 0) {
529 extern struct nb_operations nfs_ops;
531 #ifdef HAVE_LINUX_SCSI_SG
532 } else if (strcmp(options.backend, "scsi") == 0) {
533 extern struct nb_operations scsi_ops;
535 #endif /* HAVE_LINUX_SCSI_SG */
536 } else if (strcmp(options.backend, "iscsi") == 0) {
537 extern struct nb_operations iscsi_ops;
539 #ifdef HAVE_LIBSMBCLIENT
540 } else if (strcmp(options.backend, "smb") == 0) {
541 extern struct nb_operations smb_ops;
545 printf("Unknown backend '%s'\n", options.backend);
549 if (options.warmup == -1) {
550 options.warmup = options.timelimit / 5;
554 if (nb_ops->init() != 0) {
555 printf("Failed to initialize dbench\n");
560 printf("Running for %d seconds with load '%s' and minimum warmup %d secs\n",
561 options.timelimit, options.loadfile, options.warmup);
563 create_procs(options.nprocs, child_run);
565 for (i=0;i<options.nprocs*options.clients_per_process;i++) {
566 total_bytes += children[i].bytes - children[i].bytes_done_warmup;
567 latency = MAX(latency, children[i].worst_latency);
570 t = timeval_elapsed2(&tv_start, &tv_end);
572 if (options.machine_readable) {
573 printf(";%g;%d;%d;%.03f;\n",
575 options.nprocs*options.clients_per_process,
576 options.nprocs, latency*1000);
578 printf("Throughput %g MB/sec%s%s %d clients %d procs max_latency=%.03f ms\n",
580 options.sync_open ? " (sync open)" : "",
581 options.sync_dirs ? " (sync dirs)" : "",
582 options.nprocs*options.clients_per_process,
583 options.nprocs, latency*1000);