/* $Id: fsperf1.c,v 1.6 2004/07/15 18:21:19 ca Exp $ */
#include <stdarg.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <time.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sysexits.h>
#define PERLEVEL 100
#define FNAMEFMT0 "%06d"
#define FNAMEFMT1 "%02d/%06d"
#define FNAMEFMT2 "%02d/%02d/%06d"
#define DNAMEFMT1 "%02d"
#define DNAMEFMT2 "%02d/%02d"
#if BUFSIZ < 16
# error BUFSIZ too small
#endif
#define FSP_SIZE 2
#define FSP_LINES 400
#define FSP_LEVEL 0
static void
fatal(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
fprintf(stderr, "\n");
va_end(ap);
exit(1);
}
static void
filename(char *fn, int n, int level)
{
switch(level)
{
case 0:
sprintf(fn, FNAMEFMT0, n);
break;
case 1:
sprintf(fn, FNAMEFMT1, n % PERLEVEL, n);
break;
case 2:
sprintf(fn, FNAMEFMT2, (n / PERLEVEL) % PERLEVEL, n % PERLEVEL,
n);
break;
default:
fatal("filename: wrong level %d", level);
/* NOTREACHED */
}
}
/* rename_file - rename a file */
static void
rename_file(int old, int new, int level)
{
char new_path[BUFSIZ];
char old_path[BUFSIZ];
filename(new_path, new, level);
filename(old_path, old, level);
/* printf("/: %s -> %s\n", old_path, new_path); */
if (rename(old_path, new_path))
fatal("rename %s to %s: %d", old_path, new_path, errno);
}
/* make_file - create a little file and use it */
static void make_file(int seqno, int size, int level, int syncit)
{
char path[BUFSIZ];
char buf[1024];
FILE *fp;
int i;
filename(path, seqno, level);
/* printf("+: %s\n", path); */
if ((fp = fopen(path, "w")) == 0)
fatal("open(%s): %d", path, errno);
memset(buf, 'x', sizeof(buf));
for (i = 0; i < size; i++)
if (fwrite(buf, sizeof(buf), 1, fp) != 1)
fatal("fwrite: %d", errno);
if (syncit && fsync(fileno(fp)))
fatal("fsync(%s): %d", path, errno);
if (fclose(fp))
fatal("fclose(%s): %d", path, errno);
if ((fp = fopen(path, "r")) == 0)
fatal("open(%s): %d", path, errno);
while (fgets(path, sizeof(path), fp))
/* void */ ;
if (fclose(fp))
fatal("fclose(%s): %d", path, errno);
}
/* use_file - use existing file */
static void
use_file(int seqno, int size, int level)
{
char path[BUFSIZ];
FILE *fp;
int i;
filename(path, seqno, level);
if ((fp = fopen(path, "w")) == NULL)
fatal("open %s: %d", path, errno);
for (i = 0; i < size; i++)
fprintf(fp, "hello %d", i);
if (fsync(fileno(fp)))
fatal("fsync: %d", errno);
if (fclose(fp))
fatal("fclose: %d", errno);
if ((fp = fopen(path, "r+")) == NULL)
fatal("open %s: %d", path, errno);
while (fgets(path, sizeof(path), fp) != NULL)
/* void */ ;
if (ftruncate(fileno(fp), (off_t) 0))
fatal("ftruncate: %d", errno);
if (fclose(fp))
fatal("fclose: %d", errno);
}
/* remove_file - delete specified file */
static void remove_file(int n, int level)
{
char path[BUFSIZ];
filename(path, n, level);
/* printf("-: %s\n", path); */
if (remove(path))
fatal("remove %s: %d", path, errno);
}
/* remove_silent - delete specified file, silently */
static void remove_silent(int n, int level)
{
char path[BUFSIZ];
filename(path, n, level);
(void) remove(path);
}
/* usage - explain */
static void usage(char *myname)
{
fprintf(stderr, "usage: %s [-cr] [-C concurrency] [-h hash] [-l lines] [-s size] messages directory_entries\n",
myname);
fprintf(stderr, " -C n keep n messages open concurrently\n");
fprintf(stderr, " -c create/delete file\n");
fprintf(stderr, " -h hashlevel 0, 1, 2: number of directory levels\n");
fprintf(stderr, " (default: %d)\n", FSP_LEVEL);
fprintf(stderr, " -l lines lines for file to use (default: %d)\n",
FSP_LINES);
fprintf(stderr, " -p populate directory with files before start\n");
fprintf(stderr, " -r rename file twice\n");
fprintf(stderr, " -s size size of file [KB] (default: %d)\n",
FSP_SIZE);
fprintf(stderr, " -S do not use fscync(2)\n");
#ifdef EX_USAGE
exit(EX_USAGE);
#else
exit(1);
#endif
}
int
main(int argc, char **argv)
{
int op_count;
int max_file;
time_t start;
int do_rename = 0;
int do_create = 0;
int syncit = 1;
int populate = 0;
int seq, h;
int o, n;
int ch;
int size = FSP_SIZE;
int lines = FSP_LINES;
int level = FSP_LEVEL;
int concurrent = 0;
char path[BUFSIZ];
while ((ch = getopt(argc, argv, "C:ch:l:prs:S")) != EOF)
{
switch (ch)
{
case 'C':
if ((concurrent = atoi(optarg)) < 0)
usage(argv[0]);
break;
case 'c':
do_create = 1;
break;
case 'h':
if ((level = atoi(optarg)) < 0)
usage(argv[0]);
break;
case 'l':
if ((lines = atoi(optarg)) <= 0)
usage(argv[0]);
break;
case 'p':
populate = 1;
break;
case 'r':
do_rename = 1;
break;
case 'S':
syncit = 0;
break;
case 's':
if ((size = atoi(optarg)) <= 0)
usage(argv[0]);
break;
default:
usage(argv[0]);
}
}
if (argc != optind + 2 || (do_rename && !do_create))
usage(argv[0]);
if ((op_count = atoi(argv[optind])) <= 0)
usage(argv[0]);
if ((max_file = atoi(argv[optind + 1])) <= 0)
usage(argv[0]);
if (concurrent >= max_file)
usage(argv[0]);
if (op_count < max_file)
usage(argv[0]);
if (level > 0)
{
for (seq = 0; seq < PERLEVEL; seq++)
{
sprintf(path, DNAMEFMT1, seq);
if (mkdir(path, 0750))
fatal("mkdir(%s): %d", path, errno);
}
}
if (level > 1)
{
for (h = 0; h < PERLEVEL; h++)
{
for (seq = 0; seq < PERLEVEL; seq++)
{
sprintf(path, DNAMEFMT2, h, seq);
if (mkdir(path, 0750))
fatal("mkdir(%s): %d", path, errno);
}
}
}
/* Populate the directory with little files. */
if (populate)
for (seq = 0; seq < max_file; seq++)
make_file(seq, size, level, syncit);
/* Simulate arrival and delivery of mail messages. */
seq = 0;
start = time((time_t *) 0);
if (concurrent > 0 && do_create)
{
for (h = 0; h < concurrent; h++)
{
seq %= max_file;
make_file(seq, size, level, syncit);
seq++;
}
}
while (op_count > 0)
{
seq %= max_file;
if (do_create)
{
make_file(seq, size, level, syncit);
o = seq - concurrent;
if (o < 0)
o += max_file;
if (do_rename)
{
n = o + max_file;
rename_file(o, n, level);
rename_file(n, o, level);
}
remove_file(o, level);
}
else
{
use_file(seq, lines, level);
}
seq++;
op_count--;
}
if (concurrent > 0 && do_create)
{
for (h = 0; h < concurrent; h++)
{
o = seq - concurrent;
if (o < 0)
o += max_file;
remove_file(o, level);
seq++;
}
}
printf("elapsed time: %ld\n", (long) time((time_t *) 0) - start);
/* Clean up directory fillers. */
if (populate || !do_create)
for (seq = 0; seq < max_file; seq++)
remove_silent(seq, level);
if (level > 1)
{
for (h = 0; h < PERLEVEL; h++)
{
for (seq = 0; seq < PERLEVEL; seq++)
{
sprintf(path, DNAMEFMT2, h, seq);
(void) rmdir(path);
}
}
}
if (level > 0)
{
for (seq = 0; seq < PERLEVEL; seq++)
{
sprintf(path, DNAMEFMT1, seq);
(void) rmdir(path);
}
}
return (0);
}
syntax highlighted by Code2HTML, v. 0.9.1