diff options
author | Pedro Alves <palves@redhat.com> | 2015-11-30 16:05:21 +0000 |
---|---|---|
committer | Pedro Alves <palves@redhat.com> | 2015-11-30 18:40:30 +0000 |
commit | 65706a29bac50c2c971227a1945e46502845766b (patch) | |
tree | 170c4ec5d7e5ea2620858226b6561cdbd854a3ea /gdb/remote.c | |
parent | 09df4675f2e4f8f098954f9a38f44d12089f1c4e (diff) | |
download | gdb-65706a29bac50c2c971227a1945e46502845766b.zip gdb-65706a29bac50c2c971227a1945e46502845766b.tar.gz gdb-65706a29bac50c2c971227a1945e46502845766b.tar.bz2 |
Remote thread create/exit events
When testing with "maint set target-non-stop on", a few
threading-related tests expose an issue that requires new RSP packets.
Say there are 3 threads running, 1-3. If GDB tries to stop thread 1,
2 and 3, and then waits for their stops, but meanwhile say, thread 2
exits, GDB hangs forever waiting for a stop for thread 2 that won't
ever happen.
This patch fixes the issue by adding support for thread exit events to
the protocol. However, we don't want these always enabled, as they're
useless most of the time, and would slow down remote debugging. So I
made it so that GDB can enable/disable them, and then made gdb do that
around the cases that need it, which currently is only
infrun.c:stop_all_threads.
In turn, if we have thread exit events, then the extra "thread x
exited" traffic slows down attach-many-short-lived-threads.exp enough
that gdb has trouble keeping up with new threads that are spawned
while gdb tries to stop existing ones. To fix that I added support
for the counterpart thread created events too. Enabling those when we
try to stop threads ensures that new threads never get a chance to
themselves start new threads, killing the race.
gdb/doc/ChangeLog:
2015-11-30 Pedro Alves <palves@redhat.com>
* gdb.texinfo (Remote Configuration): List "set/show remote
thread-events" command in configuration table.
(Stop Reply Packets): Document "T05 create" stop
reason and 'w' stop reply.
(General Query Packets): Document QThreadEvents packet. Document
QThreadEvents qSupported feature.
gdb/gdbserver/ChangeLog:
2015-11-30 Pedro Alves <palves@redhat.com>
* linux-low.c (handle_extended_wait): Assert that the LWP's
waitstatus is TARGET_WAITKIND_IGNORE. If GDB wants to hear about
thread create events, leave the new child's status pending.
(linux_low_filter_event): If GDB wants to hear about thread exit
events, leave the LWP marked dead and don't delete it.
(linux_wait_for_event_filtered): Don't check for thread exit.
(filter_exit_event): New function.
(linux_wait_1): Use it, when returning an exit event.
(linux_resume_one_lwp_throw): Assert that the LWP's
waitstatus is TARGET_WAITKIND_IGNORE.
* remote-utils.c (prepare_resume_reply): Handle
TARGET_WAITKIND_THREAD_CREATED and TARGET_WAITKIND_THREAD_EXITED.
* server.c (report_thread_events): New global.
(handle_general_set): Handle QThreadEvents.
(handle_query) <qSupported>: Handle and report QThreadEvents+;
(handle_target_event): Handle TARGET_WAITKIND_THREAD_CREATED and
TARGET_WAITKIND_THREAD_EXITED.
* server.h (report_thread_events): Declare.
gdb/ChangeLog:
2015-11-30 Pedro Alves <palves@redhat.com>
* NEWS (New commands): Mention "set/show remote thread-events"
commands.
(New remote packets): Mention thread created/exited stop reasons
and QThreadEvents packet.
* infrun.c (disable_thread_events): New function.
(stop_all_threads): Disable/enable thread create/exit events.
Handle TARGET_WAITKIND_THREAD_EXITED.
(handle_inferior_event_1): Handle TARGET_WAITKIND_THREAD_CREATED
and TARGET_WAITKIND_THREAD_EXITED.
* remote.c (remove_child_of_pending_fork): Also remove threads of
threads that have TARGET_WAITKIND_THREAD_EXITED events.
(remote_parse_stop_reply): Handle "create" magic register. Handle
'w' stop reply.
(initialize_remote): Install remote_thread_events as
to_thread_events target hook.
(remote_thread_events): New function.
* target-delegates.c: Regenerate.
* target.c (target_thread_events): New function.
* target.h (struct target_ops) <to_thread_events>: New field.
(target_thread_events): Declare.
* target/waitstatus.c (target_waitstatus_to_string): Handle
TARGET_WAITKIND_THREAD_CREATED and TARGET_WAITKIND_THREAD_EXITED.
* target/waitstatus.h (enum target_waitkind)
<TARGET_WAITKIND_THREAD_CREATED, TARGET_WAITKIND_THREAD_EXITED):
New values.
Diffstat (limited to 'gdb/remote.c')
-rw-r--r-- | gdb/remote.c | 71 |
1 files changed, 66 insertions, 5 deletions
diff --git a/gdb/remote.c b/gdb/remote.c index 7256c23..4daaf08 100644 --- a/gdb/remote.c +++ b/gdb/remote.c @@ -139,6 +139,8 @@ static int remote_is_async_p (struct target_ops *); static void remote_async (struct target_ops *ops, int enable); +static void remote_thread_events (struct target_ops *ops, int enable); + static void sync_remote_interrupt_twice (int signo); static void interrupt_query (void); @@ -1436,6 +1438,9 @@ enum { /* Support for the QNonStop packet. */ PACKET_QNonStop, + /* Support for the QThreadEvents packet. */ + PACKET_QThreadEvents, + /* Support for multi-process extensions. */ PACKET_multiprocess_feature, @@ -4520,7 +4525,8 @@ static const struct protocol_feature remote_protocol_features[] = { PACKET_exec_event_feature }, { "Qbtrace-conf:pt:size", PACKET_DISABLE, remote_supported_packet, PACKET_Qbtrace_conf_pt_size }, - { "vContSupported", PACKET_DISABLE, remote_supported_packet, PACKET_vContSupported } + { "vContSupported", PACKET_DISABLE, remote_supported_packet, PACKET_vContSupported }, + { "QThreadEvents", PACKET_DISABLE, remote_supported_packet, PACKET_QThreadEvents }, }; static char *remote_support_xml; @@ -4613,6 +4619,9 @@ remote_query_supported (void) if (packet_set_cmd_state (PACKET_vContSupported) != AUTO_BOOLEAN_FALSE) q = remote_query_supported_append (q, "vContSupported+"); + if (packet_set_cmd_state (PACKET_QThreadEvents) != AUTO_BOOLEAN_FALSE) + q = remote_query_supported_append (q, "QThreadEvents+"); + /* Keep this one last to work around a gdbserver <= 7.10 bug in the qSupported:xmlRegisters=i386 handling. */ if (remote_support_xml != NULL) @@ -6045,10 +6054,9 @@ remove_child_of_pending_fork (QUEUE (stop_reply_p) *q, = (struct threads_listing_context *) param->input; if (event->ws.kind == TARGET_WAITKIND_FORKED - || event->ws.kind == TARGET_WAITKIND_VFORKED) - { - threads_listing_context_remove (&event->ws, context); - } + || event->ws.kind == TARGET_WAITKIND_VFORKED + || event->ws.kind == TARGET_WAITKIND_THREAD_EXITED) + threads_listing_context_remove (&event->ws, context); return 1; } @@ -6446,6 +6454,11 @@ Packet: '%s'\n"), one used by the original program. */ skipregs = 1; } + else if (strprefix (p, p1, "create")) + { + event->ws.kind = TARGET_WAITKIND_THREAD_CREATED; + p = skip_to_semicolon (p1 + 1); + } else { ULONGEST pnum; @@ -6516,6 +6529,19 @@ Packet: '%s'\n"), event->ws.value.sig = GDB_SIGNAL_UNKNOWN; } break; + case 'w': /* Thread exited. */ + { + char *p; + ULONGEST value; + + event->ws.kind = TARGET_WAITKIND_THREAD_EXITED; + p = unpack_varlen_hex (&buf[1], &value); + event->ws.value.integer = value; + if (*p != ';') + error (_("stop reply packet badly formatted: %s"), buf); + event->ptid = read_ptid (++p, &p); + break; + } case 'W': /* Target exited. */ case 'X': { @@ -12965,6 +12991,7 @@ Specify the serial device it is connected to\n\ remote_ops.to_can_async_p = remote_can_async_p; remote_ops.to_is_async_p = remote_is_async_p; remote_ops.to_async = remote_async; + remote_ops.to_thread_events = remote_thread_events; remote_ops.to_can_do_single_step = remote_can_do_single_step; remote_ops.to_terminal_inferior = remote_terminal_inferior; remote_ops.to_terminal_ours = remote_terminal_ours; @@ -13150,6 +13177,37 @@ remote_async (struct target_ops *ops, int enable) } } +/* Implementation of the to_thread_events method. */ + +static void +remote_thread_events (struct target_ops *ops, int enable) +{ + struct remote_state *rs = get_remote_state (); + size_t size = get_remote_packet_size (); + char *p = rs->buf; + + if (packet_support (PACKET_QThreadEvents) == PACKET_DISABLE) + return; + + xsnprintf (rs->buf, size, "QThreadEvents:%x", enable ? 1 : 0); + putpkt (rs->buf); + getpkt (&rs->buf, &rs->buf_size, 0); + + switch (packet_ok (rs->buf, + &remote_protocol_packets[PACKET_QThreadEvents])) + { + case PACKET_OK: + if (strcmp (rs->buf, "OK") != 0) + error (_("Remote refused setting thread events: %s"), rs->buf); + break; + case PACKET_ERROR: + warning (_("Remote failure reply: %s"), rs->buf); + break; + case PACKET_UNKNOWN: + break; + } +} + static void set_remote_cmd (char *args, int from_tty) { @@ -13694,6 +13752,9 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL, add_packet_config_cmd (&remote_protocol_packets[PACKET_vCtrlC], "vCtrlC", "ctrl-c", 0); + add_packet_config_cmd (&remote_protocol_packets[PACKET_QThreadEvents], + "QThreadEvents", "thread-events", 0); + /* Assert that we've registered "set remote foo-packet" commands for all packet configs. */ { |