All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH v2 0/3] qtest: avoid pidfile and QEMU process leaks
@ 2014-02-18 14:54 Stefan Hajnoczi
  2014-02-18 14:54 ` [Qemu-devel] [PATCH v2 1/3] qtest: drop unused child_pid field Stefan Hajnoczi
                   ` (3 more replies)
  0 siblings, 4 replies; 6+ messages in thread
From: Stefan Hajnoczi @ 2014-02-18 14:54 UTC (permalink / raw)
  To: qemu-devel
  Cc: Paolo Bonzini, Markus Armbruster, Anthony Liguori,
	Andreas Faerber

v2:
 * Don't call qtest_end() from SIGABRT handler to avoid reentrancy [Paolo]
 * Use sigemptyset() to avoid assumption about signal mask [Markus]
 * if (fd != -1) close(fd) is no longer necessary [Markus]

This series prevents the following qtest issues:

1. Leaking the pidfile if QEMU startup fails, as discovered by Andreas Färber.
2. Leaking the QEMU process when a test case aborts.

Applying this series should make buildbots and manual "make check" users have a
more pleasant and less leaky experience :).

Stefan Hajnoczi (3):
  qtest: drop unused child_pid field
  qtest: make QEMU our direct child process
  qtest: kill QEMU process on g_assert() failure

 tests/libqtest.c | 59 +++++++++++++++++++++++++-------------------------------
 1 file changed, 26 insertions(+), 33 deletions(-)

-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [Qemu-devel] [PATCH v2 1/3] qtest: drop unused child_pid field
  2014-02-18 14:54 [Qemu-devel] [PATCH v2 0/3] qtest: avoid pidfile and QEMU process leaks Stefan Hajnoczi
@ 2014-02-18 14:54 ` Stefan Hajnoczi
  2014-02-18 14:54 ` [Qemu-devel] [PATCH v2 2/3] qtest: make QEMU our direct child process Stefan Hajnoczi
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 6+ messages in thread
From: Stefan Hajnoczi @ 2014-02-18 14:54 UTC (permalink / raw)
  To: qemu-devel
  Cc: Paolo Bonzini, Markus Armbruster, Anthony Liguori,
	Andreas Faerber

Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
---
 tests/libqtest.c | 2 --
 1 file changed, 2 deletions(-)

diff --git a/tests/libqtest.c b/tests/libqtest.c
index c9a4f89..2876ce4 100644
--- a/tests/libqtest.c
+++ b/tests/libqtest.c
@@ -43,7 +43,6 @@ struct QTestState
     int qmp_fd;
     bool irq_level[MAX_IRQ];
     GString *rx;
-    int child_pid;   /* Child process created to execute QEMU */
     pid_t qemu_pid;  /* QEMU process spawned by our child */
 };
 
@@ -152,7 +151,6 @@ QTestState *qtest_init(const char *extra_args)
     g_free(qmp_socket_path);
 
     s->rx = g_string_new("");
-    s->child_pid = pid;
     for (i = 0; i < MAX_IRQ; i++) {
         s->irq_level[i] = false;
     }
-- 
1.8.5.3

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [Qemu-devel] [PATCH v2 2/3] qtest: make QEMU our direct child process
  2014-02-18 14:54 [Qemu-devel] [PATCH v2 0/3] qtest: avoid pidfile and QEMU process leaks Stefan Hajnoczi
  2014-02-18 14:54 ` [Qemu-devel] [PATCH v2 1/3] qtest: drop unused child_pid field Stefan Hajnoczi
@ 2014-02-18 14:54 ` Stefan Hajnoczi
  2014-02-18 14:54 ` [Qemu-devel] [PATCH v2 3/3] qtest: kill QEMU process on g_assert() failure Stefan Hajnoczi
  2014-02-18 16:32 ` [Qemu-devel] [PATCH v2 0/3] qtest: avoid pidfile and QEMU process leaks Markus Armbruster
  3 siblings, 0 replies; 6+ messages in thread
From: Stefan Hajnoczi @ 2014-02-18 14:54 UTC (permalink / raw)
  To: qemu-devel
  Cc: Paolo Bonzini, Markus Armbruster, Anthony Liguori,
	Andreas Faerber

qtest_init() cannot use exec*p() to launch QEMU since the exec*p()
functions take an argument array while qtest_init() takes char
*extra_args.  Therefore we execute /bin/sh -c <command-line> and let the
shell parse the argument string.

This left /bin/sh as our child process and our child's child was QEMU.
We still want QEMU's pid so the -pidfile option was used to let QEMU
report its pid.

The pidfile needs to be unlinked when the test case exits or fails.  In
other words, the pidfile creates a new problem for us!

