--- /dev/null
+/*
+ * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define MAXNAMELEN 256
+typedef unsigned int uint_t;
+
+/*
+ * Loop over directory sizes:
+ * make m directories
+ * touch n files in each directory
+ * stat the files round-robin
+ * readdir/unlink the files
+ * Change directory sizes by multiplication or addition.
+ * Allow control of starting & stopping sizes, name length, target directory.
+ * Print size and wallclock time (ms per file).
+ * Output can be used to make graphs (gnuplot)
+ */
+
+static uint_t addval;
+static uint_t dirchars;
+static char *dirname;
+static uint_t firstsize;
+static uint_t lastsize;
+static uint_t minchars;
+static double mulval;
+static uint_t nchars;
+static uint_t ndirs;
+static uint_t pfxchars;
+static uint_t stats;
+
+static void filename(int, int, char *);
+static int hexchars(uint_t);
+static uint_t nextsize(uint_t);
+static double now(void);
+static void usage(void);
+
+/*
+ * Maximum size allowed, this is pretty nuts.
+ * The largest one we've ever built has been about 2 million.
+ */
+#define MAX_DIR_SIZE (16 * 1024 * 1024)
+#define DFL_FIRST_SIZE 1
+#define DFL_LAST_SIZE (1024 * 1024)
+#define MAX_DIR_COUNT 1024
+
+int
+main(int argc, char **argv)
+{
+ int c;
+ uint_t cursize;
+ DIR *dirp;
+ int i;
+ int j;
+ char name[MAXNAMELEN];
+ struct stat stb;
+ double stime;
+
+ while ((c = getopt(argc, argv, "a:c:d:f:l:m:n:s:")) != -1) {
+ switch (c) {
+ case 'a':
+ addval = (uint_t)atoi(optarg);
+ break;
+ case 'c':
+ nchars = (uint_t)atoi(optarg);
+ break;
+ case 'd':
+ dirname = optarg;
+ break;
+ case 'f':
+ firstsize = (uint_t)atoi(optarg);
+ break;
+ case 'l':
+ lastsize = (uint_t)atoi(optarg);
+ break;
+ case 'm':
+ mulval = atof(optarg);
+ break;
+ case 'n':
+ ndirs = (uint_t)atoi(optarg);
+ break;
+ case 's':
+ stats = (uint_t)atoi(optarg);
+ break;
+ case '?':
+ default:
+ usage();
+ exit(1);
+ }
+ }
+ if (!addval && !mulval)
+ mulval = 2.0;
+ else if ((addval && mulval) || mulval < 0.0) {
+ usage();
+ exit(1);
+ }
+ if (stats == 0)
+ stats = 1;
+ if (!dirname)
+ dirname = ".";
+ else {
+ if (mkdir(dirname, 0777) < 0 && errno != EEXIST) {
+ perror(dirname);
+ exit(1);
+ }
+ if (chdir(dirname) < 0) {
+ perror(dirname);
+ exit(1);
+ }
+ }
+ if (firstsize == 0)
+ firstsize = DFL_FIRST_SIZE;
+ else if (firstsize > MAX_DIR_SIZE)
+ firstsize = MAX_DIR_SIZE;
+ if (lastsize == 0)
+ lastsize = DFL_LAST_SIZE;
+ else if (lastsize > MAX_DIR_SIZE)
+ lastsize = MAX_DIR_SIZE;
+ if (lastsize < firstsize)
+ lastsize = firstsize;
+ minchars = hexchars(lastsize - 1);
+ if (nchars < minchars)
+ nchars = minchars;
+ else if (nchars >= MAXNAMELEN)
+ nchars = MAXNAMELEN - 1;
+ if (ndirs > MAX_DIR_COUNT)
+ ndirs = MAX_DIR_COUNT;
+ dirchars = hexchars(ndirs);
+ pfxchars = nchars - minchars;
+ if (pfxchars)
+ memset(&name[dirchars + 1], 'a', pfxchars);
+ for (j = 0; j < ndirs; j++) {
+ filename(0, j, name);
+ name[dirchars] = '\0';
+ mkdir(name, 0777);
+ }
+ for (cursize = firstsize;
+ cursize <= lastsize;
+ cursize = nextsize(cursize)) {
+ stime = now();
+ for (i = 0; i < cursize; i++) {
+ for (j = 0; j < ndirs; j++) {
+ filename((i + j) % cursize, j, name);
+ close(creat(name, 0666));
+ }
+ }
+ for (i = 0; i < cursize * stats; i++) {
+ for (j = 0; j < ndirs; j++) {
+ filename((i + j) % cursize, j, name);
+ stat(name, &stb);
+ }
+ }
+ for (j = 0; j < ndirs; j++) {
+ filename(0, j, name);
+ name[dirchars] = '\0';
+ dirp = opendir(name);
+ while (readdir(dirp))
+ continue;
+ closedir(dirp);
+ }
+ for (i = 0; i < cursize; i++) {
+ for (j = 0; j < ndirs; j++) {
+ filename((i + j) % cursize, j, name);
+ unlink(name);
+ }
+ }
+ printf("%d %.3f\n", cursize,
+ (now() - stime) * 1.0e3 / (cursize * ndirs));
+ }
+ for (j = 0; j < ndirs; j++) {
+ filename(0, j, name);
+ name[dirchars] = '\0';
+ rmdir(name);
+ }
+ return 0;
+}
+
+static void
+filename(int idx, int dir, char *name)
+{
+ static char hexc[16] = "0123456789abcdef";
+ int i;
+
+ for (i = dirchars - 1; i >= 0; i--)
+ *name++ = hexc[(dir >> (4 * i)) & 0xf];
+ *name++ = '/';
+ name += pfxchars; /* skip pfx a's */
+ for (i = minchars - 1; i >= 0; i--)
+ *name++ = hexc[(idx >> (4 * i)) & 0xf];
+ *name = '\0';
+}
+
+static int
+hexchars(uint_t maxval)
+{
+ if (maxval < 16)
+ return 1;
+ if (maxval < 16 * 16)
+ return 2;
+ if (maxval < 16 * 16 * 16)
+ return 3;
+ if (maxval < 16 * 16 * 16 * 16)
+ return 4;
+ if (maxval < 16 * 16 * 16 * 16 * 16)
+ return 5;
+ if (maxval < 16 * 16 * 16 * 16 * 16 * 16)
+ return 6;
+ if (maxval < 16 * 16 * 16 * 16 * 16 * 16 * 16)
+ return 7;
+ return 8;
+}
+
+static uint_t
+nextsize(uint_t cursize)
+{
+ double n;
+
+ n = cursize;
+ if (addval)
+ n += addval;
+ else
+ n *= mulval;
+ if (n > (double)lastsize + 0.5)
+ return lastsize + 1; /* i.e. out of bounds */
+ else if ((uint_t)n == cursize)
+ return cursize + 1;
+ else
+ return (uint_t)n;
+}
+
+static double
+now(void)
+{
+ struct timeval tv;
+
+ gettimeofday(&tv, NULL);
+ return (double)tv.tv_sec + 1.0e-6 * (double)tv.tv_usec;
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr,
+ "usage: dirperf [-d dir] [-a addstep | -m mulstep] [-f first] "
+ "[-l last] [-c nchars] [-n ndirs] [-s nstats]\n");
+}
--- /dev/null
+/*
+ * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifdef HAVE_GETDENTS
+#include <sys/dirent.h>
+#endif
+
+typedef void *(*fpi_t)(void);
+typedef void (*fpt_t)(int, void *);
+typedef void (*fpd_t)(void *);
+typedef struct tdesc
+{
+ char *name;
+ fpi_t init;
+ fpt_t test;
+ fpd_t done;
+} tdesc_t;
+
+#ifdef HAVE_GETDENTS
+static void d_getdents(void *);
+static void *i_getdents(void);
+static void t_getdents(int, void *);
+#endif
+static void crfiles(char **, int, char *);
+static void d_chown(void *);
+static void d_create(void *);
+static void d_linkun(void *);
+static void d_open(void *);
+static void d_rename(void *);
+static void d_stat(void *);
+static void delflist(char **);
+static void dotest(tdesc_t *);
+static void *i_chown(void);
+static void *i_create(void);
+static void *i_linkun(void);
+static void *i_open(void);
+static void *i_rename(void);
+static void *i_stat(void);
+static char **mkflist(int, int, char);
+static double now(void);
+static void prtime(char *, int, double);
+static void rmfiles(char **);
+static void t_chown(int, void *);
+static void t_create(int, void *);
+static void t_crunlink(int, void *);
+static void t_linkun(int, void *);
+static void t_open(int, void *);
+static void t_rename(int, void *);
+static void t_stat(int, void *);
+static void usage(void);
+
+tdesc_t tests[] = {
+ { "chown", i_chown, t_chown, d_chown },
+ { "create", i_create, t_create, d_create },
+ { "crunlink", (fpi_t)0, t_crunlink, (fpd_t)0 },
+#ifdef HAVE_GETDENTS
+ { "getdents", i_getdents, t_getdents, d_getdents },
+#endif
+ { "linkun", i_linkun, t_linkun, d_linkun },
+ { "open", i_open, t_open, d_open },
+ { "rename", i_rename, t_rename, d_rename },
+ { "stat", i_stat, t_stat, d_stat },
+ { NULL }
+};
+
+char *buffer;
+int compact = 0;
+int files_bg = 0;
+int files_op = 1;
+char **flist_bg;
+char **flist_op;
+int fnlen_bg = 5;
+int fnlen_op = 5;
+int fsize = 0;
+int iters = 0;
+double time_end;
+double time_start;
+int totsec = 0;
+int verbose = 0;
+
+struct getdents_data
+{
+ void *buffer;
+ int buflen;
+ int fd;
+};
+
+int
+main(int argc, char **argv)
+{
+ int c;
+ char *testdir;
+ tdesc_t *tp;
+
+ testdir = getenv("TMPDIR");
+ if (testdir == NULL)
+ testdir = ".";
+ while ((c = getopt(argc, argv, "cd:i:l:L:n:N:s:t:v")) != -1) {
+ switch (c) {
+ case 'c':
+ compact = 1;
+ break;
+ case 'd':
+ testdir = optarg;
+ break;
+ case 'i':
+ iters = atoi(optarg);
+ break;
+ case 'l':
+ fnlen_op = atoi(optarg);
+ break;
+ case 'L':
+ fnlen_bg = atoi(optarg);
+ break;
+ case 'n':
+ files_op = atoi(optarg);
+ break;
+ case 'N':
+ files_bg = atoi(optarg);
+ break;
+ case 's':
+ fsize = atoi(optarg);
+ break;
+ case 't':
+ totsec = atoi(optarg);
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case '?':
+ fprintf(stderr, "bad option\n");
+ usage();
+ }
+ }
+ if (!iters && !totsec)
+ iters = 1;
+ if (chdir(testdir) < 0) {
+ perror(testdir);
+ return 1;
+ }
+ if (mkdir("metaperf", 0777) < 0 || chdir("metaperf") < 0) {
+ perror("metaperf");
+ return 1;
+ }
+ for (; optind < argc; optind++) {
+ for (tp = tests; tp->name; tp++) {
+ if (strcmp(argv[optind], tp->name) == 0) {
+ dotest(tp);
+ break;
+ }
+ }
+ }
+ chdir("..");
+ rmdir("metaperf");
+ return 0;
+}
+
+static void
+crfiles(char **flist, int fsize, char *buf)
+{
+ int fd;
+ char **fnp;
+
+ for (fnp = flist; *fnp; fnp++) {
+ fd = creat(*fnp, 0666);
+ if (fsize)
+ write(fd, buf, fsize);
+ close(fd);
+ }
+}
+
+/* ARGSUSED */
+static void
+d_chown(void *v)
+{
+ rmfiles(flist_op);
+}
+
+/* ARGSUSED */
+static void
+d_create(void *v)
+{
+ rmfiles(flist_op);
+}
+
+#ifdef HAVE_GETDENTS
+static void
+d_getdents(void *v)
+{
+ struct getdents_data *g;
+
+ rmfiles(flist_op);
+ g = v;
+ close(g->fd);
+ free(g->buffer);
+ free(g);
+}
+#endif
+
+/* ARGSUSED */
+static void
+d_linkun(void *v)
+{
+ unlink("a");
+}
+
+/* ARGSUSED */
+static void
+d_open(void *v)
+{
+ rmfiles(flist_op);
+}
+
+static void
+d_rename(void *v)
+{
+ rmfiles(flist_op);
+ rmfiles((char **)v);
+ delflist((char **)v);
+}
+
+/* ARGSUSED */
+static void
+d_stat(void *v)
+{
+ rmfiles(flist_op);
+}
+
+static void
+delflist(char **flist)
+{
+ char **fnp;
+
+ for (fnp = flist; *fnp; fnp++)
+ free(*fnp);
+ free(flist);
+}
+
+static void
+dotest(tdesc_t *tp)
+{
+ double dn;
+ double gotsec;
+ int n;
+ void *v;
+
+ flist_bg = mkflist(files_bg, fnlen_bg, 'b');
+ flist_op = mkflist(files_op, fnlen_op, 'o');
+ if (fsize)
+ buffer = calloc(fsize, 1);
+ else
+ buffer = NULL;
+ crfiles(flist_bg, 0, (char *)0);
+ n = iters ? iters : 1;
+ v = (void *)0;
+ for (;;) {
+ if (tp->init)
+ v = (tp->init)();
+ sync();
+ sleep(1);
+ time_start = now();
+ (tp->test)(n, v);
+ time_end = now();
+ if (tp->done)
+ (tp->done)(v);
+ gotsec = time_end - time_start;
+ if (!totsec || gotsec >= 0.9 * totsec)
+ break;
+ if (verbose)
+ prtime(tp->name, n, gotsec);
+ if (!gotsec)
+ gotsec = 1.0 / (2 * HZ);
+ if (gotsec < 0.001 * totsec)
+ dn = n * (0.01 * totsec / gotsec);
+ else if (gotsec < 0.01 * totsec)
+ dn = n * (0.1 * totsec / gotsec);
+ else
+ dn = n * (totsec / gotsec);
+ if ((int)dn <= n)
+ n++;
+ else
+ n = (int)dn;
+ }
+ prtime(tp->name, n, gotsec);
+ rmfiles(flist_bg);
+ delflist(flist_bg);
+ delflist(flist_op);
+ if (fsize)
+ free(buffer);
+}
+
+static void *
+i_chown(void)
+{
+ char **fnp;
+
+ crfiles(flist_op, 0, (char *)0);
+ for (fnp = flist_op; *fnp; fnp++)
+ chown(*fnp, 1, -1);
+ return (void *)0;
+}
+
+static void *
+i_create(void)
+{
+ crfiles(flist_op, fsize, buffer);
+ return (void *)0;
+}
+
+#ifdef HAVE_GETDENTS
+static void *
+i_getdents(void)
+{
+ struct getdents_data *g;
+
+ crfiles(flist_op, 0, (char *)0);
+ g = malloc(sizeof(*g));
+ g->buflen = 16 * 1024;
+ g->buffer = malloc(g->buflen);
+ g->fd = open(".", O_RDONLY);
+ return g;
+}
+#endif
+
+static void *
+i_linkun(void)
+{
+ close(creat("a", 0666));
+ return (void *)0;
+}
+
+static void *
+i_open(void)
+{
+ crfiles(flist_op, 0, (char *)0);
+ return (void *)0;
+}
+
+static void *
+i_rename(void)
+{
+ crfiles(flist_op, 0, (char *)0);
+ return (void *)mkflist(files_op, fnlen_op, 'r');
+}
+
+static void *
+i_stat(void)
+{
+ crfiles(flist_op, 0, (char *)0);
+ return (void *)0;
+}
+
+static char **
+mkflist(int files, int fnlen, char start)
+{
+ int i;
+ char **rval;
+
+ rval = calloc(files + 1, sizeof(char *));
+ for (i = 0; i < files; i++) {
+ rval[i] = malloc(fnlen + 1);
+ sprintf(rval[i], "%0*d%c", fnlen - 1, i, start);
+ }
+ return rval;
+}
+
+static double
+now(void)
+{
+ struct timeval t;
+
+ gettimeofday(&t, (void *)0);
+ return (double)t.tv_sec + 1.0e-6 * (double)t.tv_usec;
+}
+
+static void
+prtime(char *name, int n, double t)
+{
+ double ops_per_sec;
+ double usec_per_op;
+
+ ops_per_sec = (double)n * (double)files_op / t;
+ usec_per_op = t * 1.0e6 / ((double)n * (double)files_op);
+ if (compact)
+ printf("%s %d %d %d %d %d %d %f %f %f\n",
+ name, n, files_op, fnlen_op, fsize, files_bg, fnlen_bg,
+ t, ops_per_sec, usec_per_op);
+ else {
+ printf("%s: %d times, %d file(s) namelen %d",
+ name, n, files_op, fnlen_op);
+ if (fsize)
+ printf(" size %d", fsize);
+ if (files_bg)
+ printf(", bg %d file(s) namelen %d",
+ files_bg, fnlen_bg);
+ printf(", time = %f sec, ops/sec=%f, usec/op = %f\n",
+ t, ops_per_sec, usec_per_op);
+ }
+}
+
+static void
+rmfiles(char **flist)
+{
+ char **fnp;
+
+ for (fnp = flist; *fnp; fnp++)
+ unlink(*fnp);
+}
+
+/* ARGSUSED */
+static void
+t_chown(int n, void *v)
+{
+ char **fnp;
+ int i;
+
+ for (i = 0; i < n; i++) {
+ for (fnp = flist_op; *fnp; fnp++) {
+ if ((i & 1) == 0)
+ chown(*fnp, 2, -1);
+ else
+ chown(*fnp, 1, -1);
+ }
+ }
+}
+
+/* ARGSUSED */
+static void
+t_create(int n, void *v)
+{
+ int i;
+
+ for (i = 0; i < n; i++)
+ crfiles(flist_op, fsize, buffer);
+}
+
+/* ARGSUSED */
+static void
+t_crunlink(int n, void *v)
+{
+ int i;
+
+ for (i = 0; i < n; i++) {
+ crfiles(flist_op, fsize, buffer);
+ rmfiles(flist_op);
+ }
+}
+
+#ifdef HAVE_GETDENTS
+static void
+t_getdents(int n, void *v)
+{
+ int eof;
+ struct getdents_data *g;
+ int i;
+ int j;
+
+ for (g = v, i = 0; i < n; i++) {
+ (void)lseek(g->fd, 0, SEEK_SET);
+ eof = 0;
+ do {
+ j = ngetdents(g->fd, (dirent_t *)g->buffer, g->buflen,
+ &eof);
+ } while (j > 0 && eof == 0);
+ }
+}
+#endif
+
+/* ARGSUSED */
+static void
+t_linkun(int n, void *v)
+{
+ char **fnp;
+ int i;
+
+ for (i = 0; i < n; i++) {
+ for (fnp = flist_op; *fnp; fnp++)
+ link("a", *fnp);
+ rmfiles(flist_op);
+ }
+}
+
+/* ARGSUSED */
+static void
+t_open(int n, void *v)
+{
+ char **fnp;
+ int i;
+
+ for (i = 0; i < n; i++) {
+ for (fnp = flist_op; *fnp; fnp++)
+ close(open(*fnp, O_RDWR));
+ }
+}
+
+static void
+t_rename(int n, void *v)
+{
+ char **fnp;
+ int i;
+ char **rflist;
+ char **rfp;
+
+ for (rflist = (char **)v, i = 0; i < n; i++) {
+ for (fnp = flist_op, rfp = rflist; *fnp; fnp++, rfp++) {
+ if ((i & 1) == 0)
+ rename(*fnp, *rfp);
+ else
+ rename(*rfp, *fnp);
+ }
+ }
+}
+
+/* ARGSUSED */
+static void
+t_stat(int n, void *v)
+{
+ char **fnp;
+ int i;
+ struct stat stb;
+
+ for (i = 0; i < n; i++) {
+ for (fnp = flist_op; *fnp; fnp++)
+ stat(*fnp, &stb);
+ }
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr,
+ "usage: metaperf [-d dname] [-i iters|-t seconds] [-s fsize]\n\t[-l opfnamelen] [-L bgfnamelen]\n\t[-n opfcount] [-N bgfcount] test...\n");
+ fprintf(stderr,
+ "tests available: chown, create, crunlink, linkun, open, rename, stat\n");
+ exit(1);
+}