Mirror of the gdb mailing list
 help / color / mirror / Atom feed
* Multi-threading support on DragonFlyBSD
@ 2025-03-05 10:22 Sergey Zigachev via Gdb
  2025-03-09 15:16 ` Sergey Zigachev via Gdb
  2025-03-10 16:22 ` Simon Marchi via Gdb
  0 siblings, 2 replies; 6+ messages in thread
From: Sergey Zigachev via Gdb @ 2025-03-05 10:22 UTC (permalink / raw)
  To: gdb

Hi,
I'm trying to enable debugging of multi-threaded programs on 
DragonFlyBSD using gdb and have several questions.

I checked out gdb 15.2 sources, applied out-of-tree patches with 
native-dependent code implementation for amd64 DragonFlyBSD target. 
These patches have basic support for enumerating registers, accessing 
memory etc. All the OS-specific things. Single-thread debugging works 
well with these patches. However, multiprocess debugging, async and 
non-stop modes are not implemented. As is any notion of multiple threads 
in the inferior. My goal is to add missing bits and pieces both in 
userspace (gdb's nat-dep) and kernelspace (mostly ptrace syscall).

I'm at the stage where I have the basics working. I can report events 
from different threads, suspend/resume single or all threads, inspect 
TLS variables etc. Schedule-locking mode also supported.

My questions concern mostly step-over ("step") command issued to 
multiple threads, not at once of course. Although I see approx. the same 
behavior with the "next" command.
I wrote a little test C program that has main thread spawning two extra 
threads running two separate but mostly identical functions. Each 
function prints message to output, sleeps for several seconds, prints 
another message, and then returns. Main function waits for both threads 
to complete.

First test run.
I put a breakpoint at the start of the first function, run the program 
and input "step" command until process exits. Essentially I'm debbuging 
as-if it was a single-thread program. Everything works as expected. By 
that i mean after each "step" command gdb steps on the next source line 
until the end of the function. It skips instructions that are part of a 
single source line, and skips functions that don't have debugging 
symbols. This is the baseline I'm trying to replicate with stepping over 
multiple threads.

Second test run.
Same as the first run but this time I put a breakpoint at the start of 
both functions. This results in gdb stopping at different threads when I 
enter "step" command.
I observe the following issues:
   - each source line gets hit several times, I think by the number of 
asm instructions it implements
   - gdb stops at functions without debug symbols; sometimes it can step 
over and continue; sometimes it doesn't if it can't find the end of the 
function
Basically, gdb's acting as if it's running "stepi" command.

With trial and error and some printf sprinkled around I came up with a 
theory why that could happen. Consider this part of the function:

 > 23 printf("Running thread thr=%d sleep=%d!\n", d->thr_num, d->sleep);
 >   0x0000000000400b2d <+29>:    mov    0xc(%rdi),%edx
 >   0x0000000000400b30 <+32>:    mov    $0x400cff,%edi
 >   0x0000000000400b35 <+37>:    xor    %eax,%eax
 >   0x0000000000400b37 <+39>:    call   0x4007f0 <printf@plt>
 >
 > 24 sleep(d->sleep);
 >   0x0000000000400b3c <+44>:    mov    0xc(%rbx),%edi

When gdb stops at line 23 and I enter "step" command, it calculates step 
range thread_info->control.step_range_{start,end}. In this case it will 
be [0x400b2d-0x400b3c). Then it uses this info to step through asm 
instructions 4 times transparently to the user.
The happy call chain looks like this (infrun.c):
   - clear_proceed_status_thread *for each thread*
   - prepare_one_step (infcmd.c) -- this one updates the range
   - proceed
   - 4x fetch_inferior_event
   - stop at next line waiting for input
This works for single-thread case because gdb clears thread_info state 
and then loads range for the *current* thread after clearing.
If I return event for a different thread, then its info was cleared and 
haven't been loaded by gdb before that. Gdb can't recognize this signal 
and assumes it's a "random signal". It goes downhill from there, I think 
most consecutive signals become "random".

I don't want to track down heisenbugs caused by my dirty hands touching 
gdb thread state and accidentally braking invariants. Hence this 
message, any advice is appreciated.

My questions:
   1. Does my theory make sense?
   2. Is it a job of native-dependent code to save/restore thread state 
before reporting a signal?
   3. If yes, what else should I store, per-thread, per-process etc?

Thanks,
Sergey

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: Multi-threading support on DragonFlyBSD
  2025-03-05 10:22 Multi-threading support on DragonFlyBSD Sergey Zigachev via Gdb
@ 2025-03-09 15:16 ` Sergey Zigachev via Gdb
  2025-03-10 16:22 ` Simon Marchi via Gdb
  1 sibling, 0 replies; 6+ messages in thread
From: Sergey Zigachev via Gdb @ 2025-03-09 15:16 UTC (permalink / raw)
  To: gdb

Ok, skimmed through linux target I saw this:

   /* In all-stop, give preference to the LWP that is being
      single-stepped.  There will be at most one, and it will be the
      LWP that the core is most interested in.  If we didn't do this,
      then we'd have to handle pending step SIGTRAPs somehow in case
      the core later continues the previously-stepped thread, as
      otherwise we'd report the pending SIGTRAP then, and the core, not
      having stepped the thread, wouldn't understand what the trap was
      for, and therefore would report it to the user as a random
      signal.  */

I guess this answers my question.


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: Multi-threading support on DragonFlyBSD
  2025-03-05 10:22 Multi-threading support on DragonFlyBSD Sergey Zigachev via Gdb
  2025-03-09 15:16 ` Sergey Zigachev via Gdb
@ 2025-03-10 16:22 ` Simon Marchi via Gdb
  2025-03-11  7:59   ` Sergey Zigachev via Gdb
  1 sibling, 1 reply; 6+ messages in thread
From: Simon Marchi via Gdb @ 2025-03-10 16:22 UTC (permalink / raw)
  To: Sergey Zigachev, gdb

On 3/5/25 5:22 AM, Sergey Zigachev via Gdb wrote:
> Hi,
> I'm trying to enable debugging of multi-threaded programs on DragonFlyBSD using gdb and have several questions.
> 
> I checked out gdb 15.2 sources, applied out-of-tree patches with native-dependent code implementation for amd64 DragonFlyBSD target. These patches have basic support for enumerating registers, accessing memory etc. All the OS-specific things. Single-thread debugging works well with these patches. However, multiprocess debugging, async and non-stop modes are not implemented. As is any notion of multiple threads in the inferior. My goal is to add missing bits and pieces both in userspace (gdb's nat-dep) and kernelspace (mostly ptrace syscall).
> 
> I'm at the stage where I have the basics working. I can report events from different threads, suspend/resume single or all threads, inspect TLS variables etc. Schedule-locking mode also supported.
> 
> My questions concern mostly step-over ("step") command issued to multiple threads, not at once of course. Although I see approx. the same behavior with the "next" command.
> I wrote a little test C program that has main thread spawning two extra threads running two separate but mostly identical functions. Each function prints message to output, sleeps for several seconds, prints another message, and then returns. Main function waits for both threads to complete.
> 
> First test run.
> I put a breakpoint at the start of the first function, run the program and input "step" command until process exits. Essentially I'm debbuging as-if it was a single-thread program. Everything works as expected. By that i mean after each "step" command gdb steps on the next source line until the end of the function. It skips instructions that are part of a single source line, and skips functions that don't have debugging symbols. This is the baseline I'm trying to replicate with stepping over multiple threads.
> 
> Second test run.
> Same as the first run but this time I put a breakpoint at the start of both functions. This results in gdb stopping at different threads when I enter "step" command.
> I observe the following issues:
>   - each source line gets hit several times, I think by the number of asm instructions it implements
>   - gdb stops at functions without debug symbols; sometimes it can step over and continue; sometimes it doesn't if it can't find the end of the function
> Basically, gdb's acting as if it's running "stepi" command.
> 
> With trial and error and some printf sprinkled around I came up with a theory why that could happen. Consider this part of the function:
> 
>> 23 printf("Running thread thr=%d sleep=%d!\n", d->thr_num, d->sleep);
>>   0x0000000000400b2d <+29>:    mov    0xc(%rdi),%edx
>>   0x0000000000400b30 <+32>:    mov    $0x400cff,%edi
>>   0x0000000000400b35 <+37>:    xor    %eax,%eax
>>   0x0000000000400b37 <+39>:    call   0x4007f0 <printf@plt>
>>
>> 24 sleep(d->sleep);
>>   0x0000000000400b3c <+44>:    mov    0xc(%rbx),%edi
> 
> When gdb stops at line 23 and I enter "step" command, it calculates step range thread_info->control.step_range_{start,end}. In this case it will be [0x400b2d-0x400b3c). Then it uses this info to step through asm instructions 4 times transparently to the user.
> The happy call chain looks like this (infrun.c):
>   - clear_proceed_status_thread *for each thread*
>   - prepare_one_step (infcmd.c) -- this one updates the range
>   - proceed
>   - 4x fetch_inferior_event
>   - stop at next line waiting for input
> This works for single-thread case because gdb clears thread_info state and then loads range for the *current* thread after clearing.
> If I return event for a different thread, then its info was cleared and haven't been loaded by gdb before that. Gdb can't recognize this signal and assumes it's a "random signal". It goes downhill from there, I think most consecutive signals become "random".
> 
> I don't want to track down heisenbugs caused by my dirty hands touching gdb thread state and accidentally braking invariants. Hence this message, any advice is appreciated.
> 
> My questions:
>   1. Does my theory make sense?
>   2. Is it a job of native-dependent code to save/restore thread state before reporting a signal?
>   3. If yes, what else should I store, per-thread, per-process etc?
> 
> Thanks,
> Sergey

Hi Sergey,

What I don't understand here is: what other event do you report for the
other threads?  When you step one thread, I assume that your resume
method gets called with inferior_ptid (the global variable) equal to the
thread being stepped, and scope_ptid equal to (pid, 0, 0)?  If so, the
threads other than the one being stepped should be resumed freely.  In
your scenario, I don't see what signal these other threads would
receive.

Once your stepping thread is done stepping, it should generate a
SIGTRAP, after which your target should send some signal (SIGSTOP?) to
ask the other threads to stop and wait for these threads to report
"stopped by SIGSTOP" (since your target operates in non-stop mode).  But
these SIGSTOPs should not be reported to the core, as it's an
implementation detail of the target.  That's my understanding of how
things work on Linux, perhaps things are different on DragonflyBSD.

I assume that you have enabled and started at the "set debug infrun"
logs?  This is how you generally debug those issues, look at what infrun
asks your target to do, look at what your target answers, try to see if
that makes sense.

Finally, it's perhaps outside of the scope of your work, but "modern"
targets use non-stop (related to "maint set target-non-stop", not the
user-visible "set non-stop") and target-async.  I'm more used to reason
in terms of non-stop & target-async, and that code path of infrun is
likely more tested.  It's probably no small task, but switching to that
would be more future-proof.

Simon

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: Multi-threading support on DragonFlyBSD
  2025-03-10 16:22 ` Simon Marchi via Gdb
@ 2025-03-11  7:59   ` Sergey Zigachev via Gdb
  2025-03-11 14:37     ` Simon Marchi via Gdb
  0 siblings, 1 reply; 6+ messages in thread
From: Sergey Zigachev via Gdb @ 2025-03-11  7:59 UTC (permalink / raw)
  To: Simon Marchi, gdb

[-- Attachment #1: Type: text/plain, Size: 4616 bytes --]

> What I don't understand here is: what other event do you report for the
> other threads?  When you step one thread, I assume that your resume
> method gets called with inferior_ptid (the global variable) equal to the
> thread being stepped, and scope_ptid equal to (pid, 0, 0)?  If so, the
> threads other than the one being stepped should be resumed freely.  In
> your scenario, I don't see what signal these other threads would
> receive.
> 
> Once your stepping thread is done stepping, it should generate a
> SIGTRAP, after which your target should send some signal (SIGSTOP?) to
> ask the other threads to stop and wait for these threads to report
> "stopped by SIGSTOP" (since your target operates in non-stop mode).  But
> these SIGSTOPs should not be reported to the core, as it's an
> implementation detail of the target.  That's my understanding of how
> things work on Linux, perhaps things are different on DragonflyBSD.
> 

That's overall correct. Other threads can receive SIGTRAP if I put a 
breakpoint, for example. I thought that should work, isn't it? If GDB 
resumes all threads that means any number of threads can stop at a 
breakpoint or SIGTRAP caused by single-stepping a process.

Here's what I think is happening:
   1. put a breakpoint_1 at function_1 for thread_1
   2. put a breakpoint_2 at function_2 for thread_2
   3. -> "run" the process, main thread starts threads approx. at once
   4. breakpoint_1 hit, current thread is thread_1
   5. -> enter "step" command
   6. GDB resumes thread_1, then all threads, every time with step=1
   7. breakpoint_2 hit, GDB switches to thread_2
   8. -> enter "step" command
   9. GDB resumes thread_2, then all threads, every time with step=1
   10. thread_1 generates SIGTRAP
   11. GDB can't tell this SIGTRAP is the result of (5), so can't skip it
   12. GDB reports it as a random signal

If GDB receives events for the current thread at steps (6) and (9), then 
it processes them just fine, skipping as necessary.

WRT implementation details: I don't send SIGSTOP to stop all threads 
when SIGTRAP generated and I don't use waitpid. Natdep target 
communicates with the kernel through ptrace syscall only. Ptrace request 
waits for a event, stops all threads and waits for them to actually stop 
before returning to userspace. Then in the natdep code I pull all 
pending events from all threads, stash them in a list, and return one 
event according to what GDB expects to get: if it resumed just a single 
thread, then I return an event strictly for that thread (if any); if it 
resumed all threads, then I return the next event from the list, which 
may or may not be for the current thread. In my test program, I only get 
SIGTRAPs triggered by single-stepping threads, no other signals generated.
Please see attached debug log output. It's rather verbose though.

Couple notes:
   - for DragonFly, I'm using ptid_t format as (pid, thread_id, 0), 
thread_id is unique for a process, but is NOT globally unique
   - you can ignore lines with [dfly-nat], that's my implementation's 
debug info, it mostly duplicates what infrun writes anyway

I'm sorry, my explanations are hard to understand, hopefully it'll be 
clearer with the log.

Ideally, I would like to get GDB to transparently skip assembly 
instructions which are not relevant to report regardless which thread is 
reporting an event, be it currently selected or not. Like part of the 
already reported source line, dynsym code, missing debug symbols or 
anything else. For that to work I think I need to save and restore GDB's 
thread "context" (in the thread_info struct) before reporting an event. 
If it's not possible or doesn't make sense, then I'll to do what Linux 
is doing -- give currently selected thread higher priority and report it 
first.

> I assume that you have enabled and started at the "set debug infrun"
> logs?  This is how you generally debug those issues, look at what infrun
> asks your target to do, look at what your target answers, try to see if
> that makes sense.

Yes, I enabled infrun debug output and it was invaluable for gathering 
info.

> Finally, it's perhaps outside of the scope of your work, but "modern"
> targets use non-stop (related to "maint set target-non-stop", not the
> user-visible "set non-stop") and target-async.  I'm more used to reason
> in terms of non-stop & target-async, and that code path of infrun is
> likely more tested.  It's probably no small task, but switching to that
> would be more future-proof.

I'll take a look at non-stop and async modes.
Also, I wasn't aware of maintenance command, thanks!

-Sergey

[-- Attachment #2: gdb_output.txt --]
[-- Type: text/plain, Size: 46818 bytes --]

[infrun] scoped_disable_commit_resumed: reason=running
Starting program: /root/gdb-tests/tls_threads-clang
[dfly-nat] wait: [in] ptid = -1.0.0
[dfly-nat] wait: resumed_lwps -1.0.0
[dfly-nat] wait: PT_WAIT err 0 errno 22
[dfly-nat] wait: err 0 errno 22 event status 2 lwpid 1 signal 5
[dfly-nat] wait: store lwp=1 signal=5
[dfly-nat] wait: err 0 errno 22 event status 0 lwpid 1 signal 5
[dfly-nat] wait: no event, exiting
[dfly-nat] wait: return lwp=1 signal=5
[dfly-nat] resume: ptid=-1.0.0 step=0 signal=0
[dfly-nat] resume: ptid=7456.1.0 resuming (all)
[dfly-nat] resume: ptid=7456.1.0 unstopped
[dfly-nat] wait: [in] ptid = -1.0.0
[dfly-nat] wait: resumed_lwps -1.0.0
[dfly-nat] wait: PT_WAIT err 0 errno 0
[dfly-nat] wait: err 0 errno 0 event status 2 lwpid 1 signal 5
[dfly-nat] wait: store lwp=1 signal=5
[dfly-nat] wait: err 0 errno 0 event status 0 lwpid 1 signal 5
[dfly-nat] wait: no event, exiting
[dfly-nat] wait: return lwp=1 signal=5
[infrun] infrun_debug_show_threads: enter
  [infrun] infrun_debug_show_threads: immediately after create_process:
  [infrun] infrun_debug_show_threads:   thread 7456.1.0, executing = 0, resumed = 0, state = STOPPED
[infrun] infrun_debug_show_threads: exit
[infrun] infrun_debug_show_threads: enter
  [infrun] infrun_debug_show_threads: threads in the newly created inferior:
  [infrun] infrun_debug_show_threads:   thread 7456.1.0, executing = 0, resumed = 0, state = STOPPED
[infrun] infrun_debug_show_threads: exit
[infrun] proceed: enter
  [infrun] follow_fork: enter
  [infrun] follow_fork: exit
  [infrun] proceed: cur_thr = 7456.1.0
  [infrun] proceed: addr=0x800403820, signal=GDB_SIGNAL_0, resume_ptid=-1.0.0
  [infrun] scoped_disable_commit_resumed: reason=proceeding
  [infrun] start_step_over: enter
    [infrun] start_step_over: stealing global queue of threads to step, length = 0
    [infrun] operator(): step-over queue now empty
  [infrun] start_step_over: exit
  [infrun] proceed_resume_thread_checked: resuming 7456.1.0
  [infrun] resume_1: step=0, signal=GDB_SIGNAL_0, trap_expected=0, current thread [7456.1.0] at 0x800403820
  [infrun] do_target_resume: resume_ptid=-1.0.0, step=0, sig=GDB_SIGNAL_0
  [dfly-nat] resume: ptid=-1.0.0 step=0 signal=0
  [dfly-nat] resume: ptid=7456.1.0 resuming (all)
  [dfly-nat] resume: ptid=7456.1.0 unstopped
  [infrun] prepare_to_wait: prepare_to_wait
  [infrun] reset: reason=proceeding
[infrun] proceed: exit
[infrun] reset: reason=running
[infrun] maybe_set_commit_resumed_all_targets: enabling commit-resumed for target native
[infrun] maybe_call_commit_resumed_all_targets: calling commit_resumed for target native
[infrun] fetch_inferior_event: enter
  [infrun] scoped_disable_commit_resumed: reason=handling event
  [infrun] random_pending_event_thread: None found.
  [dfly-nat] wait: [in] ptid = -1.0.0
  [dfly-nat] wait: resumed_lwps -1.0.0
  [dfly-nat] wait: PT_WAIT err 0 errno 35
  [dfly-nat] wait: err 0 errno 35 event status 2 lwpid 1 signal 5
  [dfly-nat] wait: store lwp=1 signal=5
  [dfly-nat] wait: err 0 errno 35 event status 0 lwpid 1 signal 5
  [dfly-nat] wait: no event, exiting
  [dfly-nat] wait: return lwp=1 signal=5
  [infrun] print_target_wait_results: target_wait (-1.0.0 [LWP 1 of process -1], status) =
  [infrun] print_target_wait_results:   7456.1.0 [LWP 1 of process 7456],
  [infrun] print_target_wait_results:   status->kind = STOPPED, sig = GDB_SIGNAL_TRAP
  [infrun] handle_inferior_event: status->kind = STOPPED, sig = GDB_SIGNAL_TRAP
  [infrun] context_switch: Switching context from 0.0.0 to 7456.1.0
  [infrun] handle_signal_stop: stop_pc=0x8004042fb
  [infrun] process_event_stop_test: BPSTAT_WHAT_SINGLE
  [infrun] switch_back_to_stepped_thread: thread [7456.1.0] still needs step-over
  [infrun] should_be_inserted: skipping breakpoint: stepping past insn at: 0x8004042fb
  [infrun] should_be_inserted: skipping breakpoint: stepping past insn at: 0x8004042fb
  [infrun] should_be_inserted: skipping breakpoint: stepping past insn at: 0x8004042fb
  [infrun] resume_1: step=1, signal=GDB_SIGNAL_0, trap_expected=1, current thread [7456.1.0] at 0x8004042fb
  [infrun] do_target_resume: resume_ptid=7456.1.0, step=1, sig=GDB_SIGNAL_0
  [dfly-nat] resume: ptid=7456.1.0 step=1 signal=0
  [dfly-nat] resume: ptid=7456.1.0 resuming (single)
  [dfly-nat] resume: ptid=7456.1.0 unstopped
  [infrun] prepare_to_wait: prepare_to_wait
  [infrun] reset: reason=handling event
  [infrun] maybe_set_commit_resumed_all_targets: enabling commit-resumed for target native
  [infrun] maybe_call_commit_resumed_all_targets: calling commit_resumed for target native
[infrun] fetch_inferior_event: exit
[infrun] fetch_inferior_event: enter
  [infrun] scoped_disable_commit_resumed: reason=handling event
  [infrun] random_pending_event_thread: None found.
  [dfly-nat] wait: [in] ptid = -1.0.0
  [dfly-nat] wait: resumed_lwps 7456.1.0
  [dfly-nat] wait: PT_WAIT err 0 errno 35
  [dfly-nat] wait: err 0 errno 35 event status 2 lwpid 1 signal 5
  [dfly-nat] wait: store lwp=1 signal=5
  [dfly-nat] wait: err 0 errno 35 event status 0 lwpid 1 signal 5
  [dfly-nat] wait: no event, exiting
  [dfly-nat] wait: return lwp=1 signal=5
  [infrun] print_target_wait_results: target_wait (-1.0.0 [LWP 1 of process -1], status) =
  [infrun] print_target_wait_results:   7456.1.0 [LWP 1 of process 7456],
  [infrun] print_target_wait_results:   status->kind = STOPPED, sig = GDB_SIGNAL_TRAP
  [infrun] handle_inferior_event: status->kind = STOPPED, sig = GDB_SIGNAL_TRAP
  [infrun] clear_step_over_info: clearing step over info
  [infrun] context_switch: Switching context from 0.0.0 to 7456.1.0
  [infrun] handle_signal_stop: stop_pc=0x80040b105
  [infrun] start_step_over: enter
    [infrun] start_step_over: stealing global queue of threads to step, length = 0
    [infrun] operator(): step-over queue now empty
  [infrun] start_step_over: exit
  [infrun] process_event_stop_test: no stepping, continue
  [infrun] resume_1: step=0, signal=GDB_SIGNAL_0, trap_expected=0, current thread [7456.1.0] at 0x80040b105
  [infrun] do_target_resume: resume_ptid=-1.0.0, step=0, sig=GDB_SIGNAL_0
  [dfly-nat] resume: ptid=-1.0.0 step=0 signal=0
  [dfly-nat] resume: ptid=7456.1.0 resuming (all)
  [dfly-nat] resume: ptid=7456.1.0 unstopped
  [infrun] prepare_to_wait: prepare_to_wait
  [infrun] reset: reason=handling event
  [infrun] maybe_set_commit_resumed_all_targets: enabling commit-resumed for target native
  [infrun] maybe_call_commit_resumed_all_targets: calling commit_resumed for target native
[infrun] fetch_inferior_event: exit
[infrun] fetch_inferior_event: enter
  [infrun] scoped_disable_commit_resumed: reason=handling event
  [infrun] random_pending_event_thread: None found.
  [dfly-nat] wait: [in] ptid = -1.0.0
  [dfly-nat] wait: resumed_lwps -1.0.0
  [dfly-nat] wait: PT_WAIT err 0 errno 35
  [dfly-nat] wait: err 0 errno 35 event status 4 lwpid 2 signal 0
  [dfly-nat] wait: add new thread 2
[New LWP 2 of process 7456]
  [dfly-nat] wait: err 0 errno 35 event status 0 lwpid 2 signal 0
  [dfly-nat] wait: no event, exiting
  [dfly-nat] wait: reached EOF!
  [infrun] print_target_wait_results: target_wait (-1.0.0 [LWP 1 of process -1], status) =
  [infrun] print_target_wait_results:   7456.2.0 [LWP 2 of process 7456],
  [infrun] print_target_wait_results:   status->kind = SPURIOUS
  [infrun] handle_inferior_event: status->kind = SPURIOUS
  [infrun] context_switch: Switching context from 0.0.0 to 7456.2.0
  [infrun] resume_1: step=0, signal=GDB_SIGNAL_0, trap_expected=0, current thread [7456.2.0] at 0x8006de96e
  [infrun] do_target_resume: resume_ptid=-1.0.0, step=0, sig=GDB_SIGNAL_0
  [dfly-nat] resume: ptid=-1.0.0 step=0 signal=0
  [dfly-nat] resume: ptid=7456.1.0 resuming (all)
  [dfly-nat] resume: ptid=7456.2.0 resuming (all)
  [dfly-nat] resume: ptid=7456.2.0 unstopped
  [infrun] prepare_to_wait: prepare_to_wait
  [infrun] reset: reason=handling event
  [infrun] maybe_set_commit_resumed_all_targets: enabling commit-resumed for target native
  [infrun] maybe_call_commit_resumed_all_targets: calling commit_resumed for target native
[infrun] fetch_inferior_event: exit
[infrun] fetch_inferior_event: enter
  [infrun] scoped_disable_commit_resumed: reason=handling event
  [infrun] random_pending_event_thread: None found.
  [dfly-nat] wait: [in] ptid = -1.0.0
  [dfly-nat] wait: resumed_lwps -1.0.0
  [dfly-nat] wait: PT_WAIT err 0 errno 35
  [dfly-nat] wait: err 0 errno 35 event status 4 lwpid 3 signal 0
  [dfly-nat] wait: add new thread 3
[New LWP 3 of process 7456]
  [dfly-nat] wait: err 0 errno 35 event status 0 lwpid 3 signal 0
  [dfly-nat] wait: no event, exiting
  [dfly-nat] wait: reached EOF!
  [infrun] print_target_wait_results: target_wait (-1.0.0 [LWP 1 of process -1], status) =
  [infrun] print_target_wait_results:   7456.3.0 [LWP 3 of process 7456],
  [infrun] print_target_wait_results:   status->kind = SPURIOUS
  [infrun] handle_inferior_event: status->kind = SPURIOUS
  [infrun] context_switch: Switching context from 0.0.0 to 7456.3.0
  [infrun] resume_1: step=0, signal=GDB_SIGNAL_0, trap_expected=0, current thread [7456.3.0] at 0x8006de96e
  [infrun] do_target_resume: resume_ptid=-1.0.0, step=0, sig=GDB_SIGNAL_0
  [dfly-nat] resume: ptid=-1.0.0 step=0 signal=0
  [dfly-nat] resume: ptid=7456.1.0 resuming (all)
  [dfly-nat] resume: ptid=7456.2.0 resuming (all)
  [dfly-nat] resume: ptid=7456.3.0 resuming (all)
  [dfly-nat] resume: ptid=7456.3.0 unstopped
  [infrun] prepare_to_wait: prepare_to_wait
  [infrun] reset: reason=handling event
  [infrun] maybe_set_commit_resumed_all_targets: enabling commit-resumed for target native
  [infrun] maybe_call_commit_resumed_all_targets: calling commit_resumed for target native
[infrun] fetch_inferior_event: exit
[infrun] fetch_inferior_event: enter
  [infrun] scoped_disable_commit_resumed: reason=handling event
  [infrun] random_pending_event_thread: None found.
  [dfly-nat] wait: [in] ptid = -1.0.0
  [dfly-nat] wait: resumed_lwps -1.0.0
  [dfly-nat] wait: PT_WAIT err 0 errno 35
  [dfly-nat] wait: err 0 errno 35 event status 2 lwpid 2 signal 5
  [dfly-nat] wait: store lwp=2 signal=5
  [dfly-nat] wait: err 0 errno 35 event status 0 lwpid 2 signal 5
  [dfly-nat] wait: no event, exiting
  [dfly-nat] wait: return lwp=2 signal=5
  [infrun] print_target_wait_results: target_wait (-1.0.0 [LWP 1 of process -1], status) =
  [infrun] print_target_wait_results:   7456.2.0 [LWP 2 of process 7456],
  [infrun] print_target_wait_results:   status->kind = STOPPED, sig = GDB_SIGNAL_TRAP
  [infrun] handle_inferior_event: status->kind = STOPPED, sig = GDB_SIGNAL_TRAP
  [infrun] context_switch: Switching context from 0.0.0 to 7456.2.0
  [infrun] handle_signal_stop: stop_pc=0x400b1a
  [infrun] bpstat_check_breakpoint_conditions: enter
    [infrun] bpstat_check_breakpoint_conditions: thread = 7456.2.0, breakpoint 1.1
    [infrun] bpstat_check_breakpoint_conditions: stopping at this breakpoint
  [infrun] bpstat_check_breakpoint_conditions: exit
  [infrun] process_event_stop_test: BPSTAT_WHAT_STOP_NOISY
  [infrun] stop_waiting: stop_waiting
[Switching to LWP 2 of process 7456]

Thread 2 hit Breakpoint 1, thread_runner_1 (arg=0x7fffffdfd6e0) at ./tls-threads.c:22
22              counter = data->thr_num;
  [infrun] reset: reason=handling event
  [infrun] maybe_set_commit_resumed_all_targets: not requesting commit-resumed for target native, no resumed threads
[infrun] fetch_inferior_event: exit



=== ENTER "step" command

[infrun] clear_proceed_status_thread: 7456.1.0
[infrun] clear_proceed_status_thread: 7456.2.0
[infrun] clear_proceed_status_thread: 7456.3.0
[infrun] set_step_info: symtab = ./tls-threads.c, line = 22, step_frame_id = {stack=0x7fdfe0000fd0,code=0x0000000000400b10,!special}, step_stack_frame_id = {stack=0x7fdfe0000fd0,code=0x0000000000400b10,!special}
[infrun] proceed: enter
  [infrun] follow_fork: enter
  [infrun] follow_fork: exit
  [infrun] proceed: cur_thr = 7456.2.0
  [infrun] proceed: addr=0xffffffffffffffff, signal=GDB_SIGNAL_DEFAULT, resume_ptid=-1.0.0
  [infrun] global_thread_step_over_chain_enqueue: enqueueing thread 7456.2.0 in global step over chain
  [infrun] scoped_disable_commit_resumed: reason=proceeding
  [infrun] start_step_over: enter
    [infrun] start_step_over: stealing global queue of threads to step, length = 1
    [infrun] start_step_over: resuming [7456.2.0] for step-over
    [infrun] should_be_inserted: skipping breakpoint: stepping past insn at: 0x400b1a
    [infrun] should_be_inserted: skipping breakpoint: stepping past insn at: 0x400b1a
    [infrun] should_be_inserted: skipping breakpoint: stepping past insn at: 0x400b1a
    [infrun] resume_1: step=1, signal=GDB_SIGNAL_0, trap_expected=1, current thread [7456.2.0] at 0x400b1a
    [infrun] do_target_resume: resume_ptid=7456.2.0, step=1, sig=GDB_SIGNAL_0
    [dfly-nat] resume: ptid=7456.2.0 step=1 signal=0
    [dfly-nat] resume: ptid=7456.1.0 suspending
    [dfly-nat] resume: ptid=7456.2.0 resuming (single)
    [dfly-nat] resume: ptid=7456.3.0 suspending
    [dfly-nat] resume: ptid=7456.2.0 unstopped
    [infrun] prepare_to_wait: prepare_to_wait
    [infrun] start_step_over: [7456.2.0] was resumed.
    [infrun] operator(): step-over queue now empty
  [infrun] start_step_over: exit
  [infrun] reset: reason=proceeding
  [infrun] maybe_set_commit_resumed_all_targets: enabling commit-resumed for target native
  [infrun] maybe_call_commit_resumed_all_targets: calling commit_resumed for target native
[infrun] proceed: exit
[infrun] fetch_inferior_event: enter
  [infrun] scoped_disable_commit_resumed: reason=handling event
  [infrun] random_pending_event_thread: None found.
  [dfly-nat] wait: [in] ptid = -1.0.0
  [dfly-nat] wait: resumed_lwps 7456.2.0
  [dfly-nat] wait: PT_WAIT err 0 errno 35
  [dfly-nat] wait: err 0 errno 35 event status 2 lwpid 2 signal 5
  [dfly-nat] wait: store lwp=2 signal=5
  [dfly-nat] wait: err 0 errno 35 event status 0 lwpid 2 signal 5
  [dfly-nat] wait: no event, exiting
  [dfly-nat] wait: return lwp=2 signal=5
  [infrun] print_target_wait_results: target_wait (-1.0.0 [LWP 1 of process -1], status) =
  [infrun] print_target_wait_results:   7456.2.0 [LWP 2 of process 7456],
  [infrun] print_target_wait_results:   status->kind = STOPPED, sig = GDB_SIGNAL_TRAP
  [infrun] handle_inferior_event: status->kind = STOPPED, sig = GDB_SIGNAL_TRAP
  [infrun] clear_step_over_info: clearing step over info
  [infrun] context_switch: Switching context from 0.0.0 to 7456.2.0
  [infrun] handle_signal_stop: stop_pc=0x400b1d
  [infrun] process_event_stop_test: stepping inside range [0x400b1a-0x400b2d]
  [infrun] resume_1: step=1, signal=GDB_SIGNAL_0, trap_expected=0, current thread [7456.2.0] at 0x400b1d
  [infrun] do_target_resume: resume_ptid=-1.0.0, step=1, sig=GDB_SIGNAL_0
  [dfly-nat] resume: ptid=-1.0.0 step=1 signal=0
  [dfly-nat] resume: ptid=7456.1.0 resuming (all)
  [dfly-nat] resume: ptid=7456.2.0 resuming (all)
  [dfly-nat] resume: ptid=7456.3.0 resuming (all)
  [dfly-nat] resume: ptid=7456.2.0 unstopped
  [infrun] prepare_to_wait: prepare_to_wait
  [infrun] reset: reason=handling event
  [infrun] maybe_set_commit_resumed_all_targets: enabling commit-resumed for target native
  [infrun] maybe_call_commit_resumed_all_targets: calling commit_resumed for target native
[infrun] fetch_inferior_event: exit
[infrun] fetch_inferior_event: enter
  [infrun] scoped_disable_commit_resumed: reason=handling event
  [infrun] random_pending_event_thread: None found.
  [dfly-nat] wait: [in] ptid = -1.0.0
  [dfly-nat] wait: resumed_lwps -1.0.0
  [dfly-nat] wait: PT_WAIT err 0 errno 35
  [dfly-nat] wait: err 0 errno 35 event status 2 lwpid 2 signal 5
  [dfly-nat] wait: store lwp=2 signal=5
  [dfly-nat] wait: err 0 errno 35 event status 0 lwpid 2 signal 5
  [dfly-nat] wait: no event, exiting
  [dfly-nat] wait: return lwp=2 signal=5
  [infrun] print_target_wait_results: target_wait (-1.0.0 [LWP 1 of process -1], status) =
  [infrun] print_target_wait_results:   7456.2.0 [LWP 2 of process 7456],
  [infrun] print_target_wait_results:   status->kind = STOPPED, sig = GDB_SIGNAL_TRAP
  [infrun] handle_inferior_event: status->kind = STOPPED, sig = GDB_SIGNAL_TRAP
  [infrun] context_switch: Switching context from 0.0.0 to 7456.2.0
  [infrun] handle_signal_stop: stop_pc=0x400b26
  [infrun] process_event_stop_test: stepping inside range [0x400b1a-0x400b2d]
  [infrun] resume_1: step=1, signal=GDB_SIGNAL_0, trap_expected=0, current thread [7456.2.0] at 0x400b26
  [infrun] do_target_resume: resume_ptid=-1.0.0, step=1, sig=GDB_SIGNAL_0
  [dfly-nat] resume: ptid=-1.0.0 step=1 signal=0
  [dfly-nat] resume: ptid=7456.1.0 resuming (all)
  [dfly-nat] resume: ptid=7456.2.0 resuming (all)
  [dfly-nat] resume: ptid=7456.3.0 resuming (all)
  [dfly-nat] resume: ptid=7456.2.0 unstopped
  [infrun] prepare_to_wait: prepare_to_wait
  [infrun] reset: reason=handling event
  [infrun] maybe_set_commit_resumed_all_targets: enabling commit-resumed for target native
  [infrun] maybe_call_commit_resumed_all_targets: calling commit_resumed for target native
[infrun] fetch_inferior_event: exit
[infrun] fetch_inferior_event: enter
  [infrun] scoped_disable_commit_resumed: reason=handling event
  [infrun] random_pending_event_thread: None found.
  [dfly-nat] wait: [in] ptid = -1.0.0
  [dfly-nat] wait: resumed_lwps -1.0.0
  [dfly-nat] wait: PT_WAIT err 0 errno 35
  [dfly-nat] wait: err 0 errno 35 event status 2 lwpid 3 signal 5
  [dfly-nat] wait: store lwp=3 signal=5
  [dfly-nat] wait: err 0 errno 35 event status 0 lwpid 3 signal 5
  [dfly-nat] wait: no event, exiting
  [dfly-nat] wait: return lwp=3 signal=5
  [infrun] print_target_wait_results: target_wait (-1.0.0 [LWP 1 of process -1], status) =
  [infrun] print_target_wait_results:   7456.3.0 [LWP 3 of process 7456],
  [infrun] print_target_wait_results:   status->kind = STOPPED, sig = GDB_SIGNAL_TRAP
  [infrun] handle_inferior_event: status->kind = STOPPED, sig = GDB_SIGNAL_TRAP
  [infrun] context_switch: Switching context from 0.0.0 to 7456.3.0
  [infrun] handle_signal_stop: stop_pc=0x400b7a
  [infrun] bpstat_check_breakpoint_conditions: enter
    [infrun] bpstat_check_breakpoint_conditions: thread = 7456.3.0, breakpoint 2.1
    [infrun] bpstat_check_breakpoint_conditions: stopping at this breakpoint
  [infrun] bpstat_check_breakpoint_conditions: exit
  [infrun] process_event_stop_test: BPSTAT_WHAT_STOP_NOISY
  [infrun] stop_waiting: stop_waiting
[Switching to LWP 3 of process 7456]

Thread 3 hit Breakpoint 2, thread_runner_2 (arg=0x7fffffdfd6f0) at ./tls-threads.c:37
37              counter = data->thr_num;
  [infrun] reset: reason=handling event
  [infrun] maybe_set_commit_resumed_all_targets: not requesting commit-resumed for target native, no resumed threads
[infrun] fetch_inferior_event: exit



=== ENTER "step" command

[infrun] clear_proceed_status_thread: 7456.1.0
[infrun] clear_proceed_status_thread: 7456.2.0
[infrun] clear_proceed_status_thread: 7456.3.0
[infrun] set_step_info: symtab = ./tls-threads.c, line = 37, step_frame_id = {stack=0x7fdfe0201fd0,code=0x0000000000400b70,!special}, step_stack_frame_id = {stack=0x7fdfe0201fd0,code=0x0000000000400b70,!special}
[infrun] proceed: enter
  [infrun] follow_fork: enter
  [infrun] follow_fork: exit
  [infrun] proceed: cur_thr = 7456.3.0
  [infrun] proceed: addr=0xffffffffffffffff, signal=GDB_SIGNAL_DEFAULT, resume_ptid=-1.0.0
  [infrun] global_thread_step_over_chain_enqueue: enqueueing thread 7456.3.0 in global step over chain
  [infrun] scoped_disable_commit_resumed: reason=proceeding
  [infrun] start_step_over: enter
    [infrun] start_step_over: stealing global queue of threads to step, length = 1
    [infrun] start_step_over: resuming [7456.3.0] for step-over
    [infrun] should_be_inserted: skipping breakpoint: stepping past insn at: 0x400b7a
    [infrun] should_be_inserted: skipping breakpoint: stepping past insn at: 0x400b7a
    [infrun] should_be_inserted: skipping breakpoint: stepping past insn at: 0x400b7a
    [infrun] resume_1: step=1, signal=GDB_SIGNAL_0, trap_expected=1, current thread [7456.3.0] at 0x400b7a
    [infrun] do_target_resume: resume_ptid=7456.3.0, step=1, sig=GDB_SIGNAL_0
    [dfly-nat] resume: ptid=7456.3.0 step=1 signal=0
    [dfly-nat] resume: ptid=7456.1.0 suspending
    [dfly-nat] resume: ptid=7456.2.0 suspending
    [dfly-nat] resume: ptid=7456.3.0 resuming (single)
    [dfly-nat] resume: ptid=7456.3.0 unstopped
    [infrun] prepare_to_wait: prepare_to_wait
    [infrun] start_step_over: [7456.3.0] was resumed.
    [infrun] operator(): step-over queue now empty
  [infrun] start_step_over: exit
  [infrun] reset: reason=proceeding
  [infrun] maybe_set_commit_resumed_all_targets: enabling commit-resumed for target native
  [infrun] maybe_call_commit_resumed_all_targets: calling commit_resumed for target native
[infrun] proceed: exit
[infrun] fetch_inferior_event: enter
  [infrun] scoped_disable_commit_resumed: reason=handling event
  [infrun] random_pending_event_thread: None found.
  [dfly-nat] wait: [in] ptid = -1.0.0
  [dfly-nat] wait: resumed_lwps 7456.3.0
  [dfly-nat] wait: PT_WAIT err 0 errno 35
  [dfly-nat] wait: err 0 errno 35 event status 2 lwpid 3 signal 5
  [dfly-nat] wait: store lwp=3 signal=5
  [dfly-nat] wait: err 0 errno 35 event status 0 lwpid 3 signal 5
  [dfly-nat] wait: no event, exiting
  [dfly-nat] wait: return lwp=3 signal=5
  [infrun] print_target_wait_results: target_wait (-1.0.0 [LWP 1 of process -1], status) =
  [infrun] print_target_wait_results:   7456.3.0 [LWP 3 of process 7456],
  [infrun] print_target_wait_results:   status->kind = STOPPED, sig = GDB_SIGNAL_TRAP
  [infrun] handle_inferior_event: status->kind = STOPPED, sig = GDB_SIGNAL_TRAP
  [infrun] clear_step_over_info: clearing step over info
  [infrun] context_switch: Switching context from 0.0.0 to 7456.3.0
  [infrun] handle_signal_stop: stop_pc=0x400b7d
  [infrun] process_event_stop_test: stepping inside range [0x400b7a-0x400b8d]
  [infrun] resume_1: step=1, signal=GDB_SIGNAL_0, trap_expected=0, current thread [7456.3.0] at 0x400b7d
  [infrun] do_target_resume: resume_ptid=-1.0.0, step=1, sig=GDB_SIGNAL_0
  [dfly-nat] resume: ptid=-1.0.0 step=1 signal=0
  [dfly-nat] resume: ptid=7456.1.0 resuming (all)
  [dfly-nat] resume: ptid=7456.2.0 resuming (all)
  [dfly-nat] resume: ptid=7456.3.0 resuming (all)
  [dfly-nat] resume: ptid=7456.3.0 unstopped
  [infrun] prepare_to_wait: prepare_to_wait
  [infrun] reset: reason=handling event
  [infrun] maybe_set_commit_resumed_all_targets: enabling commit-resumed for target native
  [infrun] maybe_call_commit_resumed_all_targets: calling commit_resumed for target native
[infrun] fetch_inferior_event: exit
[infrun] fetch_inferior_event: enter
  [infrun] scoped_disable_commit_resumed: reason=handling event
  [infrun] random_pending_event_thread: None found.
  [dfly-nat] wait: [in] ptid = -1.0.0
  [dfly-nat] wait: resumed_lwps -1.0.0
  [dfly-nat] wait: PT_WAIT err 0 errno 35
  [dfly-nat] wait: err 0 errno 35 event status 2 lwpid 3 signal 5
  [dfly-nat] wait: store lwp=3 signal=5
  [dfly-nat] wait: err 0 errno 35 event status 0 lwpid 3 signal 5
  [dfly-nat] wait: no event, exiting
  [dfly-nat] wait: return lwp=3 signal=5
  [infrun] print_target_wait_results: target_wait (-1.0.0 [LWP 1 of process -1], status) =
  [infrun] print_target_wait_results:   7456.3.0 [LWP 3 of process 7456],
  [infrun] print_target_wait_results:   status->kind = STOPPED, sig = GDB_SIGNAL_TRAP
  [infrun] handle_inferior_event: status->kind = STOPPED, sig = GDB_SIGNAL_TRAP
  [infrun] context_switch: Switching context from 0.0.0 to 7456.3.0
  [infrun] handle_signal_stop: stop_pc=0x400b86
  [infrun] process_event_stop_test: stepping inside range [0x400b7a-0x400b8d]
  [infrun] resume_1: step=1, signal=GDB_SIGNAL_0, trap_expected=0, current thread [7456.3.0] at 0x400b86
  [infrun] do_target_resume: resume_ptid=-1.0.0, step=1, sig=GDB_SIGNAL_0
  [dfly-nat] resume: ptid=-1.0.0 step=1 signal=0
  [dfly-nat] resume: ptid=7456.1.0 resuming (all)
  [dfly-nat] resume: ptid=7456.2.0 resuming (all)
  [dfly-nat] resume: ptid=7456.3.0 resuming (all)
  [dfly-nat] resume: ptid=7456.3.0 unstopped
  [infrun] prepare_to_wait: prepare_to_wait
  [infrun] reset: reason=handling event
  [infrun] maybe_set_commit_resumed_all_targets: enabling commit-resumed for target native
  [infrun] maybe_call_commit_resumed_all_targets: calling commit_resumed for target native
[infrun] fetch_inferior_event: exit
[infrun] fetch_inferior_event: enter
  [infrun] scoped_disable_commit_resumed: reason=handling event
  [infrun] random_pending_event_thread: None found.
  [dfly-nat] wait: [in] ptid = -1.0.0
  [dfly-nat] wait: resumed_lwps -1.0.0
  [dfly-nat] wait: PT_WAIT err 0 errno 35
  [dfly-nat] wait: err 0 errno 35 event status 2 lwpid 2 signal 5
  [dfly-nat] wait: store lwp=2 signal=5
  [dfly-nat] wait: err 0 errno 35 event status 0 lwpid 2 signal 5
  [dfly-nat] wait: no event, exiting
  [dfly-nat] wait: return lwp=2 signal=5
  [infrun] print_target_wait_results: target_wait (-1.0.0 [LWP 1 of process -1], status) =
  [infrun] print_target_wait_results:   7456.2.0 [LWP 2 of process 7456],
  [infrun] print_target_wait_results:   status->kind = STOPPED, sig = GDB_SIGNAL_TRAP
  [infrun] handle_inferior_event: status->kind = STOPPED, sig = GDB_SIGNAL_TRAP
  [infrun] context_switch: Switching context from 0.0.0 to 7456.2.0
  [infrun] handle_signal_stop: stop_pc=0x400b2d
  [infrun] handle_signal_stop: random signal (GDB_SIGNAL_TRAP)
  [infrun] stop_waiting: stop_waiting
  [infrun] print_signal_received_reason: signal = Trace/breakpoint trap

Thread 2 received signal SIGTRAP, Trace/breakpoint trap.
[Switching to LWP 2 of process 7456]
thread_runner_1 (arg=0x7fffffdfd6e0) at ./tls-threads.c:23
23              printf("Running 1 thread thr=%d sleep=%d!\n", data->thr_num, data->sleep_ms);
  [infrun] reset: reason=handling event
  [infrun] maybe_set_commit_resumed_all_targets: not requesting commit-resumed for target native, no resumed threads
[infrun] fetch_inferior_event: exit



=== ENTER "step" command

[infrun] clear_proceed_status_thread: 7456.1.0
[infrun] clear_proceed_status_thread: 7456.2.0
[infrun] clear_proceed_status_thread: 7456.3.0
[infrun] set_step_info: symtab = ./tls-threads.c, line = 23, step_frame_id = {stack=0x7fdfe0000fd0,code=0x0000000000400b10,!special}, step_stack_frame_id = {stack=0x7fdfe0000fd0,code=0x0000000000400b10,!special}
[infrun] proceed: enter
  [infrun] follow_fork: enter
  [infrun] follow_fork: exit
  [infrun] proceed: cur_thr = 7456.2.0
  [infrun] proceed: addr=0xffffffffffffffff, signal=GDB_SIGNAL_DEFAULT, resume_ptid=-1.0.0
  [infrun] scoped_disable_commit_resumed: reason=proceeding
  [infrun] start_step_over: enter
    [infrun] start_step_over: stealing global queue of threads to step, length = 0
    [infrun] operator(): step-over queue now empty
  [infrun] start_step_over: exit
  [infrun] proceed_resume_thread_checked: resuming 7456.2.0
  [infrun] resume_1: step=1, signal=GDB_SIGNAL_0, trap_expected=0, current thread [7456.2.0] at 0x400b2d
  [infrun] do_target_resume: resume_ptid=-1.0.0, step=1, sig=GDB_SIGNAL_0
  [dfly-nat] resume: ptid=-1.0.0 step=1 signal=0
  [dfly-nat] resume: ptid=7456.1.0 resuming (all)
  [dfly-nat] resume: ptid=7456.2.0 resuming (all)
  [dfly-nat] resume: ptid=7456.3.0 resuming (all)
  [dfly-nat] resume: ptid=7456.2.0 unstopped
  [infrun] prepare_to_wait: prepare_to_wait
  [infrun] reset: reason=proceeding
  [infrun] maybe_set_commit_resumed_all_targets: enabling commit-resumed for target native
  [infrun] maybe_call_commit_resumed_all_targets: calling commit_resumed for target native
[infrun] proceed: exit
[infrun] fetch_inferior_event: enter
  [infrun] scoped_disable_commit_resumed: reason=handling event
  [infrun] random_pending_event_thread: None found.
  [dfly-nat] wait: [in] ptid = -1.0.0
  [dfly-nat] wait: resumed_lwps -1.0.0
  [dfly-nat] wait: PT_WAIT err 0 errno 35
  [dfly-nat] wait: err 0 errno 35 event status 2 lwpid 3 signal 5
  [dfly-nat] wait: store lwp=3 signal=5
  [dfly-nat] wait: err 0 errno 35 event status 0 lwpid 3 signal 5
  [dfly-nat] wait: no event, exiting
  [dfly-nat] wait: return lwp=3 signal=5
  [infrun] print_target_wait_results: target_wait (-1.0.0 [LWP 1 of process -1], status) =
  [infrun] print_target_wait_results:   7456.3.0 [LWP 3 of process 7456],
  [infrun] print_target_wait_results:   status->kind = STOPPED, sig = GDB_SIGNAL_TRAP
  [infrun] handle_inferior_event: status->kind = STOPPED, sig = GDB_SIGNAL_TRAP
  [infrun] context_switch: Switching context from 0.0.0 to 7456.3.0
  [infrun] handle_signal_stop: stop_pc=0x400b8d
  [infrun] handle_signal_stop: random signal (GDB_SIGNAL_TRAP)
  [infrun] stop_waiting: stop_waiting
  [infrun] print_signal_received_reason: signal = Trace/breakpoint trap

Thread 3 received signal SIGTRAP, Trace/breakpoint trap.
[Switching to LWP 3 of process 7456]
thread_runner_2 (arg=0x7fffffdfd6f0) at ./tls-threads.c:38
38              printf("Running extra thread thr=%d sleep=%d!\n", data->thr_num, data->sleep_ms);
  [infrun] reset: reason=handling event
  [infrun] maybe_set_commit_resumed_all_targets: not requesting commit-resumed for target native, no resumed threads
[infrun] fetch_inferior_event: exit



=== ENTER "step" command

[infrun] clear_proceed_status_thread: 7456.1.0
[infrun] clear_proceed_status_thread: 7456.2.0
[infrun] clear_proceed_status_thread: 7456.3.0
[infrun] set_step_info: symtab = ./tls-threads.c, line = 38, step_frame_id = {stack=0x7fdfe0201fd0,code=0x0000000000400b70,!special}, step_stack_frame_id = {stack=0x7fdfe0201fd0,code=0x0000000000400b70,!special}
[infrun] proceed: enter
  [infrun] follow_fork: enter
  [infrun] follow_fork: exit
  [infrun] proceed: cur_thr = 7456.3.0
  [infrun] proceed: addr=0xffffffffffffffff, signal=GDB_SIGNAL_DEFAULT, resume_ptid=-1.0.0
  [infrun] scoped_disable_commit_resumed: reason=proceeding
  [infrun] start_step_over: enter
    [infrun] start_step_over: stealing global queue of threads to step, length = 0
    [infrun] operator(): step-over queue now empty
  [infrun] start_step_over: exit
  [infrun] proceed_resume_thread_checked: resuming 7456.3.0
  [infrun] resume_1: step=1, signal=GDB_SIGNAL_0, trap_expected=0, current thread [7456.3.0] at 0x400b8d
  [infrun] do_target_resume: resume_ptid=-1.0.0, step=1, sig=GDB_SIGNAL_0
  [dfly-nat] resume: ptid=-1.0.0 step=1 signal=0
  [dfly-nat] resume: ptid=7456.1.0 resuming (all)
  [dfly-nat] resume: ptid=7456.2.0 resuming (all)
  [dfly-nat] resume: ptid=7456.3.0 resuming (all)
  [dfly-nat] resume: ptid=7456.3.0 unstopped
  [infrun] prepare_to_wait: prepare_to_wait
  [infrun] reset: reason=proceeding
  [infrun] maybe_set_commit_resumed_all_targets: enabling commit-resumed for target native
  [infrun] maybe_call_commit_resumed_all_targets: calling commit_resumed for target native
[infrun] proceed: exit
[infrun] fetch_inferior_event: enter
  [infrun] scoped_disable_commit_resumed: reason=handling event
  [infrun] random_pending_event_thread: None found.
  [dfly-nat] wait: [in] ptid = -1.0.0
  [dfly-nat] wait: resumed_lwps -1.0.0
  [dfly-nat] wait: PT_WAIT err 0 errno 35
  [dfly-nat] wait: err 0 errno 35 event status 2 lwpid 2 signal 5
  [dfly-nat] wait: store lwp=2 signal=5
  [dfly-nat] wait: err 0 errno 35 event status 0 lwpid 2 signal 5
  [dfly-nat] wait: no event, exiting
  [dfly-nat] wait: return lwp=2 signal=5
  [infrun] print_target_wait_results: target_wait (-1.0.0 [LWP 1 of process -1], status) =
  [infrun] print_target_wait_results:   7456.2.0 [LWP 2 of process 7456],
  [infrun] print_target_wait_results:   status->kind = STOPPED, sig = GDB_SIGNAL_TRAP
  [infrun] handle_inferior_event: status->kind = STOPPED, sig = GDB_SIGNAL_TRAP
  [infrun] context_switch: Switching context from 0.0.0 to 7456.2.0
  [infrun] handle_signal_stop: stop_pc=0x400b30
  [infrun] handle_signal_stop: random signal (GDB_SIGNAL_TRAP)
  [infrun] stop_waiting: stop_waiting
  [infrun] print_signal_received_reason: signal = Trace/breakpoint trap

Thread 2 received signal SIGTRAP, Trace/breakpoint trap.
[Switching to LWP 2 of process 7456]
0x0000000000400b30 in thread_runner_1 (arg=0x7fffffdfd6e0) at ./tls-threads.c:23
23              printf("Running 1 thread thr=%d sleep=%d!\n", data->thr_num, data->sleep_ms);
  [infrun] reset: reason=handling event
  [infrun] maybe_set_commit_resumed_all_targets: not requesting commit-resumed for target native, no resumed threads
[infrun] fetch_inferior_event: exit



=== ENTER "step" command

[infrun] clear_proceed_status_thread: 7456.1.0
[infrun] clear_proceed_status_thread: 7456.2.0
[infrun] clear_proceed_status_thread: 7456.3.0
[infrun] set_step_info: symtab = ./tls-threads.c, line = 23, step_frame_id = {stack=0x7fdfe0000fd0,code=0x0000000000400b10,!special}, step_stack_frame_id = {stack=0x7fdfe0000fd0,code=0x0000000000400b10,!special}
[infrun] proceed: enter
  [infrun] follow_fork: enter
  [infrun] follow_fork: exit
  [infrun] proceed: cur_thr = 7456.2.0
  [infrun] proceed: addr=0xffffffffffffffff, signal=GDB_SIGNAL_DEFAULT, resume_ptid=-1.0.0
  [infrun] scoped_disable_commit_resumed: reason=proceeding
  [infrun] start_step_over: enter
    [infrun] start_step_over: stealing global queue of threads to step, length = 0
    [infrun] operator(): step-over queue now empty
  [infrun] start_step_over: exit
  [infrun] proceed_resume_thread_checked: resuming 7456.2.0
  [infrun] resume_1: step=1, signal=GDB_SIGNAL_0, trap_expected=0, current thread [7456.2.0] at 0x400b30
  [infrun] do_target_resume: resume_ptid=-1.0.0, step=1, sig=GDB_SIGNAL_0
  [dfly-nat] resume: ptid=-1.0.0 step=1 signal=0
  [dfly-nat] resume: ptid=7456.1.0 resuming (all)
  [dfly-nat] resume: ptid=7456.2.0 resuming (all)
  [dfly-nat] resume: ptid=7456.3.0 resuming (all)
  [dfly-nat] resume: ptid=7456.2.0 unstopped
  [infrun] prepare_to_wait: prepare_to_wait
  [infrun] reset: reason=proceeding
  [infrun] maybe_set_commit_resumed_all_targets: enabling commit-resumed for target native
  [infrun] maybe_call_commit_resumed_all_targets: calling commit_resumed for target native
[infrun] proceed: exit
[infrun] fetch_inferior_event: enter
  [infrun] scoped_disable_commit_resumed: reason=handling event
  [infrun] random_pending_event_thread: None found.
  [dfly-nat] wait: [in] ptid = -1.0.0
  [dfly-nat] wait: resumed_lwps -1.0.0
  [dfly-nat] wait: PT_WAIT err 0 errno 35
  [dfly-nat] wait: err 0 errno 35 event status 2 lwpid 2 signal 5
  [dfly-nat] wait: store lwp=2 signal=5
  [dfly-nat] wait: err 0 errno 35 event status 0 lwpid 2 signal 5
  [dfly-nat] wait: no event, exiting
  [dfly-nat] wait: return lwp=2 signal=5
  [infrun] print_target_wait_results: target_wait (-1.0.0 [LWP 1 of process -1], status) =
  [infrun] print_target_wait_results:   7456.2.0 [LWP 2 of process 7456],
  [infrun] print_target_wait_results:   status->kind = STOPPED, sig = GDB_SIGNAL_TRAP
  [infrun] handle_inferior_event: status->kind = STOPPED, sig = GDB_SIGNAL_TRAP
  [infrun] context_switch: Switching context from 0.0.0 to 7456.2.0
  [infrun] handle_signal_stop: stop_pc=0x400b35
  [infrun] process_event_stop_test: stepping inside range [0x400b30-0x400b3c]
  [infrun] resume_1: step=1, signal=GDB_SIGNAL_0, trap_expected=0, current thread [7456.2.0] at 0x400b35
  [infrun] do_target_resume: resume_ptid=-1.0.0, step=1, sig=GDB_SIGNAL_0
  [dfly-nat] resume: ptid=-1.0.0 step=1 signal=0
  [dfly-nat] resume: ptid=7456.1.0 resuming (all)
  [dfly-nat] resume: ptid=7456.2.0 resuming (all)
  [dfly-nat] resume: ptid=7456.3.0 resuming (all)
  [dfly-nat] resume: ptid=7456.2.0 unstopped
  [infrun] prepare_to_wait: prepare_to_wait
  [infrun] reset: reason=handling event
  [infrun] maybe_set_commit_resumed_all_targets: enabling commit-resumed for target native
  [infrun] maybe_call_commit_resumed_all_targets: calling commit_resumed for target native
[infrun] fetch_inferior_event: exit
[infrun] fetch_inferior_event: enter
  [infrun] scoped_disable_commit_resumed: reason=handling event
  [infrun] random_pending_event_thread: None found.
  [dfly-nat] wait: [in] ptid = -1.0.0
  [dfly-nat] wait: resumed_lwps -1.0.0
  [dfly-nat] wait: PT_WAIT err 0 errno 35
  [dfly-nat] wait: err 0 errno 35 event status 2 lwpid 3 signal 5
  [dfly-nat] wait: store lwp=3 signal=5
  [dfly-nat] wait: err 0 errno 35 event status 0 lwpid 3 signal 5
  [dfly-nat] wait: no event, exiting
  [dfly-nat] wait: return lwp=3 signal=5
  [infrun] print_target_wait_results: target_wait (-1.0.0 [LWP 1 of process -1], status) =
  [infrun] print_target_wait_results:   7456.3.0 [LWP 3 of process 7456],
  [infrun] print_target_wait_results:   status->kind = STOPPED, sig = GDB_SIGNAL_TRAP
  [infrun] handle_inferior_event: status->kind = STOPPED, sig = GDB_SIGNAL_TRAP
  [infrun] context_switch: Switching context from 0.0.0 to 7456.3.0
  [infrun] handle_signal_stop: stop_pc=0x400b90
  [infrun] handle_signal_stop: random signal (GDB_SIGNAL_TRAP)
  [infrun] stop_waiting: stop_waiting
  [infrun] print_signal_received_reason: signal = Trace/breakpoint trap

Thread 3 received signal SIGTRAP, Trace/breakpoint trap.
[Switching to LWP 3 of process 7456]
0x0000000000400b90 in thread_runner_2 (arg=0x7fffffdfd6f0) at ./tls-threads.c:38
38              printf("Running extra thread thr=%d sleep=%d!\n", data->thr_num, data->sleep_ms);
  [infrun] reset: reason=handling event
  [infrun] maybe_set_commit_resumed_all_targets: not requesting commit-resumed for target native, no resumed threads
[infrun] fetch_inferior_event: exit



=== ENTER "step" command

[infrun] clear_proceed_status_thread: 7456.1.0
[infrun] clear_proceed_status_thread: 7456.2.0
[infrun] clear_proceed_status_thread: 7456.3.0
[infrun] set_step_info: symtab = ./tls-threads.c, line = 38, step_frame_id = {stack=0x7fdfe0201fd0,code=0x0000000000400b70,!special}, step_stack_frame_id = {stack=0x7fdfe0201fd0,code=0x0000000000400b70,!special}
[infrun] proceed: enter
  [infrun] follow_fork: enter
  [infrun] follow_fork: exit
  [infrun] proceed: cur_thr = 7456.3.0
  [infrun] proceed: addr=0xffffffffffffffff, signal=GDB_SIGNAL_DEFAULT, resume_ptid=-1.0.0
  [infrun] scoped_disable_commit_resumed: reason=proceeding
  [infrun] start_step_over: enter
    [infrun] start_step_over: stealing global queue of threads to step, length = 0
    [infrun] operator(): step-over queue now empty
  [infrun] start_step_over: exit
  [infrun] proceed_resume_thread_checked: resuming 7456.3.0
  [infrun] resume_1: step=1, signal=GDB_SIGNAL_0, trap_expected=0, current thread [7456.3.0] at 0x400b90
  [infrun] do_target_resume: resume_ptid=-1.0.0, step=1, sig=GDB_SIGNAL_0
  [dfly-nat] resume: ptid=-1.0.0 step=1 signal=0
  [dfly-nat] resume: ptid=7456.1.0 resuming (all)
  [dfly-nat] resume: ptid=7456.2.0 resuming (all)
  [dfly-nat] resume: ptid=7456.3.0 resuming (all)
  [dfly-nat] resume: ptid=7456.3.0 unstopped
  [infrun] prepare_to_wait: prepare_to_wait
  [infrun] reset: reason=proceeding
  [infrun] maybe_set_commit_resumed_all_targets: enabling commit-resumed for target native
  [infrun] maybe_call_commit_resumed_all_targets: calling commit_resumed for target native
[infrun] proceed: exit
[infrun] fetch_inferior_event: enter
  [infrun] scoped_disable_commit_resumed: reason=handling event
  [infrun] random_pending_event_thread: None found.
  [dfly-nat] wait: [in] ptid = -1.0.0
  [dfly-nat] wait: resumed_lwps -1.0.0
  [dfly-nat] wait: PT_WAIT err 0 errno 35
  [dfly-nat] wait: err 0 errno 35 event status 2 lwpid 3 signal 5
  [dfly-nat] wait: store lwp=3 signal=5
  [dfly-nat] wait: err 0 errno 35 event status 0 lwpid 3 signal 5
  [dfly-nat] wait: no event, exiting
  [dfly-nat] wait: return lwp=3 signal=5
  [infrun] print_target_wait_results: target_wait (-1.0.0 [LWP 1 of process -1], status) =
  [infrun] print_target_wait_results:   7456.3.0 [LWP 3 of process 7456],
  [infrun] print_target_wait_results:   status->kind = STOPPED, sig = GDB_SIGNAL_TRAP
  [infrun] handle_inferior_event: status->kind = STOPPED, sig = GDB_SIGNAL_TRAP
  [infrun] context_switch: Switching context from 0.0.0 to 7456.3.0
  [infrun] handle_signal_stop: stop_pc=0x400b95
  [infrun] process_event_stop_test: stepping inside range [0x400b90-0x400b9c]
  [infrun] resume_1: step=1, signal=GDB_SIGNAL_0, trap_expected=0, current thread [7456.3.0] at 0x400b95
  [infrun] do_target_resume: resume_ptid=-1.0.0, step=1, sig=GDB_SIGNAL_0
  [dfly-nat] resume: ptid=-1.0.0 step=1 signal=0
  [dfly-nat] resume: ptid=7456.1.0 resuming (all)
  [dfly-nat] resume: ptid=7456.2.0 resuming (all)
  [dfly-nat] resume: ptid=7456.3.0 resuming (all)
  [dfly-nat] resume: ptid=7456.3.0 unstopped
  [infrun] prepare_to_wait: prepare_to_wait
  [infrun] reset: reason=handling event
  [infrun] maybe_set_commit_resumed_all_targets: enabling commit-resumed for target native
  [infrun] maybe_call_commit_resumed_all_targets: calling commit_resumed for target native
[infrun] fetch_inferior_event: exit
[infrun] fetch_inferior_event: enter
  [infrun] scoped_disable_commit_resumed: reason=handling event
  [infrun] random_pending_event_thread: None found.
  [dfly-nat] wait: [in] ptid = -1.0.0
  [dfly-nat] wait: resumed_lwps -1.0.0
  [dfly-nat] wait: PT_WAIT err 0 errno 35
  [dfly-nat] wait: err 0 errno 35 event status 2 lwpid 3 signal 5
  [dfly-nat] wait: store lwp=3 signal=5
  [dfly-nat] wait: err 0 errno 35 event status 0 lwpid 3 signal 5
  [dfly-nat] wait: no event, exiting
  [dfly-nat] wait: return lwp=3 signal=5
  [infrun] print_target_wait_results: target_wait (-1.0.0 [LWP 1 of process -1], status) =
  [infrun] print_target_wait_results:   7456.3.0 [LWP 3 of process 7456],
  [infrun] print_target_wait_results:   status->kind = STOPPED, sig = GDB_SIGNAL_TRAP
  [infrun] handle_inferior_event: status->kind = STOPPED, sig = GDB_SIGNAL_TRAP
  [infrun] context_switch: Switching context from 0.0.0 to 7456.3.0
  [infrun] handle_signal_stop: stop_pc=0x400b97
  [infrun] process_event_stop_test: stepping inside range [0x400b90-0x400b9c]
  [infrun] resume_1: step=1, signal=GDB_SIGNAL_0, trap_expected=0, current thread [7456.3.0] at 0x400b97
  [infrun] do_target_resume: resume_ptid=-1.0.0, step=1, sig=GDB_SIGNAL_0
  [dfly-nat] resume: ptid=-1.0.0 step=1 signal=0
  [dfly-nat] resume: ptid=7456.1.0 resuming (all)
  [dfly-nat] resume: ptid=7456.2.0 resuming (all)
  [dfly-nat] resume: ptid=7456.3.0 resuming (all)
  [dfly-nat] resume: ptid=7456.3.0 unstopped
  [infrun] prepare_to_wait: prepare_to_wait
  [infrun] reset: reason=handling event
  [infrun] maybe_set_commit_resumed_all_targets: enabling commit-resumed for target native
  [infrun] maybe_call_commit_resumed_all_targets: calling commit_resumed for target native
[infrun] fetch_inferior_event: exit
[infrun] fetch_inferior_event: enter
  [infrun] scoped_disable_commit_resumed: reason=handling event
  [infrun] random_pending_event_thread: None found.
  [dfly-nat] wait: [in] ptid = -1.0.0
  [dfly-nat] wait: resumed_lwps -1.0.0
  [dfly-nat] wait: PT_WAIT err 0 errno 35
  [dfly-nat] wait: err 0 errno 35 event status 2 lwpid 3 signal 5
  [dfly-nat] wait: store lwp=3 signal=5
  [dfly-nat] wait: err 0 errno 35 event status 0 lwpid 3 signal 5
  [dfly-nat] wait: no event, exiting
  [dfly-nat] wait: return lwp=3 signal=5
  [infrun] print_target_wait_results: target_wait (-1.0.0 [LWP 1 of process -1], status) =
  [infrun] print_target_wait_results:   7456.3.0 [LWP 3 of process 7456],
  [infrun] print_target_wait_results:   status->kind = STOPPED, sig = GDB_SIGNAL_TRAP
  [infrun] handle_inferior_event: status->kind = STOPPED, sig = GDB_SIGNAL_TRAP
  [infrun] context_switch: Switching context from 0.0.0 to 7456.3.0
  [infrun] handle_signal_stop: stop_pc=0x4007f0
  [infrun] process_event_stop_test: stepped into dynsym resolve code
  [infrun] resume_1: step=1, signal=GDB_SIGNAL_0, trap_expected=0, current thread [7456.3.0] at 0x4007f0
  [infrun] do_target_resume: resume_ptid=-1.0.0, step=1, sig=GDB_SIGNAL_0
  [dfly-nat] resume: ptid=-1.0.0 step=1 signal=0
  [dfly-nat] resume: ptid=7456.1.0 resuming (all)
  [dfly-nat] resume: ptid=7456.2.0 resuming (all)
  [dfly-nat] resume: ptid=7456.3.0 resuming (all)
  [dfly-nat] resume: ptid=7456.3.0 unstopped
  [infrun] prepare_to_wait: prepare_to_wait
  [infrun] reset: reason=handling event
  [infrun] maybe_set_commit_resumed_all_targets: enabling commit-resumed for target native
  [infrun] maybe_call_commit_resumed_all_targets: calling commit_resumed for target native
[infrun] fetch_inferior_event: exit
[infrun] fetch_inferior_event: enter
  [infrun] scoped_disable_commit_resumed: reason=handling event
  [infrun] random_pending_event_thread: None found.
  [dfly-nat] wait: [in] ptid = -1.0.0
  [dfly-nat] wait: resumed_lwps -1.0.0
  [dfly-nat] wait: PT_WAIT err 0 errno 35
  [dfly-nat] wait: err 0 errno 35 event status 2 lwpid 2 signal 5
  [dfly-nat] wait: store lwp=2 signal=5
  [dfly-nat] wait: err 0 errno 35 event status 0 lwpid 2 signal 5
  [dfly-nat] wait: no event, exiting
  [dfly-nat] wait: return lwp=2 signal=5
  [infrun] print_target_wait_results: target_wait (-1.0.0 [LWP 1 of process -1], status) =
  [infrun] print_target_wait_results:   7456.2.0 [LWP 2 of process 7456],
  [infrun] print_target_wait_results:   status->kind = STOPPED, sig = GDB_SIGNAL_TRAP
  [infrun] handle_inferior_event: status->kind = STOPPED, sig = GDB_SIGNAL_TRAP
  [infrun] context_switch: Switching context from 0.0.0 to 7456.2.0
  [infrun] handle_signal_stop: stop_pc=0x400b37
  [infrun] handle_signal_stop: random signal (GDB_SIGNAL_TRAP)
  [infrun] stop_waiting: stop_waiting
  [infrun] print_signal_received_reason: signal = Trace/breakpoint trap

Thread 2 received signal SIGTRAP, Trace/breakpoint trap.
[Switching to LWP 2 of process 7456]
0x0000000000400b37 in thread_runner_1 (arg=0x7fffffdfd6e0) at ./tls-threads.c:23
23              printf("Running 1 thread thr=%d sleep=%d!\n", data->thr_num, data->sleep_ms);
  [infrun] reset: reason=handling event
  [infrun] maybe_set_commit_resumed_all_targets: not requesting commit-resumed for target native, no resumed threads
[infrun] fetch_inferior_event: exit

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: Multi-threading support on DragonFlyBSD
  2025-03-11  7:59   ` Sergey Zigachev via Gdb
@ 2025-03-11 14:37     ` Simon Marchi via Gdb
  2025-03-12  4:12       ` Sergey Zigachev via Gdb
  0 siblings, 1 reply; 6+ messages in thread
From: Simon Marchi via Gdb @ 2025-03-11 14:37 UTC (permalink / raw)
  To: Sergey Zigachev, gdb

On 3/11/25 3:59 AM, Sergey Zigachev wrote:
> That's overall correct. Other threads can receive SIGTRAP if I put a
> breakpoint, for example. I thought that should work, isn't it? If GDB
> resumes all threads that means any number of threads can stop at a
> breakpoint or SIGTRAP caused by single-stepping a process.

If another thread (that is being ran freely) happens to hit a breakpoint
while single stepping the selected thread, then indeed you should report
that SIGTRAP.  GDB will present that stop to the user, and the step will
be aborted.  The stepping thread will be asked to stop, and will be
stopped at an arbitrary place.

> Here's what I think is happening:
>   1. put a breakpoint_1 at function_1 for thread_1
>   2. put a breakpoint_2 at function_2 for thread_2
>   3. -> "run" the process, main thread starts threads approx. at once
>   4. breakpoint_1 hit, current thread is thread_1
>   5. -> enter "step" command
>   6. GDB resumes thread_1, then all threads, every time with step=1
>   7. breakpoint_2 hit, GDB switches to thread_2
>   8. -> enter "step" command
>   9. GDB resumes thread_2, then all threads, every time with step=1
>   10. thread_1 generates SIGTRAP
>   11. GDB can't tell this SIGTRAP is the result of (5), so can't skip it
>   12. GDB reports it as a random signal

I think that the SIGTRAP at #10 is spurious.  It is perhaps a leftover
from the step command at #5/#6?

When thread 2 hits breakpoint_2 at #7, then the step from thread 1
aborts and should be forgotten.  When you step again at #8/#9, you start
from a blank slate.

> If GDB receives events for the current thread at steps (6) and (9),
> then it processes them just fine, skipping as necessary.

It's possible that the code path taken in that case clears something
that causes the spurious SIGTRAP to be forgotten.

> WRT implementation details: I don't send SIGSTOP to stop all threads
> when SIGTRAP generated and I don't use waitpid. Natdep target
> communicates with the kernel through ptrace syscall only. Ptrace
> request waits for a event, stops all threads and waits for them to
> actually stop before returning to userspace. Then in the natdep code I
> pull all pending events from all threads, stash them in a list, and
> return one event according to what GDB expects to get: if it resumed
> just a single thread, then I return an event strictly for that thread
> (if any); if it resumed all threads, then I return the next event from
> the list, which may or may not be for the current thread. In my test
> program, I only get SIGTRAPs triggered by single-stepping threads, no
> other signals generated.

Ok, so if I understand correctly, you do some kind of "ptrace resume",
which is blocking, and it unblocks when something happens.  When that
returns, the kernel has stopped all threads for you.  In a sense that
simplifies some things, but it would make async/non-stop harder to
implement.

> Please see attached debug log output. It's rather verbose though.

I'll try to go through it later.

> Couple notes:
>   - for DragonFly, I'm using ptid_t format as (pid, thread_id, 0),
>   thread_id is unique for a process, but is NOT globally unique

I think that's fine.  The core of GDB never assumes that ptid_t::lwp is
globally unique.

>   - you can ignore lines with [dfly-nat], that's my implementation's
>   debug info, it mostly duplicates what infrun writes anyway

That's fine, it's very useful to add such logging.

> I'm sorry, my explanations are hard to understand, hopefully it'll be
> clearer with the log.
> 
> Ideally, I would like to get GDB to transparently skip assembly
> instructions which are not relevant to report regardless which thread
> is reporting an event, be it currently selected or not. Like part of
> the already reported source line, dynsym code, missing debug symbols
> or anything else. For that to work I think I need to save and restore
> GDB's thread "context" (in the thread_info struct) before reporting an
> event. If it's not possible or doesn't make sense, then I'll to do
> what Linux is doing -- give currently selected thread higher priority
> and report it first.

This kind of logic is managed at a higher level (infrun), not by
targets.  I don't think that your target_ops::wait method is the place
for such decision.  I'm not super familiar with this, I just know it's
managed by spaghetti-like functions like `handle_signal_stop`.

>> I assume that you have enabled and started at the "set debug infrun"
>> logs?  This is how you generally debug those issues, look at what infrun
>> asks your target to do, look at what your target answers, try to see if
>> that makes sense.
> 
> Yes, I enabled infrun debug output and it was invaluable for gathering info.
> 
>> Finally, it's perhaps outside of the scope of your work, but "modern"
>> targets use non-stop (related to "maint set target-non-stop", not the
>> user-visible "set non-stop") and target-async.  I'm more used to reason
>> in terms of non-stop & target-async, and that code path of infrun is
>> likely more tested.  It's probably no small task, but switching to that
>> would be more future-proof.
> 
> I'll take a look at non-stop and async modes.
> Also, I wasn't aware of maintenance command, thanks!

Given what you said above, about how your debug interface works,
non-stop/async is probably not a priority.

Simon

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: Multi-threading support on DragonFlyBSD
  2025-03-11 14:37     ` Simon Marchi via Gdb
@ 2025-03-12  4:12       ` Sergey Zigachev via Gdb
  0 siblings, 0 replies; 6+ messages in thread
From: Sergey Zigachev via Gdb @ 2025-03-12  4:12 UTC (permalink / raw)
  To: Simon Marchi, gdb

> If another thread (that is being ran freely) happens to hit a breakpoint
> while single stepping the selected thread, then indeed you should report
> that SIGTRAP.  GDB will present that stop to the user, and the step will
> be aborted.  The stepping thread will be asked to stop, and will be
> stopped at an arbitrary place.

Ok, that means I got the "abort the step" part wrong.

> I think that the SIGTRAP at #10 is spurious.  It is perhaps a leftover
> from the step command at #5/#6?

Yes, it is, I didn't think this was a leftover though. 🙂

> When thread 2 hits breakpoint_2 at #7, then the step from thread 1
> aborts and should be forgotten.  When you step again at #8/#9, you start
> from a blank slate.

Got that, too. Currently, I made sure to report to GDB result of every 
event it requested to get. Apparently, that's not how it should work.

> It's possible that the code path taken in that case clears something
> that causes the spurious SIGTRAP to be forgotten.

I think there's no spurious SIGTRAPs for other threads, for GDB it looks 
like the inferior runs only single thread at this point in time.
I can be wrong on that one.

> Ok, so if I understand correctly, you do some kind of "ptrace resume",
> which is blocking, and it unblocks when something happens.  When that
> returns, the kernel has stopped all threads for you.  In a sense that
> simplifies some things, but it would make async/non-stop harder to
> implement.

Yes, it's a "ptrace wait" request which is blocking, I have "ptrace 
resume", too, which doesn't block. I'm ok to change both user and kernel 
space if that would make GDB usage better for end user.

> This kind of logic is managed at a higher level (infrun), not by
> targets.  I don't think that your target_ops::wait method is the place
> for such decision.  I'm not super familiar with this, I just know it's
> managed by spaghetti-like functions like `handle_signal_stop`.

OK, it makes sense. Thanks for your comments, it's rather eye-opening 
experience. My mental model about how gdb core operates is really wrong.

> Given what you said above, about how your debug interface works,
> non-stop/async is probably not a priority.

Well, I implemented the kernel part myself. So if it doesn't fit GDB 
(and later LLDB), then I can redo that.
Any information about how one should implement target dependent code for 
GDB is appreciated. Wiki pages, articles, existing "canonical" targets 
to learn from (maybe less complex than Linux one?), etc.

-Sergey


^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2025-03-12  4:13 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-03-05 10:22 Multi-threading support on DragonFlyBSD Sergey Zigachev via Gdb
2025-03-09 15:16 ` Sergey Zigachev via Gdb
2025-03-10 16:22 ` Simon Marchi via Gdb
2025-03-11  7:59   ` Sergey Zigachev via Gdb
2025-03-11 14:37     ` Simon Marchi via Gdb
2025-03-12  4:12       ` Sergey Zigachev via Gdb

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox