e354a5
commit 4052fa22f69c0964bb42c0f13daa791617253de5
e354a5
Author: DJ Delorie <dj@redhat.com>
e354a5
Date:   Wed Oct 2 14:46:46 2019 -0400
e354a5
e354a5
    Add wait-for-debugger test harness hooks
e354a5
    
e354a5
    If WAIT_FOR_DEBUGGER is set to a non-zero value in the environment,
e354a5
    any test that runs will print some useful gdb information and wait
e354a5
    for gdb to attach to it and clear the "wait_for_debugger" variable.
e354a5
    
e354a5
    Reviewed-by: Carlos O'Donell <carlos@redhat.com>
e354a5
e354a5
diff --git a/support/support_test_main.c b/support/support_test_main.c
e354a5
index fa3c2e06dee5ae0f..def84d803928176b 100644
e354a5
--- a/support/support_test_main.c
e354a5
+++ b/support/support_test_main.c
e354a5
@@ -19,6 +19,7 @@
e354a5
 #include <support/test-driver.h>
e354a5
 #include <support/check.h>
e354a5
 #include <support/temp_file-internal.h>
e354a5
+#include <support/support.h>
e354a5
 
e354a5
 #include <assert.h>
e354a5
 #include <errno.h>
e354a5
@@ -36,6 +37,8 @@
e354a5
 #include <time.h>
e354a5
 #include <unistd.h>
e354a5
 
e354a5
+#include <xstdio.h>
e354a5
+
e354a5
 static const struct option default_options[] =
e354a5
 {
e354a5
   TEST_DEFAULT_OPTIONS
e354a5
@@ -176,10 +179,55 @@ signal_handler (int sig)
e354a5
   exit (1);
e354a5
 }
e354a5
 
e354a5
+/* This must be volatile as it will be modified by the debugger.  */
e354a5
+static volatile int wait_for_debugger = 0;
e354a5
+
e354a5
 /* Run test_function or test_function_argv.  */
e354a5
 static int
e354a5
 run_test_function (int argc, char **argv, const struct test_config *config)
