2 * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
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.
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.
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.
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.
23 * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
24 * Mountain View, CA 94043, or:
28 * For further information regarding this notice, see:
30 * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
38 * Run a fully automatic, random test of the directory routines.
40 * Given an input file of a list of filenames (one per line)
41 * It does a number of iterations of operations
42 * chosen pseudo-randomly in certain percentages:
44 * deleting (unlink) and
46 * on a pseudo-randomly chosen filename (from input file).
48 * The percentage thresholds for operation selection change
49 * every <number-of-names> iterations.
51 * If had 100 names then:
53 * 1-100: pct_remove = 33; pct_create = 33;
54 * 101-200: pct_remove = 60; pct_create = 20;
55 * 201-300: pct_remove = 20; pct_create = 60;
56 * 301-400: pct_remove = 33; pct_create = 33;
57 * 401-500: pct_remove = 60; pct_create = 20;
58 * 501-600: pct_remove = 20; pct_create = 60;
61 * op > (pct_remove + pct_create) => auto_lookup(ip);
62 * op > pct_remove => auto_create(ip);
63 * t => auto_remove(ip);
65 * Each iteration an op is chosen as shown above
66 * and a filename is randomly chosen.
68 * The operation is done and any error codes are
69 * verified considering whether file exists (info.exists)
70 * or not. The stat(3) call also compares inode number.
74 #define DOT_COUNT 100 /* print a '.' every X operations */
83 char *table_data; /* char string storage for info table */
85 int good_adds, good_rms, good_looks, good_tot; /* ops that suceeded */
86 int bad_adds, bad_rms, bad_looks, bad_tot; /* ops that failed */
90 int auto_lookup(struct info *);
91 int auto_create(struct info *);
92 int auto_remove(struct info *);
99 printf("usage: nametest [-l srcfile] [-i iterations] [-s seed] [-z] [-v]\n");
104 main(int argc, char *argv[])
106 char *sourcefile, *c;
107 int totalnames, iterations, zeroout;
108 int zone, op, pct_remove, pct_create, ch, i, retval, fd;
113 linedots = zeroout = verbose = 0;
114 seed = (int)time(NULL) % 1000;
116 sourcefile = "input";
117 while ((ch = getopt(argc, argv, "l:i:s:zv")) != EOF) {
119 case 'l': sourcefile = optarg; break;
120 case 's': seed = atoi(optarg); break;
121 case 'i': iterations = atoi(optarg); break;
122 case 'z': zeroout++; break;
123 case 'v': verbose++; break;
124 default: usage(); break;
129 * Read in the source file.
131 if (stat64(sourcefile, &statb) < 0) {
136 if ((table_data = malloc(statb.st_size)) == NULL) {
140 if ((fd = open(sourcefile, O_RDONLY)) < 0) {
144 if (read(fd, table_data, statb.st_size) < 0) {
151 * Allocate space for the info table and fill it in.
155 * Add up number of lines in file
156 * and replace '\n' by '\0'
159 for (c = table_data, i = 0; i < statb.st_size; c++, i++) {
166 printf("no names found in input file\n");
170 table = (struct info *)calloc(totalnames+1, sizeof(struct info));
176 * Copy over names from file (in <table_data>) into name fields
177 * of info structures in <table>.
180 ip->name = c = table_data;
181 for (i = 0; i < totalnames; ) {
191 * Check table of names.
192 * Name are of files and not commands.
194 * ??? I guess use of an input file with commands
195 * has been done before ???
196 * "touch fred" => "fred"
200 for (ip = table, i = 0; i < totalnames; ip++, i++) {
201 if (strncmp(ip->name, "touch ", strlen("touch ")) == 0) {
202 /* make name skip over "touch " string */
203 ip->name += strlen("touch ");
204 ip->namelen -= strlen("touch ");
205 } else if (strncmp(ip->name, "rm ", strlen("rm ")) == 0) {
206 printf("bad input file, \"rm\" cmds not allowed\n");
208 } else if (strncmp(ip->name, "ls ", strlen("ls ")) == 0) {
209 printf("bad input file, \"ls\" cmds not allowed\n");
215 * Run random transactions against the directory.
218 printf("Seed = %d (use \"-s %d\" to re-execute this test)\n", seed, seed);
221 for (i = 0; i < iterations; i++) {
223 * The distribution of transaction types changes over time.
224 * At first we have an equal distribution which gives us
225 * a steady state directory of 50% total size.
226 * Later, we have an unequal distribution which gives us
227 * more creates than removes, growing the directory.
228 * Later still, we have an unequal distribution which gives
229 * us more removes than creates, shrinking the directory.
231 if ((i % totalnames) == 0) {
234 case 0: pct_remove = 20; pct_create = 60; break;
235 case 1: pct_remove = 33; pct_create = 33; break;
236 case 2: pct_remove = 60; pct_create = 20; break;
241 * Choose an operation based on the current distribution.
243 ip = &table[ random() % totalnames ];
245 if (op > (pct_remove + pct_create)) {
246 retval = auto_lookup(ip);
247 } else if (op > pct_remove) {
248 retval = auto_create(ip);
250 retval = auto_remove(ip);
253 /* output '.' every DOT_COUNT ops
254 * and output '\n" every 72 dots
256 if ((i % DOT_COUNT) == 0) {
257 if (linedots++ == 72) {
267 printf("creates: %6d OK, %6d EEXIST (%6d total, %2d%% EEXIST)\n",
268 good_adds, bad_adds, good_adds + bad_adds,
270 ? (bad_adds*100) / (good_adds+bad_adds)
272 printf("removes: %6d OK, %6d ENOENT (%6d total, %2d%% ENOENT)\n",
273 good_rms, bad_rms, good_rms + bad_rms,
275 ? (bad_rms*100) / (good_rms+bad_rms)
277 printf("lookups: %6d OK, %6d ENOENT (%6d total, %2d%% ENOENT)\n",
278 good_looks, bad_looks, good_looks + bad_looks,
279 (good_looks+bad_looks)
280 ? (bad_looks*100) / (good_looks+bad_looks)
282 good_tot = good_looks + good_adds + good_rms;
283 bad_tot = bad_looks + bad_adds + bad_rms;
284 printf("total : %6d OK, %6d w/error (%6d total, %2d%% w/error)\n",
285 good_tot, bad_tot, good_tot + bad_tot,
287 ? (bad_tot*100) / (good_tot+bad_tot)
291 * If asked to clear the directory out after the run,
292 * remove everything that is left.
296 for (ip = table, i = 0; i < totalnames; ip++, i++) {
300 retval = unlink(ip->name);
302 if (errno == ENOENT) {
303 printf("\"%s\"(%llu) not removed, should have existed\n", ip->name, (unsigned long long)ip->inumber);
305 printf("\"%s\"(%llu) on remove: ", ip->name, (unsigned long long)ip->inumber);
310 if ((good_rms % DOT_COUNT) == 0) {
315 printf("\ncleanup: %6d removes\n", good_rms);
321 auto_lookup(struct info *ip)
326 retval = stat64(ip->name, &statb);
330 if (ip->exists == 0) {
331 printf("\"%s\"(%llu) lookup, should not exist\n",
332 ip->name, (unsigned long long)statb.st_ino);
334 } else if (ip->inumber != statb.st_ino) {
335 printf("\"%s\"(%llu) lookup, should be inumber %llu\n",
336 ip->name, (unsigned long long)statb.st_ino,
337 (unsigned long long)ip->inumber);
339 } else if (verbose) {
340 printf("\"%s\"(%llu) lookup ok\n",
341 ip->name, (unsigned long long)statb.st_ino);
343 } else if (errno == ENOENT) {
346 if (ip->exists == 1) {
347 printf("\"%s\"(%llu) lookup, should exist\n",
348 ip->name, (unsigned long long)ip->inumber);
350 } else if (verbose) {
351 printf("\"%s\"(%llu) lookup ENOENT ok\n",
352 ip->name, (unsigned long long)ip->inumber);
356 printf("\"%s\"(%llu) on lookup: ",
357 ip->name, (unsigned long long)ip->inumber);
364 auto_create(struct info *ip)
369 retval = open(ip->name, O_RDWR|O_EXCL|O_CREAT, 0666);
374 if (stat64(ip->name, &statb) < 0) {
378 if (ip->exists == 1) {
379 printf("\"%s\"(%llu) created, but already existed as inumber %llu\n", ip->name, (unsigned long long)statb.st_ino, (unsigned long long)ip->inumber);
381 } else if (verbose) {
382 printf("\"%s\"(%llu) create new ok\n",
383 ip->name, (unsigned long long)statb.st_ino);
386 ip->inumber = statb.st_ino;
387 } else if (errno == EEXIST) {
390 if (ip->exists == 0) {
391 if (stat64(ip->name, &statb) < 0) {
395 printf("\"%s\"(%llu) not created, should not exist\n",
396 ip->name, (unsigned long long)statb.st_ino);
398 } else if (verbose) {
399 printf("\"%s\"(%llu) not created ok\n",
400 ip->name, (unsigned long long)ip->inumber);
405 printf("\"%s\"(%llu) on create: ",
406 ip->name, (unsigned long long)ip->inumber);
413 auto_remove(struct info *ip)
417 retval = unlink(ip->name);
421 if (ip->exists == 0) {
422 printf("\"%s\"(%llu) removed, should not have existed\n",
423 ip->name, (unsigned long long)ip->inumber);
425 } else if (verbose) {
426 printf("\"%s\"(%llu) remove ok\n",
427 ip->name, (unsigned long long)ip->inumber);
431 } else if (errno == ENOENT) {
434 if (ip->exists == 1) {
435 printf("\"%s\"(%llu) not removed, should have existed\n",
436 ip->name, (unsigned long long)ip->inumber);
438 } else if (verbose) {
439 printf("\"%s\"(%llu) not removed ok\n",
440 ip->name, (unsigned long long)ip->inumber);
445 printf("\"%s\"(%llu) on remove: ",
446 ip->name, (unsigned long long)ip->inumber);