You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
569 lines
19 KiB
569 lines
19 KiB
6 years ago
|
From 6681535d2cecea1f67255c140f11b217518a2f4e Mon Sep 17 00:00:00 2001
|
||
|
From: Pedro Alves <palves@redhat.com>
|
||
|
Date: Wed, 17 Jun 2015 20:41:05 +0100
|
||
|
Subject: [PATCH] Fix BZ1184724 - gdb output a internal-error: Assertion
|
||
|
`num_lwps (GET_PID (inferior_ptid)) == 1'
|
||
|
|
||
|
The real issue with this bug is in the ptrace error:
|
||
|
|
||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
(gdb) c
|
||
|
Continuing.
|
||
|
netcfStateCleanup () at interface/interface_backend_netcf.c:116
|
||
|
116 }
|
||
|
ptrace: No such process.
|
||
|
^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
(gdb) quit
|
||
|
Inferior 1 [process 11205] will be detached.
|
||
|
Quit anyway? (y or n) y
|
||
|
[Thread 0x7fefdf322880 (LWP 11205) exited]
|
||
|
|
||
|
../../gdb/linux-nat.c:1869: internal-error: linux_nat_detach: Assertion `num_lwps (GET_PID (inferior_ptid)) == 1' failed.
|
||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
|
||
|
The assertion is just a consequence.
|
||
|
|
||
|
So the main fix is to backport the GDB [1] bits of upstream 23f238d3
|
||
|
(Fix race exposed by gdb.threads/killed.exp). That makes
|
||
|
linux_resume_one_lwp detect when the LWP being resumed disappears.
|
||
|
However, we need to backport also part of 8a99810d (linux-nat.c: clean
|
||
|
up pending status checking and resuming LWPs), which is what
|
||
|
introduced the centralized linux_resume_one_lwp in the first place,
|
||
|
and also one bit from 8817a6f2 (PR gdb/15713 - errors from
|
||
|
i386_linux_resume lead to lock-up), which makes linux_nat_resume not
|
||
|
clear the leader LWP's lwp->stopped flag too early, otherwise we'd hit
|
||
|
the new assertion in check_ptrace_stopped_lwp_gone.
|
||
|
|
||
|
The test is new (not a backport), and without the fix triggers the
|
||
|
"detach" assertion shown above.
|
||
|
|
||
|
[1] - The equivalent GDBserver bits are harder to backport, as they
|
||
|
rely on GDB exceptions, which GDBserver 7.6.1 doesn't support yet.
|
||
|
Fortunately, GDBserver already handles ESRCH from within its
|
||
|
linux_resume_one_lwp, so doesn't trigger this particular bug and
|
||
|
passes the test.
|
||
|
|
||
|
gdb/ChangeLog:
|
||
|
2015-06-17 Pedro Alves <palves@redhat.com>
|
||
|
|
||
|
* common/linux-procfs.c (linux_proc_pid_is_trace_stopped): New
|
||
|
function.
|
||
|
* common/linux-procfs.h (linux_proc_pid_is_trace_stopped): New
|
||
|
declaration.
|
||
|
* linux-nat.c (linux_resume_one_lwp_throw, check_ptrace_stopped_lwp_gone)
|
||
|
(linux_resume_one_lwp): New functions.
|
||
|
(resume_lwp): Use linux_resume_one_lwp.
|
||
|
(linux_nat_resume_callback): Skip if LWP is the same as the passed
|
||
|
in data pointer.
|
||
|
(linux_nat_resume): Don't clear the selected LWP's stopped flag
|
||
|
before resuming the sibling LWPs. Instead pass LWP to
|
||
|
linux_nat_resume_callback. Use linux_resume_one_lwp.
|
||
|
(linux_handle_extended_wait, linux_nat_filter_event)
|
||
|
(linux_nat_wait_1): Use linux_resume_one_lwp.
|
||
|
(resume_stopped_resumed_lwps): Try register reads in TRY/CATCH and
|
||
|
swallows errors if the LWP is gone. Use
|
||
|
linux_resume_one_lwp_throw instead of linux_resume_one_lwp.
|
||
|
|
||
|
gdb/testsuite/ChangeLog:
|
||
|
2015-06-17 Pedro Alves <palves@redhat.com>
|
||
|
|
||
|
* gdb.threads/thread-kills-process.c: New file.
|
||
|
* gdb.threads/thread-kills-process.exp: New file.
|
||
|
---
|
||
|
gdb/ChangeLog | 13 ++
|
||
|
gdb/common/linux-procfs.c | 6 +
|
||
|
gdb/common/linux-procfs.h | 2 +
|
||
|
gdb/linux-nat.c | 200 +++++++++++++--------
|
||
|
gdb/testsuite/gdb.threads/thread-kills-process.c | 62 +++++++
|
||
|
gdb/testsuite/gdb.threads/thread-kills-process.exp | 70 ++++++++
|
||
|
6 files changed, 277 insertions(+), 76 deletions(-)
|
||
|
create mode 100644 gdb/testsuite/gdb.threads/thread-kills-process.c
|
||
|
create mode 100644 gdb/testsuite/gdb.threads/thread-kills-process.exp
|
||
|
|
||
|
Index: gdb-7.6.1/gdb/common/linux-procfs.c
|
||
|
===================================================================
|
||
|
--- gdb-7.6.1.orig/gdb/common/linux-procfs.c
|
||
|
+++ gdb-7.6.1/gdb/common/linux-procfs.c
|
||
|
@@ -111,6 +111,12 @@ linux_proc_pid_is_stopped (pid_t pid)
|
||
|
return linux_proc_pid_has_state (pid, "T (stopped)");
|
||
|
}
|
||
|
|
||
|
+int
|
||
|
+linux_proc_pid_is_trace_stopped (pid_t pid)
|
||
|
+{
|
||
|
+ return linux_proc_pid_has_state (pid, "T (tracing stop)");
|
||
|
+}
|
||
|
+
|
||
|
/* See linux-procfs.h declaration. */
|
||
|
|
||
|
int
|
||
|
Index: gdb-7.6.1/gdb/common/linux-procfs.h
|
||
|
===================================================================
|
||
|
--- gdb-7.6.1.orig/gdb/common/linux-procfs.h
|
||
|
+++ gdb-7.6.1/gdb/common/linux-procfs.h
|
||
|
@@ -36,6 +36,8 @@ extern pid_t linux_proc_get_tracerpid (p
|
||
|
|
||
|
extern int linux_proc_pid_is_stopped (pid_t pid);
|
||
|
|
||
|
+extern int linux_proc_pid_is_trace_stopped (pid_t pid);
|
||
|
+
|
||
|
/* Return non-zero if PID is a zombie. */
|
||
|
|
||
|
extern int linux_proc_pid_is_zombie (pid_t pid);
|
||
|
Index: gdb-7.6.1/gdb/linux-nat.c
|
||
|
===================================================================
|
||
|
--- gdb-7.6.1.orig/gdb/linux-nat.c
|
||
|
+++ gdb-7.6.1/gdb/linux-nat.c
|
||
|
@@ -1928,6 +1928,85 @@ linux_nat_detach (struct target_ops *ops
|
||
|
linux_ops->to_detach (ops, args, from_tty);
|
||
|
}
|
||
|
|
||
|
+/* Resume execution of the inferior process. If STEP is nonzero,
|
||
|
+ single-step it. If SIGNAL is nonzero, give it that signal. */
|
||
|
+
|
||
|
+static void
|
||
|
+linux_resume_one_lwp_throw (struct lwp_info *lp, int step,
|
||
|
+ enum gdb_signal signo)
|
||
|
+{
|
||
|
+ ptid_t ptid;
|
||
|
+
|
||
|
+ lp->step = step;
|
||
|
+
|
||
|
+ if (linux_nat_prepare_to_resume != NULL)
|
||
|
+ linux_nat_prepare_to_resume (lp);
|
||
|
+
|
||
|
+ ptid = pid_to_ptid (GET_LWP (lp->ptid));
|
||
|
+ linux_ops->to_resume (linux_ops, ptid, step, signo);
|
||
|
+
|
||
|
+ /* Successfully resumed. Clear state that no longer makes sense,
|
||
|
+ and mark the LWP as running. Must not do this before resuming
|
||
|
+ otherwise if that fails other code will be confused. E.g., we'd
|
||
|
+ later try to stop the LWP and hang forever waiting for a stop
|
||
|
+ status. Note that we must not throw after this is cleared,
|
||
|
+ otherwise handle_zombie_lwp_error would get confused. */
|
||
|
+ lp->stopped = 0;
|
||
|
+ lp->stopped_by_watchpoint = 0;
|
||
|
+ registers_changed_ptid (lp->ptid);
|
||
|
+}
|
||
|
+
|
||
|
+/* Called when we try to resume a stopped LWP and that errors out. If
|
||
|
+ the LWP is no longer in ptrace-stopped state (meaning it's zombie,
|
||
|
+ or about to become), discard the error, clear any pending status
|
||
|
+ the LWP may have, and return true (we'll collect the exit status
|
||
|
+ soon enough). Otherwise, return false. */
|
||
|
+
|
||
|
+static int
|
||
|
+check_ptrace_stopped_lwp_gone (struct lwp_info *lp)
|
||
|
+{
|
||
|
+ /* If we get an error after resuming the LWP successfully, we'd
|
||
|
+ confuse !T state for the LWP being gone. */
|
||
|
+ gdb_assert (lp->stopped);
|
||
|
+
|
||
|
+ /* We can't just check whether the LWP is in 'Z (Zombie)' state,
|
||
|
+ because even if ptrace failed with ESRCH, the tracee may be "not
|
||
|
+ yet fully dead", but already refusing ptrace requests. In that
|
||
|
+ case the tracee has 'R (Running)' state for a little bit
|
||
|
+ (observed in Linux 3.18). See also the note on ESRCH in the
|
||
|
+ ptrace(2) man page. Instead, check whether the LWP has any state
|
||
|
+ other than ptrace-stopped. */
|
||
|
+
|
||
|
+ /* Don't assume anything if /proc/PID/status can't be read. */
|
||
|
+ if (linux_proc_pid_is_trace_stopped (ptid_get_lwp (lp->ptid)) == 0)
|
||
|
+ {
|
||
|
+ lp->status = 0;
|
||
|
+ lp->waitstatus.kind = TARGET_WAITKIND_IGNORE;
|
||
|
+ lp->stopped_by_watchpoint = 0;
|
||
|
+ return 1;
|
||
|
+ }
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+/* Like linux_resume_one_lwp_throw, but no error is thrown if the LWP
|
||
|
+ disappears while we try to resume it. */
|
||
|
+
|
||
|
+static void
|
||
|
+linux_resume_one_lwp (struct lwp_info *lp, int step, enum gdb_signal signo)
|
||
|
+{
|
||
|
+ volatile struct gdb_exception ex;
|
||
|
+
|
||
|
+ TRY_CATCH (ex, RETURN_MASK_ERROR)
|
||
|
+ {
|
||
|
+ linux_resume_one_lwp_throw (lp, step, signo);
|
||
|
+ }
|
||
|
+ if (ex.reason < 0)
|
||
|
+ {
|
||
|
+ if (!check_ptrace_stopped_lwp_gone (lp))
|
||
|
+ throw_exception (ex);
|
||
|
+ }
|
||
|
+}
|
||
|
+
|
||
|
/* Resume LP. */
|
||
|
|
||
|
static void
|
||
|
@@ -1956,14 +2035,7 @@ resume_lwp (struct lwp_info *lp, int ste
|
||
|
: "0"),
|
||
|
step ? "step" : "resume");
|
||
|
|
||
|
- if (linux_nat_prepare_to_resume != NULL)
|
||
|
- linux_nat_prepare_to_resume (lp);
|
||
|
- linux_ops->to_resume (linux_ops,
|
||
|
- pid_to_ptid (GET_LWP (lp->ptid)),
|
||
|
- step, signo);
|
||
|
- lp->stopped = 0;
|
||
|
- lp->step = step;
|
||
|
- lp->stopped_by_watchpoint = 0;
|
||
|
+ linux_resume_one_lwp (lp, step, signo);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
@@ -1982,13 +2054,17 @@ resume_lwp (struct lwp_info *lp, int ste
|
||
|
}
|
||
|
}
|
||
|
|
||
|
-/* Resume LWP, with the last stop signal, if it is in pass state. */
|
||
|
+/* Callback for iterate_over_lwps. If LWP is EXCEPT, do nothing.
|
||
|
+ Resume LWP with the last stop signal, if it is in pass state. */
|
||
|
|
||
|
static int
|
||
|
-linux_nat_resume_callback (struct lwp_info *lp, void *data)
|
||
|
+linux_nat_resume_callback (struct lwp_info *lp, void *except)
|
||
|
{
|
||
|
enum gdb_signal signo = GDB_SIGNAL_0;
|
||
|
|
||
|
+ if (lp == except)
|
||
|
+ return 0;
|
||
|
+
|
||
|
if (lp->stopped)
|
||
|
{
|
||
|
struct thread_info *thread;
|
||
|
@@ -2108,20 +2184,10 @@ linux_nat_resume (struct target_ops *ops
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
- /* Mark LWP as not stopped to prevent it from being continued by
|
||
|
- linux_nat_resume_callback. */
|
||
|
- lp->stopped = 0;
|
||
|
-
|
||
|
if (resume_many)
|
||
|
- iterate_over_lwps (ptid, linux_nat_resume_callback, NULL);
|
||
|
+ iterate_over_lwps (ptid, linux_nat_resume_callback, lp);
|
||
|
|
||
|
- /* Convert to something the lower layer understands. */
|
||
|
- ptid = pid_to_ptid (GET_LWP (lp->ptid));
|
||
|
-
|
||
|
- if (linux_nat_prepare_to_resume != NULL)
|
||
|
- linux_nat_prepare_to_resume (lp);
|
||
|
- linux_ops->to_resume (linux_ops, ptid, step, signo);
|
||
|
- lp->stopped_by_watchpoint = 0;
|
||
|
+ linux_resume_one_lwp (lp, step, signo);
|
||
|
|
||
|
if (debug_linux_nat)
|
||
|
fprintf_unfiltered (gdb_stdlog,
|
||
|
@@ -2287,11 +2353,7 @@ linux_handle_syscall_trap (struct lwp_in
|
||
|
|
||
|
/* Note that gdbarch_get_syscall_number may access registers, hence
|
||
|
fill a regcache. */
|
||
|
- registers_changed ();
|
||
|
- if (linux_nat_prepare_to_resume != NULL)
|
||
|
- linux_nat_prepare_to_resume (lp);
|
||
|
- linux_ops->to_resume (linux_ops, pid_to_ptid (GET_LWP (lp->ptid)),
|
||
|
- lp->step, GDB_SIGNAL_0);
|
||
|
+ linux_resume_one_lwp (lp, lp->step, GDB_SIGNAL_0);
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
@@ -2486,22 +2548,15 @@ linux_handle_extended_wait (struct lwp_i
|
||
|
fprintf_unfiltered (gdb_stdlog,
|
||
|
"LHEW: resuming new LWP %ld\n",
|
||
|
GET_LWP (new_lp->ptid));
|
||
|
- if (linux_nat_prepare_to_resume != NULL)
|
||
|
- linux_nat_prepare_to_resume (new_lp);
|
||
|
- linux_ops->to_resume (linux_ops, pid_to_ptid (new_pid),
|
||
|
- 0, GDB_SIGNAL_0);
|
||
|
- new_lp->stopped = 0;
|
||
|
+
|
||
|
+ linux_resume_one_lwp (new_lp, 0, GDB_SIGNAL_0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (debug_linux_nat)
|
||
|
fprintf_unfiltered (gdb_stdlog,
|
||
|
"LHEW: resuming parent LWP %d\n", pid);
|
||
|
- if (linux_nat_prepare_to_resume != NULL)
|
||
|
- linux_nat_prepare_to_resume (lp);
|
||
|
- linux_ops->to_resume (linux_ops, pid_to_ptid (GET_LWP (lp->ptid)),
|
||
|
- 0, GDB_SIGNAL_0);
|
||
|
-
|
||
|
+ linux_resume_one_lwp (lp, 0, GDB_SIGNAL_0);
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
@@ -3382,12 +3437,7 @@ linux_nat_filter_event (int lwpid, int s
|
||
|
{
|
||
|
/* This is a delayed SIGSTOP. */
|
||
|
|
||
|
- registers_changed ();
|
||
|
-
|
||
|
- if (linux_nat_prepare_to_resume != NULL)
|
||
|
- linux_nat_prepare_to_resume (lp);
|
||
|
- linux_ops->to_resume (linux_ops, pid_to_ptid (GET_LWP (lp->ptid)),
|
||
|
- lp->step, GDB_SIGNAL_0);
|
||
|
+ linux_resume_one_lwp (lp, lp->step, GDB_SIGNAL_0);
|
||
|
if (debug_linux_nat)
|
||
|
fprintf_unfiltered (gdb_stdlog,
|
||
|
"LLW: %s %s, 0, 0 (discard SIGSTOP)\n",
|
||
|
@@ -3395,7 +3445,6 @@ linux_nat_filter_event (int lwpid, int s
|
||
|
"PTRACE_SINGLESTEP" : "PTRACE_CONT",
|
||
|
target_pid_to_str (lp->ptid));
|
||
|
|
||
|
- lp->stopped = 0;
|
||
|
gdb_assert (lp->resumed);
|
||
|
|
||
|
/* Discard the event. */
|
||
|
@@ -3416,11 +3465,7 @@ linux_nat_filter_event (int lwpid, int s
|
||
|
/* This is a delayed SIGINT. */
|
||
|
lp->ignore_sigint = 0;
|
||
|
|
||
|
- registers_changed ();
|
||
|
- if (linux_nat_prepare_to_resume != NULL)
|
||
|
- linux_nat_prepare_to_resume (lp);
|
||
|
- linux_ops->to_resume (linux_ops, pid_to_ptid (GET_LWP (lp->ptid)),
|
||
|
- lp->step, GDB_SIGNAL_0);
|
||
|
+ linux_resume_one_lwp (lp, lp->step, GDB_SIGNAL_0);
|
||
|
if (debug_linux_nat)
|
||
|
fprintf_unfiltered (gdb_stdlog,
|
||
|
"LLW: %s %s, 0, 0 (discard SIGINT)\n",
|
||
|
@@ -3428,7 +3473,6 @@ linux_nat_filter_event (int lwpid, int s
|
||
|
"PTRACE_SINGLESTEP" : "PTRACE_CONT",
|
||
|
target_pid_to_str (lp->ptid));
|
||
|
|
||
|
- lp->stopped = 0;
|
||
|
gdb_assert (lp->resumed);
|
||
|
|
||
|
/* Discard the event. */
|
||
|
@@ -3796,11 +3840,7 @@ retry:
|
||
|
other threads to run. On the other hand, not resuming
|
||
|
newly attached threads may cause an unwanted delay in
|
||
|
getting them running. */
|
||
|
- registers_changed ();
|
||
|
- if (linux_nat_prepare_to_resume != NULL)
|
||
|
- linux_nat_prepare_to_resume (lp);
|
||
|
- linux_ops->to_resume (linux_ops, pid_to_ptid (GET_LWP (lp->ptid)),
|
||
|
- lp->step, signo);
|
||
|
+ linux_resume_one_lwp (lp, lp->step, signo);
|
||
|
if (debug_linux_nat)
|
||
|
fprintf_unfiltered (gdb_stdlog,
|
||
|
"LLW: %s %s, %s (preempt 'handle')\n",
|
||
|
@@ -3810,7 +3850,6 @@ retry:
|
||
|
(signo != GDB_SIGNAL_0
|
||
|
? strsignal (gdb_signal_to_host (signo))
|
||
|
: "0"));
|
||
|
- lp->stopped = 0;
|
||
|
goto retry;
|
||
|
}
|
||
|
|
||
|
@@ -3935,32 +3974,41 @@ resume_stopped_resumed_lwps (struct lwp_
|
||
|
{
|
||
|
struct regcache *regcache = get_thread_regcache (lp->ptid);
|
||
|
struct gdbarch *gdbarch = get_regcache_arch (regcache);
|
||
|
- CORE_ADDR pc = regcache_read_pc (regcache);
|
||
|
+ volatile struct gdb_exception ex;
|
||
|
|
||
|
gdb_assert (is_executing (lp->ptid));
|
||
|
|
||
|
- /* Don't bother if there's a breakpoint at PC that we'd hit
|
||
|
- immediately, and we're not waiting for this LWP. */
|
||
|
- if (!ptid_match (lp->ptid, *wait_ptid_p))
|
||
|
+ TRY_CATCH (ex, RETURN_MASK_ERROR)
|
||
|
{
|
||
|
- if (breakpoint_inserted_here_p (get_regcache_aspace (regcache), pc))
|
||
|
- return 0;
|
||
|
- }
|
||
|
+ CORE_ADDR pc = regcache_read_pc (regcache);
|
||
|
+ int leave_stopped = 0;
|
||
|
|
||
|
- if (debug_linux_nat)
|
||
|
- fprintf_unfiltered (gdb_stdlog,
|
||
|
- "RSRL: resuming stopped-resumed LWP %s at %s: step=%d\n",
|
||
|
- target_pid_to_str (lp->ptid),
|
||
|
- paddress (gdbarch, pc),
|
||
|
- lp->step);
|
||
|
+ /* Don't bother if there's a breakpoint at PC that we'd hit
|
||
|
+ immediately, and we're not waiting for this LWP. */
|
||
|
+ if (!ptid_match (lp->ptid, *wait_ptid_p))
|
||
|
+ {
|
||
|
+ if (breakpoint_inserted_here_p (get_regcache_aspace (regcache), pc))
|
||
|
+ leave_stopped = 1;
|
||
|
+ }
|
||
|
|
||
|
- registers_changed ();
|
||
|
- if (linux_nat_prepare_to_resume != NULL)
|
||
|
- linux_nat_prepare_to_resume (lp);
|
||
|
- linux_ops->to_resume (linux_ops, pid_to_ptid (GET_LWP (lp->ptid)),
|
||
|
- lp->step, GDB_SIGNAL_0);
|
||
|
- lp->stopped = 0;
|
||
|
- lp->stopped_by_watchpoint = 0;
|
||
|
+ if (!leave_stopped)
|
||
|
+ {
|
||
|
+ if (debug_linux_nat)
|
||
|
+ fprintf_unfiltered (gdb_stdlog,
|
||
|
+ "RSRL: resuming stopped-resumed LWP %s at "
|
||
|
+ "%s: step=%d\n",
|
||
|
+ target_pid_to_str (lp->ptid),
|
||
|
+ paddress (gdbarch, pc),
|
||
|
+ lp->step);
|
||
|
+
|
||
|
+ linux_resume_one_lwp_throw (lp, lp->step, GDB_SIGNAL_0);
|
||
|
+ }
|
||
|
+ }
|
||
|
+ if (ex.reason < 0)
|
||
|
+ {
|
||
|
+ if (!check_ptrace_stopped_lwp_gone (lp))
|
||
|
+ throw_exception (ex);
|
||
|
+ }
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
Index: gdb-7.6.1/gdb/testsuite/gdb.threads/thread-kills-process.c
|
||
|
===================================================================
|
||
|
--- /dev/null
|
||
|
+++ gdb-7.6.1/gdb/testsuite/gdb.threads/thread-kills-process.c
|
||
|
@@ -0,0 +1,62 @@
|
||
|
+/* Copyright 2015 Free Software Foundation, Inc.
|
||
|
+
|
||
|
+ This file is part of GDB.
|
||
|
+
|
||
|
+ 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 <pthread.h>
|
||
|
+#include <unistd.h>
|
||
|
+
|
||
|
+volatile int call_exit;
|
||
|
+static pthread_barrier_t barrier;
|
||
|
+#define NUMTHREADS 256
|
||
|
+
|
||
|
+void *
|
||
|
+thread_function (void *arg)
|
||
|
+{
|
||
|
+ pthread_barrier_wait (&barrier);
|
||
|
+
|
||
|
+ while (!call_exit)
|
||
|
+ usleep (1);
|
||
|
+
|
||
|
+ _exit (0);
|
||
|
+ return NULL;
|
||
|
+}
|
||
|
+
|
||
|
+void
|
||
|
+all_threads_started (void)
|
||
|
+{
|
||
|
+ call_exit = 1;
|
||
|
+}
|
||
|
+
|
||
|
+int
|
||
|
+main (int argc, char **argv)
|
||
|
+{
|
||
|
+ pthread_t threads[NUMTHREADS];
|
||
|
+ int i;
|
||
|
+
|
||
|
+ pthread_barrier_init (&barrier, NULL, NUMTHREADS + 1);
|
||
|
+
|
||
|
+ for (i = 0; i < NUMTHREADS; ++i)
|
||
|
+ pthread_create (&threads[i], NULL, thread_function, NULL);
|
||
|
+
|
||
|
+ pthread_barrier_wait (&barrier);
|
||
|
+
|
||
|
+ all_threads_started ();
|
||
|
+
|
||
|
+ for (i = 0; i < NUMTHREADS; ++i)
|
||
|
+ pthread_join (threads[i], NULL);
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
Index: gdb-7.6.1/gdb/testsuite/gdb.threads/thread-kills-process.exp
|
||
|
===================================================================
|
||
|
--- /dev/null
|
||
|
+++ gdb-7.6.1/gdb/testsuite/gdb.threads/thread-kills-process.exp
|
||
|
@@ -0,0 +1,70 @@
|
||
|
+# 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/>.
|
||
|
+
|
||
|
+standard_testfile
|
||
|
+
|
||
|
+if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
|
||
|
+ return -1
|
||
|
+}
|
||
|
+
|
||
|
+clean_restart ${binfile}
|
||
|
+if {![runto_main]} {
|
||
|
+ fail "Couldn't run to main"
|
||
|
+ return
|
||
|
+}
|
||
|
+
|
||
|
+gdb_breakpoint "all_threads_started"
|
||
|
+
|
||
|
+gdb_continue_to_breakpoint "all_threads_started"
|
||
|
+
|
||
|
+# Update the thread list, otherwise when testing against GDBserver,
|
||
|
+# GDB won't know about thread 2. (Only necessary with GDB < 7.9.)
|
||
|
+gdb_test "info threads" ".*"
|
||
|
+
|
||
|
+# Select any thread but the leader.
|
||
|
+gdb_test "thread 2" ".*" "switch to non-leader thread"
|
||
|
+
|
||
|
+# Delete breakpoints so that GDB doesn't switch back the to leader to
|
||
|
+# step over its breakpoint.
|
||
|
+delete_breakpoints
|
||
|
+
|
||
|
+# Let threads exit the process on next resume.
|
||
|
+gdb_test "p call_exit = 0" " = 0"
|
||
|
+
|
||
|
+# While GDB is busy resuming all threads one by one, one of the
|
||
|
+# threads should manage to exit the process. GDB should handle that
|
||
|
+# gracefully instead of erroring out.
|
||
|
+#
|
||
|
+# gdb_continue_to_end doesn't work with GDBserver until the
|
||
|
+# introduction of the "exit_is_reliable" board variable
|
||
|
+# (b477a5e649150) in GDB 7.7.
|
||
|
+#gdb_continue_to_end "" continue 1
|
||
|
+gdb_test "continue" "$inferior_exited_re normally.*"
|
||
|
+
|
||
|
+# On the buggy GDB where the "continue" above would error out, a
|
||
|
+# subsequent "detach" (e.g., the user tries to quit GDB, and quit
|
||
|
+# offers to detach) would hit this assertion:
|
||
|
+#
|
||
|
+# linux-nat.c:1869: internal-error: linux_nat_detach: Assertion `num_lwps (GET_PID (inferior_ptid)) == 1' failed.
|
||
|
+
|
||
|
+# That was a consequence of the original bug, but let's make sure that
|
||
|
+# even when "continue" is handled properly, detach doesn't stumble on
|
||
|
+# anything stale.
|
||
|
+gdb_test "detach" "The program is not being run\\." \
|
||
|
+ "detach after exit"
|
||
|
+
|
||
|
+# Likewise "continue".
|
||
|
+gdb_test "continue" "The program is not being run\\." \
|
||
|
+ "continue after exit"
|