<commit_before>#include <fcntl.h>
#include <getopt.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/un.h>
#include <unistd.h>
#include <wait.h>

#include <iostream>
#include <string>
#include <vector>

#include "../utils/utils.h"
#include "../utils/communication/communication.h"
#include "../utils/communication/ServerSocket.h"
#include "Tester.h"
#include "../tests/BaseTestCase.h"

#define TEST_SO_PATH "tests/"
#define PERMUTER_SO_PATH "permuter/"
// TODO(ashmrtn): Find a good delay time to use for tests.
#define TEST_DIRTY_EXPIRE_TIME "500"
#define WRITE_DELAY 20
#define MOUNT_DELAY 3

#define DIRECTORY_PERMS \
  (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)

#define OPTS_STRING "bd:f:e:l:m:np:r:s:t:v"

using std::cerr;
using std::cout;
using std::endl;
using std::string;
using fs_testing::Tester;
using fs_testing::utils::communication::ServerSocket;

static const option long_options[] = {
  {"background", no_argument, NULL, 'b'},
  {"test-dev", required_argument, NULL, 'd'},
  {"disk_size", required_argument, NULL, 'e'},
  {"flag-device", required_argument, NULL, 'f'},
  {"log-file", required_argument, NULL, 'l'},
  {"mount-opts", required_argument, NULL, 'm'},
  {"dry-run", no_argument, NULL, 'n'},
  {"permuter", required_argument, NULL, 'p'},
  {"reload-log-file", required_argument, NULL, 'r'},
  {"iterations", required_argument, NULL, 's'},
  {"fs-type", required_argument, NULL, 't'},
  {"verbose", no_argument, NULL, 'v'},
  {0, 0, 0, 0},
};

