2 * Copyright (c) 2000-2001 Silicon Graphics, Inc.
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.
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.
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
25 * Run a fully automatic, random test of the directory routines.
27 * Given an input file of a list of filenames (one per line)
28 * It does a number of iterations of operations
29 * chosen pseudo-randomly in certain percentages:
31 * deleting (unlink) and
33 * on a pseudo-randomly chosen filename (from input file).
35 * The percentage thresholds for operation selection change
36 * every <number-of-names> iterations.
38 * If had 100 names then:
40 * 1-100: pct_remove = 33; pct_create = 33;
41 * 101-200: pct_remove = 60; pct_create = 20;
42 * 201-300: pct_remove = 20; pct_create = 60;
43 * 301-400: pct_remove = 33; pct_create = 33;
44 * 401-500: pct_remove = 60; pct_create = 20;
45 * 501-600: pct_remove = 20; pct_create = 60;
48 * op > (pct_remove + pct_create) => auto_lookup(ip);
49 * op > pct_remove => auto_create(ip);
50 * t => auto_remove(ip);
52 * Each iteration an op is chosen as shown above
53 * and a filename is randomly chosen.
55 * The operation is done and any error codes are
56 * verified considering whether file exists (info.exists)
57 * or not. The stat(3) call also compares inode number.
61 #define DOT_COUNT 100 /* print a '.' every X operations */
70 char *table_data; /* char string storage for info table */
72 int good_adds, good_rms, good_looks, good_tot; /* ops that suceeded */
73 int bad_adds, bad_rms, bad_looks, bad_tot; /* ops that failed */
78 int auto_lookup(struct info *);
79 int auto_create(struct info *);
80 int auto_remove(struct info *);
87 printf("usage: nametest [-l srcfile] [-i iterations] [-s seed] [-z] [-v] [-c]\n");
92 main(int argc, char *argv[])
95 int totalnames, iterations, zeroout;
96 int zone, op, pct_remove=0, pct_create=0, ch, i, retval, fd;
101 linedots = zeroout = verbose = mixcase = 0;
102 seed = (int)time(NULL) % 1000;
104 sourcefile = "input";
105 while ((ch = getopt(argc, argv, "l:i:s:zvc")) != EOF) {
107 case 'l': sourcefile = optarg; break;
108 case 's': seed = atoi(optarg); break;
109 case 'i': iterations = atoi(optarg); break;
110 case 'z': zeroout++; break;
111 case 'v': verbose++; break;
112 case 'c': mixcase++; break;
113 default: usage(); break;
118 * Read in the source file.
120 if (stat64(sourcefile, &statb) < 0) {
125 if ((table_data = malloc(statb.st_size)) == NULL) {
129 if ((fd = open(sourcefile, O_RDONLY)) < 0) {
133 if (read(fd, table_data, statb.st_size) < 0) {
140 * Allocate space for the info table and fill it in.
144 * Add up number of lines in file
145 * and replace '\n' by '\0'
148 for (c = table_data, i = 0; i < statb.st_size; c++, i++) {
155 printf("no names found in input file\n");
159 table = (struct info *)calloc(totalnames+1, sizeof(struct info));
165 * Copy over names from file (in <table_data>) into name fields
166 * of info structures in <table>.
169 ip->name = c = table_data;
170 for (i = 0; i < totalnames; ) {
180 * Check table of names.
181 * Name are of files and not commands.
183 * ??? I guess use of an input file with commands
184 * has been done before ???
185 * "touch fred" => "fred"
189 for (ip = table, i = 0; i < totalnames; ip++, i++) {
190 if (strncmp(ip->name, "touch ", strlen("touch ")) == 0) {
191 /* make name skip over "touch " string */
192 ip->name += strlen("touch ");
193 ip->namelen -= strlen("touch ");
194 } else if (strncmp(ip->name, "rm ", strlen("rm ")) == 0) {
195 printf("bad input file, \"rm\" cmds not allowed\n");
197 } else if (strncmp(ip->name, "ls ", strlen("ls ")) == 0) {
198 printf("bad input file, \"ls\" cmds not allowed\n");
204 * Run random transactions against the directory.
207 printf("Seed = %d (use \"-s %d\" to re-execute this test)\n", seed, seed);
210 for (i = 0; i < iterations; i++) {
212 * The distribution of transaction types changes over time.
213 * At first we have an equal distribution which gives us
214 * a steady state directory of 50% total size.
215 * Later, we have an unequal distribution which gives us
216 * more creates than removes, growing the directory.
217 * Later still, we have an unequal distribution which gives
218 * us more removes than creates, shrinking the directory.
220 if ((i % totalnames) == 0) {
223 case 0: pct_remove = 20; pct_create = 60; break;
224 case 1: pct_remove = 33; pct_create = 33; break;
225 case 2: pct_remove = 60; pct_create = 20; break;
230 * Choose an operation based on the current distribution.
232 ip = &table[ random() % totalnames ];
234 if (op > (pct_remove + pct_create)) {
235 retval = auto_lookup(ip);
236 } else if (op > pct_remove) {
237 retval = auto_create(ip);
239 retval = auto_remove(ip);
242 /* output '.' every DOT_COUNT ops
243 * and output '\n" every 72 dots
245 if ((i % DOT_COUNT) == 0) {
246 if (linedots++ == 72) {
256 printf("creates: %6d OK, %6d EEXIST (%6d total, %2d%% EEXIST)\n",
257 good_adds, bad_adds, good_adds + bad_adds,
259 ? (bad_adds*100) / (good_adds+bad_adds)
261 printf("removes: %6d OK, %6d ENOENT (%6d total, %2d%% ENOENT)\n",
262 good_rms, bad_rms, good_rms + bad_rms,
264 ? (bad_rms*100) / (good_rms+bad_rms)
266 printf("lookups: %6d OK, %6d ENOENT (%6d total, %2d%% ENOENT)\n",
267 good_looks, bad_looks, good_looks + bad_looks,
268 (good_looks+bad_looks)
269 ? (bad_looks*100) / (good_looks+bad_looks)
271 good_tot = good_looks + good_adds + good_rms;
272 bad_tot = bad_looks + bad_adds + bad_rms;
273 printf("total : %6d OK, %6d w/error (%6d total, %2d%% w/error)\n",
274 good_tot, bad_tot, good_tot + bad_tot,
276 ? (bad_tot*100) / (good_tot+bad_tot)
280 * If asked to clear the directory out after the run,
281 * remove everything that is left.
285 for (ip = table, i = 0; i < totalnames; ip++, i++) {
289 retval = unlink(ip->name);
291 if (errno == ENOENT) {
292 printf("\"%s\"(%llu) not removed, should have existed\n", ip->name, (unsigned long long)ip->inumber);
294 printf("\"%s\"(%llu) on remove: ", ip->name, (unsigned long long)ip->inumber);
299 if ((good_rms % DOT_COUNT) == 0) {
304 printf("\ncleanup: %6d removes\n", good_rms);
309 char *get_name(struct info *ip)
311 static char path[PATH_MAX];
317 /* pick a random character to change case in path */
318 strcpy(path, ip->name);
319 p = strrchr(path, '/');
322 p += random() % strlen(p);
331 auto_lookup(struct info *ip)
336 retval = stat64(get_name(ip), &statb);
340 if (ip->exists == 0) {
341 printf("\"%s\"(%llu) lookup, should not exist\n",
342 ip->name, (unsigned long long)statb.st_ino);
344 } else if (ip->inumber != statb.st_ino) {
345 printf("\"%s\"(%llu) lookup, should be inumber %llu\n",
346 ip->name, (unsigned long long)statb.st_ino,
347 (unsigned long long)ip->inumber);
349 } else if (verbose) {
350 printf("\"%s\"(%llu) lookup ok\n",
351 ip->name, (unsigned long long)statb.st_ino);
353 } else if (errno == ENOENT) {
356 if (ip->exists == 1) {
357 printf("\"%s\"(%llu) lookup, should exist\n",
358 ip->name, (unsigned long long)ip->inumber);
360 } else if (verbose) {
361 printf("\"%s\"(%llu) lookup ENOENT ok\n",
362 ip->name, (unsigned long long)ip->inumber);
366 printf("\"%s\"(%llu) on lookup: ",
367 ip->name, (unsigned long long)ip->inumber);
374 auto_create(struct info *ip)
379 retval = open(get_name(ip), O_RDWR|O_EXCL|O_CREAT, 0666);
384 if (stat64(ip->name, &statb) < 0) {
388 if (ip->exists == 1) {
389 printf("\"%s\"(%llu) created, but already existed as inumber %llu\n", ip->name, (unsigned long long)statb.st_ino, (unsigned long long)ip->inumber);
391 } else if (verbose) {
392 printf("\"%s\"(%llu) create new ok\n",
393 ip->name, (unsigned long long)statb.st_ino);
396 ip->inumber = statb.st_ino;
397 } else if (errno == EEXIST) {
400 if (ip->exists == 0) {
401 if (stat64(ip->name, &statb) < 0) {
405 printf("\"%s\"(%llu) not created, should not exist\n",
406 ip->name, (unsigned long long)statb.st_ino);
408 } else if (verbose) {
409 printf("\"%s\"(%llu) not created ok\n",
410 ip->name, (unsigned long long)ip->inumber);
415 printf("\"%s\"(%llu) on create: ",
416 ip->name, (unsigned long long)ip->inumber);
423 auto_remove(struct info *ip)
427 retval = unlink(get_name(ip));
431 if (ip->exists == 0) {
432 printf("\"%s\"(%llu) removed, should not have existed\n",
433 ip->name, (unsigned long long)ip->inumber);
435 } else if (verbose) {
436 printf("\"%s\"(%llu) remove ok\n",
437 ip->name, (unsigned long long)ip->inumber);
441 } else if (errno == ENOENT) {
444 if (ip->exists == 1) {
445 printf("\"%s\"(%llu) not removed, should have existed\n",
446 ip->name, (unsigned long long)ip->inumber);
448 } else if (verbose) {
449 printf("\"%s\"(%llu) not removed ok\n",
450 ip->name, (unsigned long long)ip->inumber);
455 printf("\"%s\"(%llu) on remove: ",
456 ip->name, (unsigned long long)ip->inumber);