[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