e354a5
 {
e354a5
+  const char *wfd = getenv("WAIT_FOR_DEBUGGER");
e354a5
+  if (wfd != NULL)
e354a5
+    wait_for_debugger = atoi (wfd);
e354a5
+  if (wait_for_debugger)
e354a5
+    {
e354a5
+      pid_t mypid;
e354a5
+      FILE *gdb_script;
e354a5
+      char *gdb_script_name;
e354a5
+      int inside_container = 0;
e354a5
+
e354a5
+      mypid = getpid();
e354a5
+      if (mypid < 3)
e354a5
+	{
e354a5
+	  const char *outside_pid = getenv("PID_OUTSIDE_CONTAINER");
e354a5
+	  if (outside_pid)
e354a5
+	    {
e354a5
+	      mypid = atoi (outside_pid);
e354a5
+	      inside_container = 1;
e354a5
+	    }
e354a5
+	}
e354a5
+
e354a5
+      gdb_script_name = (char *) xmalloc (strlen (argv[0]) + strlen (".gdb") + 1);
e354a5
+      sprintf (gdb_script_name, "%s.gdb", argv[0]);
e354a5
+      gdb_script = xfopen (gdb_script_name, "w");
e354a5
+
e354a5
+      fprintf (stderr, "Waiting for debugger, test process is pid %d\n", mypid);
e354a5
+      fprintf (stderr, "gdb -x %s\n", gdb_script_name);
e354a5
+      if (inside_container)
e354a5
+	fprintf (gdb_script, "set sysroot %s/testroot.root\n", support_objdir_root);
e354a5
+      fprintf (gdb_script, "file\n");
e354a5
+      fprintf (gdb_script, "file %s\n", argv[0]);
e354a5
+      fprintf (gdb_script, "symbol-file %s\n", argv[0]);
e354a5
+      fprintf (gdb_script, "exec-file %s\n", argv[0]);
e354a5
+      fprintf (gdb_script, "attach %ld\n", (long int) mypid);
e354a5
+      fprintf (gdb_script, "set wait_for_debugger = 0\n");
e354a5
+      fclose (gdb_script);
e354a5
+    }
e354a5
+
e354a5
+  /* Wait for the debugger to set wait_for_debugger to zero.  */
e354a5
+  while (wait_for_debugger)
e354a5
+    usleep (1000);
e354a5
+
e354a5
   if (config->test_function != NULL)
e354a5
     return config->test_function ();
e354a5
   else if (config->test_function_argv != NULL)
e354a5
@@ -229,6 +277,11 @@ support_test_main (int argc, char **argv, const struct test_config *config)
e354a5
   unsigned int timeoutfactor = 1;
e354a5
   pid_t termpid;
e354a5
 
e354a5
+  /* If we're debugging the test, we need to disable timeouts and use
e354a5
+     the initial pid (esp if we're running inside a container).  */
e354a5
+  if (getenv("WAIT_FOR_DEBUGGER") != NULL)
e354a5
+    direct = 1;
e354a5
+
e354a5
   if (!config->no_mallopt)
e354a5
     {
e354a5
       /* Make uses of freed and uninitialized memory known.  Do not
e354a5
diff --git a/support/test-container.c b/support/test-container.c
e354a5
index f0d9e3060e80bda5..6503cea90309b9b0 100644
e354a5
--- a/support/test-container.c
e354a5
+++ b/support/test-container.c
e354a5
@@ -676,6 +676,9 @@ main (int argc, char **argv)
e354a5
   char *so_base;
e354a5
   int do_postclean = 0;
e354a5
 
e354a5
+  int pipes[2];
e354a5
+  char pid_buf[20];
e354a5
+
e354a5
   uid_t original_uid;
e354a5
   gid_t original_gid;
e354a5
   /* If set, the test runs as root instead of the user running the testsuite.  */
e354a5
@@ -999,6 +1002,11 @@ main (int argc, char **argv)
e354a5
   if (chdir (new_cwd_path) < 0)
e354a5
     FAIL_EXIT1 ("Can't cd to new %s - ", new_cwd_path);
e354a5
 
e354a5
+  /* This is to pass the "outside" PID to the child, which will be PID
e354a5
+     1.  */
e354a5
+  if (pipe2 (pipes, O_CLOEXEC) < 0)
e354a5
+    FAIL_EXIT1 ("Can't create pid pipe");
e354a5
+
e354a5
   /* To complete the containerization, we need to fork () at least
e354a5
      once.  We can't exec, nor can we somehow link the new child to
e354a5
      our parent.  So we run the child and propogate it's exit status
e354a5
@@ -1010,6 +1018,12 @@ main (int argc, char **argv)
e354a5
     {
e354a5
       /* Parent.  */
e354a5
       int status;
e354a5
+
e354a5
+      /* Send the child's "outside" pid to it.  */
e354a5
+      write (pipes[1], &child, sizeof(child));
e354a5
+      close (pipes[0]);
e354a5
+      close (pipes[1]);
e354a5
+
e354a5
       waitpid (child, &status, 0);
e354a5
 
e354a5
       if (WIFEXITED (status))
e354a5
@@ -1028,6 +1042,14 @@ main (int argc, char **argv)
e354a5
   /* The rest is the child process, which is now PID 1 and "in" the
e354a5
      new root.  */
e354a5
 
e354a5
+  /* Get our "outside" pid from our parent.  We use this to help with
e354a5
+     debugging from outside the container.  */
e354a5
+  read (pipes[0], &child, sizeof(child));
e354a5
+  close (pipes[0]);
e354a5
+  close (pipes[1]);
e354a5
+  sprintf (pid_buf, "%lu", (long unsigned)child);
e354a5
+  setenv ("PID_OUTSIDE_CONTAINER", pid_buf, 0);
e354a5
+
e354a5
   maybe_xmkdir ("/tmp", 0755);
e354a5
 
e354a5
   /* Now that we're pid 1 (effectively "root") we can mount /proc  */