/* $Id: fsperf1.c,v 1.6 2004/07/15 18:21:19 ca Exp $ */ #include #include #include #include #include #include #include #include #include #include #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); }