diff --git a/.agents/rules/coding-style.md b/.agents/rules/coding-style.md new file mode 100644 index 0000000..28d6a98 --- /dev/null +++ b/.agents/rules/coding-style.md @@ -0,0 +1,51 @@ +--- +trigger: model_decision +description: After generating or editing code +--- + +# F3 Coding Style + +## 1. General Principles +- **Language**: C (C17 standard as specified in the Makefile). +- **Line Length**: Maximum 80 columns. The only exception is for literal strings to facilitate grep-ability of output messages. +- **Portability**: Uses POSIX features and includes OS-specific blocks (e.g., `__APPLE__`, `__OpenBSD__`, `__CYGWIN__`) where necessary. + +## 2. Formatting and Indentation +- **Indentation**: Uses **Tabs** for indentation. +- **Braces**: K&R style. Opening braces `{` are on the same line as the statement (`if`, `while`, `for`, `switch`, `struct`, and function definitions). +- **Whitespace**: + - Space after keywords (`if`, `while`, `for`, `switch`, `do`). + - No space between function name and the opening parenthesis `(`. + - Pointers: `char *ptr` (space before the asterisk, not after). + - Alignment: Struct member assignments and function parameters are often aligned using tabs when spanning multiple lines. + +## 3. Naming Conventions +- **Files**: Lowercase with `snake_case` (e.g., `f3read.c`, `libutils.h`). +- **Variables and Functions**: `snake_case` (e.g., `check_chunk`, `bytes_read`). +- **Types (structs, enums, typedefs)**: `snake_case` (e.g., `struct block_stats`, `enum block_state`). +- **Macros and Constants**: `UPPER_CASE` (e.g., `SECTOR_ORDER`, `UNUSED`). +- **Enum Members**: Lowercase with a short prefix (e.g., `bs_unknown`, `bs_good`). + +## 4. Functions and Variables +- **Scope**: Internal helper functions are marked `static`. Small, performance-critical functions are marked `static inline`. +- **Declarations**: Variables are declared at the beginning of their scope. +- **Initialization**: Structs are often initialized using designated initializers or `memset`. + +## 5. Macros +- **Safety**: Multi-line macros use the `do { ... } while (0)` idiom. +- **Parentheses**: Arguments and the final expression are wrapped in parentheses to prevent precedence issues. +- **Alignment**: Backslashes `\` in multi-line macros are aligned. + +## 6. Error Handling and Correctness +- **Assertions**: Heavy use of `assert()` for internal state verification and "can't happen" cases. +- **Error Propagation**: Uses `goto out` patterns for resource cleanup or direct returns of error codes (e.g., `errno`). +- **Safety**: Prefers `snprintf` over `sprintf`. + +## 7. Headers and Includes +- **Include Guards**: Format `HEADER_FILENAME_H` (e.g., `#ifndef HEADER_LIBUTILS_H`). +- **Order**: Standard library headers followed by local project headers. +- **Documentation**: Comments in headers are often aligned using tabs for readability. + +## 8. Comments +- **Style**: Uses C-style comments `/* ... */`. +- **Placement**: Comments are placed above the code they describe or as trailing comments aligned with tabs. \ No newline at end of file diff --git a/.gitignore b/.gitignore index e13da71..a25c2dc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,10 @@ -cscope.out - *.swp *~ doc/_build build/ +a/ +cscope.out +perf.data +perf.data.old diff --git a/.vscode/gdb-sudo.sh b/.vscode/gdb-sudo.sh new file mode 100755 index 0000000..6a7e5f5 --- /dev/null +++ b/.vscode/gdb-sudo.sh @@ -0,0 +1,3 @@ +#!/bin/bash +#sudo /usr/bin/gdb "$@" +pkexec /usr/bin/gdb "$@" diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..ab14d0a --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,66 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "f3brew test", + "type": "cppdbg", + "request": "launch", + "cwd": "${workspaceFolder}", + "program": "${workspaceFolder}/build/f3brew", + "args": ["--debug", "test"], + "MIMode": "gdb", + }, + { + "name": "f3probe unit-test", + "type": "cppdbg", + "request": "launch", + "cwd": "${workspaceFolder}", + "program": "${workspaceFolder}/build/f3probe", + "args": ["--debug-unit-test", "test"], + "MIMode": "gdb", + }, + { + "name": "f3probe simple test", + "type": "cppdbg", + "request": "launch", + "cwd": "${workspaceFolder}", + "program": "${workspaceFolder}/build/f3probe", + "args": ["--debug", "--destructive", "--verbose", "test"], + "MIMode": "gdb", + }, + { + "name": "f3probe test", + "type": "cppdbg", + "request": "launch", + "cwd": "${workspaceFolder}", + "program": "${workspaceFolder}/build/f3probe", + "args": ["--debug-real-size=1G", "--debug-fake-size=1T", "--debug-wrap=40", "--debug-cache-order=21", "--debug-strict-cache", "--destructive", "--verbose", "test"], + "MIMode": "gdb", + // Uncomment the following line to run under sudo: + //"miDebuggerPath": "${workspaceFolder}/.vscode/gdb-sudo.sh" + }, + { + // Create folder "a" before launching. + "name": "f3write a", + "type": "cppdbg", + "request": "launch", + "cwd": "${workspaceFolder}", + "program": "${workspaceFolder}/build/f3write", + "args": ["--end-at=5", "a"], + "MIMode": "gdb", + }, + { + // Create folder "a" before launching. + "name": "f3read a", + "type": "cppdbg", + "request": "launch", + "cwd": "${workspaceFolder}", + "program": "${workspaceFolder}/build/f3read", + "args": ["a"], + "MIMode": "gdb", + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..a98be52 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,29 @@ +{ + "makefile.launchConfigurations": [ + { + "cwd": "${workspaceFolder}", + "binaryPath": "${workspaceFolder}/build/f3brew", + "binaryArgs": [] + }, + { + "cwd": "${workspaceFolder}", + "binaryPath": "${workspaceFolder}/build/f3fix", + "binaryArgs": [] + }, + { + "cwd": "${workspaceFolder}", + "binaryPath": "${workspaceFolder}/build/f3probe", + "binaryArgs": [] + }, + { + "cwd": "${workspaceFolder}", + "binaryPath": "${workspaceFolder}/build/f3read", + "binaryArgs": [] + }, + { + "cwd": "${workspaceFolder}", + "binaryPath": "${workspaceFolder}/build/f3write", + "binaryArgs": [] + } + ] +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..de9b826 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,18 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "label": "make", + "type": "shell", + "command": "make", + "args": ["all", "extra"], + "group": { + "kind": "build", + "isDefault": true + }, + "problemMatcher": "$gcc" + } + ] +} \ No newline at end of file diff --git a/README.rst b/README.rst index eeece2e..3ead8e2 100644 --- a/README.rst +++ b/README.rst @@ -70,7 +70,7 @@ The files of the stable version of F3 are `here `__. The following command uncompresses the files:: - $ unzip f3-9.0.zip + $ unzip f3-10.0.zip .. highlight:: bash diff --git a/changelog b/changelog index 9bcce91..2b87cb3 100644 --- a/changelog +++ b/changelog @@ -1,3 +1,11 @@ +Version 10.0 - Apr 29, 2026 + + * f3probe: New probing algorithm and --verbose flag + * f3brew: Progress report and rate limiting (i.e., --max-write-rate and --max-read-rate) + * f3write/f3read: Report per-file min/max and average speeds + * GitHub Actions: FreeBSD and OpenBSD support + * Move codebase from C99 to C17 and refactor project + Version 9.0 - Mar 27, 2025 * f3read/f3write: Avoid the execution stack to list files diff --git a/doc/conf.py b/doc/conf.py index 93a0185..709c9f5 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -58,9 +58,9 @@ # built documents. # # The short X.Y version. -version = '9.0' +version = '10.0' # The full version, including alpha/beta/rc tags. -release = '9.0' +release = '10.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/doc/contribute.rst b/doc/contribute.rst index 5ec8578..39fe98d 100644 --- a/doc/contribute.rst +++ b/doc/contribute.rst @@ -17,26 +17,26 @@ How can you help? an article about fake flash. The media has not been covering this subject, and having more users aware that fake flash exists will make counterfeiters' life harder. -- If you own fake flash, consider donating them to me. I've been - working on ``f3probe`` to tell in a few seconds if a flash drive is - fake or not. I expect that ``f3probe`` will be a game changer, but I - lack a diverse set of fake flash samples to check my hypotheses. - Before you mail me the card, e-mail me the output you got from - ``f3write``, ``f3read``, and (if possible) ``f3probe`` as well as the - size the card was expected to be; I'm trying to amass a diverse set - of fake flash, not necessarily a large number of them. +- If you own fake flash, consider opening an issue on GitHub to offer + donating it to anyone willing to help with the development of F3. + Even if having someone accepting your offer is a long shot, + it's a bet in the right direction: growing our community. + To help someone identify how your fake drive can support their tests, + include in the issue the output you got from ``f3write``, ``f3read``, and + (if possible) ``f3probe`` as well as the size the card was expected to be. + The output of ``f3probe`` is particularly useful when you use the parameters + ``--verbose``, ``--destructive``, and ``--time-ops``. +- Improve this documentation. New users would benefit from your perspective + on how F3 works and how to use it. Create a pull request with your + improvements and I'll prioritize reviewing it. - If you know how to use F3 well on your platform, write a blog entry about it, and let me know the link so I can post it on this page. You can also send me your name and e-mail to publish on this page as someone that can help other users of your platform. -- (New) If you have a dual-boot machine with Windows and Linux, write a - blog entry that compares h2testw, f3write/f3read, FakeFlashTest.exe, - and f3probe. If you do this comparison, please send me the link to +- If you have a dual-boot machine with Windows and Linux, write a + blog entry that compares h2testw vs f3write/f3read, FakeFlashTest.exe and + ValiDrive vs f3probe. If you do this comparison, please send me the link to publish it on this page. -- If you are able to test F3 on your platform, send me your name and - e-mail, and I'll send to you a request to test new code when it is - available on GitHub before I release it as stable to everyone. I only - have Linux boxes, so other platforms are specially helpful. - If you are able to code, improve F3. I want to keep the code small to easily audit it, so focus on things that will either address a real need, for example, users' requests, or to simplify the code, or fix diff --git a/doc/usage.rst b/doc/usage.rst index 310a746..60f920a 100644 --- a/doc/usage.rst +++ b/doc/usage.rst @@ -599,7 +599,7 @@ in the example below (please use the correct device!): $ sudo build/f3probe --destructive --time-ops /dev/sdb [sudo] password for michel: - F3 probe 9.0 + F3 probe 10.0 Copyright (C) 2010 Digirati Internet LTDA. This is free software; see the source for copying conditions. @@ -674,7 +674,7 @@ outputs: $ sudo build/f3probe --time-ops /dev/sdc [sudo] password for michel: - F3 probe 9.0 + F3 probe 10.0 Copyright (C) 2010 Digirati Internet LTDA. This is free software; see the source for copying conditions. @@ -734,7 +734,7 @@ The execution of ``f3fix`` on my fake drive went as follows: :: $ sudo build/f3fix --last-sec=16477878 /dev/sdb - F3 fix 9.0 + F3 fix 10.0 Copyright (C) 2010 Digirati Internet LTDA. This is free software; see the source for copying conditions. diff --git a/man/f3read.1 b/man/f3read.1 index 95cba95..c531565 100644 --- a/man/f3read.1 +++ b/man/f3read.1 @@ -1,5 +1,5 @@ .\"Text automatically generated by txt2man -.TH F3 "1" "October 2020" "F3 9.0" "test real flash memory capacity" +.TH F3 "1" "October 2020" "F3 10.0" "test real flash memory capacity" .SH NAME \fBf3write, f3read \fP- test real flash memory capacity .SH SYNOPSIS diff --git a/src/f3brew.c b/src/f3brew.c index 34701d6..e06a5d3 100644 --- a/src/f3brew.c +++ b/src/f3brew.c @@ -296,7 +296,7 @@ static void write_blocks(struct device *dev, struct flow *fw, measure(fw, blocks_to_write, NULL); first_pos = next_pos; } - end_measurement(fw, false); + end_measurement(fw); dbuf_free(&dbuf); } @@ -314,6 +314,7 @@ static void test_write_blocks(struct device *dev, fflush(stdout); init_flow(&fw, block_order, total_blocks, max_write_rate, + FW_MAX_BLOCKS_PER_DELAY_NONE, show_progress ? printf_flush_cb : dummy_cb, 0); write_blocks(dev, &fw, first_block, last_block); @@ -452,7 +453,7 @@ static void read_blocks(struct device *dev, struct flow *fw, measure(fw, blocks_to_read, NULL); first_pos = next_pos; } - end_measurement(fw, false); + end_measurement(fw); dbuf_free(&dbuf); if (range.state != bs_unknown) @@ -475,6 +476,7 @@ static void test_read_blocks(struct device *dev, first_block != last_block ? "s" : "", first_block, last_block); init_flow(&fw, block_order, total_blocks, max_read_rate, + FW_MAX_BLOCKS_PER_DELAY_NONE, show_progress ? printf_flush_cb : dummy_cb, 0); read_blocks(dev, &fw, first_block, last_block, &stats); @@ -501,8 +503,8 @@ int main(int argc, char **argv) .strict_cache = false, .first_block = 0, .last_block = -1ULL, - .max_read_rate = 0, - .max_write_rate = 0, + .max_read_rate = FW_MAX_PROCESS_RATE_NONE, + .max_write_rate = FW_MAX_PROCESS_RATE_NONE, /* If stdout isn't a terminal, suppress progress. */ .show_progress = isatty(STDOUT_FILENO), }; diff --git a/src/f3probe.c b/src/f3probe.c index a450b55..ef828aa 100644 --- a/src/f3probe.c +++ b/src/f3probe.c @@ -569,8 +569,8 @@ int main(int argc, char **argv) .verbose = false, /* If stdout isn't a terminal, suppress progress. */ .show_progress = isatty(STDOUT_FILENO), - .max_read_rate = 0, - .max_write_rate = 0, + .max_read_rate = FW_MAX_PROCESS_RATE_NONE, + .max_write_rate = FW_MAX_PROCESS_RATE_NONE, .real_size_byte = 1ULL << 31, .fake_size_byte = 1ULL << 34, .wrap = 31, diff --git a/src/f3read.c b/src/f3read.c index 29bf457..abfc087 100644 --- a/src/f3read.c +++ b/src/f3read.c @@ -276,8 +276,8 @@ static void validate_file(struct flow *fw, struct dynamic_buffer *dbuf, assert((bytes_read & (block_size - 1)) == 0); measure(fw, bytes_read >> block_order, &m); if (m.valid) { - double inst_speed = fw_get_speed(fw, m.blocks, - m.time_ns); + double inst_speed = calc_avg_speed(block_order, + m.blocks, m.time_ns); file_speed_samples++; if (inst_speed > file_max_speed) file_max_speed = inst_speed; @@ -291,7 +291,7 @@ static void validate_file(struct flow *fw, struct dynamic_buffer *dbuf, break; } } - end_measurement(fw, true); + end_measurement(fw); assert(!clock_gettime(CLOCK_MONOTONIC, &file_t2)); print_status(stats); @@ -306,17 +306,22 @@ static void validate_file(struct flow *fw, struct dynamic_buffer *dbuf, double file_avg_speed; if (file_speed_samples >= 2) { - file_avg_speed = fw_get_speed(fw, file_tot_blocks, - file_tot_time_ns); + file_avg_speed = calc_avg_speed(block_order, + file_tot_blocks, file_tot_time_ns); print_avg_min_max_samples(" ", "", file_avg_speed, file_min_speed, file_max_speed, file_speed_samples); } else if (file_time_ns > 0) { - double file_avg_speed = fw_get_speed(fw, - (stats->bytes_read >> block_order), - file_time_ns); - const char *unit = adjust_unit(&file_avg_speed); + const uint64_t blocks_read = + stats->bytes_read >> block_order; assert((stats->bytes_read & (block_size - 1)) == 0); + if (file_tot_blocks == blocks_read && + file_tot_time_ns > 0) { + file_time_ns = file_tot_time_ns; + } + file_avg_speed = calc_avg_speed(block_order, + blocks_read, file_time_ns); + const char *unit = adjust_unit(&file_avg_speed); printf(" Avg: %.2f %s/s", file_avg_speed, unit); } } @@ -370,7 +375,8 @@ static void iterate_files(const char *path, const uint64_t *files, UNUSED(end_at); init_flow(&fw, block_order, get_total_blocks(path, files, block_order), - max_read_rate, progress ? printf_flush_cb : dummy_cb, 0); + max_read_rate, (GIGABYTE_SIZE >> block_order), + progress ? printf_flush_cb : dummy_cb, 0); dbuf_init(&dbuf); printf(" SECTORS ok/corrupted/changed/overwritten\n"); @@ -425,7 +431,7 @@ int main(int argc, char **argv) /* Defaults. */ .start_at = 0, .end_at = LONG_MAX - 1, - .max_read_rate = 0, + .max_read_rate = FW_MAX_PROCESS_RATE_NONE, /* If stdout isn't a terminal, suppress progress. */ .show_progress = isatty(STDOUT_FILENO), }; diff --git a/src/f3write.c b/src/f3write.c index 27daa9f..9ff9ee3 100644 --- a/src/f3write.c +++ b/src/f3write.c @@ -244,8 +244,8 @@ static int create_and_fill_file(struct flow *fw, struct dynamic_buffer *dbuf, written_blocks = bytes_written >> block_order; measure(fw, written_blocks, &m); if (m.valid) { - double inst_speed = fw_get_speed(fw, m.blocks, - m.time_ns); + double inst_speed = calc_avg_speed(block_order, + m.blocks, m.time_ns); file_speed_samples++; if (inst_speed > file_max_speed) file_max_speed = inst_speed; @@ -259,7 +259,7 @@ static int create_and_fill_file(struct flow *fw, struct dynamic_buffer *dbuf, if (saved_errno != 0) break; } - end_measurement(fw, true); + end_measurement(fw); assert(!clock_gettime(CLOCK_MONOTONIC, &file_t2)); close(fd); free(full_fn); @@ -272,15 +272,20 @@ static int create_and_fill_file(struct flow *fw, struct dynamic_buffer *dbuf, assert(remaining_blocks == 0); if (file_speed_samples >= 2) { - file_avg_speed = fw_get_speed(fw, file_tot_blocks, - file_tot_time_ns); + file_avg_speed = calc_avg_speed(block_order, + file_tot_blocks, file_tot_time_ns); print_avg_min_max_samples("OK! ", "\n", file_avg_speed, file_min_speed, file_max_speed, file_speed_samples); } else if (file_time_ns > 0) { - file_avg_speed = fw_get_speed(fw, - total_file_blocks - remaining_blocks, - file_time_ns); + const uint64_t blocks_written = + total_file_blocks - remaining_blocks; + if (file_tot_blocks == blocks_written && + file_tot_time_ns > 0) { + file_time_ns = file_tot_time_ns; + } + file_avg_speed = calc_avg_speed(block_order, + blocks_written, file_time_ns); const char *unit = adjust_unit(&file_avg_speed); printf("OK! Avg: %.2f %s/s\n", file_avg_speed, unit); @@ -341,6 +346,7 @@ static int fill_fs(const char *path, uint64_t start_at, uint64_t end_at, } init_flow(&fw, block_order, free_blocks, max_write_rate, + (GIGABYTE_SIZE >> block_order), progress ? printf_flush_cb : dummy_cb, 0); dbuf_init(&dbuf); for (i = start_at; i <= end_at; i++) { @@ -381,7 +387,7 @@ int main(int argc, char **argv) /* Defaults. */ .start_at = 0, .end_at = LONG_MAX - 1, - .max_write_rate = 0, + .max_write_rate = FW_MAX_PROCESS_RATE_NONE, /* If stdout isn't a terminal, suppress progress. */ .show_progress = isatty(STDOUT_FILENO), }; diff --git a/src/libflow.c b/src/libflow.c index 82029be..793f74c 100644 --- a/src/libflow.c +++ b/src/libflow.c @@ -66,16 +66,19 @@ static inline void move_to_inc_at_start(struct flow *fw) } void init_flow(struct flow *fw, unsigned int block_order, uint64_t total_blocks, - uint64_t max_process_rate, progress_cb cb, unsigned int indent) + uint64_t max_process_rate, uint64_t max_blocks_per_delay, + progress_cb cb, unsigned int indent) { fw->total_blocks = total_blocks; fw->cb = cb; fw->indent = indent; fw->block_order = block_order; fw->blocks_per_delay = 1; + fw->max_blocks_per_delay = max_blocks_per_delay == 0 + ? UINT64_MAX : max_blocks_per_delay; fw->delay_ns = 1000000000ULL; /* 1s */ fw->max_process_rate = max_process_rate == 0 - ? DBL_MAX : max_process_rate * 1024.; + ? DBL_MAX : max_process_rate << KILOBYTE_ORDER; fw->measured_blocks = 0; fw->measured_time_ns = 0; fw->erase = 0; @@ -207,7 +210,7 @@ void start_measurement(struct flow *fw) fw_get_measurements(fw, &blocks, &time_ns); if (time_ns > 0) { report_progress(fw, - (blocks << fw->block_order) * 1000000000.0 / time_ns); + calc_avg_speed(fw->block_order, blocks, time_ns)); } __start_measurement(fw); } @@ -244,10 +247,15 @@ static inline void dec_step(struct flow *fw) } } -static inline void inc_step(struct flow *fw) +static void inc_step(struct flow *fw) { fw->blocks_per_delay += fw->step_blocks; fw->step_blocks *= 2; + + if (fw->blocks_per_delay > fw->max_blocks_per_delay) { + fw->blocks_per_delay = fw->max_blocks_per_delay; + move_to_steady(fw); + } } static inline void move_to_inc(struct flow *fw) @@ -457,14 +465,15 @@ void measure(struct flow *fw, uint64_t processed_blocks, __start_measurement(fw); } -void end_measurement(struct flow *fw, bool measurement_boundary) +void end_measurement(struct flow *fw) { if (fw->processed_blocks > 0) { /* Track progress in between measurement boundaries. */ struct timespec t2; assert(!clock_gettime(CLOCK_MONOTONIC, &t2)); fw->acc_delay_ns += diff_timespec_ns(&fw->t1, &t2); - if (measurement_boundary) { + if (fw->max_blocks_per_delay < UINT64_MAX) { + /* Measurement boundary. */ fw->measured_blocks += fw->processed_blocks; fw->measured_time_ns += fw->acc_delay_ns; fw->processed_blocks = 0; diff --git a/src/libflow.h b/src/libflow.h index 2f12e61..2590bcd 100644 --- a/src/libflow.h +++ b/src/libflow.h @@ -10,6 +10,9 @@ #include "libutils.h" +#define FW_MAX_PROCESS_RATE_NONE (0) +#define FW_MAX_BLOCKS_PER_DELAY_NONE (0) + struct flow; struct flow { @@ -23,10 +26,12 @@ struct flow { unsigned int block_order; /* Delay intended between measurements in nanoseconds. */ uint64_t delay_ns; - /* Increment to apply to @blocks_per_delay. */ + /* Increment to apply to blocks_per_delay. */ uint64_t step_blocks; /* Blocks to process before measurement. */ uint64_t blocks_per_delay; + /* Maximum value that blocks_per_delay can take. */ + uint64_t max_blocks_per_delay; /* Maximum processing rate in bytes per second. */ double max_process_rate; /* Number of measured blocks. */ @@ -52,7 +57,7 @@ struct flow { /* Number of blocks processed since last measurement. */ uint64_t processed_blocks; /* - * Accumulated delay before @processed_blocks reaches @blocks_per_delay + * Accumulated delay before processed_blocks reaches blocks_per_delay * in nanoseconds. */ uint64_t acc_delay_ns; @@ -62,11 +67,22 @@ struct flow { struct timespec t1; }; -/* If @max_process_rate == 0, the maximum processing rate is infinity. - * The unit of @max_process_rate is KB per second. +/* + * If max_process_rate == FW_MAX_PROCESS_RATE_NONE, + * the maximum processing rate is infinity. + * The unit of max_process_rate is KB per second. + * + * If max_blocks_per_delay == FW_MAX_BLOCKS_PER_DELAY_NONE, + * there is no limit to blocks_per_delay. + * max_blocks_per_delay is meant to act as a measurement boundary when + * end_measurement() is called at a given limit (e.g. f3write and f3read + * call it at the end of each 1GB file). Thanks to this limit, f3write and + * f3read can make per-file measurements and libflow can still detect when + * the drive slows down. */ void init_flow(struct flow *fw, unsigned int block_order, uint64_t total_blocks, - uint64_t max_process_rate, progress_cb cb, unsigned int indent); + uint64_t max_process_rate, uint64_t max_blocks_per_delay, + progress_cb cb, unsigned int indent); /* Total number of blocks already processed. */ static inline uint64_t fw_get_total_processed_blocks(const struct flow *fw) @@ -101,13 +117,6 @@ static inline void fw_get_measurements(const struct flow *fw, *time_ns = fw->measured_time_ns + fw->acc_delay_ns; } -/* Return speed in bytes per second. */ -static inline double fw_get_speed(const struct flow *fw, uint64_t blocks, - uint64_t time_ns) -{ - return (blocks << fw->block_order) * 1000000000.0 / time_ns; -} - uint64_t get_rem_chunk_blocks(const struct flow *fw); struct fw_measurement { @@ -120,7 +129,7 @@ void start_measurement(struct flow *fw); void measure(struct flow *fw, uint64_t processed_blocks, struct fw_measurement *m); void clear_progress(struct flow *fw); -void end_measurement(struct flow *fw, bool measurement_boundary); +void end_measurement(struct flow *fw); void print_avg_seq_speed(const struct flow *fw, const char *speed_type, bool use_sectors); diff --git a/src/libprobe.c b/src/libprobe.c index 8c14bf5..a8cb174 100644 --- a/src/libprobe.c +++ b/src/libprobe.c @@ -79,7 +79,7 @@ static int write_random_blocks(struct device *dev, const uint64_t pos[], return true; measure(&rwi->randw_fw, 1, NULL); } - end_measurement(&rwi->randw_fw, false); + end_measurement(&rwi->randw_fw); return false; } @@ -128,7 +128,7 @@ static int write_blocks(struct device *dev, measure(&rwi->seqw_fw, blocks_to_write, NULL); first_pos = next_pos; } - end_measurement(&rwi->seqw_fw, false); + end_measurement(&rwi->seqw_fw); return false; } @@ -231,11 +231,11 @@ static int find_first_x_block(struct device *dev, /* Found the first x_block. */ *pfirst_x_block_idx = i; *pstate = bs; - end_measurement(&rwi->randr_fw, false); + end_measurement(&rwi->randr_fw); return false; } } - end_measurement(&rwi->randr_fw, false); + end_measurement(&rwi->randr_fw); not_found: *pfirst_x_block_idx = n_blocks; @@ -818,9 +818,12 @@ int probe_device(struct device *dev, struct probe_results *results, /* We initialize total_blocks to 0 because inc_total_blocks() is called * to update it when new blocks become available. */ - init_flow(&rwi.seqw_fw, block_order, 0, max_write_rate, fw_cb, 0); - init_flow(&rwi.randw_fw, block_order, 0, max_write_rate, fw_cb, 0); - init_flow(&rwi.randr_fw, block_order, 0, max_read_rate, fw_cb, 0); + init_flow(&rwi.seqw_fw, block_order, 0, max_write_rate, + FW_MAX_BLOCKS_PER_DELAY_NONE, fw_cb, 0); + init_flow(&rwi.randw_fw, block_order, 0, max_write_rate, + FW_MAX_BLOCKS_PER_DELAY_NONE, fw_cb, 0); + init_flow(&rwi.randr_fw, block_order, 0, max_read_rate, + FW_MAX_BLOCKS_PER_DELAY_NONE, fw_cb, 0); /* @left_pos must point to a good block. * We just point to the last block of the first 1MB of the card diff --git a/src/libprobe.h b/src/libprobe.h index 4b372b2..b1a4c19 100644 --- a/src/libprobe.h +++ b/src/libprobe.h @@ -5,6 +5,7 @@ #include "libutils.h" #include "libdevs.h" +#include "libflow.h" /* Enable usage of FW_MAX_PROCESS_RATE_NONE */ /* Provide an upper bound on the number of *unique* blocks that will be written * when probe_device(dev) is called. diff --git a/src/libutils.c b/src/libutils.c index c459529..4666a6b 100644 --- a/src/libutils.c +++ b/src/libutils.c @@ -368,7 +368,7 @@ void report_io_speed(unsigned int indent, progress_cb cb, const char *prefix, return; } - speed = (blocks << block_order) * 1000000000.0 / time_ns; + speed = calc_avg_speed(block_order, blocks, time_ns); unit = adjust_unit(&speed); nsec_to_str(time_ns, time_str); cb(indent, "%s %.2f %s/s (%" PRIu64 " %s%s / %s)\n", diff --git a/src/libutils.h b/src/libutils.h index e79334e..8f7e054 100644 --- a/src/libutils.h +++ b/src/libutils.h @@ -143,4 +143,11 @@ void report_io_speed(unsigned int indent, progress_cb cb, const char *prefix, uint64_t blocks, const char *block_unit, uint64_t time_ns, unsigned int block_order); +/* Return speed in bytes per second. */ +static inline double calc_avg_speed(unsigned int block_order, uint64_t blocks, + uint64_t time_ns) +{ + return (blocks << block_order) * 1000000000.0 / time_ns; +} + #endif /* HEADER_LIBUTILS_H */ diff --git a/src/version.h b/src/version.h index afcd6e0..ad39c8a 100644 --- a/src/version.h +++ b/src/version.h @@ -1,6 +1,6 @@ #ifndef HEADER_VERSION_H #define HEADER_VERSION_H -#define F3_STR_VERSION "9.0" +#define F3_STR_VERSION "10.0" #endif /* HEADER_VERSION_H */