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