int main(int argc, char** argv) {
  string dirty_expire_time_centisecs(TEST_DIRTY_EXPIRE_TIME);
  unsigned long int test_sleep_delay = WRITE_DELAY;
  string fs_type("ext4");
  string flags_dev("/dev/vda");
  string test_dev("/dev/ram0");
  string mount_opts("");
  string log_file_save("");
  string log_file_load("");
  string permuter(PERMUTER_SO_PATH "RandomPermuter.so");
  bool background = false;
  bool dry_run = false;
  bool no_lvm = false;
  bool verbose = false;
  int iterations = 1000;
  int disk_size = 10240;
  int option_idx = 0;
  ServerSocket* background_com;

  // Parse command line arguments.
  for (int c = getopt_long(argc, argv, OPTS_STRING, long_options, &option_idx);
        c != -1;
        c = getopt_long(argc, argv, OPTS_STRING, long_options, &option_idx)) {
    switch (c) {
      case 'b':
        background = true;
        break;
      case 'f':
        flags_dev = string(optarg);
        break;
      case 'd':
        test_dev = string(optarg);
        break;
      case 'e':
        disk_size = atoi(optarg);
        break;
      case 'l':
        log_file_save = string(optarg);
        break;
      case 'm':
        mount_opts = string(optarg);
        break;
      case 'n':
        dry_run = 1;
        break;
      case 'p':
        permuter = string(optarg);
        break;
      case 'r':
        log_file_load = string(optarg);
        break;
      case 's':
        iterations = atoi(optarg);
        break;
      case 't':
        fs_type = string(optarg);
        break;
      case 'v':
        verbose = true;
        break;
      case '?':
      default:
        return -1;
    }
  }


  /*****************************************************************************
   * PHASE 0:
   * Basic setup of the test harness:
   * 1. check arguments are sane
   * 2. load up socket connections if/when needed
   * 3. load basic kernel modules
   * 4. load static objects for permuter and test case
   ****************************************************************************/
  const unsigned int test_case_idx = optind;

  cout << "========== PHASE 0: Setting up CrashMonkey basics =========="
    << endl;
  if (test_case_idx == argc) {
    cerr << "Please give a .so test case to load" << endl;
    return -1;
  }

  if (iterations < 0) {
    cerr << "Please give a positive number of iterations to run" << endl;
    return -1;
  }

  if (disk_size <= 0) {
    cerr << "Please give a positive number for the RAM disk size to use"
      << endl;
    return -1;
  }


  // Create a socket to coordinate with the outside world.
  if (background) {
    // TODO(ashmrtn): Fix permissions on the socket.
    /*
    struct stat socket_dir;
    int res = stat(SOCKET_DIR, &socket_dir);
    // Directory does not exist.
    if (res < 0 && errno == 2) {
      if (mkdir(SOCKET_DIR, DIRECTORY_PERMS) < 0) {
        cerr << "Error creating temp directory" << endl;
        delete background_com;
        return -1;
      }
    } else if (res < 0) {
      // Some other error.
      cerr << "Error trying to find temp directory" << endl;
      delete background_com;
      return -1;
    } else if (!S_ISDIR(socket_dir.st_mode)) {
      // Something there that's not a directory.
      cerr << "Something not a directory already exists at " << SOCKET_DIR
        << endl;
      delete background_com;
      return -1;
    }

    // Incorrect permissions.
    if ((socket_dir.st_mode & DIRECTORY_PERMS) != DIRECTORY_PERMS) {
      cout << "Changing permissions on " << SOCKET_DIR << " to be world "
        << "readable and writable" << endl;
      if (chmod(SOCKET_DIR, DIRECTORY_PERMS) < 0) {
        cerr << "Error changing permissions on " << SOCKET_DIR << endl;
        delete background_com;
        return -1;
      }
    }
    */

    background_com = new ServerSocket(SOCKET_NAME_OUTBOUND);
    if (background_com->Init() < 0) {
      int err_no = errno;
      cerr << "Error starting socket to listen on " << err_no << endl;
      delete background_com;
      return -1;
    }

    // TODO(ashmrtn): Fix permissions so we can still insert kernel modules
    // after we fork. Then uncomment the following.
    // We need to phone home and tell the process that made us that we are ready
    // for users to do things.
    /*
    if (bootstrap) {
      if (background_com->WaitAndSendInt(HARNESS_PREPARE_DONE) < 0) {
        cerr << "Error contacting stub bootstrap process" << endl;
        delete background_com;
        return -1;
      }
    }
    */
  }

  Tester test_harness(disk_size, verbose);

  cout << "Inserting RAM disk module" << endl;
  if (test_harness.insert_cow_brd() != SUCCESS) {
    cerr << "Error inserting RAM disk module" << endl;
    return -1;
  }
  test_harness.set_fs_type(fs_type);
  test_harness.set_device(test_dev);

  // Load the class being tested.
  cout << "Loading test case" << endl;
  if (test_harness.test_load_class(argv[test_case_idx]) != SUCCESS) {
    test_harness.cleanup_harness();
      return -1;
  }

  // Load the permuter to use for the test.
  // TODO(ashmrtn): Consider making a line in the test file which specifies the
  // permuter to use?
  cout << "Loading permuter" << endl;
  if (test_harness.permuter_load_class(permuter.c_str()) != SUCCESS) {
    test_harness.cleanup_harness();
      return -1;
  }

  // Update dirty_expire_time.
  cout << "Updating dirty_expire_time_centisecs" << endl;
  const char* old_expire_time =
    test_harness.update_dirty_expire_time(dirty_expire_time_centisecs.c_str());
  if (old_expire_time == NULL) {
    cerr << "Error updating dirty_expire_time_centisecs" << endl;
    test_harness.cleanup_harness();
    return -1;
  }


  /*****************************************************************************
   * PHASE 1:
   * Setup the base image of the disk for snapshots later. This could happen in
   * one of several ways:
   * 1. The -r flag specifies that there are log files which contain the disk
   *    image. These will now be loaded from disk
   * 2. The -b flag specifies that CrashMonkey is running as a "background"
   *    process of sorts and should listen on its socket for commands from the
   *    user telling it when to perform certain actions. The user is responsible
   *    for running their own pre-test setup methods at the proper times
   * 3. CrashMonkey is running as a standalone program. It will run the pre-test
   *    setup methods defined in the test case static object it loaded
   ****************************************************************************/

  cout << endl << "========== PHASE 1: Creating base disk image =========="
    << endl;
  // Run the normal test setup stuff if we don't have a log file.
  if (log_file_load.empty()) {
    /***************************************************************************
     * Setup for both background operation and standalone mode operation.
     **************************************************************************/
    if (flags_dev.empty()) {
      cerr << "No device to copy flags from given" << endl;
      return -1;
    }

    // Device flags only need set if we are logging requests.
    test_harness.set_flag_device(flags_dev);

    // Format test drive to desired type.
    cout << "Formatting test drive" << endl;
    if (test_harness.format_drive() != SUCCESS) {
      cerr << "Error formatting test drive" << endl;
      test_harness.cleanup_harness();
      return -1;
    }

    // Mount test file system for pre-test setup.
    cout << "Mounting test file system for pre-test setup\n";
    if (test_harness.mount_device_raw(mount_opts.c_str()) != SUCCESS) {
      cerr << "Error mounting test device" << endl;
      test_harness.cleanup_harness();
      return -1;
    }

    // TODO(ashmrtn): Close startup socket fd here.

    if (background) {
      cout << "+++++ Please run any needed pre-test setup +++++" << endl;
      /*************************************************************************
       * Background mode user setup. Wait for the user to tell use that they
       * have finished the pre-test setup phase.
       ************************************************************************/
      int command;
      do {
        if (background_com->WaitForInt(&command) < 0) {
          cerr << "Error getting command from socket" << endl;
          delete background_com;
          test_harness.cleanup_harness();
          return -1;
        }

        if (command != HARNESS_BEGIN_LOG) {
          if (background_com->SendInt(HARNESS_WRONG_COMMAND) < 0) {
            cerr << "Error sending response to client" << endl;
            delete background_com;
            test_harness.cleanup_harness();
            return -1;
          }
          background_com->CloseClient();
        }
      } while (command != HARNESS_BEGIN_LOG);
    } else {
      /*************************************************************************
       * Standalone mode user setup. Run the pre-test "setup()" method defined
       * in the test case. Run as a separate process for the sake of
       * cleanliness.
       ************************************************************************/
      cout << "Running pre-test setup\n";
      {
        const pid_t child = fork();
        if (child < 0) {
          cerr << "Error creating child process to run pre-test setup\n";
          test_harness.cleanup_harness();
        } else if (child != 0) {
          // Parent process should wait for child to terminate before proceeding.
          pid_t status;
          wait(&status);
          if (status != 0) {
            cerr << "Error in pre-test setup\n";
            test_harness.cleanup_harness();
          }
        } else {
          return test_harness.test_setup();
        }
      }
    }

    /***************************************************************************
     * Pre-test setup complete. Unmount the test file system and snapshot the
     * disk for use in workload and tests.
     **************************************************************************/
    // Unmount the test file system after pre-test setup.
    cout << "Unmounting test file system after pre-test setup\n";
    if (test_harness.umount_device() != SUCCESS) {
      test_harness.cleanup_harness();
      return -1;
    }

    // Create snapshot of disk for testing.
    cout << "Making new snapshot\n";
    if (test_harness.clone_device() != SUCCESS) {
      test_harness.cleanup_harness();
      return -1;
    }

    // If we're logging this test run then also save the snapshot.
    if (!log_file_save.empty()) {
      /*************************************************************************
       * The -l flag specifies that we should save the information for this
       * harness execution. Therefore, save the disk image we are using as the
       * base image for our snapshots.
       ************************************************************************/
      cout << "Saving snapshot to log file" << endl;
      if (test_harness.log_snapshot_save(log_file_save + "_snap")
          != SUCCESS) {
        test_harness.cleanup_harness();
        return -1;
      }
    }
  } else {
    /***************************************************************************
     * The -r flag specifies that we should load information from the provided
     * log file. Load the base disk image for snapshots here.
     **************************************************************************/
    // Load the snapshot in the log file and then write it to disk.
    cout << "Loading saved snapshot" << endl;
    if (test_harness.log_snapshot_load(log_file_load + "_snap") != SUCCESS) {
      test_harness.cleanup_harness();
      return -1;
    }
  }


  /*****************************************************************************
   * PHASE 2:
   * Obtain a series of disk epochs to operate on in the test phase of the
   * harness. Again, this could happen in one of several ways:
   * 1. The -r flag specifies that there are log files which contain the disk
   *    epochs. These will now be loaded from disk
   * 2. The -b flag specifies that CrashMonkey is running as a "background"
   *    process of sorts and should listen on its socket for commands from the
   *    user telling it when to perform certain actions. The user is responsible
   *    for running their own workload methods at the proper times
   * 3. CrashMonkey is running as a standalone program. It will run the workload
   *    methods defined in the test case static object it loaded
   ****************************************************************************/

  cout << endl << "========== PHASE 2: Recording user workload =========="
    << endl;
  // TODO(ashmrtn): Consider making a flag for this?
  cout << "Clearing caches" << endl;
  if (test_harness.clear_caches() != SUCCESS) {
    cerr << "Error clearing caches" << endl;
    test_harness.cleanup_harness();
    return -1;
  }

  // No log file given so run the test profile.
  if (log_file_load.empty()) {
    /***************************************************************************
     * Preparations for both background operation and standalone mode operation.
     **************************************************************************/

    // Insert the disk block wrapper into the kernel.
    cout << "Inserting wrapper module into kernel\n";
    if (test_harness.insert_wrapper() != SUCCESS) {
      cerr << "Error inserting kernel wrapper module\n";
      test_harness.cleanup_harness();
      return -1;
    }

    // Mount the file system under the wrapper module for profiling.
    cout << "Mounting wrapper file system\n";
    if (test_harness.mount_wrapper_device(mount_opts.c_str()) != SUCCESS) {
      cerr << "Error mounting wrapper file system\n";
      test_harness.cleanup_harness();
      return -1;
    }

    // TODO(ashmrtn): Can probably remove this...
    cout << "Sleeping after mount" << endl;
    unsigned int to_sleep = MOUNT_DELAY;
    do {
      to_sleep = sleep(to_sleep);
    } while (to_sleep > 0);

    // Get access to wrapper module ioctl functions via FD.
    cout << "Getting wrapper device ioctl fd\n";
    if (test_harness.get_wrapper_ioctl() != SUCCESS) {
      cerr << "Error opening device file\n";
      test_harness.cleanup_harness();
      return -1;
    }

    // Clear wrapper module logs prior to test profiling.
    cout << "Clearing wrapper device logs\n";
    test_harness.clear_wrapper_log();
    cout << "Enabling wrapper device logging\n";
    test_harness.begin_wrapper_logging();

    /***************************************************************************
     * Run the actual workload that we will be testing.
     **************************************************************************/
    if (background) {
      /************************************************************************
       * Background mode user workload. Tell the user we have finished workload
       * preparations and are ready for them to run the workload since we are
       * now logging requests.
       ***********************************************************************/
      if (background_com->SendInt(HARNESS_LOG_READY) < 0) {
        cerr << "Error telling user ready for workload" << endl;
        delete background_com;
        test_harness.cleanup_harness();
        return -1;
      }
      background_com->CloseClient();

      cout << "+++++ Please run workload +++++" << endl;<commit_msg>Update test harness to use newer socket code.<commit_after>#include <fcntl.h>
#include <getopt.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/un.h>
#include <unistd.h>
#include <wait.h>

#include <iostream>
#include <string>
#include <vector>

#include "../utils/utils.h"
#include "../utils/communication/communication.h"
#include "../utils/communication/ServerSocket.h"
#include "Tester.h"
#include "../tests/BaseTestCase.h"

#define TEST_SO_PATH "tests/"
#define PERMUTER_SO_PATH "permuter/"
// TODO(ashmrtn): Find a good delay time to use for tests.
#define TEST_DIRTY_EXPIRE_TIME "500"
#define WRITE_DELAY 20
#define MOUNT_DELAY 3

#define DIRECTORY_PERMS \
  (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)

#define OPTS_STRING "bd:f:e:l:m:np:r:s:t:v"

namespace {
  unsigned int kSocketQueueDepth;
}  // namespace

using std::cerr;
using std::cout;
using std::endl;
using std::string;
using fs_testing::Tester;
using fs_testing::utils::communication::ServerSocket;

static const option long_options[] = {
  {"background", no_argument, NULL, 'b'},
  {"test-dev", required_argument, NULL, 'd'},
  {"disk_size", required_argument, NULL, 'e'},
  {"flag-device", required_argument, NULL, 'f'},
  {"log-file", required_argument, NULL, 'l'},
  {"mount-opts", required_argument, NULL, 'm'},
  {"dry-run", no_argument, NULL, 'n'},
  {"permuter", required_argument, NULL, 'p'},
  {"reload-log-file", required_argument, NULL, 'r'},
  {"iterations", required_argument, NULL, 's'},
  {"fs-type", required_argument, NULL, 't'},
  {"verbose", no_argument, NULL, 'v'},
  {0, 0, 0, 0},
};

int main(int argc, char** argv) {
  string dirty_expire_time_centisecs(TEST_DIRTY_EXPIRE_TIME);
  unsigned long int test_sleep_delay = WRITE_DELAY;
  string fs_type("ext4");
  string flags_dev("/dev/vda");
  string test_dev("/dev/ram0");
  string mount_opts("");
  string log_file_save("");
  string log_file_load("");
  string permuter(PERMUTER_SO_PATH "RandomPermuter.so");
  bool background = false;
  bool dry_run = false;
  bool no_lvm = false;
  bool verbose = false;
  int iterations = 1000;
  int disk_size = 10240;
  int option_idx = 0;
  ServerSocket* background_com;

  // Parse command line arguments.
  for (int c = getopt_long(argc, argv, OPTS_STRING, long_options, &option_idx);
        c != -1;
        c = getopt_long(argc, argv, OPTS_STRING, long_options, &option_idx)) {
    switch (c) {
      case 'b':
        background = true;
        break;
      case 'f':
        flags_dev = string(optarg);
        break;
      case 'd':
        test_dev = string(optarg);
        break;
      case 'e':
        disk_size = atoi(optarg);
        break;
      case 'l':
        log_file_save = string(optarg);
        break;
      case 'm':
        mount_opts = string(optarg);
        break;
      case 'n':
        dry_run = 1;
        break;
      case 'p':
        permuter = string(optarg);
        break;
      case 'r':
        log_file_load = string(optarg);
        break;
      case 's':
        iterations = atoi(optarg);
        break;
      case 't':
        fs_type = string(optarg);
        break;
      case 'v':
        verbose = true;
        break;
      case '?':
      default:
        return -1;
    }
  }


  /*****************************************************************************
   * PHASE 0:
   * Basic setup of the test harness:
   * 1. check arguments are sane
   * 2. load up socket connections if/when needed
   * 3. load basic kernel modules
   * 4. load static objects for permuter and test case
   ****************************************************************************/
  const unsigned int test_case_idx = optind;

  cout << "========== PHASE 0: Setting up CrashMonkey basics =========="
    << endl;
  if (test_case_idx == argc) {
    cerr << "Please give a .so test case to load" << endl;
    return -1;
  }

  if (iterations < 0) {
    cerr << "Please give a positive number of iterations to run" << endl;
    return -1;
  }

  if (disk_size <= 0) {
    cerr << "Please give a positive number for the RAM disk size to use"
      << endl;
    return -1;
  }


  // Create a socket to coordinate with the outside world.
  if (background) {
    // TODO(ashmrtn): Fix permissions on the socket.
    /*
    struct stat socket_dir;
    int res = stat(SOCKET_DIR, &socket_dir);
    // Directory does not exist.
    if (res < 0 && errno == 2) {
      if (mkdir(SOCKET_DIR, DIRECTORY_PERMS) < 0) {
        cerr << "Error creating temp directory" << endl;
        delete background_com;
        return -1;
      }
    } else if (res < 0) {
      // Some other error.
      cerr << "Error trying to find temp directory" << endl;
      delete background_com;
      return -1;
    } else if (!S_ISDIR(socket_dir.st_mode)) {
      // Something there that's not a directory.
      cerr << "Something not a directory already exists at " << SOCKET_DIR
        << endl;
      delete background_com;
      return -1;
    }

    // Incorrect permissions.
    if ((socket_dir.st_mode & DIRECTORY_PERMS) != DIRECTORY_PERMS) {
      cout << "Changing permissions on " << SOCKET_DIR << " to be world "
        << "readable and writable" << endl;
      if (chmod(SOCKET_DIR, DIRECTORY_PERMS) < 0) {
        cerr << "Error changing permissions on " << SOCKET_DIR << endl;
        delete background_com;
        return -1;
      }
    }
    */

    background_com = new ServerSocket(SOCKET_NAME_OUTBOUND);
    if (background_com->Init(kSocketQueueDepth) < 0) {
      int err_no = errno;
      cerr << "Error starting socket to listen on " << err_no << endl;
      delete background_com;
      return -1;
    }

    // TODO(ashmrtn): Fix permissions so we can still insert kernel modules
    // after we fork. Then uncomment the following.
    // We need to phone home and tell the process that made us that we are ready
    // for users to do things.
    /*
    if (bootstrap) {
      if (background_com->WaitAndSendInt(HARNESS_PREPARE_DONE) < 0) {
        cerr << "Error contacting stub bootstrap process" << endl;
        delete background_com;
        return -1;
      }
    }
    */
  }

  Tester test_harness(disk_size, verbose);

  cout << "Inserting RAM disk module" << endl;
  if (test_harness.insert_cow_brd() != SUCCESS) {
    cerr << "Error inserting RAM disk module" << endl;
    return -1;
  }
  test_harness.set_fs_type(fs_type);
  test_harness.set_device(test_dev);

  // Load the class being tested.
  cout << "Loading test case" << endl;
  if (test_harness.test_load_class(argv[test_case_idx]) != SUCCESS) {
    test_harness.cleanup_harness();
      return -1;
  }

  // Load the permuter to use for the test.
  // TODO(ashmrtn): Consider making a line in the test file which specifies the
  // permuter to use?
  cout << "Loading permuter" << endl;
  if (test_harness.permuter_load_class(permuter.c_str()) != SUCCESS) {
    test_harness.cleanup_harness();
      return -1;
  }

  // Update dirty_expire_time.
  cout << "Updating dirty_expire_time_centisecs" << endl;
  const char* old_expire_time =
    test_harness.update_dirty_expire_time(dirty_expire_time_centisecs.c_str());
  if (old_expire_time == NULL) {
    cerr << "Error updating dirty_expire_time_centisecs" << endl;
    test_harness.cleanup_harness();
    return -1;
  }


  /*****************************************************************************
   * PHASE 1:
   * Setup the base image of the disk for snapshots later. This could happen in
   * one of several ways:
   * 1. The -r flag specifies that there are log files which contain the disk
   *    image. These will now be loaded from disk
   * 2. The -b flag specifies that CrashMonkey is running as a "background"
   *    process of sorts and should listen on its socket for commands from the
   *    user telling it when to perform certain actions. The user is responsible
   *    for running their own pre-test setup methods at the proper times
   * 3. CrashMonkey is running as a standalone program. It will run the pre-test
   *    setup methods defined in the test case static object it loaded
   ****************************************************************************/

  cout << endl << "========== PHASE 1: Creating base disk image =========="
    << endl;
  // Run the normal test setup stuff if we don't have a log file.
  if (log_file_load.empty()) {
    /***************************************************************************
     * Setup for both background operation and standalone mode operation.
     **************************************************************************/
    if (flags_dev.empty()) {
      cerr << "No device to copy flags from given" << endl;
      return -1;
    }

    // Device flags only need set if we are logging requests.
    test_harness.set_flag_device(flags_dev);

    // Format test drive to desired type.
    cout << "Formatting test drive" << endl;
    if (test_harness.format_drive() != SUCCESS) {
      cerr << "Error formatting test drive" << endl;
      test_harness.cleanup_harness();
      return -1;
    }

    // Mount test file system for pre-test setup.
    cout << "Mounting test file system for pre-test setup\n";
    if (test_harness.mount_device_raw(mount_opts.c_str()) != SUCCESS) {
      cerr << "Error mounting test device" << endl;
      test_harness.cleanup_harness();
      return -1;
    }

    // TODO(ashmrtn): Close startup socket fd here.

    if (background) {
      cout << "+++++ Please run any needed pre-test setup +++++" << endl;
      /*************************************************************************
       * Background mode user setup. Wait for the user to tell use that they
       * have finished the pre-test setup phase.
       ************************************************************************/
      int command;
      do {
        if (background_com->WaitForInt(&command) < 0) {
          cerr << "Error getting command from socket" << endl;
          delete background_com;
          test_harness.cleanup_harness();
          return -1;
        }

        if (command != HARNESS_BEGIN_LOG) {
          if (background_com->SendInt(HARNESS_WRONG_COMMAND) < 0) {
            cerr << "Error sending response to client" << endl;
            delete background_com;
            test_harness.cleanup_harness();
            return -1;
          }
          background_com->CloseClient();
        }
      } while (command != HARNESS_BEGIN_LOG);
    } else {
      /*************************************************************************
       * Standalone mode user setup. Run the pre-test "setup()" method defined
       * in the test case. Run as a separate process for the sake of
       * cleanliness.
       ************************************************************************/
      cout << "Running pre-test setup\n";
      {
        const pid_t child = fork();
        if (child < 0) {
          cerr << "Error creating child process to run pre-test setup\n";
          test_harness.cleanup_harness();
        } else if (child != 0) {
          // Parent process should wait for child to terminate before proceeding.
          pid_t status;
          wait(&status);
          if (status != 0) {
            cerr << "Error in pre-test setup\n";
            test_harness.cleanup_harness();
          }
        } else {
          return test_harness.test_setup();
        }
      }
    }

    /***************************************************************************
     * Pre-test setup complete. Unmount the test file system and snapshot the
     * disk for use in workload and tests.
     **************************************************************************/
    // Unmount the test file system after pre-test setup.
    cout << "Unmounting test file system after pre-test setup\n";
    if (test_harness.umount_device() != SUCCESS) {
      test_harness.cleanup_harness();
      return -1;
    }

    // Create snapshot of disk for testing.
    cout << "Making new snapshot\n";
    if (test_harness.clone_device() != SUCCESS) {
      test_harness.cleanup_harness();
      return -1;
    }

    // If we're logging this test run then also save the snapshot.
    if (!log_file_save.empty()) {
      /*************************************************************************
       * The -l flag specifies that we should save the information for this
       * harness execution. Therefore, save the disk image we are using as the
       * base image for our snapshots.
       ************************************************************************/
      cout << "Saving snapshot to log file" << endl;
      if (test_harness.log_snapshot_save(log_file_save + "_snap")
          != SUCCESS) {
        test_harness.cleanup_harness();
        return -1;
      }
    }
  } else {
    /***************************************************************************
     * The -r flag specifies that we should load information from the provided
     * log file. Load the base disk image for snapshots here.
     **************************************************************************/
    // Load the snapshot in the log file and then write it to disk.
    cout << "Loading saved snapshot" << endl;
    if (test_harness.log_snapshot_load(log_file_load + "_snap") != SUCCESS) {
      test_harness.cleanup_harness();
      return -1;
    }
  }


  /*****************************************************************************
   * PHASE 2:
   * Obtain a series of disk epochs to operate on in the test phase of the
   * harness. Again, this could happen in one of several ways:
   * 1. The -r flag specifies that there are log files which contain the disk
   *    epochs. These will now be loaded from disk
   * 2. The -b flag specifies that CrashMonkey is running as a "background"
   *    process of sorts and should listen on its socket for commands from the
   *    user telling it when to perform certain actions. The user is responsible
   *    for running their own workload methods at the proper times
   * 3. CrashMonkey is running as a standalone program. It will run the workload
   *    methods defined in the test case static object it loaded
   ****************************************************************************/

  cout << endl << "========== PHASE 2: Recording user workload =========="
    << endl;
  // TODO(ashmrtn): Consider making a flag for this?
  cout << "Clearing caches" << endl;
  if (test_harness.clear_caches() != SUCCESS) {
    cerr << "Error clearing caches" << endl;
    test_harness.cleanup_harness();
    return -1;
  }

  // No log file given so run the test profile.
  if (log_file_load.empty()) {
    /***************************************************************************
     * Preparations for both background operation and standalone mode operation.
     **************************************************************************/

    // Insert the disk block wrapper into the kernel.
    cout << "Inserting wrapper module into kernel\n";
    if (test_harness.insert_wrapper() != SUCCESS) {
      cerr << "Error inserting kernel wrapper module\n";
      test_harness.cleanup_harness();
      return -1;
    }

    // Mount the file system under the wrapper module for profiling.
    cout << "Mounting wrapper file system\n";
    if (test_harness.mount_wrapper_device(mount_opts.c_str()) != SUCCESS) {
      cerr << "Error mounting wrapper file system\n";
      test_harness.cleanup_harness();
      return -1;
    }

    // TODO(ashmrtn): Can probably remove this...
    cout << "Sleeping after mount" << endl;
    unsigned int to_sleep = MOUNT_DELAY;
    do {
      to_sleep = sleep(to_sleep);
    } while (to_sleep > 0);

    // Get access to wrapper module ioctl functions via FD.
    cout << "Getting wrapper device ioctl fd\n";
    if (test_harness.get_wrapper_ioctl() != SUCCESS) {
      cerr << "Error opening device file\n";
      test_harness.cleanup_harness();
      return -1;
    }

    // Clear wrapper module logs prior to test profiling.
    cout << "Clearing wrapper device logs\n";
    test_harness.clear_wrapper_log();
    cout << "Enabling wrapper device logging\n";
    test_harness.begin_wrapper_logging();


    /***************************************************************************
     * Run the actual workload that we will be testing.
     **************************************************************************/
    if (background) {
      /************************************************************************
       * Background mode user workload. Tell the user we have finished workload
       * preparations and are ready for them to run the workload since we are
       * now logging requests.
       ***********************************************************************/
      if (background_com->SendInt(HARNESS_LOG_READY) < 0) {
        cerr << "Error telling user ready for workload" << endl;
        delete background_com;
        test_harness.cleanup_harness();
        return -1;
      }
      background_com->CloseClient();

      cout << "+++++ Please run workload +++++" << endl;
{'fraction_non_alphanumeric': 0.2053040293040293, 'fraction_numerical': 0.004923076923076923, 'mean_word_length': 2.9607706592386256, 'pattern_counts': {'":': 0, '<': 401, '<?xml version=': 0, '>': 41, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '742687', 'n_tokens_mistral': 9997, 'n_tokens_neox': 9489, 'n_words': 3089}