fb58fb3e0fcc4b61fc3d31701bd585b6367bf8f6
[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 #define MIN_DIR_COUNT   1
87
88 int
89 main(int argc, char **argv)
90 {
91         int             c;
92         uint_t          cursize;
93         DIR             *dirp;
94         int             i;
95         int             j;
96         char            name[MAXNAMELEN];
97         struct stat     stb;
98         double          stime;
99
100         while ((c = getopt(argc, argv, "a:c:d:f:l:m:n:s:")) != -1) {
101                 switch (c) {
102                 case 'a':
103                         addval = (uint_t)atoi(optarg);
104                         break;
105                 case 'c':
106                         nchars = (uint_t)atoi(optarg);
107                         break;
108                 case 'd':
109                         dirname = optarg;
110                         break;
111                 case 'f':
112                         firstsize = (uint_t)atoi(optarg);
113                         break;
114                 case 'l':
115                         lastsize = (uint_t)atoi(optarg);
116                         break;
117                 case 'm':
118                         mulval = atof(optarg);
119                         break;
120                 case 'n':
121                         ndirs = (uint_t)atoi(optarg);
122                         break;
123                 case 's':
124                         stats = (uint_t)atoi(optarg);
125                         break;
126                 case '?':
127                 default:
128                         usage();
129                         exit(1);
130                 }
131         }
132         if (!addval && !mulval)
133                 mulval = 2.0;
134         else if ((addval && mulval) || mulval < 0.0) {
135                 usage();
136                 exit(1);
137         }
138         if (stats == 0)
139                 stats = 1;
140         if (!dirname)
141                 dirname = ".";
142         else {
143                 if (mkdir(dirname, 0777) < 0 && errno != EEXIST) {
144                         perror(dirname);
145                         exit(1);
146                 }
147                 if (chdir(dirname) < 0) {
148                         perror(dirname);
149                         exit(1);
150                 }
151         }
152         if (firstsize == 0)
153                 firstsize = DFL_FIRST_SIZE;
154         else if (firstsize > MAX_DIR_SIZE)
155                 firstsize = MAX_DIR_SIZE;
156         if (lastsize == 0)
157                 lastsize = DFL_LAST_SIZE;
158         else if (lastsize > MAX_DIR_SIZE)
159                 lastsize = MAX_DIR_SIZE;
160         if (lastsize < firstsize)
161                 lastsize = firstsize;
162         minchars = hexchars(lastsize - 1);
163         if (nchars < minchars)
164                 nchars = minchars;
165         else if (nchars >= MAXNAMELEN)
166                 nchars = MAXNAMELEN - 1;
167         if (ndirs > MAX_DIR_COUNT)
168                 ndirs = MAX_DIR_COUNT;
169         if (ndirs < MIN_DIR_COUNT)
170                 ndirs = MIN_DIR_COUNT;
171         dirchars = hexchars(ndirs);
172         pfxchars = nchars - minchars;
173         if (pfxchars)
174                 memset(&name[dirchars + 1], 'a', pfxchars);
175         for (j = 0; j < ndirs; j++) {
176                 filename(0, j, name);
177                 name[dirchars] = '\0';
178                 mkdir(name, 0777);
179         }
180         for (cursize = firstsize;
181              cursize <= lastsize;
182              cursize = nextsize(cursize)) {
183                 stime = now();
184                 for (i = 0; i < cursize; i++) {
185                         for (j = 0; j < ndirs; j++) {
186                                 filename((i + j) % cursize, j, name);
187                                 close(creat(name, 0666));
188                         }
189                 }
190                 for (i = 0; i < cursize * stats; i++) {
191                         for (j = 0; j < ndirs; j++) {
192                                 filename((i + j) % cursize, j, name);
193                                 stat(name, &stb);
194                         }
195                 }
196                 for (j = 0; j < ndirs; j++) {
197                         filename(0, j, name);
198                         name[dirchars] = '\0';
199                         dirp = opendir(name);
200                         while (readdir(dirp))
201                                 continue;
202                         closedir(dirp);
203                 }
204                 for (i = 0; i < cursize; i++) {
205                         for (j = 0; j < ndirs; j++) {
206                                 filename((i + j) % cursize, j, name);
207                                 unlink(name);
208                         }
209                 }
210                 printf("%d %.3f\n", cursize,
211                         (now() - stime) * 1.0e3 / (cursize * ndirs));
212         }
213         for (j = 0; j < ndirs; j++) {
214                 filename(0, j, name);
215                 name[dirchars] = '\0';
216                 rmdir(name);
217         }
218         return 0;
219 }
220
221 static void
222 filename(int idx, int dir, char *name)
223 {
224         static char     hexc[16] = "0123456789abcdef";
225         int             i;
226
227         for (i = dirchars - 1; i >= 0; i--)
228                 *name++ = hexc[(dir >> (4 * i)) & 0xf];
229         *name++ = '/';
230         name += pfxchars;               /* skip pfx a's */
231         for (i = minchars - 1; i >= 0; i--)
232                 *name++ = hexc[(idx >> (4 * i)) & 0xf];
233         *name = '\0';
234 }
235
236 static int
237 hexchars(uint_t maxval)
238 {
239         if (maxval < 16)
240                 return 1;
241         if (maxval < 16 * 16)
242                 return 2;
243         if (maxval < 16 * 16 * 16)
244                 return 3;
245         if (maxval < 16 * 16 * 16 * 16)
246                 return 4;
247         if (maxval < 16 * 16 * 16 * 16 * 16)
248                 return 5;
249         if (maxval < 16 * 16 * 16 * 16 * 16 * 16)
250                 return 6;
251         if (maxval < 16 * 16 * 16 * 16 * 16 * 16 * 16)
252                 return 7;
253         return 8;
254 }
255
256 static uint_t
257 nextsize(uint_t cursize)
258 {
259         double  n;
260
261         n = cursize;
262         if (addval)
263                 n += addval;
264         else
265                 n *= mulval;
266         if (n > (double)lastsize + 0.5)
267                 return lastsize + 1;    /* i.e. out of bounds */
268         else if ((uint_t)n == cursize)
269                 return cursize + 1;
270         else
271                 return (uint_t)n;
272 }
273
274 static double
275 now(void)
276 {
277         struct timeval  tv;
278
279         gettimeofday(&tv, NULL);
280         return (double)tv.tv_sec + 1.0e-6 * (double)tv.tv_usec;
281 }
282
283 static void
284 usage(void)
285 {
286         fprintf(stderr,
287                 "usage: dirperf [-d dir] [-a addstep | -m mulstep] [-f first] "
288                 "[-l last] [-c nchars] [-n ndirs] [-s nstats]\n");
289 }