Test for PR18749: problems if whole process dies while (ptrace-) stopped
authorPedro Alves <palves@redhat.com>
Thu, 6 Aug 2015 11:33:20 +0000 (12:33 +0100)
committerPedro Alves <palves@redhat.com>
Thu, 6 Aug 2015 11:33:20 +0000 (12:33 +0100)
This adds a kfailed test that has the whole process exit just while
several threads continuously step over a breakpoint.  Usually, the
process exits just while GDB or GDBserver is handling the breakpoint
hit.  In other words, the process disappears while the event thread is
(ptrace-) stopped.  This exposes several issues in GDB and GDBserver.
Errors, crashes, etc.

I fixed some of these issues recently, but there's a lot more to do.
It's a bit like playing whack-a-mole at the moment.  You fix an issue,
which then exposes several others.

E.g., with the native target, you get (among other errors):

  (...)
  [New Thread 0x7ffff47b9700 (LWP 18077)]
  [New Thread 0x7ffff3fb8700 (LWP 18078)]
  [New Thread 0x7ffff37b7700 (LWP 18079)]
  Cannot find user-level thread for LWP 18076: generic error
  (gdb) KFAIL: gdb.threads/process-dies-while-handling-bp.exp: non_stop=on: cond_bp_target=1: inferior 1 exited (prompt) (PRMS: gdb/18749)

gdb/testsuite/ChangeLog:
2015-08-06  Pedro Alves  <palves@redhat.com>

PR gdb/18749
* gdb.threads/process-dies-while-handling-bp.c: New file.
* gdb.threads/process-dies-while-handling-bp.exp: New file.

gdb/testsuite/ChangeLog
gdb/testsuite/gdb.threads/process-dies-while-handling-bp.c [new file with mode: 0644]
gdb/testsuite/gdb.threads/process-dies-while-handling-bp.exp [new file with mode: 0644]

index ad63b027864c87c21c83d78f8a4568c9848f918c..eccdddb46c26602efa78a97e098067d7e5be0f63 100644 (file)
@@ -1,3 +1,9 @@
+2015-08-06  Pedro Alves  <palves@redhat.com>
+
+       PR gdb/18749
+       * gdb.threads/process-dies-while-handling-bp.c: New file.
+       * gdb.threads/process-dies-while-handling-bp.exp: New file.
+
 2015-08-06  Pedro Alves  <palves@redhat.com>
 
        * gdb.threads/forking-threads-plus-breakpoint.exp: New file.
