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;
62 static gzFile *open_loadfile(void)
66 if ((f = gzopen(options.loadfile, "rt")) != NULL)
70 "dbench: error opening '%s': %s\n", options.loadfile,
77 static struct child_struct *children;
79 static void sem_cleanup() {
81 semctl(barrier,0,IPC_RMID);
84 static void sig_alarm(int sig)
86 double total_bytes = 0;
89 int nclients = options.nprocs * options.clients_per_process;
92 static int in_cleanup;
99 tnow = timeval_current();
101 for (i=0;i<nclients;i++) {
102 total_bytes += children[i].bytes - children[i].bytes_done_warmup;
103 if (children[i].bytes == 0) {
108 total_lines += children[i].line;
109 if (children[i].cleanup_finished) {
114 t = timeval_elapsed(&tv_start);
116 if (!in_warmup && options.warmup>0 && t > options.warmup) {
119 for (i=0;i<nclients;i++) {
120 children[i].bytes_done_warmup = children[i].bytes;
121 children[i].worst_latency = 0;
122 memset(&children[i].ops, 0, sizeof(children[i].ops));
126 if (t < options.warmup) {
128 } else if (!in_warmup && !in_cleanup && t > options.timelimit) {
129 for (i=0;i<nclients;i++) {
130 children[i].done = 1;
141 for (i=0;i<nclients;i++) {
142 latency = MAX(children[i].max_latency, latency);
143 latency = MAX(latency, timeval_elapsed2(&children[i].lasttime, &tnow));
144 children[i].max_latency = 0;
145 if (latency > children[i].worst_latency) {
146 children[i].worst_latency = latency;
152 if (options.machine_readable) {
153 printf("@W@%d@%d@%.2f@%u@%.03f@\n",
154 num_active, total_lines/nclients,
155 1.0e-6 * total_bytes / t, (int)t, latency*1000);
157 printf("%4d %8d %7.2f MB/sec warmup %3.0f sec latency %.03f ms\n",
158 num_active, total_lines/nclients,
159 1.0e-6 * total_bytes / t, t, latency*1000);
161 } else if (in_cleanup) {
162 if (options.machine_readable) {
163 printf("@C@%d@%d@%.2f@%u@%.03f@\n",
164 num_active, total_lines/nclients,
165 1.0e-6 * total_bytes / t, (int)t, latency*1000);
167 printf("%4d cleanup %3.0f sec\n", nclients - num_finished, t);
170 if (options.machine_readable) {
171 printf("@R@%d@%d@%.2f@%u@%.03f@\n",
172 num_active, total_lines/nclients,
173 1.0e-6 * total_bytes / t, (int)t, latency*1000);
175 printf("%4d %8d %7.2f MB/sec execute %3.0f sec latency %.03f ms\n",
176 nclients, total_lines/nclients,
177 1.0e-6 * total_bytes / t, t, latency*1000);
178 throughput = 1.0e-6 * total_bytes / t;
184 signal(SIGALRM, sig_alarm);
189 static void show_one_latency(struct op *ops, struct op *ops_all)
192 printf(" Operation Count AvgLat MaxLat\n");
193 printf(" --------------------------------------------------\n");
194 for (i=0;nb_ops->ops[i].name;i++) {
195 struct op *op1, *op_all;
197 op_all = &ops_all[i];
198 if (op_all->count == 0) continue;
199 if (options.machine_readable) {
200 printf(":%s:%u:%.03f:%.03f:\n",
201 nb_ops->ops[i].name, op1->count,
202 1000*op1->total_time/op1->count,
203 op1->max_latency*1000);
205 printf(" %-22s %7u %9.03f %9.03f\n",
206 nb_ops->ops[i].name, op1->count,
207 1000*op1->total_time/op1->count,
208 op1->max_latency*1000);
214 static void report_latencies(void)
216 struct op sum[MAX_OPS];
218 struct op *op1, *op2;
219 struct child_struct *child;
221 memset(sum, 0, sizeof(sum));
222 for (i=0;nb_ops->ops[i].name;i++) {
224 for (j=0;j<options.nprocs * options.clients_per_process;j++) {
225 child = &children[j];
226 op2 = &child->ops[i];
227 op1->count += op2->count;
228 op1->total_time += op2->total_time;
229 op1->max_latency = MAX(op1->max_latency, op2->max_latency);
232 show_one_latency(sum, sum);
234 if (!options.per_client_results) {
238 printf("Per client results:\n");
239 for (i=0;i<options.nprocs * options.clients_per_process;i++) {
240 child = &children[i];
241 printf("Client %u did %u lines and %.0f bytes\n",
242 i, child->line, child->bytes - child->bytes_done_warmup);
243 show_one_latency(child->ops, sum);
247 /* this creates the specified number of child processes and runs fn()
249 static void create_procs(int nprocs, void (*fn)(struct child_struct *, const char *))
251 int nclients = nprocs * options.clients_per_process;
259 load = open_loadfile();
266 "create %d procs? you must be kidding.\n",
271 children = shm_setup(sizeof(struct child_struct)*nclients);
273 printf("Failed to setup shared memory\n");
277 memset(children, 0, sizeof(*children)*nclients);
279 for (i=0;i<nclients;i++) {
281 children[i].cleanup = 0;
282 children[i].directory = options.directory;
283 children[i].starttime = timeval_current();
284 children[i].lasttime = timeval_current();
287 if (atexit(sem_cleanup) != 0) {
288 printf("can't register cleanup function on exit\n");
292 if ( !(barrier = semget(IPC_PRIVATE,1,IPC_CREAT | S_IRUSR | S_IWUSR)) ) {
293 printf("failed to create barrier semaphore \n");
295 sbuf.sem_flg = SEM_UNDO;
297 if (semop(barrier, &sbuf, 1) == -1) {
298 printf("failed to initialize the barrier semaphore\n");
303 for (i=0;i<nprocs;i++) {
308 srandom(getpid() ^ time(NULL));
310 for (j=0;j<options.clients_per_process;j++) {
311 nb_ops->setup(&children[i*options.clients_per_process + j]);
315 if (semop(barrier, &sbuf, 1) == -1) {
316 printf("failed to use the barrier semaphore in child %d\n",getpid());
320 semctl(barrier,0,IPC_RMID);
322 fn(&children[i*options.clients_per_process], options.loadfile);
328 tv = timeval_current();
330 synccount = semctl(barrier,0,GETZCNT);
331 t = timeval_elapsed(&tv);
332 printf("%d of %d processes prepared for launch %3.0f sec\n", synccount, nprocs, t);
333 if (synccount == nprocs) break;
335 } while (timeval_elapsed(&tv) < 30);
337 if (synccount != nprocs) {
338 printf("FAILED TO START %d CLIENTS (started %d)\n", nprocs, synccount);
342 printf("releasing clients\n");
343 tv_start = timeval_current();
345 if (semop(barrier, &sbuf, 1) == -1) {
346 printf("failed to release barrier\n");
350 semctl(barrier,0,IPC_RMID);
352 signal(SIGALRM, sig_alarm);
355 for (i=0;i<nprocs;) {
356 if (waitpid(0, &status, 0) == -1) continue;
357 if (WEXITSTATUS(status) != 0) {
358 printf("Child failed with status %d\n",
359 WEXITSTATUS(status));
375 static void process_opts(int argc, const char **argv)
377 const char **extra_argv;
379 struct poptOption popt_options[] = {
381 { "backend", 'B', POPT_ARG_STRING, &options.backend, 0,
382 "dbench backend (fileio, sockio, nfs, scsi, iscsi)", "string" },
383 { "timelimit", 't', POPT_ARG_INT, &options.timelimit, 0,
384 "timelimit", "integer" },
385 { "loadfile", 'c', POPT_ARG_STRING, &options.loadfile, 0,
386 "loadfile", "filename" },
387 { "directory", 'D', POPT_ARG_STRING, &options.directory, 0,
388 "working directory", NULL },
389 { "tcp-options", 'T', POPT_ARG_STRING, &options.tcp_options, 0,
390 "TCP socket options", NULL },
391 { "target-rate", 'R', POPT_ARG_DOUBLE, &options.targetrate, 0,
392 "target throughput (MB/sec)", NULL },
393 { "sync", 's', POPT_ARG_NONE, &options.sync_open, 0,
394 "use O_SYNC", NULL },
395 { "sync-dir", 'S', POPT_ARG_NONE, &options.sync_dirs, 0,
396 "sync directory changes", NULL },
397 { "fsync", 'F', POPT_ARG_NONE, &options.do_fsync, 0,
398 "fsync on write", NULL },
399 { "xattr", 'x', POPT_ARG_NONE, &options.ea_enable, 0,
400 "use xattrs", NULL },
401 { "no-resolve", 0, POPT_ARG_NONE, &options.no_resolve, 0,
402 "disable name resolution simulation", NULL },
403 { "clients-per-process", 0, POPT_ARG_INT, &options.clients_per_process, 0,
404 "number of clients per process", NULL },
405 { "trunc-io", 0, POPT_ARG_INT, &options.trunc_io, 0,
406 "truncate all io to this size", NULL },
407 { "one-byte-write-fix", 0, POPT_ARG_NONE, &options.one_byte_write_fix, 0,
408 "try to fix 1 byte writes", NULL },
409 { "stat-check", 0, POPT_ARG_NONE, &options.stat_check, 0,
410 "check for pointless calls with stat", NULL },
411 { "fake-io", 0, POPT_ARG_NONE, &options.fake_io, 0,
412 "fake up read/write calls", NULL },
413 { "skip-cleanup", 0, POPT_ARG_NONE, &options.skip_cleanup, 0,
414 "skip cleanup operations", NULL },
415 { "per-client-results", 0, POPT_ARG_NONE, &options.per_client_results, 0,
416 "show results per client", NULL },
417 { "server", 0, POPT_ARG_STRING, &options.server, 0,
419 { "export", 0, POPT_ARG_STRING, &options.export, 0,
421 { "protocol", 0, POPT_ARG_STRING, &options.protocol, 0,
423 { "run-once", 0, POPT_ARG_NONE, &options.run_once, 0,
424 "Stop once reaching the end of the loadfile", NULL},
425 { "scsi", 0, POPT_ARG_STRING, &options.scsi_dev, 0,
426 "scsi device", NULL },
427 { "allow-scsi-writes", 0, POPT_ARG_NONE, &options.allow_scsi_writes, 0,
428 "Allow SCSI write command to the device", NULL},
429 { "iscsi-lun", 0, POPT_ARG_INT, &options.iscsi_lun, 0,
430 "iSCSI LUN to send I/O to", NULL },
431 { "iscsi-portal", 0, POPT_ARG_STRING, &options.iscsi_portal, 0,
432 "ip address of iscsi target", NULL },
433 { "iscsi-port", 0, POPT_ARG_INT, &options.iscsi_port, 0,
434 "iSCSI tcp port to connect to", NULL },
435 { "iscsi-target", 0, POPT_ARG_STRING, &options.iscsi_target, 0,
436 "iscsi IQN name of target", NULL },
437 { "warmup", 0, POPT_ARG_INT, &options.warmup, 0,
438 "How many seconds of warmup to run", NULL },
439 { "machine-readable", 0, POPT_ARG_NONE, &options.machine_readable, 0,
440 "Print data in more machine-readable friendly format", NULL},
441 { "smb-share", 0, POPT_ARG_STRING, &options.smb_share, 0,
442 "//SERVER/SHARE to use", NULL },
443 { "smb-user", 0, POPT_ARG_STRING, &options.smb_user, 0,
444 "User to authenticate as : [<domain>/]<user>%<password>", NULL },
450 pc = poptGetContext(argv[0], argc, argv, popt_options, POPT_CONTEXT_KEEP_FIRST);
452 while ((opt = poptGetNextOpt(pc)) != -1) {
453 if (strcmp(poptBadOption(pc, 0), "-h") == 0) {
454 poptPrintHelp(pc, stdout, 0);
457 fprintf(stderr, "Invalid option %s: %s\n",
458 poptBadOption(pc, 0), poptStrerror(opt));
462 /* setup the remaining options for the main program to use */
463 extra_argv = poptGetArgs(pc);
466 while (extra_argv[extra_argc]) extra_argc++;
469 if (extra_argc < 1) {
470 printf("You need to specify NPROCS\n");
471 poptPrintHelp(pc, stdout, 0);
475 #ifndef HAVE_EA_SUPPORT
476 if (options.ea_enable) {
477 printf("EA suppport not compiled in\n");
482 options.nprocs = atoi(extra_argv[0]);
484 if (extra_argc >= 2) {
485 options.server = extra_argv[1];
491 int main(int argc, const char *argv[])
493 double total_bytes = 0;
499 printf("dbench version %s - Copyright Andrew Tridgell 1999-2004\n\n", VERSION);
501 if (strstr(argv[0], "dbench")) {
502 options.backend = "fileio";
503 } else if (strstr(argv[0], "tbench")) {
504 options.backend = "sockio";
505 } else if (strstr(argv[0], "nfsbench")) {
506 options.backend = "nfs";
507 } else if (strstr(argv[0], "scsibench")) {
508 options.backend = "scsi";
509 } else if (strstr(argv[0], "iscsibench")) {
510 options.backend = "iscsi";
513 process_opts(argc, argv);
515 if (strcmp(options.backend, "fileio") == 0) {
516 extern struct nb_operations fileio_ops;
517 nb_ops = &fileio_ops;
518 } else if (strcmp(options.backend, "sockio") == 0) {
519 extern struct nb_operations sockio_ops;
520 nb_ops = &sockio_ops;
521 } else if (strcmp(options.backend, "nfs") == 0) {
522 extern struct nb_operations nfs_ops;
524 #ifdef HAVE_LINUX_SCSI_SG
525 } else if (strcmp(options.backend, "scsi") == 0) {
526 extern struct nb_operations scsi_ops;
528 #endif /* HAVE_LINUX_SCSI_SG */
529 } else if (strcmp(options.backend, "iscsi") == 0) {
530 extern struct nb_operations iscsi_ops;
532 } else if (strcmp(options.backend, "smb") == 0) {
533 extern struct nb_operations smb_ops;
536 printf("Unknown backend '%s'\n", options.backend);
540 if (options.warmup == -1) {
541 options.warmup = options.timelimit / 5;
545 if (nb_ops->init() != 0) {
546 printf("Failed to initialize dbench\n");
551 printf("Running for %d seconds with load '%s' and minimum warmup %d secs\n",
552 options.timelimit, options.loadfile, options.warmup);
554 create_procs(options.nprocs, child_run);
556 for (i=0;i<options.nprocs*options.clients_per_process;i++) {
557 total_bytes += children[i].bytes - children[i].bytes_done_warmup;
558 latency = MAX(latency, children[i].worst_latency);
561 t = timeval_elapsed2(&tv_start, &tv_end);
563 if (options.machine_readable) {
564 printf(";%g;%d;%d;%.03f;\n",
566 options.nprocs*options.clients_per_process,
567 options.nprocs, latency*1000);
569 printf("Throughput %g MB/sec%s%s %d clients %d procs max_latency=%.03f ms\n",
571 options.sync_open ? " (sync open)" : "",
572 options.sync_dirs ? " (sync dirs)" : "",
573 options.nprocs*options.clients_per_process,
574 options.nprocs, latency*1000);