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