I am seeing an issue with the remote protocol on GDB with multiple threads on a riscv32 using the continue command. Not sure if it's a bug or just me, but it may be me as I am new to GDB. I am using a custom server. The behavior is the same on both GDB 11.1 and 11.2. This GDB was configured as follows: configure --host=x86_64-pc-linux-gnu --target=riscv32-unknown-elf --with-auto-load-dir=$debugdir:$datadir/auto-load --with-auto-load-safe-path=$debugdir:$datadir/auto-load --with-expat --with-gdb-datadir=/usr/local/share/gdb (relocatable) --with-jit-reader-dir=/usr/local/lib/gdb (relocatable) --without-libunwind-ia64 --with-lzma --without-babeltrace --without-intel-pt --with-mpfr --without-xxhash --with-python=/usr --with-python-libdir=/usr/lib --without-debuginfod --without-guile --disable-source-highlight --with-separate-debug-dir=/usr/local/lib/debug (relocatable) I am running in non-stop mode. A full log of the test is at the end of the email. The GDB commands I execute after connecting are: (gdb) interrupt -a (gdb) set *0x10000000=1 (gdb) c -a& The problem is that the "c -a&" is implemented by a 'vCont;c:1' followed by a 'vCont;c', essentially telling thread 1 to continue twice, causing it to skip over a break. Am I interpreting the commands incorrectly or am I sending GDB something incorrect that is confusing it? Thanks, Mike ---[ Remote Log ]------------------ '+' ---> 'qSupported:multiprocess+;swbreak+;hwbreak+;qRelocInsn+;fork-events+;vfork-events+;exec-events+;vContSupported+;QThreadEvents+' ---> <--- '+$hwbreak+;vContSupported+;QStartNoAckMode+;QNonStop+;PacketSize=410#e3' '+' ---> 'vMustReplyEmpty' ---> <--- '+$#00' '+' ---> 'QStartNoAckMode' ---> <--- '+$OK#9a' '+' ---> '!' ---> <--- '$OK#9a' 'Hg0' ---> <--- '$OK#9a' 'QNonStop:1' ---> <--- '$OK#9a' 'qfThreadInfo' ---> <--- '$m1,2#fc' 'qsThreadInfo' ---> <--- '$l#6c' 'qAttached' ---> <--- '$1#31' 'qTStatus' ---> <--- '$#00' '?' ---> <--- '$OK#9a' 'vCont?' ---> <--- '$vCont;c;C;s;t#83' 'vCont;t:2' ---> <--- '$OK#9a' <--- '%Stop:T00thread:2;#b3' 'qSymbol::' ---> <--- '$OK#9a' 'vStopped' ---> <--- '$OK#9a' 'Hg2' ---> <--- '$OK#9a' 'g' ---> <--- '$000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000#80' 'qOffsets' ---> <--- '$#00' 'qSymbol::' ---> <--- '$OK#9a' 'vCont;c' ---> <--- '$OK#9a' (gdb) interrupt -a 'vCont;t' ---> <--- '$OK#9a' <--- '%Stop:T00thread:1;#b2' 'vStopped' ---> <--- '$T00thread:2;#d3' 'vStopped' ---> <--- '$OK#9a' 'Hg1' ---> <--- '$OK#9a' 'g' ---> <--- '$000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000#80' 'qfThreadInfo' ---> <--- '$m1,2#fc' 'qsThreadInfo' ---> <--- '$l#6c' 'Hg2' ---> <--- '$OK#9a' 'g' ---> <--- '$000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000#80' 'qfThreadInfo' ---> <--- '$m1,2#fc' 'qsThreadInfo' ---> <--- '$l#6c' (gdb) set *0x10000000 = 1 'X10000000,0:' ---> <--- '$#00' 'M10000000,4:01000000' ---> <--- '$OK#9a' 'g' ---> <--- '$000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000#80' 'Hg1' ---> <--- '$OK#9a' 'g' ---> <--- '$000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000#80' (gdb) c -a& 'vCont;c:1' ---> <--- '$OK#9a' <--- '%Stop:T05thread:1;#b7' // THIS STOP IS LOST BECAUSE GDB IMMEDIATELY TELLS THREAD 1 TO CONTINUE 'vCont;c' ---> <--- '$OK#9a' 'vStopped' ---> <--- '$OK#9a' <--- '%Stop:T05thread:2;#b8' 'g' ---> <--- '$000000000000000000000000000000000000000000000000000000000000000000000000000000000002084000800041000200400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000#a5' 'qfThreadInfo' ---> <--- '$m1,2#fc' 'qsThreadInfo' ---> <--- '$l#6c' 'vStopped' ---> <--- '$OK#9a' 'Hg2' ---> <--- '$OK#9a' 'g' ---> <--- '$000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000#80' 'qfThreadInfo' ---> <--- '$m1,2#fc' 'qsThreadInfo' ---> <--- '$l#6c'
Hi! On 2022-03-16 14:45, Denio, Mike via Gdb wrote: > (gdb) c -a& > > 'vCont;c:1' ---> > <--- '$OK#9a' > <--- '%Stop:T05thread:1;#b7' // THIS STOP IS LOST BECAUSE GDB IMMEDIATELY TELLS THREAD 1 TO CONTINUE This stop hasn't been acknowledged yet with vStopped at this point, so ... > 'vCont;c' ---> > <--- '$OK#9a' ... from this 'vCont;c''s perspective, thread 1 is still running, so the server should ignore the 'c' action for thread 1. gdbserver does this here, in linux-low.c::linux_set_resume_request ... /* If the thread has a pending event that has already been reported to GDBserver core, but GDB has not pulled the event out of the vStopped queue yet, likewise, ignore the (wildcard) resume request. */ if (in_queued_stop_replies (thread->id)) { threads_debug_printf ("not resuming LWP %ld: has queued stop reply", lwpid_of (thread)); continue; } ... Pedro Alves
Ok, thanks. I'll have to think about how I'm going to implement that. Is it safe to assume that this only affects the initial event, and that once I see the first 'vStopped', GDB will not interlace more 'vCont' commands? For example, say I had 8 newly stopped threads such that GDB will end up calling 'vStopped' 8 times. Is it safe to assume that GDB will not send any new vCont in the middle of sending those 8 vStopped? Thanks again, Mike
On 2022-03-16 15:10, Denio, Mike wrote:
> Ok, thanks. I'll have to think about how I'm going to implement that.
>
> Is it safe to assume that this only affects the initial event, and that once I see the first 'vStopped', GDB will not interlace more 'vCont' commands?
>
> For example, say I had 8 newly stopped threads such that GDB will end up calling 'vStopped' 8 times. Is it safe to assume that GDB will not send any new vCont in the middle of sending those 8 vStopped?
Currently, when GDB issues a vStopped, it keeps issuing it until the remote side returns OK, indicating no more pending events.
But I wouldn't trust that that won't ever change. Imagine at some point we come to the conclusion that it would be better to
batch fetching pending events, so that GDB went on to process the pending events once it fetches some N events out of vStopped,
maybe go on to process events out of some other remote connection / target, and then goes back to fetching another batch
of N events out of the first remote connection, and so forth.
I realized after I sent the second e-mail that I was overthinking the problem. I thought I was going to have to keep 2 states (a real state and a virtual state being what GDB thinks is the state), but then I realized this really only affects the vCont command, so I isolated the changes to that. I already tracked whether or not I was in an "event", so I wouldn't fire off a second "stop" while processing a previous "stop", and my vCont processing already filtered out threads from wildcards based on previous commands (for example: "s:2;c" would filter thread 2 from the continue). So I just added the bottom 2 lines to the "if" statement below and its working great.
if( "wildcard" )
{
// Wildcard sets all bits not previously set
tmask = m_AllMask;
tmask &= ~stop_mask;
tmask &= ~step_mask;
tmask &= ~cont_mask;
if( m_InEvent )
tmask &= ~m_EvtMask;
}
Basically, I adjust the vCont command before I even start executing it, which saved me from modifying the lower level debug processing. The "m_EvtMask" is updated on every "vStopped", so the scenario you outline in your e-mail should still work for me. The only thing that concerns me a bit with this simple approach is the signal numbers. I really don't understand them and have been using 00 for 't', 02 for '^c', and 05 for break point. But this seems problematic when processing is delayed. For example, I send a stop with 05 and then GDB sends a "vCont t" for the same thread. Obviously GDB must accept any signal for a "vCont t" because the stop may have already been sent.
Thanks again for the help.
I do have a small issue dealing with how I am using threads that I may send a separate e-mail about. Its not so much a functional issue as a limitation for which someone may be able to suggest a better solution.
Mike
-----Original Message-----
From: Pedro Alves <pedro@palves.net>
Sent: Thursday, March 17, 2022 12:52 PM
To: Denio, Mike <miked@ti.com>; gdb@sourceware.org
Subject: Re: [EXTERNAL] Re: Issue with multiple threads using remote protocol on riscv32
On 2022-03-16 15:10, Denio, Mike wrote:
> Ok, thanks. I'll have to think about how I'm going to implement that.
>
> Is it safe to assume that this only affects the initial event, and that once I see the first 'vStopped', GDB will not interlace more 'vCont' commands?
>
> For example, say I had 8 newly stopped threads such that GDB will end up calling 'vStopped' 8 times. Is it safe to assume that GDB will not send any new vCont in the middle of sending those 8 vStopped?
Currently, when GDB issues a vStopped, it keeps issuing it until the remote side returns OK, indicating no more pending events.
But I wouldn't trust that that won't ever change. Imagine at some point we come to the conclusion that it would be better to
batch fetching pending events, so that GDB went on to process the pending events once it fetches some N events out of vStopped,
maybe go on to process events out of some other remote connection / target, and then goes back to fetching another batch
of N events out of the first remote connection, and so forth.