binutils-gdb/gdb/remote-notif.c
Pedro Alves 6b8edb5101 remote: one struct remote_state per struct remote_target
'struct remote_state' today contains per-connection state, however
there's only a single global instance of that type.  In order to
support multiple connections, we must have one such object per
connection.

Thus this patch eliminates the 'remote_state' global in favor of
having a remote_state instance per remote_target instance.

The get_remote_state free function is eliminated as well, by making it
a remote_target method instead.

The patch then fixes the fallout by making all free functions that
refer to get_remote_state() directly or indirectly be methods of
remote_target too.

Likewise, remote-fileio.c and remote-notif.c routines are
parameterized with a remote_target pointer too, so they can call into
the right remote_target instance.

References to the global 'get_remote_state ()->remote_desc' to tell
whether the remote target is open (!= nullptr) must be replaced with
something else:

 - Command implementations use a new get_current_remote_target free
 function.

 - remote_target::open_1 checks the exception type instead.

Finally, remote_target and extended_remote_target are made
heap-allocated targets.  As with the earlier core target patches, it
still won't be possible to have more than one remote_target instance
in practice, but this puts us closer.

gdb/ChangeLog:
2018-05-22  Pedro Alves  <palves@redhat.com>

	* remote-fileio.c (remote_fileio_reply, remote_fileio_ioerror)
	(remote_fileio_badfd, remote_fileio_return_errno)
	(remote_fileio_return_success, remote_fileio_func_open)
	(remote_fileio_func_open, remote_fileio_func_close)
	(remote_fileio_func_read, remote_fileio_func_write)
	(remote_fileio_func_lseek, remote_fileio_func_rename)
	(remote_fileio_func_unlink, remote_fileio_func_stat)
	(remote_fileio_func_fstat, remote_fileio_func_gettimeofday)
	(remote_fileio_func_isatty, remote_fileio_func_system): Add
	remote_target parameter.
	(remote_fio_func_map) <func>: Add remote_target parameter.
	(do_remote_fileio_request, remote_fileio_request):
	* remote-fileio.h (remote_fileio_request):
	* remote-notif.c (remote_notif_ack, remote_notif_parse, ): Add
	remote_target parameter.
	(remote_notif_process, handle_notification): Adjust to pass down
	the remote.
	(remote_notif_state_allocate): Add remote_target parameter.  Save
	it.
	* remote-notif.h (struct remote_target): Forward declare.
	(struct notif_client) <parse, ack, can_get_pending_events>: Add
	remote_target parameter.
	(struct remote_notif_state) <remote>: New field.
	(remote_notif_ack, remote_notif_parse): Add remote_target
	parameter.
	(remote_notif_state_allocate, remote_notif_state_allocate): Add
	remote_target parameter.
	* remote.c (OPAQUETHREADBYTES, threadref, gdb_ext_thread_info)
	(threads_listing_context, rmt_thread_action, protocol_feature)
	(packet_reg, stop_reply, stop_reply_p, enum packet_support)
	(packet_result, struct threads_listing_context, remote_state):
	Move definitions and declarations higher up.
	(remote_target) <~remote_target>: Declare.
	(remote_download_command_source, remote_file_put, remote_file_get)
	(remote_file_delete, remote_hostio_pread, remote_hostio_pwrite)
	(remote_hostio_pread_vFile, remote_hostio_send_command)
	(remote_hostio_set_filesystem, remote_hostio_open)
	(remote_hostio_close, remote_hostio_unlink, remote_state)
	(get_remote_state, get_remote_packet_size, get_memory_packet_size)
	(get_memory_write_packet_size, get_memory_read_packet_size)
	(append_pending_thread_resumptions, remote_detach_1)
	(append_resumption, remote_resume_with_vcont)
	(add_current_inferior_and_thread, wait_ns, wait_as)
	(process_stop_reply, remote_notice_new_inferior)
	(process_initial_stop_replies, remote_add_thread)
	(btrace_sync_conf, remote_btrace_maybe_reopen)
	(remove_new_fork_children, kill_new_fork_children)
	(discard_pending_stop_replies, stop_reply_queue_length)
	(check_pending_events_prevent_wildcard_vcont)
	(discard_pending_stop_replies_in_queue, stop_reply)
	(remote_notif_remove_queued_reply, stop_reply *queued_stop_reply)
	(peek_stop_reply, remote_parse_stop_reply, remote_stop_ns)
	(remote_interrupt_as, remote_interrupt_ns)
	(remote_get_noisy_reply, remote_query_attached)
	(remote_add_inferior, remote_current_thread, get_current_thread)
	(set_thread, set_general_thread, set_continue_thread)
	(set_general_process, write_ptid)
	(remote_unpack_thread_info_response, remote_get_threadinfo)
	(parse_threadlist_response, remote_get_threadlist)
	(remote_threadlist_iterator, remote_get_threads_with_ql)
	(remote_get_threads_with_qxfer)
	(remote_get_threads_with_qthreadinfo, extended_remote_restart)
	(get_offsets, remote_check_symbols, remote_supported_packet)
	(remote_query_supported, remote_packet_size)
	(remote_serial_quit_handler, remote_detach_pid)
	(remote_vcont_probe, remote_resume_with_hc)
	(send_interrupt_sequence, interrupt_query)
	(remote_notif_get_pending_events, fetch_register_using_p)
	(send_g_packet, process_g_packet, fetch_registers_using_g)
	(store_register_using_P, store_registers_using_G)
	(set_remote_traceframe, check_binary_download)
	(remote_write_bytes_aux, remote_write_bytes, remote_read_bytes_1)
	(remote_xfer_live_readonly_partial, remote_read_bytes)
	(remote_send_printf, remote_flash_write, readchar)
	(remote_serial_write, putpkt, putpkt_binary, skip_frame)
	(read_frame, getpkt, getpkt_or_notif_sane_1, getpkt_sane)
	(getpkt_or_notif_sane, remote_vkill, remote_kill_k)
	(extended_remote_disable_randomization, extended_remote_run)
	(send_environment_packet, extended_remote_environment_support)
	(extended_remote_set_inferior_cwd, remote_write_qxfer)
	(remote_read_qxfer, push_stop_reply, vcont_r_supported)
	(packet_command): Now methods of ...
	(remote_target): ... this class.
	(m_remote_state) <remote_target>: New field.
	(struct remote_state) <stop_reply_queue,
	remote_async_inferior_event_token, wait_forever_enabled_p>: New
	fields.
	(remote_state::remote_state): Allocate stop_reply_queue.
	(remote_state): Delete global.
	(get_remote_state_raw): Delete.
	(remote_target::get_remote_state): Allocate m_remote_state on
	demand.
	(get_current_remote_target): New.
	(remote_ops, extended_remote_ops): Delete.
	(wait_forever_enabled_p, remote_async_inferior_event_token):
	Delete, moved to struct remote_state.
	(remote_target::close): Delete self.  Destruction bits split to
	...
	(remote_target::~remote_target): ... this.
	(show_memory_packet_size): Adjust to use
	get_current_remote_target.
	(struct protocol_feature) <func>: Add remote_target parameter.
	All callers adjusted.
	(curr_quit_handler_target): New.
	(remote_serial_quit_handler): Reimplement.
	(remote_target::open_1): Adjust to use get_current_remote_target.
	Heap-allocate remote_target/extended_remote_target instances.
	(vcont_builder::vcont_builder): Add remote_target parameter, and
	save it in m_remote.  All callers adjusted.
	(vcont_builder::m_remote): New field.
	(vcont_builder::restart, vcont_builder::flush)
	(vcont_builder::push_action): Use it.
	(remote_target::commit_resume): Use it.
	(struct queue_iter_param) <remote>: New field.
	(remote_target::remove_new_fork_children): Fill in 'remote' field.
	(check_pending_event_prevents_wildcard_vcont_callback_data): New.
	(check_pending_event_prevents_wildcard_vcont_callback)
	(remote_target::check_pending_events_prevent_wildcard_vcont)
	(remote_target::discard_pending_stop_replies)
	(remote_target::discard_pending_stop_replies_in_queue)
	(remote_target::remote_notif_remove_queued_reply): Fill in
	'remote' field.
	(remote_notif_get_pending_events): New.
	(remote_target::readchar, remote_target::remote_serial_write):
	Save/restore curr_quit_handler_target.
	(putpkt): New.
	(kill_new_fork_children): Fill in 'remote' field.
	(packet_command): Use get_current_remote_target, defer to
	remote_target method of same name.
	(scoped_remote_fd::scoped_remote_fd): Add 'remote_target'
	parameter, and save it in m_remote.  All callers adjusted.
	(scoped_remote_fd::release): Use m_remote.
	(scoped_remote_fd::m_remote): New field.
	(remote_file_put, remote_file_get, remote_file_delete): Use
	get_current_remote_target, defer to remote_target method of same
	name.
	(remote_btrace_reset): Add remote_state paremeter.  Update all
	callers.
	(remote_async_inferior_event_handler). Pass down 'data'.
	(remote_new_objfile): Use get_current_remote_target.
	(remote_target::vcont_r_supported): New.
	(set_range_stepping): Use get_current_remote_target and
	remote_target::vcont_r_supported.
	(_initialize_remote): Don't allocate 'remote_state' and
	'stop_reply_queue' globals.
	* remote.h (struct remote_target): Forward declare.
	(getpkt, putpkt, remote_notif_get_pending_events): Add
	'remote_target' parameter.
2018-05-22 18:35:20 +01:00

290 lines
8.1 KiB
C

/* Remote notification in GDB protocol
Copyright (C) 1988-2018 Free Software Foundation, Inc.
This file is part of GDB.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Remote async notification is sent from remote target over RSP.
Each type of notification is represented by an object of
'struct notif', which has a field 'pending_reply'. It is not
NULL when GDB receives a notification from GDBserver, but hasn't
acknowledge yet. Before GDB acknowledges the notification,
GDBserver shouldn't send notification again (see the header comments
in gdbserver/notif.c).
Notifications are processed in an almost-unified approach for both
all-stop mode and non-stop mode, except the timing to process them.
In non-stop mode, notifications are processed in
remote_async_get_pending_events_handler, while in all-stop mode,
they are processed in remote_resume. */
#include "defs.h"
#include "remote.h"
#include "remote-notif.h"
#include "observable.h"
#include "event-loop.h"
#include "target.h"
#include "inferior.h"
#include "infrun.h"
#include "gdbcmd.h"
int notif_debug = 0;
/* Supported clients of notifications. */
static struct notif_client *notifs[] =
{
&notif_client_stop,
};
gdb_static_assert (ARRAY_SIZE (notifs) == REMOTE_NOTIF_LAST);
static void do_notif_event_xfree (void *arg);
/* Parse the BUF for the expected notification NC, and send packet to
acknowledge. */
void
remote_notif_ack (remote_target *remote,
struct notif_client *nc, char *buf)
{
struct notif_event *event = nc->alloc_event ();
struct cleanup *old_chain
= make_cleanup (do_notif_event_xfree, event);
if (notif_debug)
fprintf_unfiltered (gdb_stdlog, "notif: ack '%s'\n",
nc->ack_command);
nc->parse (remote, nc, buf, event);
nc->ack (remote, nc, buf, event);
discard_cleanups (old_chain);
}
/* Parse the BUF for the expected notification NC. */
struct notif_event *
remote_notif_parse (remote_target *remote,
struct notif_client *nc, char *buf)
{
struct notif_event *event = nc->alloc_event ();
struct cleanup *old_chain
= make_cleanup (do_notif_event_xfree, event);
if (notif_debug)
fprintf_unfiltered (gdb_stdlog, "notif: parse '%s'\n", nc->name);
nc->parse (remote, nc, buf, event);
discard_cleanups (old_chain);
return event;
}
DEFINE_QUEUE_P (notif_client_p);
/* Process notifications in STATE's notification queue one by one.
EXCEPT is not expected in the queue. */
void
remote_notif_process (struct remote_notif_state *state,
struct notif_client *except)
{
while (!QUEUE_is_empty (notif_client_p, state->notif_queue))
{
struct notif_client *nc = QUEUE_deque (notif_client_p,
state->notif_queue);
gdb_assert (nc != except);
if (nc->can_get_pending_events (state->remote, nc))
remote_notif_get_pending_events (state->remote, nc);
}
}
static void
remote_async_get_pending_events_handler (gdb_client_data data)
{
gdb_assert (target_is_non_stop_p ());
remote_notif_process ((struct remote_notif_state *) data, NULL);
}
/* Remote notification handler. Parse BUF, queue notification and
update STATE. */
void
handle_notification (struct remote_notif_state *state, char *buf)
{
struct notif_client *nc;
size_t i;
for (i = 0; i < ARRAY_SIZE (notifs); i++)
{
const char *name = notifs[i]->name;
if (startswith (buf, name)
&& buf[strlen (name)] == ':')
break;
}
/* We ignore notifications we don't recognize, for compatibility
with newer stubs. */
if (i == ARRAY_SIZE (notifs))
return;
nc = notifs[i];
if (state->pending_event[nc->id] != NULL)
{
/* We've already parsed the in-flight reply, but the stub for some
reason thought we didn't, possibly due to timeout on its side.
Just ignore it. */
if (notif_debug)
fprintf_unfiltered (gdb_stdlog,
"notif: ignoring resent notification\n");
}
else
{
struct notif_event *event
= remote_notif_parse (state->remote, nc, buf + strlen (nc->name) + 1);
/* Be careful to only set it after parsing, since an error
may be thrown then. */
state->pending_event[nc->id] = event;
/* Notify the event loop there's a stop reply to acknowledge
and that there may be more events to fetch. */
QUEUE_enque (notif_client_p, state->notif_queue, nc);
if (target_is_non_stop_p ())
{
/* In non-stop, We mark REMOTE_ASYNC_GET_PENDING_EVENTS_TOKEN
in order to go on what we were doing and postpone
querying notification events to some point safe to do so.
See details in the function comment of
remote.c:remote_notif_get_pending_events.
In all-stop, GDB may be blocked to wait for the reply, we
shouldn't return to event loop until the expected reply
arrives. For example:
1.1) --> vCont;c
GDB expects getting stop reply 'T05 thread:2'.
1.2) <-- %Notif
<GDB marks the REMOTE_ASYNC_GET_PENDING_EVENTS_TOKEN>
After step #1.2, we return to the event loop, which
notices there is a new event on the
REMOTE_ASYNC_GET_PENDING_EVENTS_TOKEN and calls the
handler, which will send 'vNotif' packet.
1.3) --> vNotif
It is not safe to start a new sequence, because target
is still running and GDB is expecting the stop reply
from stub.
To solve this, whenever we parse a notification
successfully, we don't mark the
REMOTE_ASYNC_GET_PENDING_EVENTS_TOKEN and let GDB blocked
there as before to get the sequence done.
2.1) --> vCont;c
GDB expects getting stop reply 'T05 thread:2'
2.2) <-- %Notif
<Don't mark the REMOTE_ASYNC_GET_PENDING_EVENTS_TOKEN>
2.3) <-- T05 thread:2
These pending notifications can be processed later. */
mark_async_event_handler (state->get_pending_events_token);
}
if (notif_debug)
fprintf_unfiltered (gdb_stdlog,
"notif: Notification '%s' captured\n",
nc->name);
}
}
/* Invoke destructor of EVENT and xfree it. */
void
notif_event_xfree (struct notif_event *event)
{
if (event != NULL && event->dtr != NULL)
event->dtr (event);
xfree (event);
}
/* Cleanup wrapper. */
static void
do_notif_event_xfree (void *arg)
{
notif_event_xfree ((struct notif_event *) arg);
}
/* Return an allocated remote_notif_state. */
struct remote_notif_state *
remote_notif_state_allocate (remote_target *remote)
{
struct remote_notif_state *notif_state = XCNEW (struct remote_notif_state);
notif_state->remote = remote;
notif_state->notif_queue = QUEUE_alloc (notif_client_p, NULL);
/* Register async_event_handler for notification. */
notif_state->get_pending_events_token
= create_async_event_handler (remote_async_get_pending_events_handler,
notif_state);
return notif_state;
}
/* Free STATE and its fields. */
void
remote_notif_state_xfree (struct remote_notif_state *state)
{
int i;
QUEUE_free (notif_client_p, state->notif_queue);
/* Unregister async_event_handler for notification. */
if (state->get_pending_events_token != NULL)
delete_async_event_handler (&state->get_pending_events_token);
for (i = 0; i < REMOTE_NOTIF_LAST; i++)
notif_event_xfree (state->pending_event[i]);
xfree (state);
}
void
_initialize_notif (void)
{
add_setshow_boolean_cmd ("notification", no_class, &notif_debug,
_("\
Set debugging of async remote notification."), _("\
Show debugging of async remote notification."), _("\
When non-zero, debugging output about async remote notifications"
" is enabled."),
NULL,
NULL,
&setdebuglist, &showdebuglist);
}