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  }