├── .clang-format ├── AUTHORS ├── COPYING ├── ChangeLog ├── Makefile.am ├── NEWS ├── README ├── build.zig ├── configure.ac ├── man ├── Makefile.am └── blogbench.8 └── src ├── Makefile.am ├── blogbench.c ├── blogbench.h ├── blogbench_p.h ├── bsd-getopt_long.c ├── bsd-getopt_long.h ├── commenter.c ├── globals.h ├── helpers.c ├── process.c ├── reader.c ├── rewriter.c └── writer.c /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | AccessModifierOffset: -4 4 | AlignAfterOpenBracket: Align 5 | AlignConsecutiveMacros: true 6 | AlignConsecutiveAssignments: true 7 | AlignConsecutiveBitFields: true 8 | AlignConsecutiveDeclarations: true 9 | AlignEscapedNewlines: true 10 | AlignOperands: true 11 | AlignTrailingComments: false 12 | AllowAllArgumentsOnNextLine: true 13 | AllowAllConstructorInitializersOnNextLine: true 14 | AllowAllParametersOfDeclarationOnNextLine: true 15 | AllowShortEnumsOnASingleLine: true 16 | AllowShortBlocksOnASingleLine: false 17 | AllowShortCaseLabelsOnASingleLine: false 18 | AllowShortFunctionsOnASingleLine: false 19 | AllowShortLambdasOnASingleLine: All 20 | AllowShortIfStatementsOnASingleLine: Never 21 | AllowShortLoopsOnASingleLine: false 22 | AlwaysBreakAfterDefinitionReturnType: None 23 | AlwaysBreakAfterReturnType: TopLevelDefinitions 24 | AlwaysBreakBeforeMultilineStrings: true 25 | AlwaysBreakTemplateDeclarations: MultiLine 26 | AttributeMacros: 27 | - __capability 28 | BinPackArguments: true 29 | BinPackParameters: true 30 | BraceWrapping: 31 | AfterCaseLabel: false 32 | AfterClass: false 33 | AfterControlStatement: Never 34 | AfterEnum: false 35 | AfterFunction: true 36 | AfterNamespace: false 37 | AfterObjCDeclaration: false 38 | AfterStruct: false 39 | AfterUnion: false 40 | AfterExternBlock: false 41 | BeforeCatch: false 42 | BeforeElse: false 43 | BeforeLambdaBody: false 44 | BeforeWhile: false 45 | IndentBraces: false 46 | SplitEmptyFunction: true 47 | SplitEmptyRecord: true 48 | SplitEmptyNamespace: true 49 | BreakBeforeBinaryOperators: None 50 | BreakBeforeConceptDeclarations: true 51 | BreakBeforeBraces: WebKit 52 | BreakBeforeInheritanceComma: true 53 | BreakInheritanceList: BeforeColon 54 | BreakBeforeTernaryOperators: true 55 | BreakConstructorInitializersBeforeComma: false 56 | BreakConstructorInitializers: BeforeComma 57 | BreakAfterJavaFieldAnnotations: false 58 | BreakStringLiterals: true 59 | ColumnLimit: 100 60 | CommentPragmas: "^ IWYU pragma:" 61 | CompactNamespaces: false 62 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 63 | ConstructorInitializerIndentWidth: 4 64 | ContinuationIndentWidth: 4 65 | Cpp11BracedListStyle: false 66 | DeriveLineEnding: true 67 | DerivePointerAlignment: true 68 | DisableFormat: false 69 | EmptyLineBeforeAccessModifier: LogicalBlock 70 | ExperimentalAutoDetectBinPacking: true 71 | FixNamespaceComments: false 72 | ForEachMacros: 73 | - foreach 74 | - Q_FOREACH 75 | - BOOST_FOREACH 76 | StatementAttributeLikeMacros: 77 | - Q_EMIT 78 | IncludeBlocks: Preserve 79 | IncludeCategories: 80 | - Regex: '^"(llvm|llvm-c|clang|clang-c)/' 81 | Priority: 2 82 | SortPriority: 0 83 | CaseSensitive: false 84 | - Regex: '^(<|"(gtest|gmock|isl|json)/)' 85 | Priority: 3 86 | SortPriority: 0 87 | CaseSensitive: false 88 | - Regex: ".*" 89 | Priority: 1 90 | SortPriority: 0 91 | CaseSensitive: false 92 | IncludeIsMainRegex: "(Test)?$" 93 | IncludeIsMainSourceRegex: "" 94 | IndentCaseLabels: false 95 | IndentCaseBlocks: false 96 | IndentGotoLabels: true 97 | IndentPPDirectives: None 98 | IndentExternBlock: AfterExternBlock 99 | IndentRequires: false 100 | IndentWidth: 4 101 | IndentWrappedFunctionNames: false 102 | InsertTrailingCommas: None 103 | JavaScriptQuotes: Leave 104 | JavaScriptWrapImports: true 105 | KeepEmptyLinesAtTheStartOfBlocks: true 106 | MacroBlockBegin: "" 107 | MacroBlockEnd: "" 108 | MaxEmptyLinesToKeep: 1 109 | NamespaceIndentation: Inner 110 | ObjCBinPackProtocolList: Auto 111 | ObjCBlockIndentWidth: 4 112 | ObjCBreakBeforeNestedBlockParam: true 113 | ObjCSpaceAfterProperty: true 114 | ObjCSpaceBeforeProtocolList: true 115 | PenaltyBreakAssignment: 2 116 | PenaltyBreakBeforeFirstCallParameter: 19 117 | PenaltyBreakComment: 300 118 | PenaltyBreakFirstLessLess: 120 119 | PenaltyBreakString: 1000 120 | PenaltyBreakTemplateDeclaration: 10 121 | PenaltyExcessCharacter: 1000000 122 | PenaltyReturnTypeOnItsOwnLine: 60 123 | PenaltyIndentedWhitespace: 0 124 | PointerAlignment: Left 125 | ReflowComments: true 126 | SortIncludes: true 127 | SortJavaStaticImport: Before 128 | SortUsingDeclarations: true 129 | SpaceAfterCStyleCast: true 130 | SpaceAfterLogicalNot: false 131 | SpaceAfterTemplateKeyword: true 132 | SpaceBeforeAssignmentOperators: true 133 | SpaceBeforeCaseColon: false 134 | SpaceBeforeCpp11BracedList: true 135 | SpaceBeforeCtorInitializerColon: true 136 | SpaceBeforeInheritanceColon: true 137 | SpaceBeforeParens: ControlStatements 138 | SpaceAroundPointerQualifiers: Default 139 | SpaceBeforeRangeBasedForLoopColon: true 140 | SpaceInEmptyBlock: true 141 | SpaceInEmptyParentheses: false 142 | SpacesBeforeTrailingComments: 1 143 | SpacesInAngles: false 144 | SpacesInConditionalStatement: false 145 | SpacesInContainerLiterals: true 146 | SpacesInCStyleCastParentheses: false 147 | SpacesInParentheses: false 148 | SpacesInSquareBrackets: false 149 | SpaceBeforeSquareBrackets: false 150 | BitFieldColonSpacing: Both 151 | Standard: Latest 152 | StatementMacros: 153 | - Q_UNUSED 154 | - QT_REQUIRE_VERSION 155 | TabWidth: 4 156 | UseCRLF: false 157 | UseTab: Never 158 | WhitespaceSensitiveMacros: 159 | - STRINGIZE 160 | - PP_STRINGIZE 161 | - BOOST_PP_STRINGIZE 162 | - NS_SWIFT_NAME 163 | - CF_SWIFT_NAME 164 | --- 165 | 166 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Frank Denis 2 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2005-2025 Frank Denis 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | * Version 1.2: 2 | Access the memory pages being read, which is more realistic that just filling a shared buffer. 3 | Reported by Linus Torvalds, thanks! 4 | The project can also be built using Zig instead of autoconf/automake/make. 5 | 6 | * Version 1.1: 7 | Fix off-by-one that sometimes resulted in crashes. 8 | 9 | * Version 1.0: 10 | Initial release. 11 | 12 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = \ 2 | src \ 3 | man 4 | 5 | EXTRA_DIST = \ 6 | build.zig 7 | -------------------------------------------------------------------------------- /NEWS: -------------------------------------------------------------------------------- 1 | * Version 1.2: 2 | Access the memory pages being read, which is more realistic that just filling a shared buffer. 3 | Reported by Linus Torvalds, thanks! 4 | The project can also be built using Zig instead of autoconf/automake/make. 5 | 6 | * Version 1.1: 7 | Fix off-by-one that sometimes resulted in crashes. 8 | 9 | * Version 1.0: 10 | Initial release. 11 | 12 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | 2 | .:. Blogbench .:. 3 | Documentation for version 1.2 4 | https://github.com/jedisct1/Blogbench 5 | 6 | 7 | ------------------------ BLURB ------------------------ 8 | 9 | 10 | Blogbench is a portable filesystem benchmark that tries to reproduce the load 11 | of a real-world busy file server. 12 | 13 | It stresses the filesystem with multiple threads performing random reads, 14 | writes and rewrites in order to get a realistic idea of the scalability and the 15 | concurrency a system can handle. 16 | 17 | 18 | ------------------------ COMPILATION ------------------------ 19 | 20 | 21 | * Option 1: compile with Zig: 22 | 23 | zig build -Drelease 24 | 25 | * Option 2: 26 | 27 | Follow the boring traditional autoconf/automake procedure: 28 | 29 | autoreconf 30 | ./configure 31 | make install-strip 32 | 33 | For details, have a look at the INSTALL file. 34 | 35 | The software has been successfully tested on Linux, macOS, OpenBSD and 36 | DragonFlyBSD. But it should work on any system with an implementation of POSIX 37 | threads. 38 | 39 | 40 | ------------------------ BASIC USAGE ------------------------ 41 | 42 | 43 | The minimal way to run the test is to just give the path to an *empty* 44 | and writable directory: 45 | 46 | blogbench -d /path/to/the/directory 47 | 48 | Blogbench will start the required threads and the test will run during 5 49 | minutes. A final "score" will then be given as an indication of read and write 50 | performance. 51 | 52 | 53 | ------------------------ DETAILS ------------------------ 54 | 55 | 56 | Blogbench was initially designed to mimic the behavior of the Skyrock.com 57 | blog service. 58 | 59 | 4 different types of threads are started: 60 | 61 | - The writers. They create new blogs (directories) with a random amount of 62 | fake articles and fake pictures. 63 | 64 | - The rewriters. They add or they modify articles and pictures of existing 65 | blogs. 66 | 67 | - The "commenters". They add fake comments to existing blogs in random order. 68 | 69 | - The readers. They read articles, pictures and comments of random blogs. They 70 | sometimes even try to access non-existent files. 71 | 72 | New files are written atomically. The content is pushed with 8 Kb chunks in a 73 | temporary file that gets renamed if everything completes. 8 Kb is the default 74 | PHP buffer size for writes. 75 | 76 | Reads are performed with a 64 Kb buffer. 77 | 78 | Concurrent writers and rewriters can quickly create fragmentation if the 79 | preallocation is not optimal. But it is very interesting to check how 80 | different filesystems reacts to fragmentation. 81 | 82 | Every blog is a new directory withing the same parent directory. Since some 83 | filesystems are unable to manage more than 32k or 64k links to the same 84 | directory (an example is UFS), you should not force the test to run a silly 85 | amount of time on these filesystems. 86 | 87 | 88 | ------------------------ ADVANCED USAGE ------------------------ 89 | 90 | 91 | By default, there are 3 concurrent writers, 1 rewriters, 100 readers and 5 92 | commenters. 93 | 94 | Statistics are shown every 10 seconds until 30 iterations. 95 | 96 | Command-line switches will let you change these values. 97 | 98 | Try: blogbench --help to get the list of available command-line switches. 99 | 100 | 101 | Thank you, 102 | 103 | -Frank DENIS "Jedi/Sector One" . 104 | -------------------------------------------------------------------------------- /build.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | pub fn build(b: *std.Build) void { 4 | const target = b.standardTargetOptions(.{}); 5 | const optimize = b.standardOptimizeOption(.{ .preferred_optimize_mode = .ReleaseFast }); 6 | const exe = b.addExecutable(.{ 7 | .name = "blogbench", 8 | .target = target, 9 | .optimize = optimize, 10 | .single_threaded = false, 11 | }); 12 | exe.linkLibC(); 13 | const source_files = &.{ 14 | "src/blogbench.c", 15 | "src/commenter.c", 16 | "src/helpers.c", 17 | "src/process.c", 18 | "src/reader.c", 19 | "src/rewriter.c", 20 | "src/writer.c", 21 | }; 22 | exe.addCSourceFiles(.{ .files = source_files }); 23 | exe.root_module.addCMacro("PACKAGE_STRING", "\"blogbench 1.2\""); 24 | exe.root_module.addCMacro("PACKAGE_BUGREPORT", "\"https://github.com/jedisct1/Blogbench\""); 25 | exe.root_module.addCMacro("HAVE_ALLOCA", "1"); 26 | exe.root_module.addCMacro("HAVE_FCNTL_H", "1"); 27 | exe.root_module.addCMacro("HAVE_GETOPT_LONG", "1"); 28 | exe.root_module.addCMacro("HAVE_SYNC", "1"); 29 | exe.root_module.addCMacro("HAVE_STRTOULL", "1"); 30 | exe.root_module.addCMacro("HAVE_SYS_PARAM_H", "1"); 31 | exe.root_module.addCMacro("HAVE_SYS_TIME_H", "1"); 32 | 33 | b.installArtifact(exe); 34 | const run_cmd = b.addRunArtifact(exe); 35 | run_cmd.step.dependOn(b.getInstallStep()); 36 | if (b.args) |args| { 37 | run_cmd.addArgs(args); 38 | } 39 | const run_step = b.step("run", "Run the app"); 40 | run_step.dependOn(&run_cmd.step); 41 | } 42 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | AC_PREREQ([2.71]) 2 | 3 | AC_INIT([blogbench],[1.2],[https://github.com/jedisct1/Blogbench]) 4 | AM_INIT_AUTOMAKE([1.8 dist-bzip2]) 5 | AC_CONFIG_HEADERS(config.h) 6 | AC_CONFIG_LIBOBJ_DIR(src) 7 | 8 | AC_SUBST(VERSION) 9 | 10 | # Checks for programs. 11 | AC_PROG_CC 12 | AC_PROG_INSTALL 13 | AC_USE_SYSTEM_EXTENSIONS 14 | AC_SEARCH_LIBS([strerror],[cposix]) 15 | 16 | AC_CHECK_HEADERS_ONCE([sys/time.h]) 17 | AC_CHECK_HEADERS_ONCE([unistd.h]) 18 | AC_CHECK_HEADERS_ONCE([fcntl.h sys/fcntl.h sys/param.h]) 19 | 20 | dnl Types 21 | AC_TYPE_MODE_T 22 | AC_TYPE_SIZE_T 23 | AC_TYPE_SSIZE_T 24 | AC_CHECK_TYPE(sig_atomic_t, , [AC_DEFINE(sig_atomic_t, signed char, 25 | [sig_atomic_t type])], 26 | [ 27 | #ifdef HAVE_UNISTD_H 28 | # include 29 | #endif 30 | #include 31 | #include 32 | ]) 33 | 34 | dnl Options 35 | 36 | AC_ARG_WITH(debug, 37 | [AS_HELP_STRING(--with-debug,For maintainers only - please do not use)], 38 | [ if test "x$withval" = "xyes" ; then 39 | CFLAGS="$CFLAGS -DDEBUG=1 -g -Wall -W -Wcast-align -Wbad-function-cast -Wstrict-prototypes -Wwrite-strings -Wreturn-type " 40 | fi ]) 41 | 42 | dnl Checks for library functions. 43 | AC_FUNC_ALLOCA 44 | AX_PTHREAD([ 45 | LIBS="$PTHREAD_LIBS $LIBS" 46 | CFLAGS="$CFLAGS $PTHREAD_CFLAGS" 47 | CC="$PTHREAD_CC" 48 | CXX="$PTHREAD_CXX" 49 | ]) 50 | 51 | AC_CHECK_FUNCS(getopt_long) 52 | AC_CHECK_FUNCS(strtoull strtoq) 53 | AC_CHECK_FUNCS(sync) 54 | 55 | AC_SYS_LARGEFILE 56 | 57 | dnl Output 58 | 59 | AC_CONFIG_FILES(Makefile src/Makefile man/Makefile) 60 | 61 | AC_OUTPUT 62 | -------------------------------------------------------------------------------- /man/Makefile.am: -------------------------------------------------------------------------------- 1 | man_MANS = \ 2 | blogbench.8 3 | 4 | EXTRA_DIST = \ 5 | $(man_MANS) 6 | -------------------------------------------------------------------------------- /man/blogbench.8: -------------------------------------------------------------------------------- 1 | .TH "blogbench" "8" "1.2" "Frank Denis" "Benchmarks" 2 | .SH "NAME" 3 | .LP 4 | blogbench \- a realistic filesystem benchmark 5 | .SH "SYNTAX" 6 | .LP 7 | blogbench \fI\-d\fP <\fIdirectory\fP> 8 | .SH "DESCRIPTION" 9 | .LP 10 | Blogbench is a portable filesystem benchmark that tries to reproduce the load 11 | of a real\-world busy file server. 12 | .br 13 | It stresses the filesystem with multiple threads performing random reads, 14 | writes and rewrites in order to get a realistic idea of the scalability and the 15 | concurrency a system can handle. 16 | .SH "OPTIONS" 17 | .LP 18 | .TP 19 | \fB\-\-help\fR 20 | Display a complete list of available switches. 21 | .SH "EXAMPLES" 22 | .LP 23 | The minimal way to run the test is to just give the path to an \fBempty\fR 24 | and writable directory: 25 | .LP 26 | blogbench \-d /path/to/the/directory 27 | .LP 28 | Blogbench will start the required threads and the test will run during 5 29 | minutes. A final "score" will then be given as an indication of read and write 30 | performance. 31 | .SH "AUTHORS" 32 | .LP 33 | Frank Denis 34 | -------------------------------------------------------------------------------- /src/Makefile.am: -------------------------------------------------------------------------------- 1 | bin_PROGRAMS = \ 2 | blogbench 3 | 4 | blogbench_SOURCES = \ 5 | blogbench.c \ 6 | blogbench_p.h \ 7 | process.c \ 8 | writer.c \ 9 | rewriter.c \ 10 | reader.c \ 11 | commenter.c \ 12 | helpers.c \ 13 | blogbench.h \ 14 | globals.h \ 15 | bsd-getopt_long.c \ 16 | bsd-getopt_long.h 17 | -------------------------------------------------------------------------------- /src/blogbench.c: -------------------------------------------------------------------------------- 1 | #ifdef HAVE_CONFIG_H 2 | #include 3 | #endif 4 | #define DEFINE_GLOBALS 1 5 | #include "blogbench.h" 6 | #ifndef HAVE_GETOPT_LONG 7 | #include "bsd-getopt_long.h" 8 | #else 9 | #include 10 | #endif 11 | #include "blogbench_p.h" 12 | 13 | static void 14 | usage(void) 15 | { 16 | puts("\n" PACKAGE_STRING "\n"); 17 | fputs( 18 | "--commenters= (-c ): number of comments posters\n" 19 | "--directory= (-d ): working directory\n" 20 | "--help (-h): usage\n" 21 | "--iterations= (-i ): number of iterations\n" 22 | "--readers= (-r ): number of readers\n" 23 | "--rewriters= (-W ): number of rewriters\n" 24 | "--sleep= (-s ): delay after every iteration\n" 25 | "--writers= (-w ): number of writers\n" 26 | "\n" 27 | "Sample usage:\n" 28 | "\n" 29 | "blogbench -d /mnt/bench\n" 30 | "\n\n" 31 | "Please report bugs to ", 32 | stdout); 33 | puts(PACKAGE_BUGREPORT ".\n"); 34 | 35 | exit(EXIT_SUCCESS); 36 | } 37 | 38 | int 39 | main(int argc, char *argv[]) 40 | { 41 | int option_index = 0; 42 | int fodder; 43 | 44 | if (argc <= 1) { 45 | usage(); 46 | } 47 | while ((fodder = getopt_long(argc, argv, GETOPT_OPTIONS, long_options, &option_index)) != -1) { 48 | switch (fodder) { 49 | case 'h': 50 | usage(); 51 | case 'c': 52 | nb_commenters = (unsigned int) strtoul(optarg, NULL, 10); 53 | break; 54 | case 'd': 55 | free(scratch_dir); 56 | if ((scratch_dir = strdup(optarg)) == NULL) { 57 | perror("Out of memory for strdup()"); 58 | return 1; 59 | } 60 | break; 61 | case 'i': 62 | stats_iterations = (unsigned int) strtoul(optarg, NULL, 10); 63 | break; 64 | case 'r': 65 | nb_readers = (unsigned int) strtoul(optarg, NULL, 10); 66 | break; 67 | case 'W': 68 | nb_rewriters = (unsigned int) strtoul(optarg, NULL, 10); 69 | break; 70 | case 's': 71 | sleep_stats = (unsigned int) strtoul(optarg, NULL, 10); 72 | break; 73 | case 'w': 74 | nb_writers = (unsigned int) strtoul(optarg, NULL, 10); 75 | break; 76 | default: 77 | usage(); 78 | } 79 | } 80 | if (scratch_dir == NULL) { 81 | fputs("Missing scratch directory (--directory=... option).\n\n", stderr); 82 | return 1; 83 | } 84 | if (process() != 0) { 85 | return 1; 86 | } 87 | return 0; 88 | } 89 | -------------------------------------------------------------------------------- /src/blogbench.h: -------------------------------------------------------------------------------- 1 | #ifndef __BLOGBENCH_H__ 2 | #define __BLOGBENCH_H__ 1 3 | 4 | #ifndef __GNUC__ 5 | #ifdef __attribute__ 6 | #undef __attribute__ 7 | #endif 8 | #define __attribute__(a) 9 | #endif 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #ifdef HAVE_SYS_TIME_H 22 | #include 23 | #endif 24 | #include 25 | #include 26 | #ifdef HAVE_FCNTL_H 27 | #include 28 | #elif defined(HAVE_SYS_FCNTL_H) 29 | #include 30 | #endif 31 | #ifdef HAVE_SYS_PARAM_H 32 | #include 33 | #endif 34 | #include 35 | #include 36 | 37 | #ifdef HAVE_ALLOCA 38 | #ifdef HAVE_ALLOCA_H 39 | #include 40 | #endif 41 | #define ALLOCA(X) alloca(X) 42 | #define ALLOCA_FREE(X) \ 43 | do { \ 44 | } while (0) 45 | #else 46 | #define ALLOCA(X) malloc(X) 47 | #define ALLOCA_FREE(X) free(X) 48 | #endif 49 | 50 | #ifndef HAVE_STRTOULL 51 | #ifdef HAVE_STRTOQ 52 | #define strtoull(X, Y, Z) strtoq(X, Y, Z) 53 | #else 54 | #define strtoull(X, Y, Z) strtoul(X, Y, Z) 55 | #endif 56 | #endif 57 | 58 | #ifndef ULONG_LONG_MAX 59 | #define ULONG_LONG_MAX (1ULL << 63) 60 | #endif 61 | 62 | #ifndef O_NOFOLLOW 63 | #define O_NOFOLLOW 0 64 | #endif 65 | 66 | #ifndef O_DIRECTORY 67 | #define O_DIRECTORY 0 68 | #endif 69 | 70 | #define DEFAULT_SCRATCH_DIR NULL 71 | 72 | #define DEFAULT_NB_WRITERS 3 73 | #define DEFAULT_NB_REWRITERS 1 74 | #define DEFAULT_NB_READERS 100 75 | #define DEFAULT_NB_COMMENTERS 5 76 | 77 | #define USLEEP_WRITERS 10000 78 | #define USLEEP_REWRITERS 1000000 79 | #define USLEEP_READERS 0 80 | #define USLEEP_COMMENTERS 10000 81 | 82 | #define DEFAULT_SLEEP_STATS 10 83 | #define DEFAULT_STATS_ITERATIONS 30 84 | 85 | #define MAX_ARTICLES 100 86 | #define ARTICLE_MIN_SIZE 1024 87 | #define ARTICLE_MAX_SIZE 32768 88 | 89 | #define MAX_PICTURES 100 90 | #define PICTURE_MIN_SIZE 8192 91 | #define PICTURE_MAX_SIZE 65536 92 | 93 | #define MAX_COMMENTS 100 94 | #define COMMENT_MIN_SIZE 2048 95 | #define COMMENT_MAX_SIZE 10240 96 | 97 | #define WRITE_CHUNK_SIZE 8192 98 | #define READ_CHUNK_SIZE 65536 99 | 100 | #define BLOG_PREFIX "blog-" 101 | #define BLOG_SUFFIX "" 102 | 103 | #define ARTICLE_PREFIX "article-" 104 | #define ARTICLE_SUFFIX ".xml" 105 | 106 | #define PICTURE_PREFIX "picture-" 107 | #define PICTURE_SUFFIX ".jpg" 108 | 109 | #define COMMENT_PREFIX "comment-" 110 | #define COMMENT_SUFFIX ".xml" 111 | 112 | #define TMP_SUFFIX ".tmp" 113 | 114 | #ifndef MAXPATHLEN 115 | #ifdef PATH_MAX 116 | #define MAXPATHLEN PATH_MAX 117 | #else 118 | #define MAXPATHLEN 65536U 119 | Warning: neither PATH_MAX nor MAXPATHLEN were found. 120 | Remove these lines if you really want to compile the server, but 121 | the server may be insecure if a wrong value is set here. 122 | #endif 123 | #endif 124 | #if (MAXPATHLEN) >= (INT_MAX) 125 | Your platform has a very large MAXPATHLEN, we should not trust it. 126 | #endif 127 | 128 | #define MAX_OUTPUT_LINE 4096U 129 | 130 | int 131 | process(void); 132 | 133 | unsigned long long get_new_blog_id(void); 134 | unsigned long long get_last_blog_id(void); 135 | unsigned long long get_random_blog_id(void); 136 | int reentrant_puts(const char *const str); 137 | int reentrant_printf(const char *const format, ...); 138 | int reentrant_perror(const char *const str); 139 | 140 | int create_atomic_file(const char *const file_name, 141 | const size_t article_min_size, 142 | const size_t article_max_size); 143 | 144 | int create_dummy_file(const char *const file_name, size_t size); 145 | int read_dummy_file(const char *const file_name); 146 | 147 | void *writer(void *const fodder); 148 | void *rewriter(void *const fodder); 149 | void *reader(void *const fodder); 150 | void *commenter(void *const fodder); 151 | 152 | #include "globals.h" 153 | 154 | #endif 155 | -------------------------------------------------------------------------------- /src/blogbench_p.h: -------------------------------------------------------------------------------- 1 | #ifndef __BLOGBENCH_P_H__ 2 | #define __BLOGBENCH_P_H__ 1 3 | 4 | static const char *GETOPT_OPTIONS = "c:d:hi:r:W:s:w:"; 5 | 6 | static struct option long_options[] = { 7 | { "commenters", 1, NULL, 'c' }, { "directory", 1, NULL, 'd' }, { "help", 0, NULL, 'h' }, 8 | { "iterations", 1, NULL, 'i' }, { "readers", 1, NULL, 'r' }, { "rewriters", 0, NULL, 'W' }, 9 | { "sleep", 1, NULL, 's' }, { "writers", 1, NULL, 'w' }, { NULL, 0, NULL, 0 } 10 | }; 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /src/bsd-getopt_long.c: -------------------------------------------------------------------------------- 1 | 2 | /* $OpenBSD: getopt_long.c,v 1.17 2004/06/03 18:46:52 millert Exp $ */ 3 | /* $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $ */ 4 | 5 | /* 6 | * Copyright (c) 2002 Todd C. Miller 7 | * 8 | * Permission to use, copy, modify, and distribute this software for any 9 | * purpose with or without fee is hereby granted, provided that the above 10 | * copyright notice and this permission notice appear in all copies. 11 | * 12 | * THE SOFTWARE IS PROVIDED "AS IS" AND TODD C. MILLER DISCLAIMS ALL 13 | * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES 14 | * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL TODD C. MILLER BE LIABLE 15 | * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 16 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION 17 | * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 18 | * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 19 | */ 20 | /*- 21 | * Copyright (c) 2000 The NetBSD Foundation, Inc. 22 | * All rights reserved. 23 | * 24 | * This code is derived from software contributed to The NetBSD Foundation 25 | * by Dieter Baron and Thomas Klausner. 26 | * 27 | * Redistribution and use in source and binary forms, with or without 28 | * modification, are permitted provided that the following conditions 29 | * are met: 30 | * 1. Redistributions of source code must retain the above copyright 31 | * notice, this list of conditions and the following disclaimer. 32 | * 2. Redistributions in binary form must reproduce the above copyright 33 | * notice, this list of conditions and the following disclaimer in the 34 | * documentation and/or other materials provided with the distribution. 35 | * 3. All advertising materials mentioning features or use of this software 36 | * must display the following acknowledgement: 37 | * This product includes software developed by the NetBSD 38 | * Foundation, Inc. and its contributors. 39 | * 4. Neither the name of The NetBSD Foundation nor the names of its 40 | * contributors may be used to endorse or promote products derived 41 | * from this software without specific prior written permission. 42 | * 43 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 44 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 45 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 46 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 47 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 48 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 49 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 50 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 51 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 52 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 53 | * POSSIBILITY OF SUCH DAMAGE. 54 | */ 55 | 56 | #define IN_GETOPT_LONG_C 1 57 | 58 | #include 59 | #include "blogbench.h" 60 | 61 | #ifndef HAVE_GETOPT_LONG 62 | 63 | # include "bsd-getopt_long.h" 64 | 65 | # ifdef WITH_DMALLOC 66 | # include 67 | # endif 68 | 69 | int pure_opterr = 1; /* if error message should be printed */ 70 | int pure_optind = 1; /* index into parent argv vector */ 71 | int pure_optopt = '?'; /* character checked for validity */ 72 | int pure_optreset; /* reset getopt */ 73 | const char *pure_optarg; /* argument associated with option */ 74 | 75 | # define PRINT_ERROR ((pure_opterr) && (*options != ':')) 76 | 77 | # define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */ 78 | # define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */ 79 | # define FLAG_LONGONLY 0x04 /* operate as pure_pure_getopt_long_only */ 80 | 81 | /* return values */ 82 | # define BADCH (int)'?' 83 | # define BADARG ((*options == ':') ? (int)':' : (int)'?') 84 | # define INORDER (int)1 85 | 86 | # define EMSG "" 87 | 88 | static int pure_getopt_internal(int, char * const *, const char *, 89 | const struct pure_option *, int *, int); 90 | static int pure_parse_long_options(char * const *, const char *, 91 | const struct pure_option *, int *, int); 92 | static int pure_gcd(int, int); 93 | static void pure_permute_args(int, int, int, char * const *); 94 | 95 | static const char *pure_place = EMSG; /* option letter processing */ 96 | 97 | /* XXX: set pure_optreset to 1 rather than these two */ 98 | static int nonopt_start = -1; /* first non option argument (for permute) */ 99 | static int nonopt_end = -1; /* first option after non options (for permute) */ 100 | 101 | /* Error messages */ 102 | static const char *recargchar = "option requires an argument -- %c"; 103 | static const char *recargstring = "option requires an argument -- %s"; 104 | static const char *ambig = "ambiguous option -- %.*s"; 105 | static const char *noarg = "option doesn't take an argument -- %.*s"; 106 | static const char *illoptchar = "unknown option -- %c"; 107 | static const char *illoptstring = "unknown option -- %s"; 108 | 109 | /* 110 | * Compute the greatest common divisor of a and b. 111 | */ 112 | static int pure_gcd(int a, int b) 113 | { 114 | int c; 115 | 116 | c = a % b; 117 | while (c != 0) { 118 | a = b; 119 | b = c; 120 | c = a % b; 121 | } 122 | return b; 123 | } 124 | 125 | /* 126 | * Exchange the block from nonopt_start to nonopt_end with the block 127 | * from nonopt_end to opt_end (keeping the same order of arguments 128 | * in each block). 129 | */ 130 | static void pure_permute_args(int panonopt_start, int panonopt_end, 131 | int opt_end, char * const *nargv) 132 | { 133 | int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos; 134 | char *swap; 135 | 136 | /* 137 | * compute lengths of blocks and number and size of cycles 138 | */ 139 | nnonopts = panonopt_end - panonopt_start; 140 | nopts = opt_end - panonopt_end; 141 | ncycle = pure_gcd(nnonopts, nopts); 142 | cyclelen = (opt_end - panonopt_start) / ncycle; 143 | 144 | for (i = 0; i < ncycle; i++) { 145 | cstart = panonopt_end+i; 146 | pos = cstart; 147 | for (j = 0; j < cyclelen; j++) { 148 | if (pos >= panonopt_end) 149 | pos -= nnonopts; 150 | else 151 | pos += nopts; 152 | swap = nargv[pos]; 153 | /* LINTED const cast */ 154 | ((char **) nargv)[pos] = nargv[cstart]; 155 | /* LINTED const cast */ 156 | ((char **)nargv)[cstart] = swap; 157 | } 158 | } 159 | } 160 | 161 | /* 162 | * pure_parse_long_options -- 163 | * Parse long options in argc/argv argument vector. 164 | * Returns -1 if short_too is set and the option does not match long_options. 165 | */ 166 | static int pure_parse_long_options(char * const *nargv, const char *options, 167 | const struct pure_option *long_options, 168 | int *idx, int short_too) 169 | { 170 | const char *current_argv, *has_equal; 171 | size_t current_argv_len; 172 | int i, match; 173 | 174 | current_argv = pure_place; 175 | match = -1; 176 | 177 | pure_optind++; 178 | 179 | if ((has_equal = strchr(current_argv, '=')) != NULL) { 180 | /* argument found (--option=arg) */ 181 | current_argv_len = has_equal - current_argv; 182 | has_equal++; 183 | } else 184 | current_argv_len = strlen(current_argv); 185 | 186 | for (i = 0; long_options[i].name; i++) { 187 | /* find matching long option */ 188 | if (strncmp(current_argv, long_options[i].name, 189 | current_argv_len)) 190 | continue; 191 | 192 | if (strlen(long_options[i].name) == current_argv_len) { 193 | /* exact match */ 194 | match = i; 195 | break; 196 | } 197 | /* 198 | * If this is a known short option, don't allow 199 | * a partial match of a single character. 200 | */ 201 | if (short_too && current_argv_len == 1) 202 | continue; 203 | 204 | if (match == -1) /* partial match */ 205 | match = i; 206 | else { 207 | /* ambiguous abbreviation */ 208 | if (PRINT_ERROR) 209 | fprintf(stderr, ambig, (int)current_argv_len, 210 | current_argv); 211 | pure_optopt = 0; 212 | return BADCH; 213 | } 214 | } 215 | if (match != -1) { /* option found */ 216 | if (long_options[match].has_arg == no_argument 217 | && has_equal) { 218 | if (PRINT_ERROR) 219 | fprintf(stderr, noarg, (int)current_argv_len, 220 | current_argv); 221 | /* 222 | * XXX: GNU sets pure_optopt to val regardless of flag 223 | */ 224 | if (long_options[match].flag == NULL) 225 | pure_optopt = long_options[match].val; 226 | else 227 | pure_optopt = 0; 228 | return BADARG; 229 | } 230 | if (long_options[match].has_arg == required_argument || 231 | long_options[match].has_arg == optional_argument) { 232 | if (has_equal) 233 | pure_optarg = has_equal; 234 | else if (long_options[match].has_arg == 235 | required_argument) { 236 | /* 237 | * optional argument doesn't use next nargv 238 | */ 239 | pure_optarg = nargv[pure_optind++]; 240 | } 241 | } 242 | if ((long_options[match].has_arg == required_argument) 243 | && (pure_optarg == NULL)) { 244 | /* 245 | * Missing argument; leading ':' indicates no error 246 | * should be generated. 247 | */ 248 | if (PRINT_ERROR) 249 | fprintf(stderr, recargstring, 250 | current_argv); 251 | /* 252 | * XXX: GNU sets pure_optopt to val regardless of flag 253 | */ 254 | if (long_options[match].flag == NULL) 255 | pure_optopt = long_options[match].val; 256 | else 257 | pure_optopt = 0; 258 | --pure_optind; 259 | return BADARG; 260 | } 261 | } else { /* unknown option */ 262 | if (short_too) { 263 | --pure_optind; 264 | return -1; 265 | } 266 | if (PRINT_ERROR) 267 | fprintf(stderr, illoptstring, current_argv); 268 | pure_optopt = 0; 269 | return BADCH; 270 | } 271 | if (idx) 272 | *idx = match; 273 | if (long_options[match].flag) { 274 | *long_options[match].flag = long_options[match].val; 275 | return 0; 276 | } else 277 | return long_options[match].val; 278 | } 279 | 280 | /* 281 | * getopt_internal -- 282 | * Parse argc/argv argument vector. Called by user level routines. 283 | */ 284 | static int pure_getopt_internal(int nargc, char * const *nargv, 285 | const char *options, 286 | const struct pure_option *long_options, 287 | int *idx, int flags) 288 | { 289 | char *oli; /* option letter list index */ 290 | int optchar, short_too; 291 | static int posixly_correct = -1; 292 | 293 | if (options == NULL) 294 | return -1; 295 | 296 | /* 297 | * Disable GNU extensions if POSIXLY_CORRECT is set or options 298 | * string begins with a '+'. 299 | */ 300 | if (posixly_correct == -1) 301 | posixly_correct = (getenv("POSIXLY_CORRECT") != NULL); 302 | if (posixly_correct || *options == '+') 303 | flags &= ~FLAG_PERMUTE; 304 | else if (*options == '-') 305 | flags |= FLAG_ALLARGS; 306 | if (*options == '+' || *options == '-') 307 | options++; 308 | 309 | /* 310 | * XXX Some GNU programs (like cvs) set pure_optind to 0 instead of 311 | * XXX using pure_optreset. Work around this braindamage. 312 | */ 313 | if (pure_optind == 0) 314 | pure_optind = pure_optreset = 1; 315 | 316 | pure_optarg = NULL; 317 | if (pure_optreset) 318 | nonopt_start = nonopt_end = -1; 319 | start: 320 | if (pure_optreset || !*pure_place) { /* update scanning pointer */ 321 | pure_optreset = 0; 322 | if (pure_optind >= nargc) { /* end of argument vector */ 323 | pure_place = EMSG; 324 | if (nonopt_end != -1) { 325 | /* do permutation, if we have to */ 326 | pure_permute_args(nonopt_start, nonopt_end, 327 | pure_optind, nargv); 328 | pure_optind -= nonopt_end - nonopt_start; 329 | } 330 | else if (nonopt_start != -1) { 331 | /* 332 | * If we skipped non-options, set pure_optind 333 | * to the first of them. 334 | */ 335 | pure_optind = nonopt_start; 336 | } 337 | nonopt_start = nonopt_end = -1; 338 | return -1; 339 | } 340 | if (*(pure_place = nargv[pure_optind]) != '-' || 341 | (pure_place[1] == '\0' && strchr(options, '-') == NULL)) { 342 | pure_place = EMSG; /* found non-option */ 343 | if (flags & FLAG_ALLARGS) { 344 | /* 345 | * GNU extension: 346 | * return non-option as argument to option 1 347 | */ 348 | pure_optarg = nargv[pure_optind++]; 349 | return INORDER; 350 | } 351 | if (!(flags & FLAG_PERMUTE)) { 352 | /* 353 | * If no permutation wanted, stop parsing 354 | * at first non-option. 355 | */ 356 | return -1; 357 | } 358 | /* do permutation */ 359 | if (nonopt_start == -1) 360 | nonopt_start = pure_optind; 361 | else if (nonopt_end != -1) { 362 | pure_permute_args(nonopt_start, nonopt_end, 363 | pure_optind, nargv); 364 | nonopt_start = pure_optind - 365 | (nonopt_end - nonopt_start); 366 | nonopt_end = -1; 367 | } 368 | pure_optind++; 369 | /* process next argument */ 370 | goto start; 371 | } 372 | if (nonopt_start != -1 && nonopt_end == -1) 373 | nonopt_end = pure_optind; 374 | 375 | /* If we have "-" do nothing, if "--" we are done. */ 376 | 377 | if (pure_place[1] != '\0' && *++pure_place == '-' && 378 | pure_place[1] == '\0') { 379 | pure_optind++; 380 | pure_place = EMSG; 381 | /* 382 | * We found an option (--), so if we skipped 383 | * non-options, we have to permute. 384 | */ 385 | if (nonopt_end != -1) { 386 | pure_permute_args(nonopt_start, nonopt_end, 387 | pure_optind, nargv); 388 | pure_optind -= nonopt_end - nonopt_start; 389 | } 390 | nonopt_start = nonopt_end = -1; 391 | return -1; 392 | } 393 | } 394 | 395 | /* 396 | * Check long options if: 397 | * 1) we were passed some 398 | * 2) the arg is not just "-" 399 | * 3) either the arg starts with -- we are pure_pure_getopt_long_only() 400 | */ 401 | if (long_options != NULL && pure_place != nargv[pure_optind] && 402 | (*pure_place == '-' || (flags & FLAG_LONGONLY))) { 403 | short_too = 0; 404 | if (*pure_place == '-') 405 | pure_place++; /* --foo long option */ 406 | else if (*pure_place != ':' && strchr(options, *pure_place) != NULL) 407 | short_too = 1; /* could be short option too */ 408 | 409 | optchar = pure_parse_long_options(nargv, options, long_options, 410 | idx, short_too); 411 | if (optchar != -1) { 412 | pure_place = EMSG; 413 | return optchar; 414 | } 415 | } 416 | 417 | if ((optchar = (int)*pure_place++) == (int)':' || 418 | (oli = strchr(options, optchar)) == NULL) { 419 | /* 420 | * If the user specified "-" and '-' isn't listed in 421 | * options, return -1 (non-option) as per POSIX. 422 | * Otherwise, it is an unknown option character (or :'). 423 | */ 424 | if (optchar == (int) '-' && *pure_place == '\0') 425 | return -1; 426 | if (!*pure_place) 427 | ++pure_optind; 428 | if (PRINT_ERROR) 429 | fprintf(stderr, illoptchar, optchar); 430 | pure_optopt = optchar; 431 | return BADCH; 432 | } 433 | if (long_options != NULL && optchar == 'W' && oli[1] == ';') { 434 | /* -W long-option */ 435 | if (*pure_place) /* no space */ 436 | /* NOTHING */; 437 | else if (++pure_optind >= nargc) { /* no arg */ 438 | pure_place = EMSG; 439 | if (PRINT_ERROR) 440 | fprintf(stderr, recargchar, optchar); 441 | pure_optopt = optchar; 442 | return BADARG; 443 | } else /* white space */ 444 | pure_place = nargv[pure_optind]; 445 | optchar = pure_parse_long_options(nargv, options, long_options, 446 | idx, 0); 447 | pure_place = EMSG; 448 | return optchar; 449 | } 450 | if (*++oli != ':') { /* doesn't take argument */ 451 | if (!*pure_place) 452 | ++pure_optind; 453 | } else { /* takes (optional) argument */ 454 | pure_optarg = NULL; 455 | if (*pure_place) /* no white space */ 456 | pure_optarg = pure_place; 457 | /* XXX: disable test for :: if PC? (GNU doesn't) */ 458 | else if (oli[1] != ':') { /* arg not optional */ 459 | if (++pure_optind >= nargc) { /* no arg */ 460 | pure_place = EMSG; 461 | if (PRINT_ERROR) 462 | fprintf(stderr, recargchar, optchar); 463 | pure_optopt = optchar; 464 | return BADARG; 465 | } else if (!(flags & FLAG_PERMUTE)) { 466 | /* 467 | * If permutation is disabled, we can accept an 468 | * optional arg separated by whitespace so long 469 | * as it does not start with a dash (-). 470 | */ 471 | if (pure_optind + 1 < nargc && pure_optind + 1 > 0 && 472 | *nargv[pure_optind + 1] != '-') { 473 | pure_optarg = nargv[++pure_optind]; 474 | } 475 | } 476 | } 477 | pure_place = EMSG; 478 | ++pure_optind; 479 | } 480 | /* dump back option letter */ 481 | return optchar; 482 | } 483 | 484 | /* 485 | * getopt -- 486 | * Parse argc/argv argument vector. 487 | */ 488 | int pure_getopt(int nargc, char * const *nargv, const char *options) 489 | { 490 | 491 | /* 492 | * We dont' pass FLAG_PERMUTE to pure_getopt_internal() since 493 | * the BSD getopt(3) (unlike GNU) has never done this. 494 | * 495 | * Furthermore, since many privileged programs call getopt() 496 | * before dropping privileges it makes sense to keep things 497 | * as simple (and bug-free) as possible. 498 | */ 499 | return pure_getopt_internal(nargc, nargv, options, NULL, NULL, 0); 500 | } 501 | 502 | /* 503 | * pure_getopt_long -- 504 | * Parse argc/argv argument vector. 505 | */ 506 | int pure_getopt_long(int nargc, char * const *nargv, const char *options, 507 | const struct pure_option *long_options, int *idx) 508 | { 509 | return pure_getopt_internal(nargc, nargv, options, long_options, idx, 510 | FLAG_PERMUTE); 511 | } 512 | 513 | /* 514 | * pure_pure_getopt_long_only -- 515 | * Parse argc/argv argument vector. 516 | */ 517 | int pure_pure_getopt_long_only(int nargc, char * const *nargv, 518 | const char *options, 519 | const struct pure_option *long_options, 520 | int *idx) 521 | { 522 | return pure_getopt_internal(nargc, nargv, options, long_options, idx, 523 | FLAG_PERMUTE|FLAG_LONGONLY); 524 | } 525 | 526 | #endif 527 | -------------------------------------------------------------------------------- /src/bsd-getopt_long.h: -------------------------------------------------------------------------------- 1 | 2 | /* $OpenBSD: getopt_long.c,v 1.13 2003/06/03 01:52:40 millert Exp $ */ 3 | /* $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $ */ 4 | 5 | /* 6 | * Copyright (c) 2002 Todd C. Miller 7 | * 8 | * Permission to use, copy, modify, and distribute this software for any 9 | * purpose with or without fee is hereby granted, provided that the above 10 | * copyright notice and this permission notice appear in all copies. 11 | * 12 | * THE SOFTWARE IS PROVIDED "AS IS" AND TODD C. MILLER DISCLAIMS ALL 13 | * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES 14 | * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL TODD C. MILLER BE LIABLE 15 | * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 16 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION 17 | * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 18 | * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 19 | */ 20 | /*- 21 | * Copyright (c) 2000 The NetBSD Foundation, Inc. 22 | * All rights reserved. 23 | * 24 | * This code is derived from software contributed to The NetBSD Foundation 25 | * by Dieter Baron and Thomas Klausner. 26 | * 27 | * Redistribution and use in source and binary forms, with or without 28 | * modification, are permitted provided that the following conditions 29 | * are met: 30 | * 1. Redistributions of source code must retain the above copyright 31 | * notice, this list of conditions and the following disclaimer. 32 | * 2. Redistributions in binary form must reproduce the above copyright 33 | * notice, this list of conditions and the following disclaimer in the 34 | * documentation and/or other materials provided with the distribution. 35 | * 3. All advertising materials mentioning features or use of this software 36 | * must display the following acknowledgement: 37 | * This product includes software developed by the NetBSD 38 | * Foundation, Inc. and its contributors. 39 | * 4. Neither the name of The NetBSD Foundation nor the names of its 40 | * contributors may be used to endorse or promote products derived 41 | * from this software without specific prior written permission. 42 | * 43 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 44 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 45 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 46 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 47 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 48 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 49 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 50 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 51 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 52 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 53 | * POSSIBILITY OF SUCH DAMAGE. 54 | */ 55 | 56 | #ifndef __BSD_GETOPT_LONG_H__ 57 | #define __BSD_GETOPT_LONG_H__ 58 | 59 | #ifndef HAVE_GETOPT_LONG 60 | 61 | /* 62 | * GNU-like getopt_long() and 4.4BSD getsubopt()/optreset extensions 63 | */ 64 | # ifndef no_argument 65 | # define no_argument 0 66 | # endif 67 | # ifndef required_argument 68 | # define required_argument 1 69 | # endif 70 | # ifndef optional_argument 71 | # define optional_argument 2 72 | # endif 73 | 74 | struct pure_option { 75 | /* name of long option */ 76 | const char *name; 77 | /* 78 | * one of no_argument, required_argument, and optional_argument: 79 | * whether option takes an argument 80 | */ 81 | int has_arg; 82 | /* if not NULL, set *flag to val when option found */ 83 | int *flag; 84 | /* if flag not NULL, value to set *flag to; else return value */ 85 | int val; 86 | }; 87 | 88 | int pure_getopt_long(int nargc, char * const *nargv, const char *options, 89 | const struct pure_option *long_options, int *idx); 90 | 91 | int pure_pure_getopt_long_only(int nargc, char * const *nargv, 92 | const char *options, 93 | const struct pure_option *long_options, 94 | int *idx); 95 | 96 | int pure_getopt(int nargc, char * const *nargv, const char *options); 97 | 98 | extern const char *pure_optarg; /* getopt(3) external variables */ 99 | extern int pure_opterr; 100 | extern int pure_optind; 101 | extern int pure_optopt; 102 | extern int pure_optreset; 103 | 104 | /* prefix+macros just to avoid clashes with existing getopt() implementations */ 105 | 106 | # ifndef IN_GETOPT_LONG_C 107 | # undef option 108 | # define option pure_option 109 | # undef getopt_long 110 | # define getopt_long(A, B, C, D, E) pure_getopt_long(A, B, C, D, E) 111 | # undef getopt_long_only 112 | # define getopt_long_only(A, B, C, D, E) pure_getopt_long_only(A, B, C, D, E) 113 | # undef getopt 114 | # define getopt(A, B, C) pure_getopt(A, B, C) 115 | # undef optarg 116 | # define optarg pure_optarg 117 | # undef opterr 118 | # define opterr pure_opterr 119 | # undef optind 120 | # define optind pure_optind 121 | # undef optopt 122 | # define optopt pure_optopt 123 | # undef optreset 124 | # define optreset pure_optreset 125 | # endif 126 | 127 | #endif 128 | 129 | #endif 130 | -------------------------------------------------------------------------------- /src/commenter.c: -------------------------------------------------------------------------------- 1 | #include "blogbench.h" 2 | #ifdef HAVE_CONFIG_H 3 | #include 4 | #endif 5 | 6 | static int 7 | create_comments(const unsigned long long blog_id) 8 | { 9 | char file_name[MAXPATHLEN]; 10 | int comment_nb; 11 | 12 | while ((comment_nb = 1 + rand() % MAX_COMMENTS) <= 0) 13 | ; 14 | do { 15 | snprintf(file_name, sizeof file_name, 16 | BLOG_PREFIX "%llu/" BLOG_SUFFIX COMMENT_PREFIX "%d" COMMENT_SUFFIX, blog_id, 17 | comment_nb); 18 | if (create_atomic_file(file_name, COMMENT_MIN_SIZE, COMMENT_MAX_SIZE) != 0) { 19 | return -1; 20 | } 21 | stats_comments_write++; 22 | } while (--comment_nb != 0); 23 | 24 | return 0; 25 | } 26 | 27 | int 28 | create_random_comment(void) 29 | { 30 | if (create_comments(get_random_blog_id()) != 0) { 31 | return -1; 32 | } 33 | return 0; 34 | } 35 | 36 | void * 37 | commenter(void *const fodder) 38 | { 39 | (void) fodder; 40 | 41 | do { 42 | if (create_random_comment() != 0) { 43 | sleep(1); 44 | } 45 | #if USLEEP_COMMENTER > 0 46 | sleep(USLEEP_COMMENTER); 47 | #endif 48 | } while (stop == (sig_atomic_t) 0); 49 | 50 | return NULL; 51 | } 52 | -------------------------------------------------------------------------------- /src/globals.h: -------------------------------------------------------------------------------- 1 | #ifndef __GLOBALS_H__ 2 | #define __GLOBALS_H__ 1 3 | 4 | #ifdef DEFINE_GLOBALS 5 | #define GLOBAL0(A) A 6 | #define GLOBAL(A, B) A = B 7 | #else 8 | #define GLOBAL0(A) extern A 9 | #define GLOBAL(A, B) extern A 10 | #endif 11 | 12 | GLOBAL(pthread_mutex_t mx_next_blog_id, PTHREAD_MUTEX_INITIALIZER); 13 | GLOBAL(unsigned long long next_blog_id, 0ULL); 14 | 15 | /* XXX - FIXME : these counters should be atomic */ 16 | 17 | GLOBAL(unsigned long long stats_blogs_read, 0ULL); 18 | GLOBAL(unsigned long long stats_blogs_write, 0ULL); 19 | 20 | GLOBAL(unsigned long long stats_articles_read, 0ULL); 21 | GLOBAL(unsigned long long stats_articles_write, 0ULL); 22 | 23 | GLOBAL(unsigned long long stats_pictures_read, 0ULL); 24 | GLOBAL(unsigned long long stats_pictures_write, 0ULL); 25 | 26 | GLOBAL(unsigned long long stats_comments_read, 0ULL); 27 | GLOBAL(unsigned long long stats_comments_write, 0ULL); 28 | 29 | /* XXX - END OF FIXME : no more need for atomic operations */ 30 | 31 | GLOBAL(unsigned long long stats_total_blogs_read, 0ULL); 32 | GLOBAL(unsigned long long stats_total_blogs_write, 0ULL); 33 | 34 | GLOBAL(sig_atomic_t stop, (sig_atomic_t) 0); 35 | 36 | GLOBAL(unsigned int nb_commenters, DEFAULT_NB_COMMENTERS); 37 | GLOBAL(char *scratch_dir, DEFAULT_SCRATCH_DIR); 38 | GLOBAL(unsigned int stats_iterations, DEFAULT_STATS_ITERATIONS); 39 | GLOBAL(unsigned int nb_readers, DEFAULT_NB_READERS); 40 | GLOBAL(unsigned int nb_rewriters, DEFAULT_NB_REWRITERS); 41 | GLOBAL(unsigned int sleep_stats, DEFAULT_SLEEP_STATS); 42 | GLOBAL(unsigned int nb_writers, DEFAULT_NB_WRITERS); 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /src/helpers.c: -------------------------------------------------------------------------------- 1 | #include "blogbench.h" 2 | #ifdef HAVE_CONFIG_H 3 | #include 4 | #endif 5 | 6 | unsigned long long 7 | get_new_blog_id(void) 8 | { 9 | unsigned long long blog_id; 10 | 11 | pthread_mutex_lock(&mx_next_blog_id); 12 | blog_id = next_blog_id; 13 | next_blog_id++; 14 | pthread_mutex_unlock(&mx_next_blog_id); 15 | 16 | return blog_id; 17 | } 18 | 19 | unsigned long long 20 | get_last_blog_id(void) 21 | { 22 | unsigned long long blog_id; 23 | 24 | pthread_mutex_lock(&mx_next_blog_id); 25 | blog_id = next_blog_id; 26 | pthread_mutex_unlock(&mx_next_blog_id); 27 | if (blog_id == 0ULL) { 28 | return 0; 29 | } 30 | return blog_id - 1ULL; 31 | } 32 | 33 | unsigned long long 34 | get_random_blog_id(void) 35 | { 36 | unsigned long long last_blog_id; 37 | unsigned long long rnd; 38 | 39 | if ((last_blog_id = get_last_blog_id()) <= 0ULL) { 40 | return 0ULL; 41 | } 42 | rnd = (unsigned long long) rand() ^ ((unsigned long long) rand() << 16) ^ 43 | ((unsigned long long) rand() << 32) ^ ((unsigned long long) rand() << 48); 44 | 45 | return rnd % last_blog_id; 46 | } 47 | 48 | int 49 | create_dummy_file(const char *const file_name, size_t size) 50 | { 51 | static char garbage[WRITE_CHUNK_SIZE]; 52 | size_t towrite; 53 | ssize_t written; 54 | int fd; 55 | 56 | if ((fd = open(file_name, O_CREAT | O_TRUNC | O_WRONLY, (mode_t) 0600)) == -1) { 57 | if (errno != ENOENT) { 58 | reentrant_printf("open(\"%s\"): %s\n", file_name, strerror(errno)); 59 | } 60 | return -1; 61 | } 62 | while (size > (size_t) 0U) { 63 | if (sizeof garbage >= size) { 64 | towrite = size; 65 | } else { 66 | towrite = sizeof garbage; 67 | } 68 | while ((written = write(fd, garbage, towrite)) < (ssize_t) 0 && errno == EINTR) 69 | ; 70 | if (written < (ssize_t) 0) { 71 | reentrant_perror("write()"); 72 | (void) close(fd); 73 | (void) unlink(file_name); 74 | return -1; 75 | } 76 | size -= written; 77 | } 78 | if (close(fd) != 0) { 79 | reentrant_perror("close()"); 80 | (void) unlink(file_name); 81 | return -1; 82 | } 83 | return 0; 84 | } 85 | 86 | int 87 | create_atomic_file(const char *const file_name, 88 | const size_t article_min_size, 89 | const size_t article_max_size) 90 | { 91 | char tmp_file_name[MAXPATHLEN]; 92 | size_t size; 93 | 94 | if (article_min_size > article_max_size) { 95 | abort(); 96 | } 97 | snprintf(tmp_file_name, sizeof tmp_file_name, "%s" TMP_SUFFIX, file_name); 98 | size = article_min_size + rand() % (article_max_size - article_min_size); 99 | if (create_dummy_file(tmp_file_name, size) != 0) { 100 | return -1; 101 | } 102 | if (rename(tmp_file_name, file_name) != 0) { 103 | (void) unlink(tmp_file_name); 104 | (void) unlink(file_name); 105 | return -1; 106 | } 107 | return 0; 108 | } 109 | 110 | int 111 | read_dummy_file(const char *const file_name) 112 | { 113 | char *garbage; 114 | ssize_t readen; 115 | size_t i; 116 | const int page_size = getpagesize(); 117 | int fd; 118 | char dummy = 0; 119 | 120 | if ((fd = open(file_name, O_RDONLY)) == -1) { 121 | return -1; 122 | } 123 | if ((garbage = malloc(READ_CHUNK_SIZE)) == NULL) { 124 | return -1; 125 | } 126 | do { 127 | if ((readen = read(fd, garbage, sizeof garbage)) < (ssize_t) 0) { 128 | if (errno == EINTR) { 129 | continue; 130 | } 131 | reentrant_perror("read()"); 132 | break; 133 | } 134 | for (i = (size_t) 0U; i < READ_CHUNK_SIZE; i += page_size) { 135 | dummy ^= garbage[i]; 136 | } 137 | } while (readen > (ssize_t) 0); 138 | __asm__ __volatile__("" : : "r"(dummy)); 139 | free(garbage); 140 | if (close(fd) != 0) { 141 | reentrant_perror("close()"); 142 | return -1; 143 | } 144 | return 0; 145 | } 146 | 147 | static int 148 | safe_write(const int fd, const void *buf_, size_t count) 149 | { 150 | ssize_t written; 151 | register const char *buf = (const char *) buf_; 152 | 153 | while (count > (size_t) 0U) { 154 | for (;;) { 155 | if ((written = write(fd, buf, count)) <= (ssize_t) 0) { 156 | if (errno == EAGAIN) { 157 | sleep(1); 158 | } else if (errno != EINTR) { 159 | return -1; 160 | } 161 | continue; 162 | } 163 | break; 164 | } 165 | buf += written; 166 | count -= written; 167 | } 168 | return 0; 169 | } 170 | 171 | static int 172 | write_str(const char *const str) 173 | { 174 | return safe_write(1, str, strlen(str)); 175 | } 176 | 177 | int 178 | reentrant_puts(const char *const str) 179 | { 180 | char line[MAX_OUTPUT_LINE]; 181 | 182 | strncpy(line, str, sizeof line); 183 | line[sizeof line - (size_t) 1U] = 0; 184 | write_str(line); 185 | write_str("\n"); 186 | 187 | return 0; 188 | } 189 | 190 | int 191 | reentrant_printf(const char *const format, ...) 192 | { 193 | char line[MAX_OUTPUT_LINE]; 194 | va_list va; 195 | 196 | va_start(va, format); 197 | vsnprintf(line, sizeof line, format, va); 198 | write_str(line); 199 | va_end(va); 200 | 201 | return 0; 202 | } 203 | 204 | int 205 | reentrant_perror(const char *const str) 206 | { 207 | return reentrant_printf("%s: %s", str, strerror(errno)); 208 | } 209 | -------------------------------------------------------------------------------- /src/process.c: -------------------------------------------------------------------------------- 1 | #include "blogbench.h" 2 | #ifdef HAVE_CONFIG_H 3 | #include 4 | #endif 5 | 6 | static pthread_t *writers_threads; 7 | static pthread_t *rewriters_threads; 8 | static pthread_t *readers_threads; 9 | static pthread_t *commenters_threads; 10 | 11 | static int 12 | spawn_writers(void) 13 | { 14 | unsigned int t = nb_writers; 15 | 16 | do { 17 | t--; 18 | if (pthread_create(&writers_threads[t], NULL, writer, NULL) != 0) { 19 | return -1; 20 | } 21 | } while (t != 0U); 22 | 23 | return 0; 24 | } 25 | 26 | static int 27 | wait_writers(void) 28 | { 29 | unsigned int t = nb_writers; 30 | 31 | do { 32 | t--; 33 | pthread_join(writers_threads[t], NULL); 34 | } while (t != 0U); 35 | 36 | return 0; 37 | } 38 | 39 | static int 40 | spawn_rewriters(void) 41 | { 42 | unsigned int t = nb_rewriters; 43 | 44 | do { 45 | t--; 46 | if (pthread_create(&rewriters_threads[t], NULL, rewriter, NULL) != 0) { 47 | return -1; 48 | } 49 | } while (t != 0U); 50 | 51 | return 0; 52 | } 53 | 54 | static int 55 | wait_rewriters(void) 56 | { 57 | unsigned int t = nb_rewriters; 58 | 59 | do { 60 | t--; 61 | pthread_join(rewriters_threads[t], NULL); 62 | } while (t != 0U); 63 | 64 | return 0; 65 | } 66 | 67 | static int 68 | spawn_readers(void) 69 | { 70 | unsigned int t = nb_readers; 71 | 72 | do { 73 | t--; 74 | if (pthread_create(&readers_threads[t], NULL, reader, NULL) != 0) { 75 | return -1; 76 | } 77 | } while (t != 0U); 78 | 79 | return 0; 80 | } 81 | 82 | static int 83 | wait_readers(void) 84 | { 85 | unsigned int t = nb_readers; 86 | 87 | do { 88 | t--; 89 | pthread_join(readers_threads[t], NULL); 90 | } while (t != 0U); 91 | 92 | return 0; 93 | } 94 | 95 | static int 96 | spawn_commenters(void) 97 | { 98 | unsigned int t = nb_commenters; 99 | 100 | do { 101 | t--; 102 | if (pthread_create(&commenters_threads[t], NULL, commenter, NULL) != 0) { 103 | return -1; 104 | } 105 | } while (t != 0U); 106 | 107 | return 0; 108 | } 109 | 110 | static int 111 | wait_commenters(void) 112 | { 113 | unsigned int t = nb_commenters; 114 | 115 | do { 116 | t--; 117 | pthread_join(commenters_threads[t], NULL); 118 | } while (t != 0U); 119 | 120 | return 0; 121 | } 122 | 123 | static void 124 | show_grand_total(void) 125 | { 126 | reentrant_printf( 127 | "\n" 128 | "Final score for writes:%14llu\n" 129 | "Final score for reads :%14llu\n\n", 130 | stats_total_blogs_write, stats_total_blogs_read); 131 | } 132 | 133 | static int 134 | init_threads_area(void) 135 | { 136 | if ((writers_threads = malloc(nb_writers * sizeof writers_threads[0])) == NULL || 137 | (rewriters_threads = malloc(nb_rewriters * sizeof rewriters_threads[0])) == NULL || 138 | (readers_threads = malloc(nb_readers * sizeof readers_threads[0])) == NULL || 139 | (commenters_threads = malloc(nb_commenters * sizeof commenters_threads[0])) == NULL) { 140 | reentrant_perror("Out of memory to create threads"); 141 | return -1; 142 | } 143 | return 0; 144 | } 145 | 146 | int 147 | process(void) 148 | { 149 | unsigned int iterations = stats_iterations; 150 | 151 | if (init_threads_area() != 0) { 152 | return -1; 153 | } 154 | reentrant_printf( 155 | "\n" 156 | "Frequency = %u secs\n" 157 | "Scratch dir = [%s]\n", 158 | sleep_stats, scratch_dir); 159 | if (chdir(scratch_dir) != 0) { 160 | reentrant_printf("Unable to enter the scratch dir [%s]: [%s]\n", scratch_dir, 161 | strerror(errno)); 162 | return -1; 163 | } 164 | #ifdef HAVE_SYNC 165 | sync(); 166 | #endif 167 | reentrant_printf("Spawning %d writers...\n", nb_writers); 168 | if (spawn_writers() != 0) { 169 | reentrant_perror("Unable to create writers"); 170 | return -1; 171 | } 172 | reentrant_printf("Spawning %d rewriters...\n", nb_rewriters); 173 | if (spawn_rewriters() != 0) { 174 | reentrant_perror("Unable to create rewriters"); 175 | return -1; 176 | } 177 | reentrant_printf("Spawning %d commenters...\n", nb_commenters); 178 | if (spawn_commenters() != 0) { 179 | reentrant_perror("Unable to create commenters"); 180 | return -1; 181 | } 182 | reentrant_printf("Spawning %d readers...\n", nb_readers); 183 | if (spawn_readers() != 0) { 184 | reentrant_perror("Unable to create readers"); 185 | return -1; 186 | } 187 | reentrant_printf( 188 | "Benchmarking for %u iterations.\n" 189 | "The test will run during %u minutes.\n", 190 | stats_iterations, stats_iterations * sleep_stats / 60U); 191 | reentrant_puts( 192 | "\n" 193 | " Nb blogs R articles W articles R pictures" 194 | " W pictures R comments W comments"); 195 | do { 196 | sleep(sleep_stats); 197 | 198 | stats_total_blogs_read += stats_blogs_read; 199 | stats_total_blogs_write += stats_blogs_write; 200 | 201 | reentrant_printf("%10llu %12llu %13llu %13llu %13llu %13llu %13llu\n", 202 | stats_total_blogs_write, stats_articles_read, stats_articles_write, 203 | stats_pictures_read, stats_pictures_write, stats_comments_read, 204 | stats_comments_write); 205 | 206 | stats_blogs_read = stats_blogs_write = 0ULL; 207 | stats_articles_read = stats_articles_write = 0ULL; 208 | stats_pictures_read = stats_pictures_write = 0ULL; 209 | stats_comments_read = stats_comments_write = 0ULL; 210 | } while (--iterations != 0U && stop == (sig_atomic_t) 0); 211 | stop++; 212 | wait_commenters(); 213 | wait_readers(); 214 | wait_rewriters(); 215 | wait_writers(); 216 | show_grand_total(); 217 | 218 | return 0; 219 | } 220 | -------------------------------------------------------------------------------- /src/reader.c: -------------------------------------------------------------------------------- 1 | #include "blogbench.h" 2 | #ifdef HAVE_CONFIG_H 3 | #include 4 | #endif 5 | 6 | static int 7 | read_articles(const unsigned long long blog_id) 8 | { 9 | char file_name[MAXPATHLEN]; 10 | int article_nb = 1; 11 | 12 | for (;;) { 13 | snprintf(file_name, sizeof file_name, 14 | BLOG_PREFIX "%llu/" BLOG_SUFFIX PICTURE_PREFIX "%d" PICTURE_SUFFIX, blog_id, 15 | article_nb); 16 | if (read_dummy_file(file_name) == 0) { 17 | stats_pictures_read++; 18 | } 19 | snprintf(file_name, sizeof file_name, 20 | BLOG_PREFIX "%llu/" BLOG_SUFFIX ARTICLE_PREFIX "%d" ARTICLE_SUFFIX, blog_id, 21 | article_nb); 22 | if (read_dummy_file(file_name) != 0) { 23 | break; 24 | } 25 | stats_articles_read++; 26 | article_nb++; 27 | } 28 | return 0; 29 | } 30 | 31 | static int 32 | read_comments(const unsigned long long blog_id) 33 | { 34 | char file_name[MAXPATHLEN]; 35 | int comment_nb = 1; 36 | 37 | for (;;) { 38 | snprintf(file_name, sizeof file_name, 39 | BLOG_PREFIX "%llu/" BLOG_SUFFIX COMMENT_PREFIX "%d" COMMENT_SUFFIX, blog_id, 40 | comment_nb); 41 | if (read_dummy_file(file_name) != 0) { 42 | break; 43 | } 44 | stats_comments_read++; 45 | comment_nb++; 46 | } 47 | return 0; 48 | } 49 | 50 | static int 51 | read_random_blog(void) 52 | { 53 | unsigned long long blog_id; 54 | 55 | if ((blog_id = get_random_blog_id()) == 0ULL) { 56 | return -1; 57 | } 58 | if ((rand() & 1) == 0 && read_comments(blog_id) != 0) { 59 | return -1; 60 | } else if (read_articles(blog_id) != 0) { 61 | return -1; 62 | } 63 | stats_blogs_read++; 64 | 65 | return 0; 66 | } 67 | 68 | void * 69 | reader(void *const fodder) 70 | { 71 | (void) fodder; 72 | 73 | do { 74 | if (read_random_blog() != 0) { 75 | sleep(1); 76 | } 77 | #if USLEEP_READER > 0 78 | usleep(USLEEP_READER); 79 | #endif 80 | } while (stop == (sig_atomic_t) 0); 81 | 82 | return NULL; 83 | } 84 | -------------------------------------------------------------------------------- /src/rewriter.c: -------------------------------------------------------------------------------- 1 | #include "blogbench.h" 2 | #ifdef HAVE_CONFIG_H 3 | #include 4 | #endif 5 | 6 | static int 7 | modify_random_picture(const unsigned long long blog_id) 8 | { 9 | char file_name[MAXPATHLEN]; 10 | int picture_id; 11 | 12 | while ((picture_id = 1 + rand() % MAX_PICTURES) <= 0) 13 | ; 14 | snprintf(file_name, sizeof file_name, 15 | BLOG_PREFIX "%llu/" BLOG_SUFFIX PICTURE_PREFIX "%d" PICTURE_SUFFIX, blog_id, 16 | picture_id); 17 | if (create_atomic_file(file_name, PICTURE_MIN_SIZE, PICTURE_MAX_SIZE) != 0) { 18 | return -1; 19 | } 20 | stats_pictures_write++; 21 | 22 | return 0; 23 | } 24 | 25 | static int 26 | modify_random_article(const unsigned long long blog_id) 27 | { 28 | char file_name[MAXPATHLEN]; 29 | int article_id; 30 | 31 | while ((article_id = 1 + rand() % MAX_ARTICLES) <= 0) 32 | ; 33 | snprintf(file_name, sizeof file_name, 34 | BLOG_PREFIX "%llu/" BLOG_SUFFIX ARTICLE_PREFIX "%d" ARTICLE_SUFFIX, blog_id, 35 | article_id); 36 | if (create_atomic_file(file_name, ARTICLE_MIN_SIZE, ARTICLE_MAX_SIZE) != 0) { 37 | return -1; 38 | } 39 | stats_articles_write++; 40 | 41 | return 0; 42 | } 43 | 44 | static int 45 | modify_random_blog(void) 46 | { 47 | unsigned long long blog_id; 48 | 49 | if ((blog_id = get_random_blog_id()) == 0ULL) { 50 | return -1; 51 | } 52 | if ((rand() & 1) == 0 && modify_random_picture(blog_id) != 0) { 53 | return -1; 54 | } else if (modify_random_article(blog_id) != 0) { 55 | return -1; 56 | } 57 | return 0; 58 | } 59 | 60 | void * 61 | rewriter(void *const fodder) 62 | { 63 | (void) fodder; 64 | 65 | do { 66 | if (modify_random_blog() != 0) { 67 | sleep(1); 68 | } 69 | #if USLEEP_REWRITER > 0 70 | usleep(USLEEP_REWRITER); 71 | #endif 72 | } while (stop == (sig_atomic_t) 0); 73 | 74 | return NULL; 75 | } 76 | -------------------------------------------------------------------------------- /src/writer.c: -------------------------------------------------------------------------------- 1 | #include "blogbench.h" 2 | #ifdef HAVE_CONFIG_H 3 | #include 4 | #endif 5 | 6 | static int 7 | create_articles(char *const file_name, 8 | const size_t sizeof_file_name, 9 | const unsigned long long blog_id) 10 | { 11 | int article_nb; 12 | 13 | while ((article_nb = 1 + rand() % MAX_ARTICLES) <= 0) 14 | ; 15 | do { 16 | snprintf(file_name, sizeof_file_name, 17 | BLOG_PREFIX "%llu/" BLOG_SUFFIX ARTICLE_PREFIX "%d" ARTICLE_SUFFIX, blog_id, 18 | article_nb); 19 | if (create_atomic_file(file_name, ARTICLE_MIN_SIZE, ARTICLE_MAX_SIZE) != 0) { 20 | return -1; 21 | } 22 | stats_articles_write++; 23 | } while (--article_nb != 0); 24 | 25 | return 0; 26 | } 27 | 28 | static int 29 | create_pictures(char *const file_name, 30 | const size_t sizeof_file_name, 31 | const unsigned long long blog_id) 32 | { 33 | int picture_nb; 34 | 35 | while ((picture_nb = 1 + rand() % MAX_PICTURES) <= 0) 36 | ; 37 | do { 38 | snprintf(file_name, sizeof_file_name, 39 | BLOG_PREFIX "%llu/" BLOG_SUFFIX PICTURE_PREFIX "%d" PICTURE_SUFFIX, blog_id, 40 | picture_nb); 41 | if (create_atomic_file(file_name, PICTURE_MIN_SIZE, PICTURE_MAX_SIZE) != 0) { 42 | return -1; 43 | } 44 | stats_pictures_write++; 45 | } while (--picture_nb != 0); 46 | 47 | return 0; 48 | } 49 | 50 | static int 51 | create_blog(void) 52 | { 53 | char file_name[MAXPATHLEN]; 54 | unsigned long long blog_id = get_new_blog_id(); 55 | 56 | snprintf(file_name, sizeof file_name, BLOG_PREFIX "%llu" BLOG_SUFFIX, blog_id); 57 | if (mkdir(file_name, (mode_t) 0700) != 0 && errno != EEXIST) { 58 | reentrant_perror("mkdir()"); 59 | sleep(1); 60 | return -1; 61 | } 62 | if (create_articles(file_name, sizeof file_name, blog_id) != 0 || 63 | create_pictures(file_name, sizeof file_name, blog_id) != 0) { 64 | return -1; 65 | } 66 | stats_blogs_write++; 67 | 68 | return 0; 69 | } 70 | 71 | void * 72 | writer(void *const fodder) 73 | { 74 | (void) fodder; 75 | 76 | do { 77 | if (create_blog() != 0) { 78 | sleep(1); 79 | } 80 | #if USLEEP_WRITER > 0 81 | usleep(USLEEP_WRITER); 82 | #endif 83 | } while (stop == (sig_atomic_t) 0); 84 | 85 | return NULL; 86 | } 87 | --------------------------------------------------------------------------------