]>
Commit | Line | Data |
---|---|---|
e246ba5f | 1 | /* |
da23017d NS |
2 | * Copyright (c) 2003-2005 Silicon Graphics, Inc. |
3 | * All Rights Reserved. | |
dfc130f3 | 4 | * |
da23017d NS |
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 | |
e246ba5f | 7 | * published by the Free Software Foundation. |
dfc130f3 | 8 | * |
da23017d NS |
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. | |
dfc130f3 | 13 | * |
da23017d NS |
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 | |
e246ba5f NS |
17 | */ |
18 | ||
64a93371 | 19 | #include <sys/uio.h> |
6b803e5a CH |
20 | #include "command.h" |
21 | #include "input.h" | |
e246ba5f | 22 | #include <ctype.h> |
e246ba5f | 23 | #include "init.h" |
48c46ee3 | 24 | #include "io.h" |
e246ba5f NS |
25 | |
26 | static cmdinfo_t pread_cmd; | |
27 | ||
28 | static void | |
29 | pread_help(void) | |
30 | { | |
31 | printf(_( | |
32 | "\n" | |
33 | " reads a range of bytes in a specified block size from the given offset\n" | |
34 | "\n" | |
35 | " Example:\n" | |
48c46ee3 | 36 | " 'pread -v 512 20' - dumps 20 bytes read from 512 bytes into the file\n" |
e246ba5f NS |
37 | "\n" |
38 | " Reads a segment of the currently open file, optionally dumping it to the\n" | |
39 | " standard output stream (with -v option) for subsequent inspection.\n" | |
40 | " The reads are performed in sequential blocks starting at offset, with the\n" | |
8fb2237e NS |
41 | " blocksize tunable using the -b option (default blocksize is 4096 bytes),\n" |
42 | " unless a different pattern is requested.\n" | |
43 | " -B -- read backwards through the range from offset (backwards N bytes)\n" | |
44 | " -F -- read forwards through the range of bytes from offset (default)\n" | |
45 | " -v -- be verbose, dump out buffers (used when reading forwards)\n" | |
46 | " -R -- read at random offsets in the range of bytes\n" | |
47 | " -Z N -- zeed the random number generator (used when reading randomly)\n" | |
48 | " (heh, zorry, the -s/-S arguments were already in use in pwrite)\n" | |
197d5828 | 49 | #ifdef HAVE_PREADV |
e115d3b2 | 50 | " -V N -- use vectored IO with N iovecs of blocksize each (preadv)\n" |
197d5828 | 51 | #endif |
e115d3b2 | 52 | "\n" |
8fb2237e NS |
53 | " When in \"random\" mode, the number of read operations will equal the\n" |
54 | " number required to do a complete forward/backward scan of the range.\n" | |
55 | " Note that the offset within the range is chosen at random each time\n" | |
56 | " (an offset may be read more than once when operating in this mode).\n" | |
e246ba5f NS |
57 | "\n")); |
58 | } | |
59 | ||
60 | void *buffer; | |
2c2f6d79 NS |
61 | size_t highwater; |
62 | size_t buffersize; | |
e115d3b2 DC |
63 | int vectors; |
64 | struct iovec *iov; | |
65 | ||
66 | static int | |
67 | alloc_iovec( | |
68 | size_t bsize, | |
69 | int uflag, | |
70 | unsigned int seed) | |
71 | { | |
72 | int i; | |
73 | ||
74 | iov = calloc(vectors, sizeof(struct iovec)); | |
75 | if (!iov) | |
76 | return -1; | |
77 | ||
78 | buffersize = 0; | |
79 | for (i = 0; i < vectors; i++) { | |
80 | iov[i].iov_base = memalign(pagesize, bsize); | |
09bd34dd | 81 | if (!iov[i].iov_base) { |
e115d3b2 DC |
82 | perror("memalign"); |
83 | goto unwind; | |
84 | } | |
85 | iov[i].iov_len = bsize; | |
86 | if (!uflag) | |
87 | memset(iov[i].iov_base, seed, bsize); | |
88 | } | |
89 | buffersize = bsize * vectors; | |
90 | return 0; | |
91 | unwind: | |
92 | for( ; i >= 0; i--) | |
93 | free(iov[i].iov_base); | |
94 | free(iov); | |
95 | iov = NULL; | |
96 | return -1; | |
97 | } | |
e246ba5f NS |
98 | |
99 | int | |
100 | alloc_buffer( | |
2c2f6d79 | 101 | size_t bsize, |
48c46ee3 | 102 | int uflag, |
e246ba5f NS |
103 | unsigned int seed) |
104 | { | |
e115d3b2 DC |
105 | if (vectors) |
106 | return alloc_iovec(bsize, uflag, seed); | |
107 | ||
105f2cc2 | 108 | if (bsize > highwater) { |
ae8d6a95 | 109 | if (buffer) |
110 | free(buffer); | |
105f2cc2 | 111 | buffer = memalign(pagesize, bsize); |
e246ba5f | 112 | if (!buffer) { |
ae8d6a95 | 113 | perror("memalign"); |
105f2cc2 | 114 | highwater = buffersize = 0; |
48c46ee3 | 115 | return -1; |
e246ba5f | 116 | } |
105f2cc2 | 117 | highwater = bsize; |
e246ba5f | 118 | } |
105f2cc2 | 119 | buffersize = bsize; |
48c46ee3 NS |
120 | if (!uflag) |
121 | memset(buffer, seed, buffersize); | |
122 | return 0; | |
e246ba5f NS |
123 | } |
124 | ||
48c46ee3 | 125 | void |
e115d3b2 DC |
126 | __dump_buffer( |
127 | void *buf, | |
e246ba5f NS |
128 | off64_t offset, |
129 | ssize_t len) | |
130 | { | |
131 | int i, j; | |
132 | char *p; | |
133 | ||
09bd34dd | 134 | for (i = 0, p = (char *)buf; i < len; i += 16) { |
e246ba5f NS |
135 | char *s = p; |
136 | ||
638473d8 | 137 | printf("%08llx: ", (unsigned long long)offset + i); |
e246ba5f NS |
138 | for (j = 0; j < 16 && i + j < len; j++, p++) |
139 | printf("%02x ", *p); | |
140 | printf(" "); | |
141 | for (j = 0; j < 16 && i + j < len; j++, s++) { | |
93d9f139 | 142 | if (isalnum((int)*s)) |
e246ba5f NS |
143 | printf("%c", *s); |
144 | else | |
145 | printf("."); | |
146 | } | |
147 | printf("\n"); | |
148 | } | |
149 | } | |
150 | ||
e115d3b2 DC |
151 | void |
152 | dump_buffer( | |
153 | off64_t offset, | |
154 | ssize_t len) | |
155 | { | |
156 | int i, l; | |
157 | ||
158 | if (!vectors) { | |
159 | __dump_buffer(buffer, offset, len); | |
160 | return; | |
161 | } | |
162 | ||
163 | for (i = 0; len > 0 && i < vectors; i++) { | |
164 | l = min(len, iov[i].iov_len); | |
165 | ||
166 | __dump_buffer(iov[i].iov_base, offset, l); | |
167 | len -= l; | |
168 | offset += l; | |
169 | } | |
170 | } | |
171 | ||
197d5828 | 172 | #ifdef HAVE_PREADV |
e115d3b2 | 173 | static int |
197d5828 | 174 | do_preadv( |
e115d3b2 DC |
175 | int fd, |
176 | off64_t offset, | |
177 | ssize_t count, | |
178 | ssize_t buffer_size) | |
179 | { | |
180 | int vecs = 0; | |
181 | ssize_t oldlen = 0; | |
182 | ssize_t bytes = 0; | |
183 | ||
e115d3b2 DC |
184 | /* trim the iovec if necessary */ |
185 | if (count < buffersize) { | |
186 | size_t len = 0; | |
187 | while (len + iov[vecs].iov_len < count) { | |
188 | len += iov[vecs].iov_len; | |
189 | vecs++; | |
190 | } | |
191 | oldlen = iov[vecs].iov_len; | |
192 | iov[vecs].iov_len = count - len; | |
193 | vecs++; | |
194 | } else { | |
195 | vecs = vectors; | |
196 | } | |
197 | bytes = preadv(fd, iov, vectors, offset); | |
198 | ||
199 | /* restore trimmed iov */ | |
200 | if (oldlen) | |
201 | iov[vecs - 1].iov_len = oldlen; | |
202 | ||
203 | return bytes; | |
204 | } | |
197d5828 ES |
205 | #else |
206 | #define do_preadv(fd, offset, count, buffer_size) (0) | |
207 | #endif | |
208 | ||
209 | static int | |
210 | do_pread( | |
211 | int fd, | |
212 | off64_t offset, | |
213 | ssize_t count, | |
214 | ssize_t buffer_size) | |
215 | { | |
216 | if (!vectors) | |
2f9a125c | 217 | return pread(fd, buffer, min(count, buffer_size), offset); |
197d5828 ES |
218 | |
219 | return do_preadv(fd, offset, count, buffer_size); | |
220 | } | |
e115d3b2 | 221 | |
8fb2237e NS |
222 | static int |
223 | read_random( | |
224 | int fd, | |
225 | off64_t offset, | |
226 | long long count, | |
227 | long long *total, | |
228 | unsigned int seed, | |
229 | int eof) | |
230 | { | |
231 | off64_t end, off, range; | |
232 | ssize_t bytes; | |
233 | int ops = 0; | |
234 | ||
235 | srandom(seed); | |
dc8878f4 | 236 | end = lseek(fd, 0, SEEK_END); |
8fb2237e NS |
237 | offset = (eof || offset > end) ? end : offset; |
238 | if ((bytes = (offset % buffersize))) | |
239 | offset -= bytes; | |
240 | offset = max(0, offset); | |
241 | if ((bytes = (count % buffersize))) | |
242 | count += bytes; | |
243 | count = max(buffersize, count); | |
244 | range = count - buffersize; | |
245 | ||
246 | *total = 0; | |
247 | while (count > 0) { | |
2ab8ecbe DM |
248 | if (range) |
249 | off = ((offset + (random() % range)) / buffersize) * | |
250 | buffersize; | |
251 | else | |
252 | off = offset; | |
e115d3b2 | 253 | bytes = do_pread(fd, off, buffersize, buffersize); |
8fb2237e NS |
254 | if (bytes == 0) |
255 | break; | |
256 | if (bytes < 0) { | |
2f9a125c | 257 | perror("pread"); |
8fb2237e NS |
258 | return -1; |
259 | } | |
260 | ops++; | |
261 | *total += bytes; | |
262 | if (bytes < buffersize) | |
263 | break; | |
264 | count -= bytes; | |
265 | } | |
266 | return ops; | |
267 | } | |
268 | ||
269 | static int | |
270 | read_backward( | |
271 | int fd, | |
272 | off64_t *offset, | |
273 | long long *count, | |
274 | long long *total, | |
275 | int eof) | |
276 | { | |
277 | off64_t end, off = *offset; | |
278 | ssize_t bytes = 0, bytes_requested; | |
279 | long long cnt = *count; | |
280 | int ops = 0; | |
281 | ||
dc8878f4 FJ |
282 | end = lseek(fd, 0, SEEK_END); |
283 | off = eof ? end : min(end, lseek(fd, off, SEEK_SET)); | |
8fb2237e NS |
284 | if ((end = off - cnt) < 0) { |
285 | cnt += end; /* subtraction, end is negative */ | |
286 | end = 0; | |
287 | } | |
288 | *total = 0; | |
289 | *count = cnt; | |
290 | *offset = off; | |
291 | ||
292 | /* Do initial unaligned read if needed */ | |
293 | if ((bytes_requested = (off % buffersize))) { | |
8fb2237e | 294 | off -= bytes_requested; |
e115d3b2 | 295 | bytes = do_pread(fd, off, bytes_requested, buffersize); |
8fb2237e NS |
296 | if (bytes == 0) |
297 | return ops; | |
298 | if (bytes < 0) { | |
2f9a125c | 299 | perror("pread"); |
8fb2237e NS |
300 | return -1; |
301 | } | |
302 | ops++; | |
303 | *total += bytes; | |
304 | if (bytes < bytes_requested) | |
305 | return ops; | |
306 | cnt -= bytes; | |
307 | } | |
308 | ||
309 | /* Iterate backward through the rest of the range */ | |
310 | while (cnt > end) { | |
311 | bytes_requested = min(cnt, buffersize); | |
312 | off -= bytes_requested; | |
e115d3b2 | 313 | bytes = do_pread(fd, off, cnt, buffersize); |
8fb2237e NS |
314 | if (bytes == 0) |
315 | break; | |
316 | if (bytes < 0) { | |
2f9a125c | 317 | perror("pread"); |
8fb2237e NS |
318 | return -1; |
319 | } | |
320 | ops++; | |
321 | *total += bytes; | |
322 | if (bytes < bytes_requested) | |
323 | break; | |
324 | cnt -= bytes; | |
325 | } | |
326 | return ops; | |
327 | } | |
328 | ||
329 | static int | |
330 | read_forward( | |
e246ba5f NS |
331 | int fd, |
332 | off64_t offset, | |
92d9b902 NS |
333 | long long count, |
334 | long long *total, | |
e246ba5f | 335 | int verbose, |
8fb2237e NS |
336 | int onlyone, |
337 | int eof) | |
e246ba5f | 338 | { |
2c2f6d79 | 339 | ssize_t bytes; |
92d9b902 | 340 | int ops = 0; |
e246ba5f NS |
341 | |
342 | *total = 0; | |
8fb2237e | 343 | while (count > 0 || eof) { |
e115d3b2 | 344 | bytes = do_pread(fd, offset, count, buffersize); |
e246ba5f NS |
345 | if (bytes == 0) |
346 | break; | |
347 | if (bytes < 0) { | |
2f9a125c | 348 | perror("pread"); |
92d9b902 | 349 | return -1; |
e246ba5f | 350 | } |
92d9b902 | 351 | ops++; |
e246ba5f NS |
352 | if (verbose) |
353 | dump_buffer(offset, bytes); | |
354 | *total += bytes; | |
e115d3b2 | 355 | if (onlyone || bytes < min(count, buffersize)) |
e246ba5f NS |
356 | break; |
357 | offset += bytes; | |
358 | count -= bytes; | |
359 | } | |
92d9b902 | 360 | return ops; |
e246ba5f NS |
361 | } |
362 | ||
8fb2237e NS |
363 | int |
364 | read_buffer( | |
365 | int fd, | |
366 | off64_t offset, | |
367 | long long count, | |
368 | long long *total, | |
369 | int verbose, | |
370 | int onlyone) | |
371 | { | |
372 | return read_forward(fd, offset, count, total, verbose, onlyone, 0); | |
373 | } | |
374 | ||
e246ba5f NS |
375 | static int |
376 | pread_f( | |
377 | int argc, | |
378 | char **argv) | |
379 | { | |
8fb2237e | 380 | size_t bsize; |
e246ba5f | 381 | off64_t offset; |
8fb2237e | 382 | unsigned int zeed = 0; |
2c2f6d79 | 383 | long long count, total, tmp; |
d026b19e | 384 | size_t fsblocksize, fssectsize; |
92d9b902 | 385 | struct timeval t1, t2; |
8fb2237e | 386 | char *sp; |
d347f827 | 387 | int Cflag, qflag, uflag, vflag; |
8fb2237e | 388 | int eof = 0, direction = IO_FORWARD; |
e246ba5f NS |
389 | int c; |
390 | ||
d347f827 | 391 | Cflag = qflag = uflag = vflag = 0; |
8fb2237e NS |
392 | init_cvtnum(&fsblocksize, &fssectsize); |
393 | bsize = fsblocksize; | |
394 | ||
e115d3b2 | 395 | while ((c = getopt(argc, argv, "b:BCFRquvV:Z:")) != EOF) { |
e246ba5f NS |
396 | switch (c) { |
397 | case 'b': | |
8fb2237e | 398 | tmp = cvtnum(fsblocksize, fssectsize, optarg); |
2c2f6d79 | 399 | if (tmp < 0) { |
e246ba5f NS |
400 | printf(_("non-numeric bsize -- %s\n"), optarg); |
401 | return 0; | |
402 | } | |
8fb2237e | 403 | bsize = tmp; |
e246ba5f | 404 | break; |
5c7bef67 NS |
405 | case 'C': |
406 | Cflag = 1; | |
407 | break; | |
8fb2237e NS |
408 | case 'F': |
409 | direction = IO_FORWARD; | |
410 | break; | |
411 | case 'B': | |
412 | direction = IO_BACKWARD; | |
413 | break; | |
414 | case 'R': | |
415 | direction = IO_RANDOM; | |
416 | break; | |
d347f827 NS |
417 | case 'q': |
418 | qflag = 1; | |
419 | break; | |
48c46ee3 NS |
420 | case 'u': |
421 | uflag = 1; | |
422 | break; | |
e246ba5f NS |
423 | case 'v': |
424 | vflag = 1; | |
425 | break; | |
197d5828 | 426 | #ifdef HAVE_PREADV |
e115d3b2 DC |
427 | case 'V': |
428 | vectors = strtoul(optarg, &sp, 0); | |
429 | if (!sp || sp == optarg) { | |
197d5828 | 430 | printf(_("non-numeric vector count == %s\n"), |
e115d3b2 DC |
431 | optarg); |
432 | return 0; | |
433 | } | |
434 | break; | |
197d5828 | 435 | #endif |
8fb2237e NS |
436 | case 'Z': |
437 | zeed = strtoul(optarg, &sp, 0); | |
438 | if (!sp || sp == optarg) { | |
439 | printf(_("non-numeric seed -- %s\n"), optarg); | |
440 | return 0; | |
441 | } | |
442 | break; | |
e246ba5f | 443 | default: |
48c46ee3 | 444 | return command_usage(&pread_cmd); |
e246ba5f NS |
445 | } |
446 | } | |
48c46ee3 NS |
447 | if (optind != argc - 2) |
448 | return command_usage(&pread_cmd); | |
449 | ||
8fb2237e NS |
450 | offset = cvtnum(fsblocksize, fssectsize, argv[optind]); |
451 | if (offset < 0 && (direction & (IO_RANDOM|IO_BACKWARD))) { | |
452 | eof = -1; /* read from EOF */ | |
453 | } else if (offset < 0) { | |
454 | printf(_("non-numeric length argument -- %s\n"), argv[optind]); | |
e246ba5f NS |
455 | return 0; |
456 | } | |
457 | optind++; | |
8fb2237e NS |
458 | count = cvtnum(fsblocksize, fssectsize, argv[optind]); |
459 | if (count < 0 && (direction & (IO_RANDOM|IO_FORWARD))) { | |
460 | eof = -1; /* read to EOF */ | |
461 | } else if (count < 0) { | |
e246ba5f NS |
462 | printf(_("non-numeric length argument -- %s\n"), argv[optind]); |
463 | return 0; | |
464 | } | |
d347f827 | 465 | |
8fb2237e | 466 | if (alloc_buffer(bsize, uflag, 0xabababab) < 0) |
e246ba5f NS |
467 | return 0; |
468 | ||
92d9b902 | 469 | gettimeofday(&t1, NULL); |
8fb2237e NS |
470 | switch (direction) { |
471 | case IO_RANDOM: | |
472 | if (!zeed) /* srandom seed */ | |
473 | zeed = time(NULL); | |
474 | c = read_random(file->fd, offset, count, &total, zeed, eof); | |
475 | break; | |
476 | case IO_FORWARD: | |
477 | c = read_forward(file->fd, offset, count, &total, vflag, 0, eof); | |
478 | if (eof) | |
479 | count = total; | |
480 | break; | |
481 | case IO_BACKWARD: | |
482 | c = read_backward(file->fd, &offset, &count, &total, eof); | |
483 | break; | |
484 | default: | |
485 | ASSERT(0); | |
486 | } | |
487 | if (c < 0) | |
e246ba5f | 488 | return 0; |
d347f827 NS |
489 | if (qflag) |
490 | return 0; | |
92d9b902 NS |
491 | gettimeofday(&t2, NULL); |
492 | t2 = tsub(t2, t1); | |
e246ba5f | 493 | |
a9b61ce9 | 494 | report_io_times("read", &t2, (long long)offset, count, total, c, Cflag); |
e246ba5f NS |
495 | return 0; |
496 | } | |
497 | ||
498 | void | |
499 | pread_init(void) | |
500 | { | |
ad765595 AM |
501 | pread_cmd.name = "pread"; |
502 | pread_cmd.altname = "r"; | |
e246ba5f NS |
503 | pread_cmd.cfunc = pread_f; |
504 | pread_cmd.argmin = 2; | |
505 | pread_cmd.argmax = -1; | |
48c46ee3 | 506 | pread_cmd.flags = CMD_NOMAP_OK | CMD_FOREIGN_OK; |
e115d3b2 | 507 | pread_cmd.args = _("[-b bs] [-v] [-i N] [-FBR [-Z N]] off len"); |
e246ba5f NS |
508 | pread_cmd.oneline = _("reads a number of bytes at a specified offset"); |
509 | pread_cmd.help = pread_help; | |
510 | ||
511 | add_command(&pread_cmd); | |
512 | } |