[Pkg-libvirt-commits] [libguestfs] 39/266: Add qemu-speed-test program to test speed of qemu.

Hilko Bengen bengen at moszumanska.debian.org
Fri Oct 3 14:41:38 UTC 2014


This is an automated email from the git hooks/post-receive script.

bengen pushed a commit to annotated tag debian/1%1.27.35-1
in repository libguestfs.

commit 7378edb9faf22f1a5a7b9e9f74621509da0e223a
Author: Richard W.M. Jones <rjones at redhat.com>
Date:   Fri Jul 25 17:12:19 2014 +0100

    Add qemu-speed-test program to test speed of qemu.
    
    Currently it tests the upload and download speed of virtio-serial and
    the read and write speed of the block device (eg. virtio-scsi).
---
 .gitignore                   |   1 +
 daemon/debug.c               | 128 +++++++++++++++-
 tests/qemu/Makefile.am       |  25 ++-
 tests/qemu/qemu-speed-test.c | 356 +++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 505 insertions(+), 5 deletions(-)

diff --git a/.gitignore b/.gitignore
index 5c614d8..a29cf6d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -530,6 +530,7 @@ Makefile.in
 /tests/parallel/test-parallel
 /tests/protocol/test-error-messages
 /tests/qemu/qemu-boot
+/tests/qemu/qemu-speed-test
 /tests/regressions/rhbz501893
 /tests/regressions/rhbz790721
 /tests/regressions/rhbz914931
diff --git a/daemon/debug.c b/daemon/debug.c
index abc2bec..d29ca78 100644
--- a/daemon/debug.c
+++ b/daemon/debug.c
@@ -1,5 +1,5 @@
 /* libguestfs - the guestfsd daemon
- * Copyright (C) 2009 Red Hat Inc.
+ * Copyright (C) 2009-2014 Red Hat Inc.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -64,6 +64,7 @@ struct cmd {
 static char *debug_help (const char *subcmd, size_t argc, char *const *const argv);
 static char *debug_binaries (const char *subcmd, size_t argc, char *const *const argv);
 static char *debug_core_pattern (const char *subcmd, size_t argc, char *const *const argv);
+static char *debug_device_speed (const char *subcmd, size_t argc, char *const *const argv);
 static char *debug_env (const char *subcmd, size_t argc, char *const *const argv);
 static char *debug_error (const char *subcmd, size_t argc, char *const *const argv);
 static char *debug_fds (const char *subcmd, size_t argc, char *const *const argv);
@@ -83,6 +84,7 @@ static struct cmd cmds[] = {
   { "help", debug_help },
   { "binaries", debug_binaries },
   { "core_pattern", debug_core_pattern },
+  { "device_speed", debug_device_speed },
   { "env", debug_env },
   { "error", debug_error },
   { "fds", debug_fds },
@@ -749,6 +751,130 @@ debug_qtrace (const char *subcmd, size_t argc, char *const *const argv)
   return ret;
 }
 
+/* Used to test read and write speed. */
+static char *
+debug_device_speed (const char *subcmd, size_t argc, char *const *const argv)
+{
+  const char *device;
+  int writing, err;
+  unsigned secs;
+  int64_t size, position, copied;
+  CLEANUP_FREE void *buf = NULL;
+  struct timeval now, end;
+  ssize_t r;
+  int fd;
+  char *ret;
+
+  if (argc != 3) {
+  bad_args:
+    reply_with_error ("device_speed <device> <r|w> <secs>");
+    return NULL;
+  }
+
+  device = argv[0];
+  if (STREQ (argv[1], "r") || STREQ (argv[1], "read"))
+    writing = 0;
+  else if (STREQ (argv[1], "w") || STREQ (argv[1], "write"))
+    writing = 1;
+  else
+    goto bad_args;
+  if (sscanf (argv[2], "%u", &secs) != 1)
+    goto bad_args;
+
+  /* Find the size of the device. */
+  size = do_blockdev_getsize64 (device);
+  if (size == -1)
+    return NULL;
+
+  if (size < BUFSIZ) {
+    reply_with_error ("%s: device is too small", device);
+    return NULL;
+  }
+
+  /* Because we're using O_DIRECT, the buffer must be aligned. */
+  err = posix_memalign (&buf, 4096, BUFSIZ);
+  if (err != 0) {
+    reply_with_error_errno (err, "posix_memalign");
+    return NULL;
+  }
+
+  /* Any non-zero data will do. */
+  memset (buf, 100, BUFSIZ);
+
+  fd = open (device, (writing ? O_WRONLY : O_RDONLY) | O_CLOEXEC | O_DIRECT);
+  if (fd == -1) {
+    reply_with_perror ("open: %s", device);
+    return NULL;
+  }
+
+  /* Now we read or write to the device, wrapping around to the
+   * beginning when we reach the end, and only stop when <secs>
+   * seconds has elapsed.
+   */
+  gettimeofday (&end, NULL);
+  end.tv_sec += secs;
+
+  position = copied = 0;
+
+  for (;;) {
+    gettimeofday (&now, NULL);
+    if (now.tv_sec > end.tv_sec ||
+        (now.tv_sec == end.tv_sec && now.tv_usec > end.tv_usec))
+      break;
+
+    /* Because of O_DIRECT, only write whole, aligned buffers. */
+  again:
+    if (size - position < BUFSIZ) {
+      position = 0;
+      goto again;
+    }
+
+    /*
+    if (verbose) {
+      fprintf (stderr, "p%s (fd, buf, %d, %" PRIi64 ")\n",
+               writing ? "write" : "read", BUFSIZ, position);
+    }
+    */
+
+    if (writing) {
+      r = pwrite (fd, buf, BUFSIZ, position);
+      if (r == -1) {
+        reply_with_perror ("write: %s", device);
+        goto error;
+      }
+    }
+    else {
+      r = pread (fd, buf, BUFSIZ, position);
+      if (r == -1) {
+        reply_with_perror ("read: %s", device);
+        goto error;
+      }
+      if (r == 0) {
+        reply_with_error ("unexpected end of file while reading");
+        goto error;
+      }
+    }
+    position += BUFSIZ;
+    copied += r;
+  }
+
+  if (close (fd) == -1) {
+    reply_with_perror ("close: %s", device);
+    return NULL;
+  }
+
+  if (asprintf (&ret, "%" PRIi64, copied) == -1) {
+    reply_with_perror ("asprintf");
+    return NULL;
+  }
+
+  return ret;
+
+ error:
+  close (fd);
+  return NULL;
+}
+
 /* Has one FileIn parameter. */
 int
 do_debug_upload (const char *filename, int mode)
