From 97f89e03600d3f1403b387eecca5b26946060801 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Wed, 22 Apr 2009 13:01:54 +0000 Subject: [PATCH] * All files now get unique, and verifiable contents. * Added argument -c|--check-interval to determine how often the file contents are checked. * Added some statistics. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@30325 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- .../random_file_actions.cpp | 448 ++++++++++++++---- 1 file changed, 355 insertions(+), 93 deletions(-) diff --git a/src/tests/system/random_file_actions/random_file_actions.cpp b/src/tests/system/random_file_actions/random_file_actions.cpp index 9b493457f9..27c5a1b706 100644 --- a/src/tests/system/random_file_actions/random_file_actions.cpp +++ b/src/tests/system/random_file_actions/random_file_actions.cpp @@ -17,7 +17,7 @@ #include #include -#include +#include enum file_action { @@ -33,7 +33,19 @@ enum file_action { kNumActions }; -typedef std::vector EntryVector; +struct block_identifier { + off_t offset; + uint32 identifier; + uint16 data[0]; +}; + +struct entry { + std::string name; + uint32 identifier; + off_t size; +}; + +typedef std::vector EntryVector; const uint32 kDefaultDirCount = 1; @@ -55,9 +67,14 @@ static bool sVerbose = false; static off_t sMaxFileSize = kDefaultMaxFileSize; static uint32 sCount = 0; +static off_t sWriteTotal = 0; +static off_t sReadTotal = 0; +static bigtime_t sWriteTime = 0; +static bigtime_t sReadTime = 0; + static off_t -to_size(const char* valueWithUnit) +string_to_size(const char* valueWithUnit) { char* unit; off_t size = strtoull(valueWithUnit, &unit, 10); @@ -73,26 +90,113 @@ to_size(const char* valueWithUnit) } +static std::string +size_to_string(off_t size) +{ + char buffer[256]; + + if (size > 10LL * 1024 * 1024 * 1024) + snprintf(buffer, sizeof(buffer), "%g GB", size / (1024.0 * 1024 * 1024)); + else if (size > 10 * 1024 * 1024) + snprintf(buffer, sizeof(buffer), "%g MB", size / (1024.0 * 1024)); + else if (size > 10 * 1024) + snprintf(buffer, sizeof(buffer), "%g KB", size / (1024.0)); + else + snprintf(buffer, sizeof(buffer), "%lld B", size); + + return buffer; +} + + +static std::string +time_to_string(bigtime_t usecs) +{ + static const bigtime_t kSecond = 1000000ULL; + static const bigtime_t kHour = 3600 * kSecond; + static const bigtime_t kMinute = 60 * kSecond; + + uint32 hours = usecs / kHour; + uint32 minutes = usecs / kMinute; + uint32 seconds = usecs / kSecond; + + char buffer[256]; + if (usecs >= kHour) { + minutes %= 60; + seconds %= 60; + snprintf(buffer, sizeof(buffer), "%luh %02lum %02lus", hours, minutes, + seconds); + } else if (usecs > 100 * kSecond) { + seconds %= 60; + snprintf(buffer, sizeof(buffer), "%lum %02lus", minutes, seconds); + } else + snprintf(buffer, sizeof(buffer), "%gs", 1.0 * usecs / kSecond); + + return buffer; +} + + static void usage(int status) { fprintf(stderr, "Usage: %s [options]\n" - "Performs some random file actions.\n" + "Performs some random file actions for file system testing.\n" "\n" - " -r, --runs=\tThe number of actions to perform. Defaults to 100.\n" - " -s, --seed=\tThe base seed to use for the random numbers.\n" + " -r, --runs=\t\tThe number of actions to perform.\n" + "\t\t\t\tDefaults to %lu.\n" + " -s, --seed=\t\tThe base seed to use for the random numbers.\n" " -f, --file-count=\tThe maximal number of files to create.\n" + "\t\t\t\tDefaults to %lu.\n" " -d, --dir-count=\tThe maximal number of directories to create.\n" + "\t\t\t\tDefaults to %lu.\n" " -m, --max-file-size=\tThe maximal file size of the files.\n" - " -n, --no-cache\t\tDisables the file cache when doing I/O on a file.\n" - " -v, --verbose\t\tShow the actions as being performed\n", - kProgramName); + "\t\t\t\tDefaults to %lld.\n" + " -c, --check-interval=\tCheck after every runs. " + "Defaults to 0,\n" + "\t\t\t\tmeaning only check once at the end.\n" + " -n, --no-cache\t\tDisables the file cache when doing I/O on\n" + "\t\t\t\ta file.\n" + " -k, --keep-dirty\t\tDo not remove the working files on quit.\n" + " -v, --verbose\t\t\tShow the actions as being performed\n", + kProgramName, kDefaultRunCount, kDefaultFileCount, kDefaultDirCount, + kDefaultMaxFileSize); exit(status); } +static void +error(const char* format, ...) +{ + va_list args; + va_start(args, format); + + fprintf(stderr, "%s: ", kProgramName); + vfprintf(stderr, format, args); + fputc('\n', stderr); + + va_end(args); + fflush(stderr); + + exit(1); +} + + +static void +warning(const char* format, ...) +{ + va_list args; + va_start(args, format); + + fprintf(stderr, "%s: ", kProgramName); + vfprintf(stderr, format, args); + fputc('\n', stderr); + + va_end(args); + fflush(stderr); +} + + static void verbose(const char* format, ...) { @@ -106,6 +210,7 @@ verbose(const char* format, ...) putchar('\n'); va_end(args); + fflush(stdout); } @@ -126,7 +231,7 @@ choose_index(const EntryVector& entries) static inline const std::string& choose_parent(const EntryVector& entries) { - return entries[choose_index(entries)]; + return entries[choose_index(entries)].name; } @@ -145,19 +250,28 @@ create_name(const std::string& parent, const char* prefix) static int open_file(const std::string& name, int mode) { - return open(name.c_str(), mode | (sDisableFileCache ? O_DIRECT : 0)); + return open(name.c_str(), mode | (sDisableFileCache ? O_DIRECT : 0), 0666); } static void -generate_block(char* block, const std::string& name, off_t offset) +generate_block(char* buffer, const struct entry& entry, off_t offset) { - // TODO: generate unique and verifiable file contents. + block_identifier* block = (block_identifier*)buffer; + block->offset = offset; + block->identifier = entry.identifier; + + uint32 count = (kBlockSize - offsetof(block_identifier, data)) / 2; + offset += offsetof(block_identifier, data); + + for (uint32 i = 0; i < count; i++) { + block->data[i] = offset + i * 2; + } } static void -write_blocks(int fd, const std::string& name, bool append = false) +write_blocks(int fd, struct entry& entry, bool append = false) { off_t size = min_c(rand() % sMaxFileSize, sMaxFileSize); off_t offset = 0; @@ -165,11 +279,8 @@ write_blocks(int fd, const std::string& name, bool append = false) if (append) { // in the append case, we need to check the file size struct stat stat; - if (fstat(fd, &stat) != 0) { - fprintf(stderr, "%s: stat file failed: %s\n", kProgramName, - strerror(errno)); - exit(1); - } + if (fstat(fd, &stat) != 0) + error("stat file failed: %s\n", strerror(errno)); if (size + stat.st_size > sMaxFileSize) size = sMaxFileSize - stat.st_size; @@ -179,20 +290,132 @@ write_blocks(int fd, const std::string& name, bool append = false) verbose(" write %lu bytes", size); + entry.size += size; + uint32 blockOffset = offset % kBlockSize; + sWriteTotal += size; + + bigtime_t start = system_time(); + while (size > 0) { char block[kBlockSize]; - generate_block(block, name, offset); + generate_block(block, entry, offset - blockOffset); - ssize_t toWrite = min_c(size, kBlockSize); - ssize_t bytesWritten = write(fd, block, toWrite); - if (bytesWritten != toWrite) { - fprintf(stderr, "%s: writing failed: %s\n", kProgramName, - strerror(errno)); - exit(1); - } + ssize_t toWrite = min_c(size, kBlockSize - blockOffset); + ssize_t bytesWritten = write(fd, block + blockOffset, toWrite); + if (bytesWritten != toWrite) + error("writing failed: %s", strerror(errno)); offset += toWrite; size -= toWrite; + blockOffset = 0; + } + + sWriteTime += system_time() - start; +} + + +static void +dump_block(const char* buffer, int size, const char* prefix) +{ + const int DUMPED_BLOCK_SIZE = 16; + int i; + + for (i = 0; i < size;) { + int start = i; + + printf("%s%04x ", prefix, i); + for (; i < start + DUMPED_BLOCK_SIZE; i++) { + if (!(i % 4)) + printf(" "); + + if (i >= size) + printf(" "); + else + printf("%02x", *(unsigned char*)(buffer + i)); + } + printf(" "); + + for (i = start; i < start + DUMPED_BLOCK_SIZE; i++) { + if (i < size) { + char c = buffer[i]; + + if (c < 30) + printf("."); + else + printf("%c", c); + } else + break; + } + printf("\n"); + } +} + + +static void +check_files(EntryVector& files) +{ + verbose("check all files..."); + + for (EntryVector::iterator i = files.begin(); i != files.end(); i++) { + const struct entry& file = *i; + + int fd = open_file(file.name, O_RDONLY); + if (fd < 0) { + error("opening file \"%s\" failed: %s", file.name.c_str(), + strerror(errno)); + } + + // first check if size matches + + struct stat stat; + if (fstat(fd, &stat) != 0) { + error("stat file \"%s\" failed: %s", file.name.c_str(), + strerror(errno)); + } + + if (file.size != stat.st_size) { + warning("size does not match for \"%s\"! Expected %lld " + "reported %lld", file.name.c_str(), file.size, stat.st_size); + close(fd); + continue; + } + + // check contents + + off_t size = file.size; + off_t offset = 0; + sReadTotal += size; + + bigtime_t start = system_time(); + + while (size > 0) { + // read block + char block[kBlockSize]; + ssize_t toRead = min_c(size, kBlockSize); + ssize_t bytesRead = read(fd, block, toRead); + if (bytesRead != toRead) { + error("reading \"%s\" failed: %s", file.name.c_str(), + strerror(errno)); + } + + // compare with generated block + char generatedBlock[kBlockSize]; + generate_block(generatedBlock, file, offset); + + if (memcmp(generatedBlock, block, bytesRead) != 0) { + dump_block(generatedBlock, bytesRead, "generated: "); + dump_block(block, bytesRead, "read: "); + error("block at %lld differ in \"%s\"!", offset, + file.name.c_str()); + } + + offset += toRead; + size -= toRead; + } + + sReadTime += system_time() - start; + + close(fd); } } @@ -208,13 +431,15 @@ create_dir(EntryVector& dirs) verbose("create dir %s", name.c_str()); - if (mkdir(name.c_str(), 0777) != 0) { - fprintf(stderr, "%s: creating dir \"%s\" failed: %s\n", kProgramName, - name.c_str(), strerror(errno)); - exit(1); - } + if (mkdir(name.c_str(), 0777) != 0) + error("creating dir \"%s\" failed: %s", name.c_str(), strerror(errno)); - dirs.push_back(name); + struct entry dir; + dir.name = name; + dir.identifier = sCount; + dir.size = 0; + + dirs.push_back(dir); } @@ -225,19 +450,18 @@ remove_dir(EntryVector& dirs) return; int index = choose_index(dirs); + const std::string& name = dirs[index].name; - if (rmdir(dirs[index].c_str()) != 0) { + if (rmdir(name.c_str()) != 0) { if (errno == ENOTEMPTY || errno == EEXIST) { // TODO: in rare cases, we could remove all files return; } - fprintf(stderr, "%s: removing dir \"%s\" failed: %s\n", kProgramName, - dirs[index].c_str(), strerror(errno)); - exit(1); + error("removing dir \"%s\" failed: %s", name.c_str(), strerror(errno)); } - verbose("removed dir %s", dirs[index].c_str()); + verbose("removed dir %s", name.c_str()); EntryVector::iterator iterator = dirs.begin(); dirs.erase(iterator + index); @@ -253,15 +477,18 @@ create_file(const EntryVector& dirs, EntryVector& files) verbose("create file %s", name.c_str()); int fd = open_file(name, O_RDWR | O_CREAT | O_TRUNC); - if (fd < 0) { - fprintf(stderr, "%s: creating file \"%s\" failed: %s\n", kProgramName, - name.c_str(), strerror(errno)); - exit(1); - } + if (fd < 0) + error("creating file \"%s\" failed: %s", name.c_str(), strerror(errno)); + + struct entry file; + file.name = name; + file.identifier = sCount; + file.size = 0; + write_blocks(fd, file); + + files.push_back(file); - write_blocks(fd, name); close(fd); - files.push_back(name); } @@ -272,14 +499,12 @@ remove_file(EntryVector& files) return; int index = choose_index(files); + const std::string& name = files[index].name; - if (remove(files[index].c_str()) != 0) { - fprintf(stderr, "%s: removing file \"%s\" failed: %s\n", kProgramName, - files[index].c_str(), strerror(errno)); - exit(1); - } + if (remove(name.c_str()) != 0) + error("removing file \"%s\" failed: %s", name.c_str(), strerror(errno)); - verbose("removed file %s", files[index].c_str()); + verbose("removed file %s", name.c_str()); EntryVector::iterator iterator = files.begin(); files.erase(iterator + index); @@ -293,83 +518,84 @@ rename_file(const EntryVector& dirs, EntryVector& files) return; std::string parent = choose_parent(dirs); - std::string name = create_name(parent, "renamed-file"); + std::string newName = create_name(parent, "renamed-file"); int index = choose_index(files); + const std::string& oldName = files[index].name; - verbose("rename file \"%s\" to \"%s\"", files[index].c_str(), name.c_str()); + verbose("rename file \"%s\" to \"%s\"", oldName.c_str(), newName.c_str()); - if (rename(files[index].c_str(), name.c_str()) != 0) { - fprintf(stderr, "%s: renaming file \"%s\" to \"%s\" failed: %s\n", - kProgramName, files[index].c_str(), name.c_str(), strerror(errno)); - exit(1); + if (rename(oldName.c_str(), newName.c_str()) != 0) { + error("renaming file \"%s\" to \"%s\" failed: %s", oldName.c_str(), + newName.c_str(), strerror(errno)); } - files[index] = name; + files[index].name = newName; } static void -append_file(const EntryVector& files) +append_file(EntryVector& files) { if (files.empty()) return; - const std::string& name = files[choose_index(files)]; + struct entry& file = files[choose_index(files)]; - verbose("append to \"%s\"", name.c_str()); + verbose("append to \"%s\"", file.name.c_str()); - int fd = open_file(name, O_WRONLY | O_APPEND); + int fd = open_file(file.name, O_WRONLY | O_APPEND); if (fd < 0) { - fprintf(stderr, "%s: appending to file \"%s\" failed: %s\n", - kProgramName, name.c_str(), strerror(errno)); - exit(1); + error("appending to file \"%s\" failed: %s", file.name.c_str(), + strerror(errno)); } - write_blocks(fd, name, true); + write_blocks(fd, file, true); close(fd); } static void -replace_file(const EntryVector& files) +replace_file(EntryVector& files) { if (files.empty()) return; - const std::string& name = files[choose_index(files)]; + struct entry& file = files[choose_index(files)]; - verbose("replace \"%s\"", name.c_str()); + verbose("replace \"%s\"", file.name.c_str()); - int fd = open_file(name, O_CREAT | O_WRONLY | O_TRUNC); + int fd = open_file(file.name, O_CREAT | O_WRONLY | O_TRUNC); if (fd < 0) { - fprintf(stderr, "%s: replacing file \"%s\" failed: %s\n", - kProgramName, name.c_str(), strerror(errno)); - exit(1); + error("replacing file \"%s\" failed: %s", file.name.c_str(), + strerror(errno)); } - write_blocks(fd, name); + file.size = 0; + write_blocks(fd, file); + close(fd); } static void -truncate_file(const EntryVector& files) +truncate_file(EntryVector& files) { if (files.empty()) return; - const std::string& name = files[choose_index(files)]; + struct entry& file = files[choose_index(files)]; - verbose("truncate \"%s\"", name.c_str()); + verbose("truncate \"%s\"", file.name.c_str()); - int fd = open_file(name, O_WRONLY | O_TRUNC); + int fd = open_file(file.name, O_WRONLY | O_TRUNC); if (fd < 0) { - fprintf(stderr, "%s: truncating file \"%s\" failed: %s\n", - kProgramName, name.c_str(), strerror(errno)); - exit(1); + error("truncating file \"%s\" failed: %s", file.name.c_str(), + strerror(errno)); } + file.size = 0; + close(fd); } @@ -387,8 +613,10 @@ main(int argc, char** argv) {"seed", required_argument, 0, 's'}, {"file-count", required_argument, 0, 'f'}, {"dir-count", required_argument, 0, 'd'}, + {"check-interval", required_argument, 0, 'c'}, {"max-file-size", required_argument, 0, 'm'}, {"no-cache", no_argument, 0, 'n'}, + {"keep-dirty", no_argument, 0, 'k'}, {"verbose", no_argument, 0, 'v'}, {"help", no_argument, 0, 'h'}, {NULL} @@ -397,9 +625,11 @@ main(int argc, char** argv) uint32 maxFileCount = kDefaultFileCount; uint32 maxDirCount = kDefaultDirCount; uint32 runs = kDefaultRunCount; + uint32 checkInterval = 0; + bool keepDirty = false; int c; - while ((c = getopt_long(argc, argv, "r:s:f:d:m:nvh", kOptions, + while ((c = getopt_long(argc, argv, "r:s:f:d:c:m:nkvh", kOptions, NULL)) != -1) { switch (c) { case 0: @@ -429,15 +659,24 @@ main(int argc, char** argv) else if (maxDirCount > kMaxDirCount) maxDirCount = kMaxDirCount; break; + case 'c': + // check interval + checkInterval = strtoul(optarg, NULL, 0); + if (checkInterval < 0) + checkInterval = 0; + break; case 'm': { // max file size - sMaxFileSize = to_size(optarg); + sMaxFileSize = string_to_size(optarg); break; } case 'n': sDisableFileCache = true; break; + case 'k': + keepDirty = true; + break; case 'v': sVerbose = true; break; @@ -453,18 +692,24 @@ main(int argc, char** argv) EntryVector dirs; EntryVector files; - std::string baseName = "./random_file_temp"; - if (mkdir(baseName.c_str(), 0777) != 0 && errno != EEXIST) { + struct entry base; + base.name = "./random_file_temp"; + base.identifier = 0; + + if (mkdir(base.name.c_str(), 0777) != 0 && errno != EEXIST) { fprintf(stderr, "%s: cannot create base directory: %s\n", kProgramName, strerror(errno)); return 1; } - dirs.push_back(baseName); - + dirs.push_back(base); + for (uint32 run = 0; run < runs; run++) { file_action action = choose_action(); - + + if ((run % 100) == 0) + verbose("run %lu", run + 1); + switch (action) { case kCreateFile: if (files.size() > maxFileCount / 2) { @@ -514,14 +759,31 @@ main(int argc, char** argv) default: break; } + + if (checkInterval != 0 && run > 0 && (run % checkInterval) == 0 + && run + 1 < runs) + check_files(files); } - for (int i = files.size(); i-- > 0;) { - remove_file(files); - } - for (int i = dirs.size(); i-- > 0;) { - remove_dir(dirs); + check_files(files); + + if (!keepDirty) { + for (int i = files.size(); i-- > 0;) { + remove_file(files); + } + for (int i = dirs.size(); i-- > 0;) { + remove_dir(dirs); + } } + printf("%s written in %s, %s/s\n", size_to_string(sWriteTotal).c_str(), + time_to_string(sWriteTime).c_str(), + size_to_string(int64(0.5 + sWriteTotal + / (sWriteTime / 1000000.0))).c_str()); + printf("%s read in %s, %s/s\n", size_to_string(sReadTotal).c_str(), + time_to_string(sReadTime).c_str(), + size_to_string(int64(0.5 + sReadTotal + / (sReadTime / 1000000.0))).c_str()); + return 0; }