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. */
29 struct options options = {
31 .loadfile = DATADIR "/client.txt",
33 .tcp_options = TCP_OPTIONS,
42 .clients_per_process = 1,
43 .server = "localhost",
46 static struct timeval tv_start;
47 static struct timeval tv_end;
48 static int barrier=-1;
49 static double throughput;
51 static FILE *open_loadfile(void)
55 if ((f = fopen(options.loadfile, "rt")) != NULL)
59 "dbench: error opening '%s': %s\n", options.loadfile,
66 static struct child_struct *children;
68 static void sem_cleanup() {
70 semctl(barrier,0,IPC_RMID);
73 static void sig_alarm(int sig)
75 double total_bytes = 0;
78 int nclients = options.nprocs * options.clients_per_process;
81 static int in_cleanup;
88 tnow = timeval_current();
90 for (i=0;i<nclients;i++) {
91 total_bytes += children[i].bytes - children[i].bytes_done_warmup;
92 if (children[i].bytes == 0) {
97 total_lines += children[i].line;
98 if (children[i].cleanup_finished) {
103 t = timeval_elapsed(&tv_start);
105 if (!in_warmup && options.warmup>0 && t > options.warmup) {
108 for (i=0;i<nclients;i++) {
109 children[i].bytes_done_warmup = children[i].bytes;
110 children[i].worst_latency = 0;
111 memset(&children[i].op, 0, sizeof(children[i].op));
115 if (t < options.warmup) {
117 } else if (!in_warmup && !in_cleanup && t > options.timelimit) {
118 for (i=0;i<nclients;i++) {
119 children[i].done = 1;
130 for (i=0;i<nclients;i++) {
131 latency = MAX(children[i].max_latency, latency);
132 latency = MAX(latency, timeval_elapsed2(&children[i].lasttime, &tnow));
133 children[i].max_latency = 0;
134 if (latency > children[i].worst_latency) {
135 children[i].worst_latency = latency;
141 printf("%4d %8d %7.2f MB/sec warmup %3.0f sec latency %.03f ms\n",
142 num_active, total_lines/nclients,
143 1.0e-6 * total_bytes / t, t, latency*1000);
144 } else if (in_cleanup) {
145 printf("%4d cleanup %3.0f sec\n", nclients - num_finished, t);
147 printf("%4d %8d %7.2f MB/sec execute %3.0f sec latency %.03f ms\n",
148 nclients, total_lines/nclients,
149 1.0e-6 * total_bytes / t, t, latency*1000);
150 throughput = 1.0e-6 * total_bytes / t;
155 signal(SIGALRM, sig_alarm);
160 static const struct {
164 #define OP_NAME(opname) { #opname, offsetof(struct opnames, op_ ## opname) }
184 static void show_one_latency(struct opnames *ops, struct opnames *ops_all)
186 int i, n = (sizeof(op_names)/sizeof(op_names[0]));
187 printf(" Operation Count AvgLat MaxLat\n");
188 printf(" ----------------------------------------\n");
190 struct op *op1, *op_all;
191 op1 = (struct op *)(op_names[i].offset + (char *)ops);
192 op_all = (struct op *)(op_names[i].offset + (char *)ops_all);
193 if (op_all->count == 0) continue;
194 printf(" %-12s %7u %9.03f %9.03f\n",
195 op_names[i].name, op1->count,
196 1000*op1->total_time/op1->count,
197 op1->max_latency*1000);
202 static void report_latencies(void)
205 int i, j, n = (sizeof(op_names)/sizeof(op_names[0]));
206 struct op *op1, *op2;
207 struct child_struct *child;
209 memset(&sum, 0, sizeof(sum));
211 op1 = (struct op *)(op_names[i].offset + (char *)&sum);
212 for (j=0;j<options.nprocs * options.clients_per_process;j++) {
213 child = &children[j];
214 op2 = (struct op *)(op_names[i].offset + (char *)&child->op);
215 op1->count += op2->count;
216 op1->total_time += op2->total_time;
217 op1->max_latency = MAX(op1->max_latency, op2->max_latency);
220 show_one_latency(&sum, &sum);
222 if (!options.per_client_results) {
226 printf("Per client results:\n");
227 for (i=0;i<options.nprocs * options.clients_per_process;i++) {
228 child = &children[i];
229 printf("Client %u did %u lines and %.0f bytes\n",
230 i, child->line, child->bytes - child->bytes_done_warmup);
231 show_one_latency(&child->op, &sum);
235 /* this creates the specified number of child processes and runs fn()
237 static void create_procs(int nprocs, void (*fn)(struct child_struct *, const char *))
239 int nclients = nprocs * options.clients_per_process;
247 load = open_loadfile();
254 "create %d procs? you must be kidding.\n",
259 children = shm_setup(sizeof(struct child_struct)*nclients);
261 printf("Failed to setup shared memory\n");
265 memset(children, 0, sizeof(*children)*nclients);
267 for (i=0;i<nclients;i++) {
269 children[i].cleanup = 0;
270 children[i].directory = options.directory;
271 children[i].starttime = timeval_current();
272 children[i].lasttime = timeval_current();
275 if (atexit(sem_cleanup) != 0) {
276 printf("can't register cleanup function on exit\n");
280 if ( !(barrier = semget(IPC_PRIVATE,1,IPC_CREAT | S_IRUSR | S_IWUSR)) ) {
281 printf("failed to create barrier semaphore \n");
283 sbuf.sem_flg = SEM_UNDO;
285 if (semop(barrier, &sbuf, 1) == -1) {
286 printf("failed to initialize the barrier semaphore\n");
291 for (i=0;i<nprocs;i++) {
297 for (j=0;j<options.clients_per_process;j++) {
298 nb_setup(&children[i*options.clients_per_process + j]);
302 if (semop(barrier, &sbuf, 1) == -1) {
303 printf("failed to use the barrier semaphore in child %d\n",getpid());
307 semctl(barrier,0,IPC_RMID);
309 fn(&children[i*options.clients_per_process], options.loadfile);
315 tv = timeval_current();
317 synccount = semctl(barrier,0,GETZCNT);
318 t = timeval_elapsed(&tv);
319 printf("%d of %d processes prepared for launch %3.0f sec\n", synccount, nprocs, t);
320 if (synccount == nprocs) break;
322 } while (timeval_elapsed(&tv) < 30);
324 if (synccount != nprocs) {
325 printf("FAILED TO START %d CLIENTS (started %d)\n", nprocs, synccount);
329 printf("releasing clients\n");
330 tv_start = timeval_current();
332 if (semop(barrier, &sbuf, 1) == -1) {
333 printf("failed to release barrier\n");
337 semctl(barrier,0,IPC_RMID);
339 signal(SIGALRM, sig_alarm);
342 for (i=0;i<nprocs;) {
343 if (waitpid(0, &status, 0) == -1) continue;
344 if (WEXITSTATUS(status) != 0) {
345 printf("Child failed with status %d\n",
346 WEXITSTATUS(status));
361 static void show_usage(void)
363 printf("usage: dbench [OPTIONS] nprocs\n" \
364 "usage: tbench [OPTIONS] nprocs <server>\n" \
366 " -v show version\n" \
367 " -t timelimit run time in seconds (default 600)\n" \
368 " -D directory base directory to run in\n" \
369 " -c loadfile set location of the loadfile\n" \
370 " -R target rate (MByte/sec)\n" \
371 " -s synchronous file IO\n" \
372 " -F fsync on write\n" \
373 " -S synchronous directories (mkdir, unlink...)\n" \
374 " -x enable EA support\n" \
375 " -T options set socket options for tbench\n");
381 static int process_opts(int argc, const char **argv)
383 const char **extra_argv;
385 struct poptOption popt_options[] = {
387 { "timelimit", 't', POPT_ARG_INT, &options.timelimit, 0,
388 "timelimit", "integer" },
389 { "loadfile", 'c', POPT_ARG_STRING, &options.loadfile, 0,
390 "loadfile", "filename" },
391 { "directory", 'D', POPT_ARG_STRING, &options.directory, 0,
392 "working directory", NULL },
393 { "tcp-options", 'T', POPT_ARG_STRING, &options.tcp_options, 0,
394 "TCP socket options", NULL },
395 { "target-rate", 'R', POPT_ARG_DOUBLE, &options.targetrate, 0,
396 "target throughput (MB/sec)", NULL },
397 { "sync", 's', POPT_ARG_NONE, &options.sync_open, 0,
398 "use O_SYNC", NULL },
399 { "sync-dir", 'S', POPT_ARG_NONE, &options.sync_dirs, 0,
400 "sync directory changes", NULL },
401 { "fsync", 'F', POPT_ARG_NONE, &options.do_fsync, 0,
402 "fsync on write", NULL },
403 { "xattr", 'x', POPT_ARG_NONE, &options.ea_enable, 0,
404 "use xattrs", NULL },
405 { "no-resolve", 0, POPT_ARG_NONE, &options.no_resolve, 0,
406 "disable name resolution simulation", NULL },
407 { "clients-per-process", 0, POPT_ARG_INT, &options.clients_per_process, 0,
408 "number of clients per process", 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 },
424 pc = poptGetContext(argv[0], argc, argv, popt_options, POPT_CONTEXT_KEEP_FIRST);
426 while ((opt = poptGetNextOpt(pc)) != -1) {
429 fprintf(stderr, "Invalid option %s: %s\n",
430 poptBadOption(pc, 0), poptStrerror(opt));
435 /* setup the remaining options for the main program to use */
436 extra_argv = poptGetArgs(pc);
439 while (extra_argv[extra_argc]) extra_argc++;
442 if (extra_argc < 1) {
443 printf("You need to specify NPROCS\n");
444 poptPrintHelp(pc, stdout, 0);
448 #ifndef HAVE_EA_SUPPORT
449 if (options.ea_enable) {
450 printf("EA suppport not compiled in\n");
455 options.nprocs = atoi(extra_argv[0]);
457 if (extra_argc >= 2) {
458 options.server = extra_argv[1];
466 int main(int argc, const char *argv[])
468 double total_bytes = 0;
474 printf("dbench version %s - Copyright Andrew Tridgell 1999-2004\n\n", VERSION);
476 if (!process_opts(argc, argv))
479 if (options.warmup == -1) {
480 options.warmup = options.timelimit / 5;
483 printf("Running for %d seconds with load '%s' and minimum warmup %d secs\n",
484 options.timelimit, options.loadfile, options.warmup);
486 create_procs(options.nprocs, child_run);
488 for (i=0;i<options.nprocs*options.clients_per_process;i++) {
489 total_bytes += children[i].bytes - children[i].bytes_done_warmup;
490 latency = MAX(latency, children[i].worst_latency);
493 t = timeval_elapsed2(&tv_start, &tv_end);
495 printf("Throughput %g MB/sec%s%s %d clients %d procs max_latency=%.03f ms\n",
497 options.sync_open ? " (sync open)" : "",
498 options.sync_dirs ? " (sync dirs)" : "",
499 options.nprocs*options.clients_per_process,
500 options.nprocs, latency*1000);