diff --git a/tests/qemu/Makefile.am b/tests/qemu/Makefile.am
index fa2fe92..b1b3555 100644
--- a/tests/qemu/Makefile.am
+++ b/tests/qemu/Makefile.am
@@ -30,12 +30,13 @@ TESTS_ENVIRONMENT = $(top_builddir)/run --test
 
 EXTRA_DIST = \
 	$(TESTS) \
-	qemu-boot.c
+	qemu-boot.c \
+	qemu-speed-test.c
 
-# qemu-boot is built but not run by default as it is mainly
-# a qemu & kernel diagnostic tool.
+# qemu-boot & qemu-speed-test are built but not run by default as they
+# are mainly qemu & kernel diagnostic tools.
 
-check_PROGRAMS = qemu-boot
+check_PROGRAMS = qemu-boot qemu-speed-test
 
 qemu_boot_SOURCES = \
 	../../df/estimate-max-threads.c \
@@ -55,3 +56,19 @@ qemu_boot_LDADD = \
 	$(LIBXML2_LIBS) \
 	$(LIBVIRT_LIBS) \
 	$(top_builddir)/gnulib/lib/libgnu.la
+
+qemu_speed_test_SOURCES = \
+	qemu-speed-test.c
+qemu_speed_test_CPPFLAGS = \
+	-DGUESTFS_PRIVATE=1 \
+	-I$(top_srcdir)/gnulib/lib -I$(top_builddir)/gnulib/lib \
+	-I$(top_srcdir)/src -I$(top_builddir)/src \
+	-I$(top_srcdir)/df
+qemu_speed_test_CFLAGS = \
+	$(WARN_CFLAGS) $(WERROR_CFLAGS)
+qemu_speed_test_LDADD = \
+	$(top_builddir)/src/libutils.la \
+	$(top_builddir)/src/libguestfs.la \
+	$(LIBXML2_LIBS) \
+	$(LIBVIRT_LIBS) \
+	$(top_builddir)/gnulib/lib/libgnu.la
diff --git a/tests/qemu/qemu-speed-test.c b/tests/qemu/qemu-speed-test.c
new file mode 100644
index 0000000..667b473
--- /dev/null
+++ b/tests/qemu/qemu-speed-test.c
@@ -0,0 +1,356 @@
+/* libguestfs
+ * Copyright (C) 2014 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/* Test the speed of various qemu features.  Currently tested are:
+ *   - virtio-serial upload
+ *   - virtio-serial download
+ *   - block device read
+ *   - block device write
+ * More to come in future.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+#include <errno.h>
+#include <unistd.h>
+#include <signal.h>
+#include <assert.h>
+#include <sys/time.h>
+
+#include "guestfs.h"
+#include "guestfs-internal-frontend.h"
+
+static void test_virtio_serial (void);
+static void test_block_device (void);
+
+int
+main (int argc, char *argv[])
+{
+  if (argc != 1) {
+    fprintf (stderr, "%s: this program takes no arguments\n",
+             program_name);
+    exit (EXIT_FAILURE);
+  }
+
+  test_virtio_serial ();
+  test_block_device ();
+
+  exit (EXIT_SUCCESS);
+}
+
+static void
+print_rate (const char *msg, int64_t rate)
+{
+  printf ("%-40s %" PRIi64 " bytes/sec (%" PRIi64 " Mbytes/sec)\n",
+          msg, rate, rate / 1024 / 1024);
+  fflush (stdout);
+}
+
+/* The maximum time we will spend running the test (seconds). */
+#define TEST_SERIAL_MAX_TIME 30
+
+/* The maximum amount of data to copy.  You can safely make this very
+ * large because it's only making sparse files.
+ */
+#define TEST_SERIAL_MAX_SIZE \
+  (INT64_C(1024) * INT64_C(1024) * INT64_C(1024) * INT64_C(1024))
+
+static guestfs_h *g;
+static struct timeval start;
+static const char *operation;
+static int64_t rate;
+
+static void
+stop_transfer (int sig)
+{
+  guestfs_user_cancel (g);
+}
+
+/* Compute Y - X and return the result in milliseconds.
+ * Approximately the same as this code:
+ * http://www.mpp.mpg.de/~huber/util/timevaldiff.c
+ */
+static int64_t
+timeval_diff (const struct timeval *x, const struct timeval *y)
+{
+  int64_t msec;
+
+  msec = (y->tv_sec - x->tv_sec) * 1000;
+  msec += (y->tv_usec - x->tv_usec) / 1000;
+  return msec;
+}
+
+static void
+progress_cb (guestfs_h *g, void *vp, uint64_t event,
+             int eh, int flags,
+             const char *buf, size_t buflen,
+             const uint64_t *array, size_t arraylen)
+{
+  uint64_t transferred;
+  struct timeval now;
+  int64_t millis;
+
+  assert (event == GUESTFS_EVENT_PROGRESS);
+  assert (arraylen >= 4);
+
+  gettimeofday (&now, NULL);
+
+  /* Bytes transferred. */
+  transferred = array[2];
+
+  /* Calculate the speed of the upload or download. */
+  millis = timeval_diff (&start, &now);
+  assert (millis >= 0);
+
+  if (millis != 0) {
+    rate = 1000 * transferred / millis;
+    printf ("%s: %" PRIi64 " bytes/sec          \r",
+            operation, rate);
+    fflush (stdout);
+  }
+}
+
+static void
+test_virtio_serial (void)
+{
+  int fd, r, eh;
+  char tmpfile[] = "/tmp/speedtestXXXXXX";
+  struct sigaction sa, old_sa;
+
+  /* Create a sparse file.  We could upload from /dev/zero, but we
+   * won't get progress messages because libguestfs tests if the
+   * source file is a regular file.
+   */
+  fd = mkstemp (tmpfile);
+  if (fd == -1) {
+    perror ("mkstemp");
+    exit (EXIT_FAILURE);
+  }
+  if (ftruncate (fd, TEST_SERIAL_MAX_SIZE) == -1) {
+    perror ("ftruncate");
+    exit (EXIT_FAILURE);
+  }
+  if (close (fd) == -1) {
+    perror ("close");
+    exit (EXIT_FAILURE);
+  }
+
+  g = guestfs_create ();
+  if (!g) {
+    perror ("guestfs_create");
+    exit (EXIT_FAILURE);
+  }
+
+  if (guestfs_add_drive_scratch (g, INT64_C (100*1024*1024), -1) == -1)
+    exit (EXIT_FAILURE);
+
+  if (guestfs_launch (g) == -1)
+    exit (EXIT_FAILURE);
+
+  /* Make and mount a filesystem which will be used by the download test. */
+  if (guestfs_mkfs (g, "ext4", "/dev/sda") == -1)
+    exit (EXIT_FAILURE);
+  if (guestfs_mount (g, "/dev/sda", "/") == -1)
+    exit (EXIT_FAILURE);
+
+  /* Time out the upload after TEST_SERIAL_MAX_TIME seconds have passed. */
+  memset (&sa, 0, sizeof sa);
+  sa.sa_handler = stop_transfer;
+  sa.sa_flags = SA_RESTART;
+  sigaction (SIGALRM, &sa, &old_sa);
+
+  /* Get progress messages, which will tell us how much data has been
+   * transferred.
+   */
+  eh = guestfs_set_event_callback (g, progress_cb, GUESTFS_EVENT_PROGRESS,
+                                   0, NULL);
+  if (eh == -1)
+    exit (EXIT_FAILURE);
+
+  gettimeofday (&start, NULL);
+  rate = -1;
+  operation = "upload";
+  alarm (TEST_SERIAL_MAX_TIME);
+
+  /* For the upload test, upload the sparse file to /dev/null in the
+   * appliance.  Hopefully this is mostly testing just virtio-serial.
+   */
+  guestfs_push_error_handler (g, NULL, NULL);
+  r = guestfs_upload (g, tmpfile, "/dev/null");
+  alarm (0);
+  unlink (tmpfile);
+  guestfs_pop_error_handler (g);
+
+  /* It's possible that the upload will finish before the alarm fires,
+   * or that the upload will be stopped by the alarm.
+   */
+  if (r == -1 && guestfs_last_errno (g) != EINTR) {
+    fprintf (stderr,
+             "%s: expecting upload command to return EINTR\n%s\n",
+             program_name, guestfs_last_error (g));
+    exit (EXIT_FAILURE);
+  }
+
+  if (rate == -1) {
+  rate_error:
+    fprintf (stderr, "%s: internal error: progress callback was not called! (r=%d, errno=%d)\n",
+             program_name,
+             r, guestfs_last_errno (g));
+    exit (EXIT_FAILURE);
+  }
+
+  print_rate ("virtio-serial upload rate:", rate);
+
+  /* For the download test, download a sparse file within the
+   * appliance to /dev/null on the host.
+   */
+  if (guestfs_touch (g, "/sparse") == -1)
+    exit (EXIT_FAILURE);
+  if (guestfs_truncate_size (g, "/sparse", TEST_SERIAL_MAX_SIZE) == -1)
+    exit (EXIT_FAILURE);
+
+  gettimeofday (&start, NULL);
+  rate = -1;
+  operation = "download";
+  alarm (TEST_SERIAL_MAX_TIME);
+  guestfs_push_error_handler (g, NULL, NULL);
+  r = guestfs_download (g, "/sparse", "/dev/null");
+  alarm (0);
+  guestfs_pop_error_handler (g);
+
+  if (r == -1 && guestfs_last_errno (g) != EINTR) {
+    fprintf (stderr,
+             "%s: expecting download command to return EINTR\n%s\n",
+             program_name, guestfs_last_error (g));
+    exit (EXIT_FAILURE);
+  }
+
+  if (rate == -1)
+    goto rate_error;
+
+  print_rate ("virtio-serial download rate:", rate);
+
+  if (guestfs_shutdown (g) == -1)
+    exit (EXIT_FAILURE);
+
+  guestfs_close (g);
+
+  /* Restore SIGALRM signal handler. */
+  sigaction (SIGALRM, &old_sa, NULL);
+}
+
+/* The time we will spend running the test (seconds). */
+#define TEST_BLOCK_DEVICE_TIME 30
+
+static void
+test_block_device (void)
+{
+  int fd;
+  char tmpfile[] = "/tmp/speedtestXXXXXX";
+  CLEANUP_FREE char **devices = NULL;
+  char *r;
+  const char *argv[4];
+  int64_t bytes_written, bytes_read;
+
+  g = guestfs_create ();
+  if (!g) {
+    perror ("guestfs_create");
+    exit (EXIT_FAILURE);
+  }
+
+  /* Create a fully allocated backing file.  Note we are not testing
+   * the speed of allocation on the host.
+   */
+  fd = mkstemp (tmpfile);
+  if (fd == -1) {
+    perror ("mkstemp");
+    exit (EXIT_FAILURE);
+  }
+  close (fd);
+
+  if (guestfs_disk_create (g, tmpfile, "raw",
+                           INT64_C (1024*1024*1024),
+                           GUESTFS_DISK_CREATE_PREALLOCATION, "full",
+                           -1) == -1)
+    exit (EXIT_FAILURE);
+
+  if (guestfs_add_drive (g, tmpfile) == -1)
+    exit (EXIT_FAILURE);
+
+  if (guestfs_launch (g) == -1)
+    exit (EXIT_FAILURE);
+
+  devices = guestfs_list_devices (g);
+  if (devices == NULL)
+    exit (EXIT_FAILURE);
+  if (devices[0] == NULL) {
+    fprintf (stderr, "%s: expected guestfs_list_devices to return at least 1 device\n",
+             program_name);
+    exit (EXIT_FAILURE);
+  }
+
+  /* Test write speed. */
+  argv[0] = devices[0];
+  argv[1] = "w";
+#define XSTR(x) STR(x)
+#define STR(x) #x
+  argv[2] = XSTR(TEST_BLOCK_DEVICE_TIME);
+  argv[3] = NULL;
+  r = guestfs_debug (g, "device_speed", (char **) argv);
+  if (r == NULL)
+    exit (EXIT_FAILURE);
+
+  if (sscanf (r, "%" SCNi64, &bytes_written) != 1) {
+    fprintf (stderr, "%s: could not parse device_speed output\n",
+             program_name);
+    exit (EXIT_FAILURE);
+  }
+
+  print_rate ("block device writes:", bytes_written / TEST_BLOCK_DEVICE_TIME);
+
+  /* Test read speed. */
+  argv[0] = devices[0];
+  argv[1] = "r";
+#define XSTR(x) STR(x)
+#define STR(x) #x
+  argv[2] = XSTR(TEST_BLOCK_DEVICE_TIME);
+  argv[3] = NULL;
+  r = guestfs_debug (g, "device_speed", (char **) argv);
+  if (r == NULL)
+    exit (EXIT_FAILURE);
+
+  if (sscanf (r, "%" SCNi64, &bytes_read) != 1) {
+    fprintf (stderr, "%s: could not parse device_speed output\n",
+             program_name);
+    exit (EXIT_FAILURE);
+  }
+
+  print_rate ("block device reads:", bytes_read / TEST_BLOCK_DEVICE_TIME);
+
+  if (guestfs_shutdown (g) == -1)
+    exit (EXIT_FAILURE);
+
+  guestfs_close (g);
+
+  /* Remove temporary file. */
+  unlink (tmpfile);
+}

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-libvirt/libguestfs.git



More information about the Pkg-libvirt-commits mailing list