Age | Commit message (Collapse) | Author | Files | Lines |
|
Rationale
---------
Let's say you have multiple threads hitting a conditional breakpoint
at the same time, and all of these are going to evaluate to false.
All these threads will need to be resumed.
Currently, GDB fetches one target event (one SIGTRAP representing the
breakpoint hit) and decides that the thread should be resumed. It
calls resume and commit_resume immediately. It then fetches the
second target event, and does the same, until it went through all
threads.
The result is therefore something like:
- consume event for thread A
- resume thread A
- commit resume (affects thread A)
- consume event for thread B
- resume thread B
- commit resume (affects thread B)
- consume event for thread C
- resume thread C
- commit resume (affects thread C)
For targets where it's beneficial to group resumptions requests (most
likely those that implement target_ops::commit_resume), it would be
much better to have:
- consume event for thread A
- resume thread A
- consume event for thread B
- resume thread B
- consume event for thread C
- resume thread C
- commit resume (affects threads A, B and C)
Implementation details
----------------------
To achieve this, this patch adds another check in
maybe_set_commit_resumed_all_targets to avoid setting the
commit-resumed flag of targets that readily have events to provide to
infrun.
To determine if a target has events readily available to report, this
patch adds an `has_pending_events` target_ops method. The method
returns a simple bool to say whether or not it has pending events to
report.
Testing
=======
To test this, I start GDBserver with a program that spawns multiple
threads:
$ ../gdbserver/gdbserver --once :1234 ~/src/many-threads-stepping-over-breakpoints/many-threads-stepping-over-breakpoints
I then connect with GDB and install a conditional breakpoint that always
evaluates to false (and force the evaluation to be done by GDB):
$ ./gdb -nx --data-directory=data-directory \
/home/simark/src/many-threads-stepping-over-breakpoints/many-threads-stepping-over-breakpoints \
-ex "set breakpoint condition-evaluation host" \
-ex "set pag off" \
-ex "set confirm off" \
-ex "maint set target-non-stop on" \
-ex "tar rem :1234" \
-ex "tb main" \
-ex "b 13 if 0" \
-ex c \
-ex "set debug infrun" \
-ex "set debug remote 1" \
-ex "set debug displaced"
I then do "continue" and look at the log.
The remote target receives a bunch of stop notifications for all
threads that have hit the breakpoint. infrun consumes and processes
one event, decides it should not cause a stop, prepares a displaced
step, after which we should see:
[infrun] maybe_set_commit_resumed_all_process_targets: not requesting commit-resumed for target remote, target has pending events
Same for a second thread (since we have 2 displaced step buffers).
For the following threads, their displaced step is deferred since
there are no more buffers available.
After consuming the last event the remote target has to offer, we get:
[infrun] maybe_set_commit_resumed_all_process_targets: enabling commit-resumed for target remote
[infrun] maybe_call_commit_resumed_all_process_targets: calling commit_resumed for target remote
[remote] Sending packet: $vCont;s:p14d16b.14d1b1;s:p14d16b.14d1b2#55
[remote] Packet received: OK
Without the patch, there would have been one vCont;s just after each
prepared displaced step.
gdb/ChangeLog:
yyyy-mm-dd Simon Marchi <simon.marchi@efficios.com>
Pedro Alves <pedro@palves.net>
* async-event.c (async_event_handler_marked): New.
* async-event.h (async_event_handler_marked): Declare.
* infrun.c (maybe_set_commit_resumed_all_targets): Switch to
inferior before calling target method. Don't commit-resumed if
target_has_pending_events is true.
* remote.c (remote_target::has_pending_events): New.
* target-delegates.c: Regenerate.
* target.c (target_has_pending_events): New.
* target.h (target_ops::has_pending_events): New target method.
(target_has_pending_events): New.
Change-Id: I18112ba19a1ff4986530c660f530d847bb4a1f1d
|
|
The `ready` flag of async event handlers is cleared by the async event
handler system right before invoking the associated callback, in
check_async_event_handlers.
This is not ideal with how the infrun subsystem consumes events: all
targets' async event handler callbacks essentially just invoke
`inferior_event_handler`, which eventually calls `fetch_inferior_event`
and `do_target_wait`. `do_target_wait` picks an inferior at random,
and thus a target at random (it could be the target whose `ready` flag
was cleared, or not), and pulls one event from it.
So it's possible that:
- the async event handler for a target A is called
- we end up consuming an event for target B
- all threads of target B are stopped, target_async(0) is called on it,
so its async event handler is cleared (e.g.
record_btrace_target::async)
As a result, target A still has events to report while its async event
handler is left unmarked, so these events are not consumed. To counter
this, at the end of their async event handler callbacks, targets check
if they still have something to report and re-mark their async event
handler (e.g. remote_async_inferior_event_handler).
The linux_nat target does not suffer from this because it doesn't use an
async event handler at the moment. It only uses a pipe registered with
the event loop. It is written to in the SIGCHLD handler (and in other
spots that want to get target wait method called) and read from in
the target's wait method. So if linux_nat happened to be target A in
the example above, the pipe would just stay readable, and the event loop
would wake up again, until linux_nat's wait method is finally called and
consumes the contents of the pipe.
I think it would be nicer if targets using async_event_handler worked in
a similar way, where the flag would stay set until the target's wait
method is actually called. As a first step towards that, this patch
moves the responsibility of clearing the ready flags of async event
handlers to the invoked callback.
All async event handler callbacks are modified to clear their ready flag
before doing anything else. So in practice, nothing changes with this
patch. It's only the responsibility of clearing the flag that is
shifted toward the callee.
gdb/ChangeLog:
* async-event.h (async_event_handler_func): Add documentation.
* async-event.c (check_async_event_handlers): Don't clear
async_event_handler ready flag.
* infrun.c (infrun_async_inferior_event_handler): Clear ready
flag.
* record-btrace.c (record_btrace_handle_async_inferior_event):
Likewise.
* record-full.c (record_full_async_inferior_event_handler):
Likewise.
* remote-notif.c (remote_async_get_pending_events_handler):
Likewise.
* remote.c (remote_async_inferior_event_handler): Likewise.
Change-Id: I179ef8e99580eae642d332846fd13664dbddc0c1
|
|
This commits the result of running gdb/copyright.py as per our Start
of New Year procedure...
gdb/ChangeLog
Update copyright year range in copyright header of all GDB files.
|
|
Add debug printouts about event loop-related events:
- When a file descriptor handler gets invoked
- When an async event/signal handler gets invoked
gdb/ChangeLog:
* async-event.c (invoke_async_signal_handlers): Add debug
print.
(check_async_event_handlers): Likewise.
* event-top.c (show_debug_event_loop): New function.
(_initialize_event_top): Register "set debug event-loop"
setting.
gdbserver/ChangeLog:
* server.cc (handle_monitor_command): Handle "set
debug-event-loop".
(captured_main): Handle "--debug-event-loop".
(monitor_show_help): Mention new setting.
(gdbserver_usage): Mention new flag.
gdbsupport/ChangeLog:
* event-loop.h (debug_event_loop): New variable declaration.
(event_loop_debug_printf_1): New function declaration.
(event_loop_debug_printf): New macro.
* event-loop.cc (debug_event_loop): New variable.
(handle_file_event): Add debug print.
(event_loop_debug_printf_1): New function.
Change-Id: If78ed3a69179881368e7895b42940ce13b6a1a05
|
|
Assign names to async event/signal handlers. They will be used in debug
messages when file handlers are invoked.
Unlike in the previous patch, the names are not copied in the structure,
since we don't need to (all names are string literals for the moment).
gdb/ChangeLog:
* async-event.h (create_async_signal_handler): Add name
parameter.
(create_async_event_handler): Likewise.
* async-event.c (struct async_signal_handler) <name>: New field.
(struct async_event_handler) <name>: New field.
(create_async_signal_handler): Assign name.
(create_async_event_handler): Assign name.
* event-top.c (async_init_signals): Pass name when creating
handler.
* infrun.c (_initialize_infrun): Likewise.
* record-btrace.c (record_btrace_push_target): Likewise.
* record-full.c (record_full_open): Likewise.
* remote-notif.c (remote_notif_state_allocate): Likewise.
* remote.c (remote_target::open_1): Likewise.
* tui/tui-win.c (tui_initialize_win): Likewise.
Change-Id: Icd9d9f775542ae5fc2cd148c12f481e7885936d5
|
|
Assign names to event loop file handlers. They will be used in debug
messages when file handlers are invoked.
In GDB, each UI used to get its own unique number, until commit
cbe256847e19 ("Remove ui::num"). Re-introduce this field, and use it to
make a unique name for the handler.
I'm not too sure what goes on in ser-base.c, all I know is that it's
what is used when debugging remotely. I've just named the main handler
"serial". It would be good to have unique names there too. For instance
when debugging with two different remote connections, we'd ideally want
the handlers to have unique names. I didn't do it in this patch though.
gdb/ChangeLog:
* async-event.c (initialize_async_signal_handlers): Pass name to
add_file_handler
* event-top.c (ui_register_input_event_handler): Likewise.
* linux-nat.c (linux_nat_target::async): Likewise.
* run-on-main-thread.c (_initialize_run_on_main_thread):
Likewise
* ser-base.c (reschedule): Likewise.
(ser_base_async): Likewise.
* tui/tui-io.c: Likewise.
* top.h (struct ui) <num>: New field.
* top.c (highest_ui_num): New variable.
(ui::ui): Initialize num.
gdbserver/ChangeLog:
* linux-low.cc (linux_process_target::async): Pass name to
add_file_handler.
* remote-utils.cc (handle_accept_event): Likewise.
(remote_open): Likewise.
gdbsupport/ChangeLog:
* event-loop.h (add_file_handler): Add "name" parameter.
* event-loop.cc (struct file_handler) <name>: New field.
(create_file_handler): Add "name" parameter, assign it to file
handler.
(add_file_handler): Add "name" parameter.
Change-Id: I9f1545f73888ebb6778eb653a618ca44d105f92c
|
|
The async-event.[ch] files were introduced recently as a result of
splitting the event-loop. I believe the copyright year update was
just an oversight. So, this patch fixes that.
gdb/ChangeLog:
2020-05-13 Tankut Baris Aktemur <tankut.baris.aktemur@intel.com>
* async-event.c: Update the copyright year.
* async-event.h: Update the copyright year.
|
|
This is a small cleanup to normalize the structures in async-event.c
with the rest of the code base:
- Remove the unnecessary typedefs
- Fix indentation of struct bodies
- Put comments above fields
No functional changes expected.
gdb/ChangeLog:
* async-event.c (struct async_signal_handler, struct
async_event_handler): Reformat, remove typedef.
|
|
This patch splits out some gdb-specific code from event-loop, into new
files async-event.[ch]. Strictly speaking this code could perhaps be
put into gdbsupport/, but because gdbserver does not currently use it,
it seemed better, for size reasons, to split it out.
gdb/ChangeLog
2020-04-13 Tom Tromey <tom@tromey.com>
* tui/tui-win.c: Include async-event.h.
* remote.c: Include async-event.h.
* remote-notif.c: Include async-event.h.
* record-full.c: Include async-event.h.
* record-btrace.c: Include async-event.h.
* infrun.c: Include async-event.h.
* event-top.c: Include async-event.h.
* event-loop.h: Move some declarations to async-event.h.
* event-loop.c: Don't include ser-event.h or top.h. Move some
code to async-event.c.
* async-event.h: New file.
* async-event.c: New file.
* Makefile.in (COMMON_SFILES): Add async-event.c.
(HFILES_NO_SRCDIR): Add async-event.h.
|