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