Re: remote debugging a multi-threaded program with signal

Daniel Jacobowitz
2004-03-04T22:28:03Z

On Fri, Mar 05, 2004 at 12:05:40AM +0900, Atsushi Nemoto wrote:
> >>>>> On Thu, 04 Mar 2004 01:06:24 +0900 (JST), Atsushi Nemoto <anemo@mba.ocn.ne.jp> said:
>
> anemo> There is a program on remote-debugging a multi-threaded program
> anemo> which uses signals. After receiving a signal (configured to
> anemo> "nostop"), the thread which receives it resumes normally but
> anemo> other threads leave stopped.
>
> The last packet sent to gdbserver is "$vCont;C1e:402" (see gdb.output1).
>
> gdb's info states:
>
> `vCont'[;ACTION[`:'TID]]... -- extended resume
> Resume the inferior. Different actions may be specified for each
> thread. If an action is specified with no TID, then it is applied
> to any threads that don't have a specific action specified; if no
> default action is specified then other threads should remain
> stopped. ...
>
> So the patcket means "continue thread 0x402 with signal 0x1e, others
> remain stopped". Then current gdbserver's behavior is correct.
>
> Following the info statements, gdb should send "$vCont;C1e:402;c" to
> handle a "nostop" signal correctly?

I thought you said you got the same results without verbose-resume?
Oh, I suppose you would; without verbose-resume it's pot-luck which
thread gets the signal.

If I use a pre-vCont gdb client with the current gdbserver I see:

[sigtest:30016:16384]:main
[sigtest:30018:16386]:func
[sigtest:30016:16384]:send SIGUSR1
[sigtest:30016:16384]:send SIGUSR1
[sigtest:30016:16384]:send SIGUSR1
[sigtest:30016:16384]:send SIGUSR1
[sigtest:30016:16384]:send SIGUSR1
[sigtest:30016:16384]:send SIGUSR1
[sigtest:30016:16384]:send SIGUSR1
[sigtest:30016:16384]:send SIGUSR1

This means that the signal is being delivered to the wrong thread,
since neither thread appears to be stopped. Which is what I would
expect without vCont, since vCont was intended to solve this exact
problem.

GDB's behavior with threads is a little under-specified, but code to
generate the "correct" vCont packet is definitely there. Unfortunately
it is not reached. We get this:

Breakpoint 1, remote_vcont_resume (ptid={pid = 16386, lwp = 0, tid = 0}, step=0,
siggnal=TARGET_SIGNAL_USR1) at /opt/src/gdb/src/gdb/remote.c:2461

i.e. the remote target was asked to resume only one thread.

Aha, here's the bug:
/* If it's not SIGTRAP and not a signal we want to stop for, then
continue the thread. */

if (stop_signal != TARGET_SIGNAL_TRAP && !signal_stop[stop_signal])
{
if (printed)
target_terminal_inferior ();

/* Clear the signal if it should not be passed. */
if (signal_program[stop_signal] == 0)
stop_signal = TARGET_SIGNAL_0;

target_resume (ecs->ptid, 0, stop_signal);
prepare_to_wait (ecs);
return;
}

That resumes only the thread in question. This is in
target-indepenedent code and the only reason that lin-lwp native
execution does not show the same problem is that it shortcuts around
this code completely (see lin_lwp_wait).

Could you try this instead?

--
Daniel Jacobowitz
MontaVista Software Debian GNU/Linux Developer

2004-03-04 Daniel Jacobowitz <drow@mvista.com>

* infrun.c (handle_inferior_event): Pass nostop signals to the
correct thread.

Index: infrun.c
===================================================================
RCS file: /cvs/src/src/gdb/infrun.c,v
retrieving revision 1.137
diff -u -p -r1.137 infrun.c
--- infrun.c 16 Feb 2004 20:49:51 -0000 1.137
+++ infrun.c 4 Mar 2004 22:27:11 -0000
@@ -1918,32 +1918,29 @@ handle_inferior_event (struct execution_
#endif
}

+ context_switch (ecs);
+
+ if (context_hook)
+ context_hook (pid_to_thread_id (ecs->ptid));
+
+ flush_cached_frames ();
+
/* If it's not SIGTRAP and not a signal we want to stop for, then
continue the thread. */

if (stop_signal != TARGET_SIGNAL_TRAP && !signal_stop[stop_signal])
{
- if (printed)
- target_terminal_inferior ();
-
/* Clear the signal if it should not be passed. */
if (signal_program[stop_signal] == 0)
stop_signal = TARGET_SIGNAL_0;

- target_resume (ecs->ptid, 0, stop_signal);
+ resume (currently_stepping (ecs), stop_signal);
prepare_to_wait (ecs);
return;
}

- /* It's a SIGTRAP or a signal we're interested in. Switch threads,
- and fall into the rest of wait_for_inferior(). */
-
- context_switch (ecs);
-
- if (context_hook)
- context_hook (pid_to_thread_id (ecs->ptid));
-
- flush_cached_frames ();
+ /* It's a SIGTRAP or a signal we're interested in. Fall into the
+ rest of wait_for_inferior(). */
}

if (SOFTWARE_SINGLE_STEP_P () && singlestep_breakpoints_inserted_p)