Drop the xfs-specific MAXNAMELEN in favor of the Posix NAME_MAX
[xfstests-dev.git] / src / dirperf.c
1 /*
2  * Copyright (c) 2000-2004 Silicon Graphics, Inc.
3  * All Rights Reserved.
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License as
7  * published by the Free Software Foundation.
8  *
9  * This program is distributed in the hope that it would be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write the Free Software Foundation,
16  * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17  */
18
19 #include <sys/param.h>
20 #include <sys/stat.h>
21 #include <sys/time.h>
22 #include <dirent.h>
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <math.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <unistd.h>
30
31 #ifndef __sgi__
32 typedef unsigned int uint_t;
33 #endif
34
35 /*
36  * Loop over directory sizes:
37  *      make m directories
38  *      touch n files in each directory
39  *      stat the files round-robin
40  *      readdir/unlink the files 
41  * Change directory sizes by multiplication or addition.
42  * Allow control of starting & stopping sizes, name length, target directory.
43  * Print size and wallclock time (ms per file).
44  * Output can be used to make graphs (gnuplot)
45  */
46
47 static uint_t   addval;
48 static uint_t   dirchars;
49 static char     *directory;
50 static uint_t   firstsize;
51 static uint_t   lastsize;
52 static uint_t   minchars;
53 static double   mulval;
54 static uint_t   nchars;
55 static uint_t   ndirs;
56 static uint_t   pfxchars;
57 static uint_t   stats;
58
59 static void     filename(int, int, char *);
60 static int      hexchars(uint_t);
61 static uint_t   nextsize(uint_t);
62 static double   now(void);
63 static void     usage(void);
64
65 /*
66  * Maximum size allowed, this is pretty nuts.
67  * The largest one we've ever built has been about 2 million.
68  */
69 #define MAX_DIR_SIZE    (16 * 1024 * 1024)
70 #define DFL_FIRST_SIZE  1
71 #define DFL_LAST_SIZE   (1024 * 1024)
72 #define MAX_DIR_COUNT   1024
73 #define MIN_DIR_COUNT   1
74
75 int
76 main(int argc, char **argv)
77 {
78         int             c;
79         uint_t          cursize;
80         DIR             *dirp;
81         int             i;
82         int             j;
83         char            name[NAME_MAX + 1];
84         struct stat     stb;
85         double          stime;
86
87         while ((c = getopt(argc, argv, "a:c:d:f:l:m:n:s:")) != -1) {
88                 switch (c) {
89                 case 'a':
90                         addval = (uint_t)atoi(optarg);
91                         break;
92                 case 'c':
93                         nchars = (uint_t)atoi(optarg);
94                         break;
95                 case 'd':
96                         directory = optarg;
97                         break;
98                 case 'f':
99                         firstsize = (uint_t)atoi(optarg);
100                         break;
101                 case 'l':
102                         lastsize = (uint_t)atoi(optarg);
103                         break;
104                 case 'm':
105                         mulval = atof(optarg);
106                         break;
107                 case 'n':
108                         ndirs = (uint_t)atoi(optarg);
109                         break;
110                 case 's':
111                         stats = (uint_t)atoi(optarg);
112                         break;
113                 case '?':
114                 default:
115                         usage();
116                         exit(1);
117                 }
118         }
119         if (!addval && !mulval)
120                 mulval = 2.0;
121         else if ((addval && mulval) || mulval < 0.0) {
122                 usage();
123                 exit(1);
124         }
125         if (stats == 0)
126                 stats = 1;
127         if (!directory)
128                 directory = ".";
129         else {
130                 if (mkdir(directory, 0777) < 0 && errno != EEXIST) {
131                         perror(directory);
132                         exit(1);
133                 }
134                 if (chdir(directory) < 0) {
135                         perror(directory);
136                         exit(1);
137                 }
138         }
139         if (firstsize == 0)
140                 firstsize = DFL_FIRST_SIZE;
141         else if (firstsize > MAX_DIR_SIZE)
142                 firstsize = MAX_DIR_SIZE;
143         if (lastsize == 0)
144                 lastsize = DFL_LAST_SIZE;
145         else if (lastsize > MAX_DIR_SIZE)
146                 lastsize = MAX_DIR_SIZE;
147         if (lastsize < firstsize)
148                 lastsize = firstsize;
149         minchars = hexchars(lastsize - 1);
150         if (nchars < minchars)
151                 nchars = minchars;
152         else if (nchars >= NAME_MAX + 1)
153                 nchars = NAME_MAX;
154         if (ndirs > MAX_DIR_COUNT)
155                 ndirs = MAX_DIR_COUNT;
156         if (ndirs < MIN_DIR_COUNT)
157                 ndirs = MIN_DIR_COUNT;
158         dirchars = hexchars(ndirs);
159         pfxchars = nchars - minchars;
160         if (pfxchars)
161                 memset(&name[dirchars + 1], 'a', pfxchars);
162         for (j = 0; j < ndirs; j++) {
163                 filename(0, j, name);
164                 name[dirchars] = '\0';
165                 mkdir(name, 0777);
166         }
167         for (cursize = firstsize;
168              cursize <= lastsize;
169              cursize = nextsize(cursize)) {
170                 stime = now();
171                 for (i = 0; i < cursize; i++) {
172                         for (j = 0; j < ndirs; j++) {
173                                 filename((i + j) % cursize, j, name);
174                                 close(creat(name, 0666));
175                         }
176                 }
177                 for (i = 0; i < cursize * stats; i++) {
178                         for (j = 0; j < ndirs; j++) {
179                                 filename((i + j) % cursize, j, name);
180                                 stat(name, &stb);
181                         }
182                 }
183                 for (j = 0; j < ndirs; j++) {
184                         filename(0, j, name);
185                         name[dirchars] = '\0';
186                         dirp = opendir(name);
187                         while (readdir(dirp))
188                                 continue;
189                         closedir(dirp);
190                 }
191                 for (i = 0; i < cursize; i++) {
192                         for (j = 0; j < ndirs; j++) {
193                                 filename((i + j) % cursize, j, name);
194                                 unlink(name);
195                         }
196                 }
197                 printf("%d %.3f\n", cursize,
198                         (now() - stime) * 1.0e3 / (cursize * ndirs));
199         }
200         for (j = 0; j < ndirs; j++) {
201                 filename(0, j, name);
202                 name[dirchars] = '\0';
203                 rmdir(name);
204         }
205         return 0;
206 }
207
208 static void
209 filename(int idx, int dir, char *name)
210 {
211         static char     hexc[16] = "0123456789abcdef";
212         int             i;
213
214         for (i = dirchars - 1; i >= 0; i--)
215                 *name++ = hexc[(dir >> (4 * i)) & 0xf];
216         *name++ = '/';
217         name += pfxchars;               /* skip pfx a's */
218         for (i = minchars - 1; i >= 0; i--)
219                 *name++ = hexc[(idx >> (4 * i)) & 0xf];
220         *name = '\0';
221 }
222
223 static int
224 hexchars(uint_t maxval)
225 {
226         if (maxval < 16)
227                 return 1;
228         if (maxval < 16 * 16)
229                 return 2;
230         if (maxval < 16 * 16 * 16)
231                 return 3;
232         if (maxval < 16 * 16 * 16 * 16)
233                 return 4;
234         if (maxval < 16 * 16 * 16 * 16 * 16)
235                 return 5;
236         if (maxval < 16 * 16 * 16 * 16 * 16 * 16)
237                 return 6;
238         if (maxval < 16 * 16 * 16 * 16 * 16 * 16 * 16)
239                 return 7;
240         return 8;
241 }
242
243 static uint_t
244 nextsize(uint_t cursize)
245 {
246         double  n;
247
248         n = cursize;
249         if (addval)
250                 n += addval;
251         else
252                 n *= mulval;
253         if (n > (double)lastsize + 0.5)
254                 return lastsize + 1;    /* i.e. out of bounds */
255         else if ((uint_t)n == cursize)
256                 return cursize + 1;
257         else
258                 return (uint_t)n;
259 }
260
261 static double
262 now(void)
263 {
264         struct timeval  tv;
265
266         gettimeofday(&tv, NULL);
267         return (double)tv.tv_sec + 1.0e-6 * (double)tv.tv_usec;
268 }
269
270 static void
271 usage(void)
272 {
273         fprintf(stderr,
274                 "usage: dirperf [-d dir] [-a addstep | -m mulstep] [-f first] "
275                 "[-l last] [-c nchars] [-n ndirs] [-s nstats]\n");
276 }