Re: Stepping of multithreaded application

Pedro Alves
2011-05-11T17:29:24Z

On Tuesday 10 May 2011 17:27:20, Tomas Martinec wrote:
> From the debugging messages I can see, that the GDB steps twice. The
> first step is for the first thread - maybe because the first thread
> has just raised the breakpoint. Before the second step the GDB tries
> to change the current thread in the virtual machine to 0 (means ANY
> according to the doc).

Right.

> That is not supported in the virtual machine
> and the machine responds so. I believe not supporting the Hc0 command
> is not wrong for the virtual machine.

IMO, it's wrong. But I don't think it'd make a difference, unless
the target gives preference to the already/previously selected
Hg/c thread.

In any case, you should really teach your VM about the vCont
packet instead of 'Hc'/'s'/'c'.

> Anyway the second step is aimed
> for ANY thread and it is applied for the first thread, which both
> leads to the unexpected behaviour. I think that the GDB should send
> Hc3 command to the simulator before the second step.

I think so too.

> I have also tried to find out why the Hc3 command is not send from the
> GDB sources. The thread id that will be step is determined in resume
> function of target.c. In my case the RESUME_ALL ptid is used. That
> ptid leads to the Hc0 command. So far I have not understood why the
> resume_ptid is kept in the default RESUME_ALL.

That's due to the "set scheduler-locking" setting. Even though
you're "next"ing thread 3, by default, the other threads run
a bit too. See patch below.

> -exec-next --thread 3 1
> &"Sending packet: $Hg3#e2..."
> &"Ack\n"
> &"Packet received: OK\n"
> &"Sending packet: $g#67..."
> &"Ack\n"
> &"Packet received:
> 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000200000000000080d8060080000000004c030080040040000000000000000000000000000000000054260080\n"
> &"infrun: clear_proceed_status_thread (Thread 3)\n"
> &"infrun: clear_proceed_status_thread (Thread 2)\n"
> &"infrun: clear_proceed_status_thread (Thread 1)\n"
> &"infrun: proceed (addr=0xffffffff, signal=144, step=1)\n"
> &"Sending packet: $Hg1#e0..."
> &"Ack\n"
> &"Packet received: OK\n"
> &"Sending packet: $g#67..."
> &"Ack\n"
> &"Packet received:
> 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000080d8040080d804008038030080040040000000000000000000000000000000000040250080\n"
> &"Sending packet: $g#67..."
> &"Ack\n"
> &"Packet received:
> 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000080d8040080d804008038030080040040000000000000000000000000000000000040250080\n"
> &"infrun: resume (step=1, signal=0), trap_expected=1\n"
> &"Sending packet: $Hc1#dc..."
> &"Ack\n"
> &"Packet received: OK\n"
> &"Sending packet: $s#73..."

We want to "next" thread 3 (which resumes all other
threads as well, see the manual for "set scheduler-locking"),
but thread 1 was stopped at a breakpoint, so we need to move
it past the breakpoint first (infrun.c:prepare_to_proceed).

> &"Ack\n"
> ^running
> *running,thread-id="1"
> (gdb)
> &"infrun: wait_for_inferior (treat_exec_as_sigtrap=0)\n"
> &"Packet received: T05thread:00000001;25:44250080;\n"
> &"infrun: target_wait (-1, status) =\ninfrun: 42000 [Thread
> 1],\ninfrun: status->kind = stopped, signal = SIGTRAP\n"
> &"infrun: infwait_normal_state\n"
> &"infrun: TARGET_WAITKIND_STOPPED\n"
> &"infrun: stop_pc = 0x80002544\n"
> &"infrun: handling deferred step\n"

Done. Go back to stepping thread 3 and resuming all
others...

> &"Sending packet: $Hg3#e2..."
> &"Ack\n"
> &"Packet received: OK\n"
> &"Sending packet: $g#67..."
> &"Ack\n"
> &"Packet received:
> 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000200000000000080d8060080000000004c030080040040000000000000000000000000000000000058260080\n"
> &"infrun: resume (step=1, signal=0), trap_expected=0\n"
> &"Sending packet: $Hc0#db..."

Select any thread in the target... (Indeed strange.)

> &"Ack\n"
> &"Packet received: \n"
> &"Sending packet: $s#73..."

Step the target...

> &"Ack\n"
> *running,thread-id="all"
> &"infrun: prepare_to_wait\n"
> &"Packet received: T05thread:00000001;25:48250080;\n"

The target finished stepping thread 1. But we wanted
thread 3 to be stepped...

Please try the patch below. It's "any_thread_ptid" that causes "Hc0".
If we're stepping, obviously we need to step the correct thread, not
a random one. I don't really understand how this hasn't been a
problem before.

As explained above and in the comment in the patch, you
should really teach your target to support the vCont packet.
Hc/s are deprecated for multi-threading.

Pedro Alves

2011-05-11 Pedro Alves <pedro@codesourcery.com>

gdb/
* remote.c (remote_resume): Set the continue thread to
inferior_ptid if the core passed in a wildcard resume, instead of
setting it to a random thread.

---
gdb/remote.c | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)

Index: src/gdb/remote.c
===================================================================
--- src.orig/gdb/remote.c 2011-05-11 15:17:48.658167999 +0100
+++ src/gdb/remote.c 2011-05-11 18:00:13.988168000 +0100
@@ -4542,8 +4542,17 @@ remote_resume (struct target_ops *ops,

/* All other supported resume packets do use Hc, so set the continue
thread. */
- if (ptid_equal (ptid, minus_one_ptid))
- set_continue_thread (any_thread_ptid);
+
+ /* A wildcard PTID means either "step the current thread and
+ continue others", or "continue all threads".
+
+ A specific PTID means `step only this process id'. Problem is
+ the 's' packet is ambiguous: "step just this thread and leave
+ others stopped", or "step this thread and continue all others"?
+ vCont doesn't have this problem. */
+
+ if (ptid_equal (minus_one_ptid, ptid) || ptid_is_pid (ptid))
+ set_continue_thread (inferior_ptid);
else
set_continue_thread (ptid);