]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blob - io/pread.c
xfs_io: refactor stat functions, add raw dump
[thirdparty/xfsprogs-dev.git] / io / pread.c
1 /*
2 * Copyright (c) 2003-2005 Silicon Graphics, Inc.
3 * All Rights Reserved.
4 *
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.
8 *
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.
13 *
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
17 */
18
19 #include <sys/uio.h>
20 #include "command.h"
21 #include "input.h"
22 #include <ctype.h>
23 #include "init.h"
24 #include "io.h"
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"
36 " 'pread -v 512 20' - dumps 20 bytes read from 512 bytes into the file\n"
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"
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"
49 #ifdef HAVE_PREADV
50 " -V N -- use vectored IO with N iovecs of blocksize each (preadv)\n"
51 #endif
52 "\n"
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"
57 "\n"));
58 }
59
60 void *buffer;
61 size_t highwater;
62 size_t buffersize;
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);
81 if (!iov[i].iov_base) {
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 }
98
99 int
100 alloc_buffer(
101 size_t bsize,
102 int uflag,
103 unsigned int seed)
104 {
105 if (vectors)
106 return alloc_iovec(bsize, uflag, seed);
107
108 if (bsize > highwater) {
109 if (buffer)
110 free(buffer);
111 buffer = memalign(pagesize, bsize);
112 if (!buffer) {
113 perror("memalign");
114 highwater = buffersize = 0;
115 return -1;
116 }
117 highwater = bsize;
118 }
119 buffersize = bsize;
120 if (!uflag)
121 memset(buffer, seed, buffersize);
122 return 0;
123 }
124
125 void
126 __dump_buffer(
127 void *buf,
128 off64_t offset,
129 ssize_t len)
130 {
131 int i, j;
132 char *p;
133
134 for (i = 0, p = (char *)buf; i < len; i += 16) {
135 char *s = p;
136
137 printf("%08llx: ", (unsigned long long)offset + i);
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++) {
142 if (isalnum((int)*s))
143 printf("%c", *s);
144 else
145 printf(".");
146 }
147 printf("\n");
148 }
149 }
150
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
172 #ifdef HAVE_PREADV
173 static int
174 do_preadv(
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
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 }
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)
217 return pread(fd, buffer, min(count, buffer_size), offset);
218
219 return do_preadv(fd, offset, count, buffer_size);
220 }
221
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);
236 end = lseek(fd, 0, SEEK_END);
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) {
248 if (range)
249 off = ((offset + (random() % range)) / buffersize) *
250 buffersize;
251 else
252 off = offset;
253 bytes = do_pread(fd, off, buffersize, buffersize);
254 if (bytes == 0)
255 break;
256 if (bytes < 0) {
257 perror("pread");
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
282 end = lseek(fd, 0, SEEK_END);
283 off = eof ? end : min(end, lseek(fd, off, SEEK_SET));
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))) {
294 off -= bytes_requested;
295 bytes = do_pread(fd, off, bytes_requested, buffersize);
296 if (bytes == 0)
297 return ops;
298 if (bytes < 0) {
299 perror("pread");
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;
313 bytes = do_pread(fd, off, cnt, buffersize);
314 if (bytes == 0)
315 break;
316 if (bytes < 0) {
317 perror("pread");
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(
331 int fd,
332 off64_t offset,
333 long long count,
334 long long *total,
335 int verbose,
336 int onlyone,
337 int eof)
338 {
339 ssize_t bytes;
340 int ops = 0;
341
342 *total = 0;
343 while (count > 0 || eof) {
344 bytes = do_pread(fd, offset, count, buffersize);
345 if (bytes == 0)
346 break;
347 if (bytes < 0) {
348 perror("pread");
349 return -1;
350 }
351 ops++;
352 if (verbose)
353 dump_buffer(offset, bytes);
354 *total += bytes;
355 if (onlyone || bytes < min(count, buffersize))
356 break;
357 offset += bytes;
358 count -= bytes;
359 }
360 return ops;
361 }
362
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
375 static int
376 pread_f(
377 int argc,
378 char **argv)
379 {
380 size_t bsize;
381 off64_t offset;
382 unsigned int zeed = 0;
383 long long count, total, tmp;
384 size_t fsblocksize, fssectsize;
385 struct timeval t1, t2;
386 char *sp;
387 int Cflag, qflag, uflag, vflag;
388 int eof = 0, direction = IO_FORWARD;
389 int c;
390
391 Cflag = qflag = uflag = vflag = 0;
392 init_cvtnum(&fsblocksize, &fssectsize);
393 bsize = fsblocksize;
394
395 while ((c = getopt(argc, argv, "b:BCFRquvV:Z:")) != EOF) {
396 switch (c) {
397 case 'b':
398 tmp = cvtnum(fsblocksize, fssectsize, optarg);
399 if (tmp < 0) {
400 printf(_("non-numeric bsize -- %s\n"), optarg);
401 return 0;
402 }
403 bsize = tmp;
404 break;
405 case 'C':
406 Cflag = 1;
407 break;
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;
417 case 'q':
418 qflag = 1;
419 break;
420 case 'u':
421 uflag = 1;
422 break;
423 case 'v':
424 vflag = 1;
425 break;
426 #ifdef HAVE_PREADV
427 case 'V':
428 vectors = strtoul(optarg, &sp, 0);
429 if (!sp || sp == optarg) {
430 printf(_("non-numeric vector count == %s\n"),
431 optarg);
432 return 0;
433 }
434 break;
435 #endif
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;
443 default:
444 return command_usage(&pread_cmd);
445 }
446 }
447 if (optind != argc - 2)
448 return command_usage(&pread_cmd);
449
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]);
455 return 0;
456 }
457 optind++;
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) {
462 printf(_("non-numeric length argument -- %s\n"), argv[optind]);
463 return 0;
464 }
465
466 if (alloc_buffer(bsize, uflag, 0xabababab) < 0)
467 return 0;
468
469 gettimeofday(&t1, NULL);
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)
488 return 0;
489 if (qflag)
490 return 0;
491 gettimeofday(&t2, NULL);
492 t2 = tsub(t2, t1);
493
494 report_io_times("read", &t2, (long long)offset, count, total, c, Cflag);
495 return 0;
496 }
497
498 void
499 pread_init(void)
500 {
501 pread_cmd.name = "pread";
502 pread_cmd.altname = "r";
503 pread_cmd.cfunc = pread_f;
504 pread_cmd.argmin = 2;
505 pread_cmd.argmax = -1;
506 pread_cmd.flags = CMD_NOMAP_OK | CMD_FOREIGN_OK;
507 pread_cmd.args = _("[-b bs] [-v] [-i N] [-FBR [-Z N]] off len");
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 }