diff --git a/gdb/testsuite/gdb.threads/process-dies-while-handling-bp.c b/gdb/testsuite/gdb.threads/process-dies-while-handling-bp.c
new file mode 100644 (file)
index 0000000..16963fa
--- /dev/null
@@ -0,0 +1,73 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2015 Free Software Foundation, 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 3 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, see <http://www.gnu.org/licenses/>.  */
+
+#include <assert.h>
+#include <pthread.h>
+#include <unistd.h>
+
+/* Number of threads.  Each thread continuously steps over a
+   breakpoint.  */
+#define NTHREADS 10
+
+pthread_t threads[NTHREADS];
+
+pthread_barrier_t barrier;
+
+/* Used to create a conditional breakpoint that always fails.  */
+volatile int zero;
+
+static void *
+thread_func (void *arg)
+{
+  pthread_barrier_wait (&barrier);
+
+  while (1)
+    {
+      usleep (1); /* set break here */
+    }
+
+  return NULL;
+}
+
+int
+main (void)
+{
+  int ret;
+  int i;
+
+  /* Don't run forever.  */
+  alarm (180);
+
+  pthread_barrier_init (&barrier, NULL, NTHREADS + 1);
+
+  /* Start the threads that constantly hits a conditional breakpoint
+     that needs to be stepped over.  */
+  for (i = 0; i < NTHREADS; i++)
+    {
+      ret = pthread_create (&threads[i], NULL, thread_func, NULL);
+      assert (ret == 0);
+    }
+
+  /* Wait until all threads are up and running.  */
+  pthread_barrier_wait (&barrier);
+
+  /* Let them start hitting the breakpoint.  */
+  usleep (100);
+
+  /* Exit abruptly.  */
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.threads/process-dies-while-handling-bp.exp b/gdb/testsuite/gdb.threads/process-dies-while-handling-bp.exp
new file mode 100644 (file)
index 0000000..84e5890
--- /dev/null
@@ -0,0 +1,147 @@
+# Copyright (C) 2015 Free Software Foundation, 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 3 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, see <http://www.gnu.org/licenses/>.
+
+# This test spawns a few threads that constantly trip on a breakpoint
+# that does not cause a user-visible stop.  While one of those
+# breakpoints is being handled, the main thread exits the whole
+# process.  The result is that the current thread for which GDB is
+# handling the event disappears too and any attempt to access
+# register/memory now errors out.  GDB and GDBserver should be able to
+# handle this scenario gracefully.
+#
+# See https://sourceware.org/bugzilla/show_bug.cgi?id=18749
+
+standard_testfile
+
+set linenum [gdb_get_line_number "set break here"]
+
+if {[build_executable "failed to prepare" $testfile $srcfile {debug pthreads}] == -1} {
+    return -1
+}
+
+# The test proper.  If COND_BP_TARGET is true, then test with
+# conditional breakpoints evaluated on the target side, if possible.
+
+proc do_test { non_stop cond_bp_target } {
+    global GDBFLAGS
+    global gdb_prompt
+    global binfile
+    global linenum
+
+    set saved_gdbflags $GDBFLAGS
+    set GDBFLAGS [concat $GDBFLAGS " -ex \"set non-stop $non_stop\""]
+    clean_restart $binfile
+    set GDBFLAGS $saved_gdbflags
+
+    if ![runto_main] then {
+       fail "Can't run to main"
+       return 0
+    }
+
+    # Whether it's known that the test fails.
+    set should_kfail 0
+
+    if {![gdb_is_target_remote]} {
+       set should_kfail 1
+    } else {
+       if {!$cond_bp_target} {
+           # Leaving breakpoint evaluation to GDB exposes failures
+           # similar to native debugging.
+           gdb_test_no_output "set remote conditional-breakpoints-packet off"
+           set should_kfail 1
+       } else {
+           set test "show remote conditional-breakpoints-packet"
+           gdb_test_multiple $test $test {
+               -re "currently enabled\.\r\n$gdb_prompt $" {
+                   pass $test
+               }
+               -re "currently disabled\.\r\n$gdb_prompt $" {
+                   unsupported "no support for target-side conditional breakpoints"
+                   return
+               }
+           }
+           set should_kfail 1
+       }
+    }
+
+    gdb_test "break $linenum if zero == 1" \
+       "Breakpoint .*" \
+       "set breakpoint that evals false"
+
+    set test "continue &"
+    gdb_test_multiple $test $test {
+       -re "$gdb_prompt " {
+           pass $test
+       }
+    }
+
+    set ok 0
+
+    # Setup the kfail upfront in order to also catch GDB internal
+    # errors.
+    if {$should_kfail} {
+       setup_kfail "gdb/18749" "*-*-*"
+    }
+
+    set test "inferior 1 exited"
+    gdb_test_multiple "" $test {
+       -re "Inferior 1 \(\[^\r\n\]+\) exited normally" {
+           set ok 1
+
+           # Clear the kfail to avoid a PASS -> KPASS dance across
+           # runs.
+           clear_kfail "*-*-linux*"
+
+           pass $test
+       }
+       -re "$gdb_prompt " {
+           # Several errors end up at the top level, and printing the
+           # prompt.
+           fail "$test (prompt)"
+       }
+       -re "Cannot access memory" {
+           fail "$test (memory error)"
+       }
+       eof {
+           fail "$test (GDB died)"
+       }
+    }
+
+    if {!$ok} {
+       # No use testing further.
+       return
+    }
+
+    gdb_test "info threads" "No threads\." \
+       "no threads left"
+}
+
+# Wrapper for foreach that calls with_test_prefix on each iteration,
+# including the iterator's current value in the prefix.
+
+proc foreach_with_prefix {var list body} {
+    upvar 1 $var myvar
+    foreach myvar $list {
+       with_test_prefix "$var=$myvar" {
+           uplevel 1 $body
+       }
+    }
+}
+
+foreach_with_prefix non_stop {"on" "off"} {
+    foreach_with_prefix cond_bp_target {1 0} {
+       do_test $non_stop $cond_bp_target
+    }
+}