static int fill_fs(const char *path, int progress) { uint64_t free_space; struct flow fw; int i, fine; free_space = get_freespace(path); pr_freespace(free_space); if (free_space <= 0) { printf("No space!\n"); return 1; } init_flow(&fw, free_space, progress); i = 0; fine = 1; do { fine = create_and_fill_file(path, i, GIGABYTES, &fw); i++; } while (fine); /* Final report. */ pr_freespace(get_freespace(path)); /* Writing speed. */ if (fw.measurements > 0) { double speed = get_avg_speed(&fw); const char *unit = adjust_unit(&speed); printf("Average writing speed: %.2f %s/s\n", speed, unit); } else printf("Writing speed not available\n"); return 0; }
static void iterate_files(const char *path, const long *files, long start_at, long end_at, int progress) { uint64_t tot_ok, tot_corrupted, tot_changed, tot_overwritten, tot_size; struct timeval tot_dt = { .tv_sec = 0, .tv_usec = 0 }; double read_speed; const char *unit; int and_read_all = 1; int or_missing_file = 0; int number = start_at; tot_ok = tot_corrupted = tot_changed = tot_overwritten = tot_size = 0; printf(" SECTORS " " ok/corrupted/changed/overwritten\n"); while (*files >= 0) { uint64_t sec_ok, sec_corrupted, sec_changed, sec_overwritten, file_size; int read_all; or_missing_file = or_missing_file || (*files != number); for (; number < *files; number++) { char *full_fn; const char *filename; full_fn = full_fn_from_number(&filename, "", number); assert(full_fn); printf("Missing file %s\n", filename); free(full_fn); } number++; validate_file(path, *files, &sec_ok, &sec_corrupted, &sec_changed, &sec_overwritten, &file_size, &read_all, &tot_dt, progress); tot_ok += sec_ok; tot_corrupted += sec_corrupted; tot_changed += sec_changed; tot_overwritten += sec_overwritten; tot_size += file_size; and_read_all = and_read_all && read_all; files++; } assert(tot_size / SECTOR_SIZE == (tot_ok + tot_corrupted + tot_changed + tot_overwritten)); /* Notice that not reporting `missing' files after the last file * in @files is important since @end_at could be very large. */ report("\n Data OK:", tot_ok); report("Data LOST:", tot_corrupted + tot_changed + tot_overwritten); report("\t Corrupted:", tot_corrupted); report("\tSlightly changed:", tot_changed); report("\t Overwritten:", tot_overwritten); if (or_missing_file) printf("WARNING: Not all F3 files in the range %li to %li are available\n", start_at + 1, end_at + 1); if (!and_read_all) printf("WARNING: Not all data was read due to I/O error(s)\n"); /* Reading speed. */ read_speed = (double)tot_size / dt_to_s(&tot_dt); unit = adjust_unit(&read_speed); printf("Average reading speed: %.2f %s/s\n", read_speed, unit); } int main(int argc, char **argv) { const long *files; struct args args = { /* Defaults. */ .start_at = 0, .end_at = LONG_MAX - 1, /* If stdout isn't a terminal, supress progress. */ .show_progress = isatty(STDOUT_FILENO), }; /* Read parameters. */ argp_parse(&argp, argc, argv, 0, NULL, &args); print_header(stdout, "read"); files = ls_my_files(args.dev_path, args.start_at, args.end_at); iterate_files(args.dev_path, files, args.start_at, args.end_at, args.show_progress); free((void *)files); return 0; }
static void validate_file(const char *path, int number, uint64_t *ptr_ok, uint64_t *ptr_corrupted, uint64_t *ptr_changed, uint64_t *ptr_overwritten, uint64_t *ptr_size, int *ptr_read_all, struct timeval *ptr_dt, int progress) { char *full_fn; const char *filename; const int num_int64 = SECTOR_SIZE >> 3; uint64_t sector[num_int64]; FILE *f; int fd; size_t sectors_read; uint64_t expected_offset; int final_errno; struct timeval t1, t2; /* Progress time. */ struct timeval pt1 = { .tv_sec = -1000, .tv_usec = 0 }; *ptr_ok = *ptr_corrupted = *ptr_changed = *ptr_overwritten = 0; full_fn = full_fn_from_number(&filename, path, number); assert(full_fn); printf("Validating file %s ... %s", filename, progress ? BLANK : ""); fflush(stdout); #ifdef __CYGWIN__ /* We don't need write access, but some kernels require that * the file descriptor passed to fdatasync(2) to be writable. */ f = fopen(full_fn, "rb+"); #else f = fopen(full_fn, "rb"); #endif if (!f) err(errno, "Can't open file %s", full_fn); fd = fileno(f); assert(fd >= 0); /* If the kernel follows our advice, f3read won't ever read from cache * even when testing small memory cards without a remount, and * we should have a better reading-speed measurement. */ assert(!fdatasync(fd)); assert(!posix_fadvise(fd, 0, 0, POSIX_FADV_DONTNEED)); /* Obtain initial time. */ assert(!gettimeofday(&t1, NULL)); /* Help the kernel to help us. */ assert(!posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL)); sectors_read = fread(sector, SECTOR_SIZE, 1, f); final_errno = errno; expected_offset = (uint64_t)number * GIGABYTES; while (sectors_read > 0) { uint64_t rn; int error_count, i; assert(sectors_read == 1); rn = sector[0]; error_count = 0; for (i = 1; error_count <= TOLERANCE && i < num_int64; i++) { rn = random_number(rn); if (rn != sector[i]) error_count++; } if (expected_offset == sector[0]) { if (error_count == 0) (*ptr_ok)++; else if (error_count <= TOLERANCE) (*ptr_changed)++; else (*ptr_corrupted)++; } else if (error_count <= TOLERANCE) (*ptr_overwritten)++; else (*ptr_corrupted)++; sectors_read = fread(sector, SECTOR_SIZE, 1, f); final_errno = errno; expected_offset += SECTOR_SIZE; if (progress) { struct timeval pt2; assert(!gettimeofday(&pt2, NULL)); /* Avoid often printouts. */ if (delay_ms(&pt1, &pt2) >= 200) { PRINT_STATUS(CLEAR); fflush(stdout); pt1 = pt2; } } } assert(!gettimeofday(&t2, NULL)); update_dt(ptr_dt, &t1, &t2); *ptr_read_all = feof(f); *ptr_size = ftell(f); PRINT_STATUS(progress ? CLEAR : ""); if (!*ptr_read_all) { assert(ferror(f)); printf(" - NOT fully read due to \"%s\"", strerror(final_errno)); } printf("\n"); fclose(f); free(full_fn); } static void report(const char *prefix, uint64_t i) { double f = (double) (i * SECTOR_SIZE); const char *unit = adjust_unit(&f); printf("%s %.2f %s (%" PRIu64 " sectors)\n", prefix, f, unit, i); }
static inline void pr_freespace(uint64_t fs) { double f = (double)fs; const char *unit = adjust_unit(&f); printf("Free space: %.2f %s\n", f, unit); }
static void measure(int fd, struct flow *fw) { long delay; fw->written_blocks++; fw->total_written += fw->block_size; if (fw->written_blocks < fw->blocks_per_delay) return; assert(!fdatasync(fd)); assert(!gettimeofday(&fw->t2, NULL)); /* Help the kernel to help us. */ assert(!posix_fadvise(fd, 0, 0, POSIX_FADV_DONTNEED)); delay = delay_ms(&fw->t1, &fw->t2); switch (fw->state) { case FW_INC: if (delay > fw->delay_ms) { move_to_search(fw, fw->blocks_per_delay - fw->step / 2, fw->blocks_per_delay); } else if (delay < fw->delay_ms) { inc_step(fw); } else move_to_steady(fw); break; case FW_DEC: if (delay > fw->delay_ms) { dec_step(fw); } else if (delay < fw->delay_ms) { move_to_search(fw, fw->blocks_per_delay, fw->blocks_per_delay + fw->step / 2); } else move_to_steady(fw); break; case FW_SEARCH: if (fw->bpd2 - fw->bpd1 <= 3) { move_to_steady(fw); break; } if (delay > fw->delay_ms) { fw->bpd2 = fw->blocks_per_delay; fw->blocks_per_delay = (fw->bpd1 + fw->bpd2) / 2; } else if (delay < fw->delay_ms) { fw->bpd1 = fw->blocks_per_delay; fw->blocks_per_delay = (fw->bpd1 + fw->bpd2) / 2; } else move_to_steady(fw); break; case FW_STEADY: update_mean(fw); if (delay <= fw->delay_ms) { move_to_inc(fw); } else if (fw->blocks_per_delay > 1) { move_to_dec(fw); } break; default: assert(0); } if (fw->progress) { /* Instantaneous speed. */ double inst_speed = (double)fw->blocks_per_delay * fw->block_size * 1000 / fw->delay_ms; const char *unit = adjust_unit(&inst_speed); double percent; /* The following shouldn't be necessary, but sometimes * the initial free space isn't exactly reported * by the kernel; this issue has been seen on Macs. */ if (fw->total_size < fw->total_written) fw->total_size = fw->total_written; percent = (double)fw->total_written * 100 / fw->total_size; erase(fw->erase); fw->erase = printf("%.2f%% -- %.2f %s/s", percent, inst_speed, unit); assert(fw->erase > 0); if (fw->measurements > 0) fw->erase += pr_time( (fw->total_size - fw->total_written) / get_avg_speed(fw)); fflush(stdout); } start_measurement(fw); }
static void report(const char *prefix, uint64_t i) { double f = (double) (i * SECTOR_SIZE); const char *unit = adjust_unit(&f); printf("%s %.2f %s (%" PRIu64 " sectors)\n", prefix, f, unit, i); }