xref: /haiku/src/tests/add-ons/kernel/file_systems/shared/random_read.cpp (revision 4169a972eea20e94d1b318f0ae790b749f564fc1)
1 /*
2  * Copyright 2002-2008, Axel Dörfler, axeld@pinc-software.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 /*!	Tests if the read functions of a file system work correctly by reading
7 	arbitrary bytes at arbitrary positions of a file previously created.
8 
9 	The idea is to be able to calculate the contents on each position
10 	of the file. It currently only counts numbers, beginning from zero,
11 	which is probably not a really good test.
12 	But if there is a bug, it would be very likely to show up after some
13 	thousand (or even million) runs.
14 
15 	Works only on little-endian processors, such as x86 (or else the partial
16 	read numbers wouldn't be correctly compared).
17 
18 	Use the --help option to see how it's used.
19 */
20 
21 #include <File.h>
22 #include <StorageDefs.h>
23 
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <ctype.h>
28 
29 #define FILE_NAME		"RANDOM_READ_TEST_FILE"
30 #define FILE_SIZE		(1024 * 1024)
31 #define BUFFER_SIZE		(64 * 1024)
32 #define NUMBER_OF_LOOPS	1000
33 #define MAX_FAULTS		10
34 
35 // currently only works with 4 byte values!
36 typedef uint32 test_t;
37 
38 
39 void
40 createFile(const char *name, size_t size)
41 {
42 	BFile file;
43 	status_t status = file.SetTo(name,
44 		B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
45 	if (status < B_OK) {
46 		fprintf(stderr, "Could not create test file: %s!\n", strerror(status));
47 		return;
48 	}
49 
50 	test_t max = size / sizeof(test_t);
51 	for (uint32 i = 0; i < max; i++) {
52 		if (file.Write(&i, sizeof(test_t)) != sizeof(test_t)) {
53 			fprintf(stderr,"Could not create the whole test file!\n");
54 			break;
55 		}
56 	}
57 }
58 
59 
60 void
61 readTest(const char *name, int32 loops)
62 {
63 	BFile file;
64 	status_t status = file.SetTo(name, B_READ_ONLY);
65 	if (status < B_OK) {
66 		fprintf(stderr, "Could not open test file! Run \"randomread create "
67 			"[size]\" first.\n"
68 			"This will create a file named \"%s\" in the current directory.\n",
69 			name);
70 		return;
71 	}
72 
73 	off_t size;
74 	if ((status = file.GetSize(&size)) < B_OK) {
75 		fprintf(stderr, "Could not get file size: %s!\n", strerror(status));
76 		return;
77 	}
78 
79 	char *buffer = (char *)malloc(BUFFER_SIZE);
80 	if (buffer == NULL) {
81 		fprintf(stderr, "no memory to create read buffer.\n");
82 		return;
83 	}
84 	srand(time(NULL));
85 	int32 faults = 0;
86 
87 	for (int32 i = 0; i < loops; i++) {
88 		off_t pos = rand() % size;
89 		// we are lazy tester, minimum read size is 4 bytes
90 		int32 bytes = (rand() % BUFFER_SIZE) + 4;
91 		off_t max = size - pos;
92 		if (max > bytes)
93 			max = bytes;
94 		else
95 			bytes = max;
96 
97 		ssize_t bytesRead = file.ReadAt(pos, buffer, bytes);
98 		if (bytesRead < B_OK) {
99 			printf("  Could not read %ld bytes at offset %lld: %s\n",
100 				bytes, pos, strerror(bytesRead));
101 		} else if (bytesRead != max) {
102 			printf("  Could only read %ld bytes instead of %ld at offset %lld\n",
103 				bytesRead, bytes, pos);
104 		}
105 
106 		// test contents
107 
108 		off_t bufferPos = pos;
109 		test_t num = bufferPos / sizeof(test_t);
110 
111 		// check leading partial number
112 		int32 partial = bufferPos % sizeof(test_t);
113 		if (partial) {
114 			test_t read = *(test_t *)(buffer - partial);
115 			bool correct;
116 			switch (partial) {
117 				// byteorder little-endian
118 				case 1:
119 					correct = (num & 0xffffff00) == (read & 0xffffff00);
120 					break;
121 				case 2:
122 					correct = (num & 0xffff0000) == (read & 0xffff0000);
123 					break;
124 				case 3:
125 					correct = (num & 0xff000000) == (read & 0xff000000);
126 					break;
127 			}
128 			if (!correct) {
129 				printf("[%lld,%ld] Bytes at %lld don't match (partial begin = "
130 					"%ld, should be %08lx, is %08lx)!\n", pos, bytes,
131 					bufferPos, partial, num, read);
132 				faults++;
133 			}
134 			bufferPos += sizeof(test_t) - partial;
135 		}
136 		num++;
137 		test_t *numBuffer = (test_t *)(buffer + sizeof(test_t) - partial);
138 
139 		// test full numbers
140 		for (; bufferPos < bytesRead - sizeof(test_t);
141 				bufferPos += sizeof(test_t)) {
142 			if (faults > MAX_FAULTS) {
143 				printf("maximum number of faults reached, bail out.\n");
144 				return;
145 			}
146 			if (num != *numBuffer) {
147 				printf("[%lld,%ld] Bytes at %lld don't match (should be %08lx, "
148 					"is %08lx)!\n", pos, bytes, bufferPos, num, *numBuffer);
149 				faults++;
150 			}
151 			num++;
152 			numBuffer++;
153 		}
154 
155 		// test last partial number
156 		partial = bytesRead - bufferPos;
157 		if (partial > 0) {
158 			uint32 read = *numBuffer;
159 			bool correct;
160 			switch (partial) {
161 				// byteorder little-endian
162 				case 1:
163 					correct = (num & 0x00ffffff) == (read & 0x00ffffff);
164 					break;
165 				case 2:
166 					correct = (num & 0x0000ffff) == (read & 0x0000ffff);
167 					break;
168 				case 3:
169 					correct = (num & 0x000000ff) == (read & 0x000000ff);
170 					break;
171 			}
172 			if (!correct) {
173 				printf("[%lld,%ld] Bytes at %lld don't match (partial end = "
174 					"%ld, should be %08lx, is %08lx)!\n", pos, bytes,
175 					bufferPos, partial, num, read);
176 				faults++;
177 			}
178 		}
179 	}
180 }
181 
182 
183 int
184 main(int argc, char **argv)
185 {
186 	size_t size = FILE_SIZE;
187 	int32 loops = NUMBER_OF_LOOPS;
188 	bool create = false;
189 
190 	if (argv[1]) {
191 		if (!strcmp(argv[1], "create")) {
192 			create = true;
193 			if (argv[2])
194 				size = atol(argv[2]);
195 		}
196 		else if (isdigit(*argv[1]))
197 			loops = atol(argv[1]);
198 		else {
199 			// get a nice filename of the program
200 			char *filename = strrchr(argv[0], '/')
201 				? strrchr(argv[0] , '/') + 1 : argv[0];
202 
203 			printf("You can either create a test file or perform the test.\n"
204 				"  Create:\t%s create [filesize]\n"
205 				"  Test:  \t%s [loops]\n\n"
206 				"Default size = %d, loops = %d\n",
207 				filename, filename, FILE_SIZE, NUMBER_OF_LOOPS);
208 
209 			return 0;
210 		}
211 	}
212 
213 	if (size == 0) {
214 		fprintf(stderr, "%s: given file size too small, set to 1 MB.\n",
215 			argv[0]);
216 		size = FILE_SIZE;
217 	}
218 
219 	if (create)
220 		createFile(FILE_NAME, size);
221 	else
222 		readTest(FILE_NAME, loops);
223 
224 	return 0;
225 }
226