Simplify all this using the shell 'exec' command.  It allows us to
replace the /bin/sh process with QEMU.  Then we no longer need to use
-pidfile because we already know our fork child's pid.

Note: Yes, it seems silly to exec /bin/sh when we could just exec QEMU
directly.  But remember qtest_init() takes a single char *extra_args
command-line fragment instead of a real argv[] array, so we need
/bin/sh's argument parsing behavior.

Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
---
 tests/libqtest.c | 34 +++++-----------------------------
 1 file changed, 5 insertions(+), 29 deletions(-)

diff --git a/tests/libqtest.c b/tests/libqtest.c
index 2876ce4..8b2b2d7 100644
--- a/tests/libqtest.c
+++ b/tests/libqtest.c
@@ -43,7 +43,7 @@ struct QTestState
     int qmp_fd;
     bool irq_level[MAX_IRQ];
     GString *rx;
-    pid_t qemu_pid;  /* QEMU process spawned by our child */
+    pid_t qemu_pid;  /* our child QEMU process */
 };
 
 #define g_assert_no_errno(ret) do { \
@@ -88,32 +88,14 @@ static int socket_accept(int sock)
     return ret;
 }
 
-static pid_t read_pid_file(const char *pid_file)
-{
-    FILE *f;
-    char buffer[1024];
-    pid_t pid = -1;
-
-    f = fopen(pid_file, "r");
-    if (f) {
-        if (fgets(buffer, sizeof(buffer), f)) {
-            pid = atoi(buffer);
-        }
-        fclose(f);
-    }
-    return pid;
-}
-
 QTestState *qtest_init(const char *extra_args)
 {
     QTestState *s;
     int sock, qmpsock, i;
     gchar *socket_path;
     gchar *qmp_socket_path;
-    gchar *pid_file;
     gchar *command;
     const char *qemu_binary;
-    pid_t pid;
 
     qemu_binary = getenv("QTEST_QEMU_BINARY");
     g_assert(qemu_binary != NULL);
@@ -122,22 +104,20 @@ QTestState *qtest_init(const char *extra_args)
 
     socket_path = g_strdup_printf("/tmp/qtest-%d.sock", getpid());
     qmp_socket_path = g_strdup_printf("/tmp/qtest-%d.qmp", getpid());
-    pid_file = g_strdup_printf("/tmp/qtest-%d.pid", getpid());
 
     sock = init_socket(socket_path);
     qmpsock = init_socket(qmp_socket_path);
 
-    pid = fork();
-    if (pid == 0) {
-        command = g_strdup_printf("%s "
+    s->qemu_pid = fork();
+    if (s->qemu_pid == 0) {
+        command = g_strdup_printf("exec %s "
                                   "-qtest unix:%s,nowait "
                                   "-qtest-log /dev/null "
                                   "-qmp unix:%s,nowait "
-                                  "-pidfile %s "
                                   "-machine accel=qtest "
                                   "-display none "
                                   "%s", qemu_binary, socket_path,
-                                  qmp_socket_path, pid_file,
+                                  qmp_socket_path,
                                   extra_args ?: "");
         execlp("/bin/sh", "sh", "-c", command, NULL);
         exit(1);
@@ -159,10 +139,6 @@ QTestState *qtest_init(const char *extra_args)
     qtest_qmp_discard_response(s, "");
     qtest_qmp_discard_response(s, "{ 'execute': 'qmp_capabilities' }");
 
-    s->qemu_pid = read_pid_file(pid_file);
-    unlink(pid_file);
-    g_free(pid_file);
-
     if (getenv("QTEST_STOP")) {
         kill(s->qemu_pid, SIGSTOP);
     }
-- 
1.8.5.3

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [Qemu-devel] [PATCH v2 3/3] qtest: kill QEMU process on g_assert() failure
  2014-02-18 14:54 [Qemu-devel] [PATCH v2 0/3] qtest: avoid pidfile and QEMU process leaks Stefan Hajnoczi
  2014-02-18 14:54 ` [Qemu-devel] [PATCH v2 1/3] qtest: drop unused child_pid field Stefan Hajnoczi
  2014-02-18 14:54 ` [Qemu-devel] [PATCH v2 2/3] qtest: make QEMU our direct child process Stefan Hajnoczi
@ 2014-02-18 14:54 ` Stefan Hajnoczi
  2014-02-18 15:38   ` Paolo Bonzini
  2014-02-18 16:32 ` [Qemu-devel] [PATCH v2 0/3] qtest: avoid pidfile and QEMU process leaks Markus Armbruster
  3 siblings, 1 reply; 6+ messages in thread
From: Stefan Hajnoczi @ 2014-02-18 14:54 UTC (permalink / raw)
  To: qemu-devel
  Cc: Paolo Bonzini, Markus Armbruster, Anthony Liguori,
	Andreas Faerber

The QEMU process stays running if the test case fails.  This patch fixes
the leak by installing a SIGABRT signal handler which invokes
qtest_end().

Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
---
 tests/libqtest.c | 31 +++++++++++++++++++++++++------
 1 file changed, 25 insertions(+), 6 deletions(-)

diff --git a/tests/libqtest.c b/tests/libqtest.c
index 8b2b2d7..f587d36 100644
--- a/tests/libqtest.c
+++ b/tests/libqtest.c
@@ -44,6 +44,7 @@ struct QTestState
     bool irq_level[MAX_IRQ];
     GString *rx;
     pid_t qemu_pid;  /* our child QEMU process */
+    struct sigaction sigact_old; /* restored on exit */
 };
 
 #define g_assert_no_errno(ret) do { \
@@ -88,6 +89,19 @@ static int socket_accept(int sock)
     return ret;
 }
 
+static void kill_qemu(QTestState *s)
+{
+    if (s->qemu_pid != -1) {
+        kill(s->qemu_pid, SIGTERM);
+        waitpid(s->qemu_pid, NULL, 0);
+    }
+}
+
+static void sigabrt_handler(int signo)
+{
+    kill_qemu(global_qtest);
+}
+
 QTestState *qtest_init(const char *extra_args)
 {
     QTestState *s;
@@ -96,6 +110,7 @@ QTestState *qtest_init(const char *extra_args)
     gchar *qmp_socket_path;
     gchar *command;
     const char *qemu_binary;
+    struct sigaction sigact;
 
     qemu_binary = getenv("QTEST_QEMU_BINARY");
     g_assert(qemu_binary != NULL);
@@ -108,6 +123,14 @@ QTestState *qtest_init(const char *extra_args)
     sock = init_socket(socket_path);
     qmpsock = init_socket(qmp_socket_path);
 
+    /* Catch SIGABRT to clean up on g_assert() failure */
+    sigact = (struct sigaction){
+        .sa_handler = sigabrt_handler,
+        .sa_flags = SA_RESETHAND,
+    };
+    sigemptyset(&sigact.sa_mask);
+    sigaction(SIGABRT, &sigact, &s->sigact_old);
+
     s->qemu_pid = fork();
     if (s->qemu_pid == 0) {
         command = g_strdup_printf("exec %s "
@@ -148,13 +171,9 @@ QTestState *qtest_init(const char *extra_args)
 
 void qtest_quit(QTestState *s)
 {
-    int status;
-
-    if (s->qemu_pid != -1) {
-        kill(s->qemu_pid, SIGTERM);
-        waitpid(s->qemu_pid, &status, 0);
-    }
+    sigaction(SIGABRT, &s->sigact_old, NULL);
 
+    kill_qemu(s);
     close(s->fd);
     close(s->qmp_fd);
     g_string_free(s->rx, true);
-- 
1.8.5.3

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [Qemu-devel] [PATCH v2 3/3] qtest: kill QEMU process on g_assert() failure
  2014-02-18 14:54 ` [Qemu-devel] [PATCH v2 3/3] qtest: kill QEMU process on g_assert() failure Stefan Hajnoczi
@ 2014-02-18 15:38   ` Paolo Bonzini
  0 siblings, 0 replies; 6+ messages in thread
From: Paolo Bonzini @ 2014-02-18 15:38 UTC (permalink / raw)
  To: Stefan Hajnoczi, qemu-devel
  Cc: Markus Armbruster, Anthony Liguori, Andreas Faerber

Il 18/02/2014 15:54, Stefan Hajnoczi ha scritto:
> The QEMU process stays running if the test case fails.  This patch fixes
> the leak by installing a SIGABRT signal handler which invokes
> qtest_end().
>
> Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
> ---
>  tests/libqtest.c | 31 +++++++++++++++++++++++++------
>  1 file changed, 25 insertions(+), 6 deletions(-)
>
> diff --git a/tests/libqtest.c b/tests/libqtest.c
> index 8b2b2d7..f587d36 100644
> --- a/tests/libqtest.c
> +++ b/tests/libqtest.c
> @@ -44,6 +44,7 @@ struct QTestState
>      bool irq_level[MAX_IRQ];
>      GString *rx;
>      pid_t qemu_pid;  /* our child QEMU process */
> +    struct sigaction sigact_old; /* restored on exit */
>  };
>
>  #define g_assert_no_errno(ret) do { \
> @@ -88,6 +89,19 @@ static int socket_accept(int sock)
>      return ret;
>  }
>
> +static void kill_qemu(QTestState *s)
> +{
> +    if (s->qemu_pid != -1) {
> +        kill(s->qemu_pid, SIGTERM);
> +        waitpid(s->qemu_pid, NULL, 0);
> +    }
> +}
> +
> +static void sigabrt_handler(int signo)
> +{
> +    kill_qemu(global_qtest);
> +}
> +
>  QTestState *qtest_init(const char *extra_args)
>  {
>      QTestState *s;
> @@ -96,6 +110,7 @@ QTestState *qtest_init(const char *extra_args)
>      gchar *qmp_socket_path;
>      gchar *command;
>      const char *qemu_binary;
> +    struct sigaction sigact;
>
>      qemu_binary = getenv("QTEST_QEMU_BINARY");
>      g_assert(qemu_binary != NULL);
> @@ -108,6 +123,14 @@ QTestState *qtest_init(const char *extra_args)
>      sock = init_socket(socket_path);
>      qmpsock = init_socket(qmp_socket_path);
>
> +    /* Catch SIGABRT to clean up on g_assert() failure */
> +    sigact = (struct sigaction){
> +        .sa_handler = sigabrt_handler,
> +        .sa_flags = SA_RESETHAND,
> +    };
> +    sigemptyset(&sigact.sa_mask);
> +    sigaction(SIGABRT, &sigact, &s->sigact_old);
> +
>      s->qemu_pid = fork();
>      if (s->qemu_pid == 0) {
>          command = g_strdup_printf("exec %s "
> @@ -148,13 +171,9 @@ QTestState *qtest_init(const char *extra_args)
>
>  void qtest_quit(QTestState *s)
>  {
> -    int status;
> -
> -    if (s->qemu_pid != -1) {
> -        kill(s->qemu_pid, SIGTERM);
> -        waitpid(s->qemu_pid, &status, 0);
> -    }
> +    sigaction(SIGABRT, &s->sigact_old, NULL);
>
> +    kill_qemu(s);
>      close(s->fd);
>      close(s->qmp_fd);
>      g_string_free(s->rx, true);
>

Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [Qemu-devel] [PATCH v2 0/3] qtest: avoid pidfile and QEMU process leaks
  2014-02-18 14:54 [Qemu-devel] [PATCH v2 0/3] qtest: avoid pidfile and QEMU process leaks Stefan Hajnoczi
                   ` (2 preceding siblings ...)
  2014-02-18 14:54 ` [Qemu-devel] [PATCH v2 3/3] qtest: kill QEMU process on g_assert() failure Stefan Hajnoczi
@ 2014-02-18 16:32 ` Markus Armbruster
  3 siblings, 0 replies; 6+ messages in thread
From: Markus Armbruster @ 2014-02-18 16:32 UTC (permalink / raw)
  To: Stefan Hajnoczi
  Cc: Paolo Bonzini, qemu-devel, Anthony Liguori, Andreas Faerber

Stefan Hajnoczi <stefanha@redhat.com> writes:

> v2:
>  * Don't call qtest_end() from SIGABRT handler to avoid reentrancy [Paolo]
>  * Use sigemptyset() to avoid assumption about signal mask [Markus]
>  * if (fd != -1) close(fd) is no longer necessary [Markus]
>
> This series prevents the following qtest issues:
>
> 1. Leaking the pidfile if QEMU startup fails, as discovered by Andreas Färber.
> 2. Leaking the QEMU process when a test case aborts.
>
> Applying this series should make buildbots and manual "make check" users have a
> more pleasant and less leaky experience :).
>
> Stefan Hajnoczi (3):
>   qtest: drop unused child_pid field
>   qtest: make QEMU our direct child process
>   qtest: kill QEMU process on g_assert() failure
>
>  tests/libqtest.c | 59 +++++++++++++++++++++++++-------------------------------
>  1 file changed, 26 insertions(+), 33 deletions(-)

Reviewed-by: Markus Armbruster <armbru@redhat.com>

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2014-02-18 16:32 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-02-18 14:54 [Qemu-devel] [PATCH v2 0/3] qtest: avoid pidfile and QEMU process leaks Stefan Hajnoczi
2014-02-18 14:54 ` [Qemu-devel] [PATCH v2 1/3] qtest: drop unused child_pid field Stefan Hajnoczi
2014-02-18 14:54 ` [Qemu-devel] [PATCH v2 2/3] qtest: make QEMU our direct child process Stefan Hajnoczi
2014-02-18 14:54 ` [Qemu-devel] [PATCH v2 3/3] qtest: kill QEMU process on g_assert() failure Stefan Hajnoczi
2014-02-18 15:38   ` Paolo Bonzini
2014-02-18 16:32 ` [Qemu-devel] [PATCH v2 0/3] qtest: avoid pidfile and QEMU process leaks Markus Armbruster

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.