stress-writeread.cpp 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. /*
  2. * Copyright (c) 2021, the SerenityOS developers.
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/Random.h>
  7. #include <LibCore/ArgsParser.h>
  8. #include <errno.h>
  9. #include <fcntl.h>
  10. #include <inttypes.h>
  11. #include <string.h>
  12. #include <sys/stat.h>
  13. #include <unistd.h>
  14. bool verify_block(int fd, int seed, off_t block, AK::ByteBuffer& buffer);
  15. bool write_block(int fd, int seed, off_t block, AK::ByteBuffer& buffer);
  16. bool verify_block(int fd, int seed, off_t block, AK::ByteBuffer& buffer)
  17. {
  18. auto offset = block * buffer.size();
  19. auto rs = lseek(fd, offset, SEEK_SET);
  20. if (rs < 0) {
  21. fprintf(stderr, "Couldn't seek to block %" PRIi64 " (offset %" PRIi64 ") while verifying: %s\n", block, offset, strerror(errno));
  22. return false;
  23. }
  24. auto rw = read(fd, buffer.data(), buffer.size());
  25. if (rw != static_cast<int>(buffer.size())) {
  26. fprintf(stderr, "Failure to read block %" PRIi64 ": %s\n", block, strerror(errno));
  27. return false;
  28. }
  29. srand((seed + 1) * (block + 1));
  30. for (size_t i = 0; i < buffer.size(); i++) {
  31. if (buffer[i] != rand() % 256) {
  32. fprintf(stderr, "Discrepancy detected at block %" PRIi64 " offset %zd\n", block, i);
  33. return false;
  34. }
  35. }
  36. return true;
  37. }
  38. bool write_block(int fd, int seed, off_t block, AK::ByteBuffer& buffer)
  39. {
  40. auto offset = block * buffer.size();
  41. auto rs = lseek(fd, offset, SEEK_SET);
  42. if (rs < 0) {
  43. fprintf(stderr, "Couldn't seek to block %" PRIi64 " (offset %" PRIi64 ") while verifying: %s\n", block, offset, strerror(errno));
  44. return false;
  45. }
  46. srand((seed + 1) * (block + 1));
  47. for (size_t i = 0; i < buffer.size(); i++)
  48. buffer[i] = rand();
  49. auto rw = write(fd, buffer.data(), buffer.size());
  50. if (rw != static_cast<int>(buffer.size())) {
  51. fprintf(stderr, "Failure to write block %" PRIi64 ": %s\n", block, strerror(errno));
  52. return false;
  53. }
  54. return true;
  55. }
  56. int main(int argc, char** argv)
  57. {
  58. const char* target = nullptr;
  59. int min_block_offset = 0;
  60. int block_length = 2048;
  61. int block_size = 512;
  62. int count = 1024;
  63. int rng_seed = 0;
  64. bool paranoid_mode = false;
  65. bool random_mode = false;
  66. bool stop_mode = false;
  67. bool uninitialized_mode = false;
  68. Core::ArgsParser args_parser;
  69. args_parser.add_option(min_block_offset, "Minimum block offset to consider", "min-offset", 'o', "size");
  70. args_parser.add_option(block_length, "Number of blocks to consider", "length", 's', "size");
  71. args_parser.add_option(block_size, "Block size", "block-size", 'b', "size");
  72. args_parser.add_option(count, "Number of write/read cycles to run", "number", 'n', "number");
  73. args_parser.add_option(rng_seed, "Random number generator seed", "seed", 'S', "number");
  74. args_parser.add_option(paranoid_mode, "Check entire range for consistency after each write", "paranoid", 'p');
  75. args_parser.add_option(random_mode, "Write one block inside range at random", "random", 'r');
  76. args_parser.add_option(stop_mode, "Stop after first error", "abort-on-error", 'a');
  77. args_parser.add_option(uninitialized_mode, "Don't pre-initialize block range", "uninitialized", 'u');
  78. args_parser.add_positional_argument(target, "Target device/file path", "target");
  79. args_parser.parse(argc, argv);
  80. auto buffer_result = AK::ByteBuffer::create_zeroed(block_size);
  81. if (!buffer_result.has_value()) {
  82. warnln("Failed to allocate a buffer of {} bytes", block_size);
  83. return EXIT_FAILURE;
  84. }
  85. auto buffer = buffer_result.release_value();
  86. int fd = open(target, O_CREAT | O_RDWR, 0666);
  87. if (fd < 0) {
  88. perror("Couldn't create target file");
  89. return EXIT_FAILURE;
  90. }
  91. if (!uninitialized_mode) {
  92. int old_percent = -100;
  93. for (int i = min_block_offset; i < min_block_offset + block_length; i++) {
  94. int percent;
  95. if (block_length <= 1)
  96. percent = 100;
  97. else
  98. percent = 100 * (i - min_block_offset) / (block_length - 1);
  99. if (old_percent != percent) {
  100. printf("Pre-initializing entire block range (%3d%%)...\n", percent);
  101. old_percent = percent;
  102. }
  103. if (!write_block(fd, rng_seed, i, buffer))
  104. return EXIT_FAILURE;
  105. }
  106. }
  107. int r = EXIT_SUCCESS;
  108. for (int i = 0; i < count; i++) {
  109. printf("(%d/%d)\tPass %d...\n", i + 1, count, i + 1);
  110. for (int j = min_block_offset; j < min_block_offset + block_length; j++) {
  111. off_t block;
  112. if (random_mode)
  113. while ((block = AK::get_random<off_t>()) < 0)
  114. ;
  115. else
  116. block = j;
  117. block = min_block_offset + block % block_length;
  118. if (paranoid_mode) {
  119. for (int k = min_block_offset; j < min_block_offset + block_length; j++) {
  120. if (!verify_block(fd, rng_seed, k, buffer)) {
  121. if (stop_mode)
  122. return EXIT_FAILURE;
  123. else
  124. r = EXIT_FAILURE;
  125. }
  126. }
  127. } else {
  128. if (!verify_block(fd, rng_seed, block, buffer)) {
  129. if (stop_mode)
  130. return EXIT_FAILURE;
  131. else
  132. r = EXIT_FAILURE;
  133. }
  134. }
  135. if (!write_block(fd, rng_seed, block, buffer)) {
  136. if (stop_mode)
  137. return EXIT_FAILURE;
  138. else
  139. r = EXIT_FAILURE;
  140. }
  141. }
  142. }
  143. close(fd);
  144. return r;
  145. }