github.com/swiftstack/proxyfs@v0.0.0-20201223034610-5434d919416e/readdir-stress/readdir-stress.c (about) 1 #include <dirent.h> 2 #include <errno.h> 3 #include <fcntl.h> 4 #include <libgen.h> 5 #include <pthread.h> 6 #include <stddef.h> 7 #include <stdio.h> 8 #include <stdlib.h> 9 #include <string.h> 10 #include <unistd.h> 11 12 #include <sys/stat.h> 13 #include <sys/types.h> 14 15 typedef struct { 16 pthread_t thread; 17 int passes_completed; 18 } readdir_thread_t; 19 20 #define MAX_DIR_ENTRIES 1000 21 #define MAX_PASSES_PER_THREAD 100000 22 #define MAX_THREADS 80 // Note: Line-length of status display limited 23 24 #define SECONDS_PER_POLL 1 25 26 #define DIR_CREATION_MODE (S_IRWXU | S_IRWXG | S_IRWXO) 27 #define FILE_CREATION_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH) 28 29 static const char convert_int_modulo_0_thru_9_A_thru_Z[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 30 31 char *dir_path; 32 size_t dirent_malloc_size; 33 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 34 int num_dir_entries; 35 int num_passes_in_total; 36 int num_passes_per_thread; 37 int num_threads; 38 int passes_completed_in_total; 39 40 void usage(const char *argv_0) { 41 fprintf(stderr, "%s <dir-path> <# dir-entries> <# threads> <# passes/thread>\n", argv_0); 42 fprintf(stderr, " <dir-path>: directory to create (must not exist; parent must exist)\n"); 43 fprintf(stderr, " <# dir-entries>: # of files created in <dir-path>-specified directory\n"); 44 fprintf(stderr, " <# threads: # of threads simultaneously performing readdir_r(3)'s\n"); 45 fprintf(stderr, " <# passes/thread>: # of times to read the <dir-path>-specified directory\n"); 46 } 47 48 void *readdir_start_routine(void *arg) { 49 DIR *dir; 50 struct dirent *entry; 51 int file_index; 52 int pass_index; 53 int posix_ret; 54 readdir_thread_t *readdir_thread = (readdir_thread_t *)arg; 55 struct dirent *result; 56 57 entry = (struct dirent *)malloc(dirent_malloc_size); 58 if (NULL == entry) { 59 fprintf(stderr, "entry = malloc(dirent_malloc_size) failed: %s\n", strerror(errno)); 60 exit(1); 61 } 62 63 for (pass_index = 0; pass_index < num_passes_per_thread; pass_index++) { 64 dir = opendir(dir_path); 65 if (NULL == dir) { 66 fprintf(stderr, "opendir(%s) failed: %s\n", dir_path, strerror(errno)); 67 exit(1); 68 } 69 70 // Read ".", "..", and all but the last entry 71 for (file_index = -1; file_index <= num_dir_entries; file_index++) { 72 result = NULL; 73 posix_ret = readdir_r(dir, entry, &result); 74 if (0 != posix_ret) { 75 fprintf(stderr, "readdir_r(%s,,) failed: %s\n", dir_path, strerror(posix_ret)); 76 exit(1); 77 } 78 if (NULL == result) { // For all but the last entry 79 fprintf(stderr, "readdir_r(%s,,) should have returned non-NULL result\n", dir_path); 80 exit(1); 81 } 82 } 83 84 // Read what should be the last entry 85 result = NULL; 86 posix_ret = readdir_r(dir, entry, &result); 87 if (0 != posix_ret) { 88 fprintf(stderr, "readdir_r(%s,,) failed: %s\n", dir_path, strerror(posix_ret)); 89 exit(1); 90 } 91 if (NULL != result) { // For the last entry 92 fprintf(stderr, "readdir_r(%s,,) should have returned NULL result\n", dir_path); 93 exit(1); 94 } 95 96 posix_ret = closedir(dir); 97 if (0 != posix_ret) { 98 fprintf(stderr, "closedir(%s) failed: %s\n", dir_path, strerror(errno)); 99 exit(1); 100 } 101 102 readdir_thread->passes_completed = 1 + pass_index; 103 104 posix_ret = pthread_mutex_lock(&mutex); 105 if (0 != posix_ret) { 106 fprintf(stderr, "pthread_mutex_lock(&mutex) failed: %s\n", strerror(posix_ret)); 107 exit(1); 108 } 109 110 passes_completed_in_total++; 111 112 posix_ret = pthread_mutex_unlock(&mutex); 113 if (0 != posix_ret) { 114 fprintf(stderr, "pthread_mutex_unlock(&mutex) failed: %s\n", strerror(posix_ret)); 115 exit(1); 116 } 117 } 118 119 free(entry); 120 121 return NULL; 122 } 123 124 int main(int argc, const char * argv[]) { 125 DIR *dir_path_dir; 126 char *dir_path_dirname; 127 char *dir_path_dirname_buf; 128 size_t dirent_d_name_offset; 129 int fd; 130 int file_index; 131 char *file_path; 132 size_t file_path_len; 133 int posix_ret; 134 readdir_thread_t *readdir_thread; 135 readdir_thread_t *readdir_thread_array; 136 struct stat stat_buf; 137 char *status_string; 138 int thread_index; 139 140 // Validate arguments 141 142 if (5 != argc) { 143 usage(argv[0]); 144 exit(1); 145 } 146 147 dir_path = strdup(argv[1]); 148 if (NULL == dir_path) { 149 fprintf(stderr, "strdup(%s) failed: %s\n", argv[1], strerror(errno)); 150 exit(1); 151 } 152 153 num_dir_entries = atoi(argv[2]); 154 num_threads = atoi(argv[3]); 155 num_passes_per_thread = atoi(argv[4]); 156 157 dir_path_dirname_buf = strdup(dir_path); 158 if (NULL == dir_path_dirname_buf) { 159 fprintf(stderr, "strdup(%s) failed: %s\n", dir_path, strerror(errno)); 160 exit(1); 161 } 162 dir_path_dirname = dirname(dir_path_dirname_buf); 163 164 dir_path_dir = opendir(dir_path_dirname); 165 if (NULL == dir_path_dir) { 166 fprintf(stderr, "%s must exist\n", dir_path_dirname); 167 exit(1); 168 } else { 169 posix_ret = closedir(dir_path_dir); 170 if (0 != posix_ret) { 171 fprintf(stderr, "closedir(%s) failed: %s\n", dir_path_dirname, strerror(errno)); 172 exit(1); 173 } 174 } 175 176 posix_ret = stat(dir_path, &stat_buf); 177 if (0 == posix_ret) { 178 fprintf(stderr, "%s must not pre-exist\n", dir_path); 179 exit(1); 180 } else { 181 if (ENOENT != errno) { 182 fprintf(stderr, "stat(%s,) failed: %s\n", dir_path, strerror(errno)); 183 exit(1); 184 } 185 } 186 187 if ((1 > num_dir_entries) || (MAX_DIR_ENTRIES < num_dir_entries)) { 188 fprintf(stderr, "num_dir_entries (%d) must be between 1 and %d (inclusive)\n", num_dir_entries, MAX_DIR_ENTRIES); 189 exit(1); 190 } 191 if ((1 > num_threads) || (MAX_THREADS < num_threads)) { 192 fprintf(stderr, "num_threads (%d) must be between 1 and %d (inclusive)\n", num_threads, MAX_THREADS); 193 exit(1); 194 } 195 if ((1 > num_passes_per_thread) || (MAX_PASSES_PER_THREAD < num_passes_per_thread)) { 196 fprintf(stderr, "num_passes_per_thread (%d) must be between 1 and %d (inclusive)\n", num_passes_per_thread, MAX_PASSES_PER_THREAD); 197 exit(1); 198 } 199 200 // Build test directory 201 202 posix_ret = mkdir(dir_path, DIR_CREATION_MODE); 203 if (0 != posix_ret) { 204 fprintf(stderr, "mkdir(%s,) failed: %s\n", dir_path, strerror(errno)); 205 exit(1); 206 } 207 208 file_path_len = strlen(dir_path) + 1 + 5 + 4; // "<dir_path>/file_XXXX" 209 file_path = (char *)malloc(file_path_len + 1); 210 if (NULL == file_path) { 211 fprintf(stderr, "file_path = malloc(file_path_len + 1) failed: %s\n", strerror(errno)); 212 exit(1); 213 } 214 215 for (file_index = 0; file_index < num_dir_entries; file_index++) { 216 posix_ret = sprintf(file_path, "%s/file_%04X", dir_path, file_index); 217 if (file_path_len != posix_ret) { 218 fprintf(stderr, "sprintf(,,,) returned unexpected length: %d\n", posix_ret); 219 exit(1); 220 } 221 222 fd = open(file_path, O_WRONLY|O_CREAT|O_EXCL, FILE_CREATION_MODE); 223 if (-1 == fd) { 224 fprintf(stderr, "open(%s,,) failed: %s\n", file_path, strerror(errno)); 225 exit(1); 226 } 227 228 posix_ret = close(fd); 229 if (0 != posix_ret) { 230 fprintf(stderr, "close(fd[%s]) failed: %s\n", file_path, strerror(errno)); 231 exit(1); 232 } 233 } 234 235 // Start pthreads 236 237 dirent_d_name_offset = offsetof(struct dirent, d_name); 238 dirent_malloc_size = dirent_d_name_offset + 5 + 4 + 1; // "file_XXXX" 239 240 num_passes_in_total = num_threads * num_passes_per_thread; 241 passes_completed_in_total = 0; 242 243 readdir_thread_array = (readdir_thread_t *)malloc(num_threads * sizeof(readdir_thread_t)); 244 if (NULL == readdir_thread_array) { 245 fprintf(stderr, "readdir_thread_array = malloc(%d * sizeof(readdir_thread_t)) failed: %s\n", num_threads, strerror(errno)); 246 exit(1); 247 } 248 249 for (thread_index = 0; thread_index < num_threads; thread_index++) { 250 readdir_thread = &readdir_thread_array[thread_index]; 251 252 readdir_thread->passes_completed = 0; 253 254 posix_ret = pthread_create(&readdir_thread->thread, NULL, readdir_start_routine, readdir_thread); 255 if (0 != posix_ret) { 256 fprintf(stderr, "pthread_create(,,,) failed: %s\n", strerror(posix_ret)); 257 exit(1); 258 } 259 } 260 261 // Poll reader_thread's until they are done 262 263 status_string = (char *)malloc(num_threads + 1); 264 if (NULL == status_string) { 265 fprintf(stderr, "status_string = malloc(num_threads + 1) failed: %s\n", strerror(errno)); 266 exit(1); 267 } 268 status_string[num_threads] = '\0'; 269 270 for (;;) { 271 (void)sleep(SECONDS_PER_POLL); 272 273 for (thread_index = 0; thread_index < num_threads; thread_index++) { 274 readdir_thread = &readdir_thread_array[thread_index]; 275 status_string[thread_index] = convert_int_modulo_0_thru_9_A_thru_Z[readdir_thread->passes_completed % 36]; 276 } 277 278 printf("%s\n", status_string); 279 280 if (num_passes_in_total == passes_completed_in_total) { 281 break; 282 } 283 } 284 285 // Clean-up 286 287 for (thread_index = 0; thread_index < num_threads; thread_index++) { 288 readdir_thread = &readdir_thread_array[thread_index]; 289 290 posix_ret = pthread_join(readdir_thread->thread, NULL); 291 if (0 != posix_ret) { 292 fprintf(stderr, "pthread_join(,) failed: %s\n", strerror(posix_ret)); 293 exit(1); 294 } 295 } 296 297 for (file_index = 0; file_index < num_dir_entries; file_index++) { 298 posix_ret = sprintf(file_path, "%s/file_%04X", dir_path, file_index); 299 if (file_path_len != posix_ret) { 300 fprintf(stderr, "sprintf(,,,) returned unexpected length: %d\n", posix_ret); 301 exit(1); 302 } 303 304 posix_ret = unlink(file_path); 305 if (0 != posix_ret) { 306 fprintf(stderr, "unlink(%s) failed: %s\n", file_path, strerror(errno)); 307 exit(1); 308 } 309 } 310 311 posix_ret = rmdir(dir_path); 312 if (0 != posix_ret) { 313 fprintf(stderr, "rmdir(%s) failed: %s\n", dir_path, strerror(errno)); 314 exit(1); 315 } 316 317 free(status_string); 318 free(readdir_thread_array); 319 free(file_path); 320 free(dir_path_dirname_buf); 321 free(dir_path); 322 323 exit(0); 324 }