/* Remote target communications for serial-line targets in custom GDB protocol
- Copyright (C) 1988-2021 Free Software Foundation, Inc.
+ Copyright (C) 1988-2022 Free Software Foundation, Inc.
This file is part of GDB.
#include <algorithm>
#include <unordered_map>
#include "async-event.h"
+#include "gdbsupport/selftest.h"
/* The remote target. */
Otherwise zero, meaning to use the guessed size. */
long explicit_packet_size = 0;
- /* remote_wait is normally called when the target is running and
- waits for a stop reply packet. But sometimes we need to call it
- when the target is already stopped. We can send a "?" packet
- and have remote_wait read the response. Or, if we already have
- the response, we can stash it in BUF and tell remote_wait to
- skip calling getpkt. This flag is set when BUF contains a
- stop reply packet and the target is not waiting. */
- int cached_wait_status = 0;
-
/* True, if in no ack mode. That is, neither GDB nor the stub will
expect acks from each other. The connection is assumed to be
reliable. */
static const target_info remote_target_info = {
"remote",
- N_("Remote serial target in gdb-specific protocol"),
+ N_("Remote target using gdb-specific protocol"),
remote_doc
};
void detach (inferior *, int) override;
void disconnect (const char *, int) override;
- void commit_resume () override;
+ void commit_resumed () override;
void resume (ptid_t, int, enum gdb_signal) override;
ptid_t wait (ptid_t, struct target_waitstatus *, target_wait_flags) override;
+ bool has_pending_events () override;
void fetch_registers (struct regcache *, int) override;
void store_registers (struct regcache *, int) override;
void prepare_to_store (struct regcache *) override;
- void files_info () override;
-
int insert_breakpoint (struct gdbarch *, struct bp_target_info *) override;
int remove_breakpoint (struct gdbarch *, struct bp_target_info *,
const char *extra_thread_info (struct thread_info *) override;
- ptid_t get_ada_task_ptid (long lwp, long thread) override;
+ ptid_t get_ada_task_ptid (long lwp, ULONGEST thread) override;
thread_info *thread_handle_to_thread_info (const gdb_byte *thread_handle,
int handle_len,
bool use_agent (bool use) override;
bool can_use_agent () override;
- struct btrace_target_info *enable_btrace (ptid_t ptid,
- const struct btrace_config *conf) override;
+ struct btrace_target_info *
+ enable_btrace (thread_info *tp, const struct btrace_config *conf) override;
void disable_btrace (struct btrace_target_info *tinfo) override;
const struct btrace_config *btrace_conf (const struct btrace_target_info *) override;
bool augmented_libraries_svr4_read () override;
- bool follow_fork (bool, bool) override;
- void follow_exec (struct inferior *, const char *) override;
+ void follow_fork (inferior *, ptid_t, target_waitkind, bool, bool) override;
+ void follow_exec (inferior *, ptid_t, const char *) override;
int insert_fork_catchpoint (int) override;
int remove_fork_catchpoint (int) override;
int insert_vfork_catchpoint (int) override;
int remove_exec_catchpoint (int) override;
enum exec_direction_kind execution_direction () override;
+ bool supports_memory_tagging () override;
+
+ bool fetch_memtags (CORE_ADDR address, size_t len,
+ gdb::byte_vector &tags, int type) override;
+
+ bool store_memtags (CORE_ADDR address, size_t len,
+ const gdb::byte_vector &tags, int type) override;
+
public: /* Remote specific methods. */
void remote_download_command_source (int num, ULONGEST addr,
target_waitstatus *status);
ptid_t select_thread_for_ambiguous_stop_reply
- (const struct target_waitstatus *status);
+ (const struct target_waitstatus &status);
- void remote_notice_new_inferior (ptid_t currthread, int executing);
+ void remote_notice_new_inferior (ptid_t currthread, bool executing);
+ void print_one_stopped_thread (thread_info *thread);
void process_initial_stop_replies (int from_tty);
- thread_info *remote_add_thread (ptid_t ptid, bool running, bool executing);
+ thread_info *remote_add_thread (ptid_t ptid, bool running, bool executing,
+ bool silent_p);
void btrace_sync_conf (const btrace_config *conf);
void remote_btrace_maybe_reopen ();
void remove_new_fork_children (threads_listing_context *context);
- void kill_new_fork_children (int pid);
+ void kill_new_fork_children (inferior *inf);
void discard_pending_stop_replies (struct inferior *inf);
int stop_reply_queue_length ();
void check_pending_events_prevent_wildcard_vcont
- (int *may_global_wildcard_vcont);
+ (bool *may_global_wildcard_vcont);
void discard_pending_stop_replies_in_queue ();
struct stop_reply *remote_notif_remove_queued_reply (ptid_t ptid);
bool vcont_r_supported ();
- void packet_command (const char *args, int from_tty);
+private:
-private: /* data fields */
+ bool start_remote_1 (int from_tty, int extended_p);
/* The remote state. Don't reference this directly. Use the
get_remote_state method instead. */
static const target_info extended_remote_target_info = {
"extended-remote",
- N_("Extended remote serial target in gdb-specific protocol"),
+ N_("Extended remote target using gdb-specific protocol"),
remote_doc
};
bool supports_disable_randomization () override;
};
+struct stop_reply : public notif_event
+{
+ ~stop_reply ();
+
+ /* The identifier of the thread about this event */
+ ptid_t ptid;
+
+ /* The remote state this event is associated with. When the remote
+ connection, represented by a remote_state object, is closed,
+ all the associated stop_reply events should be released. */
+ struct remote_state *rs;
+
+ struct target_waitstatus ws;
+
+ /* The architecture associated with the expedited registers. */
+ gdbarch *arch;
+
+ /* Expedited registers. This makes remote debugging a bit more
+ efficient for those targets that provide critical registers as
+ part of their normal status mechanism (as another roundtrip to
+ fetch them is avoided). */
+ std::vector<cached_reg_t> regcache;
+
+ enum target_stop_reason stop_reason;
+
+ CORE_ADDR watch_data_address;
+
+ int core;
+};
+
+/* See remote.h. */
+
+bool
+is_remote_target (process_stratum_target *target)
+{
+ remote_target *rt = dynamic_cast<remote_target *> (target);
+ return rt != nullptr;
+}
+
/* Per-program-space data key. */
static const struct program_space_key<char, gdb::xfree_deleter<char>>
remote_pspace_data;
remote exec-file commands. While the remote exec-file setting is
per-program-space, the set/show machinery uses this as the
location of the remote exec-file value. */
-static char *remote_exec_file_var;
+static std::string remote_exec_file_var;
/* The size to align memory write packets, when practical. The protocol
does not guarantee any alignment, and gdb will generate short
static CORE_ADDR remote_address_masked (CORE_ADDR);
-static void print_packet (const char *);
-
static int stub_unpack_int (const char *buff, int fieldlength);
struct packet_config;
-static void show_packet_config_cmd (struct packet_config *config);
-
static void show_remote_protocol_packet_cmd (struct ui_file *file,
int from_tty,
struct cmd_list_element *c,
set_remote_exec_file (const char *ignored, int from_tty,
struct cmd_list_element *c)
{
- gdb_assert (remote_exec_file_var != NULL);
- set_pspace_remote_exec_file (current_program_space, remote_exec_file_var);
+ set_pspace_remote_exec_file (current_program_space,
+ remote_exec_file_var.c_str ());
}
/* The "set/show remote exec-file" show command hook. */
have an associated command always have this set to auto. */
enum auto_boolean detect;
+ /* The "show remote foo-packet" command created for this packet. */
+ cmd_list_element *show_cmd;
+
/* Does the target support this packet? */
enum packet_support support;
};
static enum packet_support packet_support (int packet);
static void
-show_packet_config_cmd (struct packet_config *config)
+show_packet_config_cmd (ui_file *file, struct packet_config *config)
{
const char *support = "internal-error";
switch (config->detect)
{
case AUTO_BOOLEAN_AUTO:
- printf_filtered (_("Support for the `%s' packet "
- "is auto-detected, currently %s.\n"),
- config->name, support);
+ fprintf_filtered (file,
+ _("Support for the `%s' packet "
+ "is auto-detected, currently %s.\n"),
+ config->name, support);
break;
case AUTO_BOOLEAN_TRUE:
case AUTO_BOOLEAN_FALSE:
- printf_filtered (_("Support for the `%s' packet is currently %s.\n"),
- config->name, support);
+ fprintf_filtered (file,
+ _("Support for the `%s' packet is currently %s.\n"),
+ config->name, support);
break;
}
}
add_packet_config_cmd (struct packet_config *config, const char *name,
const char *title, int legacy)
{
- char *set_doc;
- char *show_doc;
- char *cmd_name;
-
config->name = name;
config->title = title;
- set_doc = xstrprintf ("Set use of remote protocol `%s' (%s) packet.",
- name, title);
- show_doc = xstrprintf ("Show current use of remote "
- "protocol `%s' (%s) packet.",
- name, title);
+ gdb::unique_xmalloc_ptr<char> set_doc
+ = xstrprintf ("Set use of remote protocol `%s' (%s) packet.",
+ name, title);
+ gdb::unique_xmalloc_ptr<char> show_doc
+ = xstrprintf ("Show current use of remote protocol `%s' (%s) packet.",
+ name, title);
/* set/show TITLE-packet {auto,on,off} */
- cmd_name = xstrprintf ("%s-packet", title);
- add_setshow_auto_boolean_cmd (cmd_name, class_obscure,
- &config->detect, set_doc,
- show_doc, NULL, /* help_doc */
- NULL,
- show_remote_protocol_packet_cmd,
- &remote_set_cmdlist, &remote_show_cmdlist);
- /* The command code copies the documentation strings. */
- xfree (set_doc);
- xfree (show_doc);
+ gdb::unique_xmalloc_ptr<char> cmd_name = xstrprintf ("%s-packet", title);
+ set_show_commands cmds
+ = add_setshow_auto_boolean_cmd (cmd_name.release (), class_obscure,
+ &config->detect, set_doc.get (),
+ show_doc.get (), NULL, /* help_doc */
+ NULL,
+ show_remote_protocol_packet_cmd,
+ &remote_set_cmdlist, &remote_show_cmdlist);
+ config->show_cmd = cmds.show;
+
/* set/show remote NAME-packet {auto,on,off} -- legacy. */
if (legacy)
{
- char *legacy_name;
-
- legacy_name = xstrprintf ("%s-packet", name);
- add_alias_cmd (legacy_name, cmd_name, class_obscure, 0,
+ /* It's not clear who should take ownership of this string, so, for
+ now, make it static, and give copies to each of the add_alias_cmd
+ calls below. */
+ static gdb::unique_xmalloc_ptr<char> legacy_name
+ = xstrprintf ("%s-packet", name);
+ add_alias_cmd (legacy_name.get (), cmds.set, class_obscure, 0,
&remote_set_cmdlist);
- add_alias_cmd (legacy_name, cmd_name, class_obscure, 0,
+ add_alias_cmd (legacy_name.get (), cmds.show, class_obscure, 0,
&remote_show_cmdlist);
}
}
/* Support TARGET_WAITKIND_NO_RESUMED. */
PACKET_no_resumed,
+ /* Support for memory tagging, allocation tag fetch/store
+ packets and the tag violation stop replies. */
+ PACKET_memory_tagging_feature,
+
PACKET_MAX
};
case AUTO_BOOLEAN_AUTO:
return config->support;
default:
- gdb_assert_not_reached (_("bad switch"));
+ gdb_assert_not_reached ("bad switch");
}
}
const char *value)
{
struct packet_config *packet;
+ gdb_assert (c->var.has_value ());
for (packet = remote_protocol_packets;
packet < &remote_protocol_packets[PACKET_MAX];
packet++)
{
- if (&packet->detect == c->var)
+ if (c == packet->show_cmd)
{
- show_packet_config_cmd (packet);
+ show_packet_config_cmd (file, packet);
return;
}
}
for (i = 0; i < NR_Z_PACKET_TYPES; i++)
{
- show_packet_config_cmd (&remote_protocol_packets[PACKET_Z0 + i]);
+ show_packet_config_cmd (file, &remote_protocol_packets[PACKET_Z0 + i]);
}
}
return packet_support (PACKET_exec_event_feature) == PACKET_ENABLE;
}
+/* Returns true if memory tagging is supported, false otherwise. */
+
+static bool
+remote_memory_tagging_p ()
+{
+ return packet_support (PACKET_memory_tagging_feature) == PACKET_ENABLE;
+}
+
/* Insert fork catchpoint target routine. If fork events are enabled
then return success, nothing more to do. */
inf = add_inferior_with_spaces ();
}
switch_to_inferior_no_thread (inf);
- push_target (this);
+ inf->push_target (this);
inferior_appeared (inf, pid);
}
ptid_t ptid);
/* Add thread PTID to GDB's thread list. Tag it as executing/running
- according to RUNNING. */
+ according to EXECUTING and RUNNING respectively. If SILENT_P (or the
+ remote_state::starting_up flag) is true then the new thread is added
+ silently, otherwise the new thread will be announced to the user. */
thread_info *
-remote_target::remote_add_thread (ptid_t ptid, bool running, bool executing)
+remote_target::remote_add_thread (ptid_t ptid, bool running, bool executing,
+ bool silent_p)
{
struct remote_state *rs = get_remote_state ();
struct thread_info *thread;
consider that a single-threaded target, mentioning a new thread
might be confusing to the user. Be silent then, preserving the
age old behavior. */
- if (rs->starting_up)
+ if (rs->starting_up || silent_p)
thread = add_thread_silent (this, ptid);
else
thread = add_thread (this, ptid);
thread is (internally) executing or stopped. */
void
-remote_target::remote_notice_new_inferior (ptid_t currthread, int executing)
+remote_target::remote_notice_new_inferior (ptid_t currthread, bool executing)
{
/* In non-stop mode, we assume new found threads are (externally)
running until proven otherwise with a stop reply. In all-stop,
we can only get here if all threads are stopped. */
- int running = target_is_non_stop_p () ? 1 : 0;
+ bool running = target_is_non_stop_p ();
/* If this is a new thread, add it to GDB's thread list.
If we leave it up to WFI to do this, bad things will happen. */
{
/* We're seeing an event on a thread id we knew had exited.
This has to be a new thread reusing the old id. Add it. */
- remote_add_thread (currthread, running, executing);
+ remote_add_thread (currthread, running, executing, false);
return;
}
else
{
thread_info *thr
- = remote_add_thread (currthread, running, executing);
+ = remote_add_thread (currthread, running, executing, false);
switch_to_thread (thr);
}
return;
/* This is really a new thread. Add it. */
thread_info *new_thr
- = remote_add_thread (currthread, running, executing);
+ = remote_add_thread (currthread, running, executing, false);
/* If we found a new inferior, let the common code do whatever
it needs to with it (e.g., read shared libraries, insert
pp = unpack_varlen_hex (p + 1, &tid);
if (obuf)
*obuf = pp;
- return ptid_t (pid, tid, 0);
+ return ptid_t (pid, tid);
}
/* No multi-process. Just a tid. */
if (obuf)
*obuf = pp;
- return ptid_t (pid, tid, 0);
+ return ptid_t (pid, tid);
}
static int
executing until proven otherwise with a stop reply.
In all-stop, we can only get here if all threads are
stopped. */
- int executing = target_is_non_stop_p () ? 1 : 0;
+ bool executing = target_is_non_stop_p ();
remote_notice_new_inferior (item.ptid, executing);
/* Implement the to_get_ada_task_ptid function for the remote targets. */
ptid_t
-remote_target::get_ada_task_ptid (long lwp, long thread)
+remote_target::get_ada_task_ptid (long lwp, ULONGEST thread)
{
- return ptid_t (inferior_ptid.pid (), lwp, 0);
+ return ptid_t (inferior_ptid.pid (), lwp);
}
\f
/* Print info about a thread that was found already stopped on
connection. */
-static void
-print_one_stopped_thread (struct thread_info *thread)
+void
+remote_target::print_one_stopped_thread (thread_info *thread)
{
- struct target_waitstatus *ws = &thread->suspend.waitstatus;
+ target_waitstatus ws;
+
+ /* If there is a pending waitstatus, use it. If there isn't it's because
+ the thread's stop was reported with TARGET_WAITKIND_STOPPED / GDB_SIGNAL_0
+ and process_initial_stop_replies decided it wasn't interesting to save
+ and report to the core. */
+ if (thread->has_pending_waitstatus ())
+ {
+ ws = thread->pending_waitstatus ();
+ thread->clear_pending_waitstatus ();
+ }
+ else
+ {
+ ws.set_stopped (GDB_SIGNAL_0);
+ }
switch_to_thread (thread);
- thread->suspend.stop_pc = get_frame_pc (get_current_frame ());
+ thread->set_stop_pc (get_frame_pc (get_current_frame ()));
set_current_sal_from_frame (get_current_frame ());
- thread->suspend.waitstatus_pending_p = 0;
+ /* For "info program". */
+ set_last_target_status (this, thread->ptid, ws);
- if (ws->kind == TARGET_WAITKIND_STOPPED)
+ if (ws.kind () == TARGET_WAITKIND_STOPPED)
{
- enum gdb_signal sig = ws->value.sig;
+ enum gdb_signal sig = ws.sig ();
if (signal_print_state (sig))
gdb::observers::signal_received.notify (sig);
struct thread_info *lowest_stopped = NULL;
struct thread_info *first = NULL;
+ /* This is only used when the target is non-stop. */
+ gdb_assert (target_is_non_stop_p ());
+
/* Consume the initial pending events. */
while (pending_stop_replies-- > 0)
{
struct target_waitstatus ws;
int ignore_event = 0;
- memset (&ws, 0, sizeof (ws));
event_ptid = target_wait (waiton_ptid, &ws, TARGET_WNOHANG);
if (remote_debug)
- print_target_wait_results (waiton_ptid, event_ptid, &ws);
+ print_target_wait_results (waiton_ptid, event_ptid, ws);
- switch (ws.kind)
+ switch (ws.kind ())
{
case TARGET_WAITKIND_IGNORE:
case TARGET_WAITKIND_NO_RESUMED:
ignore_event = 1;
break;
- case TARGET_WAITKIND_EXECD:
- xfree (ws.value.execd_pathname);
- break;
default:
break;
}
thread_info *evthread = find_thread_ptid (this, event_ptid);
- if (ws.kind == TARGET_WAITKIND_STOPPED)
+ if (ws.kind () == TARGET_WAITKIND_STOPPED)
{
- enum gdb_signal sig = ws.value.sig;
+ enum gdb_signal sig = ws.sig ();
/* Stubs traditionally report SIGTRAP as initial signal,
instead of signal 0. Suppress it. */
if (sig == GDB_SIGNAL_TRAP)
sig = GDB_SIGNAL_0;
- evthread->suspend.stop_signal = sig;
- ws.value.sig = sig;
+ evthread->set_stop_signal (sig);
+ ws.set_stopped (sig);
}
- evthread->suspend.waitstatus = ws;
-
- if (ws.kind != TARGET_WAITKIND_STOPPED
- || ws.value.sig != GDB_SIGNAL_0)
- evthread->suspend.waitstatus_pending_p = 1;
+ if (ws.kind () != TARGET_WAITKIND_STOPPED
+ || ws.sig () != GDB_SIGNAL_0)
+ evthread->set_pending_waitstatus (ws);
set_executing (this, event_ptid, false);
set_running (this, event_ptid, false);
the inferiors. */
if (!non_stop)
{
- stop_all_threads ();
+ {
+ /* At this point, the remote target is not async. It needs to be for
+ the poll in stop_all_threads to consider events from it, so enable
+ it temporarily. */
+ gdb_assert (!this->is_async_p ());
+ SCOPE_EXIT { target_async (0); };
+ target_async (1);
+ stop_all_threads ();
+ }
/* If all threads of an inferior were already stopped, we
haven't setup the inferior yet. */
else if (thread->state != THREAD_STOPPED)
continue;
- if (selected == NULL
- && thread->suspend.waitstatus_pending_p)
+ if (selected == nullptr && thread->has_pending_waitstatus ())
selected = thread;
if (lowest_stopped == NULL
print_one_stopped_thread (thread);
}
-
- /* For "info program". */
- thread_info *thread = inferior_thread ();
- if (thread->state == THREAD_STOPPED)
- set_last_target_status (this, inferior_ptid, thread->suspend.waitstatus);
}
-/* Start the remote connection and sync state. */
+/* Mark a remote_target as marking (by setting the starting_up flag within
+ its remote_state) for the lifetime of this object. The reference count
+ on the remote target is temporarily incremented, to prevent the target
+ being deleted under our feet. */
-void
-remote_target::start_remote (int from_tty, int extended_p)
+struct scoped_mark_target_starting
+{
+ /* Constructor, TARGET is the target to be marked as starting, its
+ reference count will be incremented. */
+ scoped_mark_target_starting (remote_target *target)
+ : m_remote_target (target)
+ {
+ m_remote_target->incref ();
+ remote_state *rs = m_remote_target->get_remote_state ();
+ rs->starting_up = true;
+ }
+
+ /* Destructor, mark the target being worked on as no longer starting, and
+ decrement the reference count. */
+ ~scoped_mark_target_starting ()
+ {
+ remote_state *rs = m_remote_target->get_remote_state ();
+ rs->starting_up = false;
+ decref_target (m_remote_target);
+ }
+
+private:
+
+ /* The target on which we are operating. */
+ remote_target *m_remote_target;
+};
+
+/* Helper for remote_target::start_remote, start the remote connection and
+ sync state. Return true if everything goes OK, otherwise, return false.
+ This function exists so that the scoped_restore created within it will
+ expire before we return to remote_target::start_remote. */
+
+bool
+remote_target::start_remote_1 (int from_tty, int extended_p)
{
REMOTE_SCOPED_DEBUG_ENTER_EXIT;
Ctrl-C before we're connected and synced up can't interrupt the
target. Instead, it offers to drop the (potentially wedged)
connection. */
- rs->starting_up = 1;
+ scoped_mark_target_starting target_is_starting (this);
QUIT;
{
if (!extended_p)
error (_("The target is not running (try extended-remote?)"));
-
- /* We're connected, but not running. Drop out before we
- call start_remote. */
- rs->starting_up = 0;
- return;
+ return false;
}
else
{
/* Use the previously fetched status. */
gdb_assert (wait_status != NULL);
- strcpy (rs->buf.data (), wait_status);
- rs->cached_wait_status = 1;
+ struct notif_event *reply
+ = remote_notif_parse (this, ¬if_client_stop, wait_status);
+ push_stop_reply ((struct stop_reply *) reply);
::start_remote (from_tty); /* Initialize gdb process mechanisms. */
}
{
if (!extended_p)
error (_("The target is not running (try extended-remote?)"));
-
- /* We're connected, but not running. Drop out before we
- call start_remote. */
- rs->starting_up = 0;
- return;
+ return false;
}
/* Report all signals during attach/startup. */
previously; find out where things are at. */
remote_btrace_maybe_reopen ();
- /* The thread and inferior lists are now synchronized with the
- target, our symbols have been relocated, and we're merged the
- target's tracepoints with ours. We're done with basic start
- up. */
- rs->starting_up = 0;
+ return true;
+}
+
+/* Start the remote connection and sync state. */
- /* Maybe breakpoints are global and need to be inserted now. */
- if (breakpoints_should_be_inserted_now ())
+void
+remote_target::start_remote (int from_tty, int extended_p)
+{
+ if (start_remote_1 (from_tty, extended_p)
+ && breakpoints_should_be_inserted_now ())
insert_breakpoints ();
}
/* If this is a function address, return the start of code
instead of any data function descriptor. */
- sym_addr = gdbarch_convert_from_func_ptr_addr (target_gdbarch (),
- sym_addr,
- current_top_target ());
+ sym_addr = gdbarch_convert_from_func_ptr_addr
+ (target_gdbarch (), sym_addr, current_inferior ()->top_target ());
xsnprintf (msg.data (), get_remote_packet_size (), "qSymbol:%s:%s",
phex_nz (sym_addr, addr_size), &reply[8]);
{ "vContSupported", PACKET_DISABLE, remote_supported_packet, PACKET_vContSupported },
{ "QThreadEvents", PACKET_DISABLE, remote_supported_packet, PACKET_QThreadEvents },
{ "no-resumed", PACKET_DISABLE, remote_supported_packet, PACKET_no_resumed },
+ { "memory-tagging", PACKET_DISABLE, remote_supported_packet,
+ PACKET_memory_tagging_feature },
};
static char *remote_support_xml;
if (packet_set_cmd_state (PACKET_no_resumed) != AUTO_BOOLEAN_FALSE)
remote_query_supported_append (&q, "no-resumed+");
+ if (packet_set_cmd_state (PACKET_memory_tagging_feature)
+ != AUTO_BOOLEAN_FALSE)
+ remote_query_supported_append (&q, "memory-tagging+");
+
/* Keep this one last to work around a gdbserver <= 7.10 bug in
the qSupported:xmlRegisters=i386 handling. */
if (remote_support_xml != NULL
pop_all_targets_at_and_above (process_stratum);
generic_mourn_inferior ();
}
+
+ /* Don't rely on target_close doing this when the target is popped
+ from the last remote inferior above, because something may be
+ holding a reference to the target higher up on the stack, meaning
+ target_close won't be called yet. We lost the connection to the
+ target, so clear these now, otherwise we may later throw
+ TARGET_CLOSE_ERROR while trying to tell the remote target to
+ close the file. */
+ fileio_handles_invalidate_target (target);
}
static void
remote_fileio_reset ();
reopen_exec_file ();
- reread_symbols ();
+ reread_symbols (from_tty);
remote_target *remote
= (extended_p ? new extended_remote_target () : new remote_target ());
}
/* Switch to using the remote target now. */
- push_target (std::move (target_holder));
+ current_inferior ()->push_target (std::move (target_holder));
/* Register extra event sources in the event loop. */
rs->remote_async_inferior_event_token
/* Reset the target state; these things will be queried either by
remote_query_supported or as they are needed. */
reset_all_packet_configs_support ();
- rs->cached_wait_status = 0;
rs->explicit_packet_size = 0;
rs->noack_mode = 0;
rs->extended = extended_p;
rs->wait_forever_enabled_p = 1;
}
+/* Determine if WS represents a fork status. */
+
+static bool
+is_fork_status (target_waitkind kind)
+{
+ return (kind == TARGET_WAITKIND_FORKED
+ || kind == TARGET_WAITKIND_VFORKED);
+}
+
+/* Return THREAD's pending status if it is a pending fork parent, else
+ return nullptr. */
+
+static const target_waitstatus *
+thread_pending_fork_status (struct thread_info *thread)
+{
+ const target_waitstatus &ws
+ = (thread->has_pending_waitstatus ()
+ ? thread->pending_waitstatus ()
+ : thread->pending_follow);
+
+ if (!is_fork_status (ws.kind ()))
+ return nullptr;
+
+ return &ws;
+}
+
/* Detach the specified process. */
void
if (from_tty && !rs->extended && number_of_live_inferiors (this) == 1)
puts_filtered (_("Ending remote debugging.\n"));
+ /* See if any thread of the inferior we are detaching has a pending fork
+ status. In that case, we must detach from the child resulting from
+ that fork. */
+ for (thread_info *thread : inf->non_exited_threads ())
+ {
+ const target_waitstatus *ws = thread_pending_fork_status (thread);
+
+ if (ws == nullptr)
+ continue;
+
+ remote_detach_pid (ws->child_ptid ().pid ());
+ }
+
+ /* Check also for any pending fork events in the stop reply queue. */
+ remote_notif_get_pending_events (¬if_client_stop);
+ for (stop_reply_up &reply : rs->stop_reply_queue)
+ {
+ if (reply->ptid.pid () != pid)
+ continue;
+
+ if (!is_fork_status (reply->ws.kind ()))
+ continue;
+
+ remote_detach_pid (reply->ws.child_ptid ().pid ());
+ }
+
thread_info *tp = find_thread_ptid (this, inferior_ptid);
/* Check to see if we are detaching a fork parent. Note that if we
are detaching a fork child, tp == NULL. */
is_fork_parent = (tp != NULL
- && tp->pending_follow.kind == TARGET_WAITKIND_FORKED);
+ && tp->pending_follow.kind () == TARGET_WAITKIND_FORKED);
/* If doing detach-on-fork, we don't mourn, because that will delete
breakpoints that should be available for the followed inferior. */
it is named remote_follow_fork in anticipation of using it for the
remote target as well. */
-bool
-remote_target::follow_fork (bool follow_child, bool detach_fork)
+void
+remote_target::follow_fork (inferior *child_inf, ptid_t child_ptid,
+ target_waitkind fork_kind, bool follow_child,
+ bool detach_fork)
{
+ process_stratum_target::follow_fork (child_inf, child_ptid,
+ fork_kind, follow_child, detach_fork);
+
struct remote_state *rs = get_remote_state ();
- enum target_waitkind kind = inferior_thread ()->pending_follow.kind;
- if ((kind == TARGET_WAITKIND_FORKED && remote_fork_event_p (rs))
- || (kind == TARGET_WAITKIND_VFORKED && remote_vfork_event_p (rs)))
+ if ((fork_kind == TARGET_WAITKIND_FORKED && remote_fork_event_p (rs))
+ || (fork_kind == TARGET_WAITKIND_VFORKED && remote_vfork_event_p (rs)))
{
/* When following the parent and detaching the child, we detach
the child here. For the case of following the child and
if (detach_fork && !follow_child)
{
/* Detach the fork child. */
- ptid_t child_ptid;
- pid_t child_pid;
-
- child_ptid = inferior_thread ()->pending_follow.value.related_pid;
- child_pid = child_ptid.pid ();
-
- remote_detach_pid (child_pid);
+ remote_detach_pid (child_ptid.pid ());
}
}
-
- return false;
}
/* Target follow-exec function for remote targets. Save EXECD_PATHNAME
- in the program space of the new inferior. On entry and at return the
- current inferior is the exec'ing inferior. INF is the new exec'd
- inferior, which may be the same as the exec'ing inferior unless
- follow-exec-mode is "new". */
+ in the program space of the new inferior. */
void
-remote_target::follow_exec (struct inferior *inf, const char *execd_pathname)
+remote_target::follow_exec (inferior *follow_inf, ptid_t ptid,
+ const char *execd_pathname)
{
+ process_stratum_target::follow_exec (follow_inf, ptid, execd_pathname);
+
/* We know that this is a target file name, so if it has the "target:"
prefix we strip it off before saving it in the program space. */
if (is_target_filename (execd_pathname))
execd_pathname += strlen (TARGET_SYSROOT_PREFIX);
- set_pspace_remote_exec_file (inf->pspace, execd_pathname);
+ set_pspace_remote_exec_file (follow_inf->pspace, execd_pathname);
}
/* Same as remote_detach, but don't send the "D" packet; just disconnect. */
if (packet_support (PACKET_vAttach) == PACKET_DISABLE)
error (_("This target does not support attaching to a process"));
- if (from_tty)
- {
- const char *exec_file = get_exec_file (0);
-
- if (exec_file)
- printf_unfiltered (_("Attaching to program: %s, %s\n"), exec_file,
- target_pid_to_str (ptid_t (pid)).c_str ());
- else
- printf_unfiltered (_("Attaching to %s\n"),
- target_pid_to_str (ptid_t (pid)).c_str ());
- }
+ target_announce_attach (from_tty, pid);
xsnprintf (rs->buf.data (), get_remote_packet_size (), "vAttach;%x", pid);
putpkt (rs->buf);
ptid. */
ptid_t curr_ptid = remote_current_thread (ptid_t (pid));
- /* Add the main thread to the thread list. */
- thread_info *thr = add_thread_silent (this, curr_ptid);
+ /* Add the main thread to the thread list. We add the thread
+ silently in this case (the final true parameter). */
+ thread_info *thr = remote_add_thread (curr_ptid, true, true, true);
switch_to_thread (thr);
-
- /* Don't consider the thread stopped until we've processed the
- saved stop reply. */
- set_executing (this, thr->ptid, true);
}
/* Next, if the target can specify a description, read it. We do
/* Use the previously fetched status. */
gdb_assert (wait_status != NULL);
- if (target_can_async_p ())
- {
- struct notif_event *reply
- = remote_notif_parse (this, ¬if_client_stop, wait_status);
+ struct notif_event *reply
+ = remote_notif_parse (this, ¬if_client_stop, wait_status);
- push_stop_reply ((struct stop_reply *) reply);
-
- target_async (1);
- }
- else
- {
- gdb_assert (wait_status != NULL);
- strcpy (rs->buf.data (), wait_status);
- rs->cached_wait_status = 1;
- }
+ push_stop_reply ((struct stop_reply *) reply);
}
else
{
gdb_assert (wait_status == NULL);
gdb_assert (target_can_async_p ());
- target_async (1);
}
}
ptid_t nptid;
/* All (-1) threads of process. */
- nptid = ptid_t (ptid.pid (), -1, 0);
+ nptid = ptid_t (ptid.pid (), -1);
p += xsnprintf (p, endp - p, ":");
p = write_ptid (p, endp, nptid);
{
for (thread_info *thread : all_non_exited_threads (this, ptid))
if (inferior_ptid != thread->ptid
- && thread->suspend.stop_signal != GDB_SIGNAL_0)
+ && thread->stop_signal () != GDB_SIGNAL_0)
{
p = append_resumption (p, endp, thread->ptid,
- 0, thread->suspend.stop_signal);
- thread->suspend.stop_signal = GDB_SIGNAL_0;
+ 0, thread->stop_signal ());
+ thread->set_stop_signal (GDB_SIGNAL_0);
resume_clear_thread_private_info (thread);
}
for (thread_info *tp : all_non_exited_threads (this, ptid))
get_remote_thread_info (tp)->set_resumed ();
- /* We are about to start executing the inferior, let's register it
- with the event loop. NOTE: this is the one place where all the
- execution commands end up. We could alternatively do this in each
- of the execution commands in infcmd.c. */
- /* FIXME: ezannoni 1999-09-28: We may need to move this out of here
- into infcmd.c in order to allow inferior function calls to work
- NOT asynchronously. */
- if (target_can_async_p ())
- target_async (1);
-
/* We've just told the target to resume. The remote server will
wait for the inferior to stop, and then send a stop reply. In
the mean time, we can't start another command/query ourselves
rs->waiting_for_stop_reply = 1;
}
-static int is_pending_fork_parent_thread (struct thread_info *thread);
-
/* Private per-inferior info for target remote processes. */
struct remote_inferior : public private_inferior
/* to_commit_resume implementation. */
void
-remote_target::commit_resume ()
+remote_target::commit_resumed ()
{
- int any_process_wildcard;
- int may_global_wildcard_vcont;
-
/* If connected in all-stop mode, we'd send the remote resume
request directly from remote_resume. Likewise if
reverse-debugging, as there are no defined vCont actions for
(vCont;c). We can still send process-wide wildcards though. */
/* Start by assuming a global wildcard (vCont;c) is possible. */
- may_global_wildcard_vcont = 1;
+ bool may_global_wildcard_vcont = true;
/* And assume every process is individually wildcard-able too. */
for (inferior *inf : all_non_exited_inferiors (this))
disable process and global wildcard resumes appropriately. */
check_pending_events_prevent_wildcard_vcont (&may_global_wildcard_vcont);
+ bool any_pending_vcont_resume = false;
+
for (thread_info *tp : all_non_exited_threads (this))
{
remote_thread_info *priv = get_remote_thread_info (tp);
/* And if we can't wildcard a process, we can't wildcard
everything either. */
- may_global_wildcard_vcont = 0;
+ may_global_wildcard_vcont = false;
continue;
}
+ if (priv->get_resume_state () == resume_state::RESUMED_PENDING_VCONT)
+ any_pending_vcont_resume = true;
+
/* If a thread is the parent of an unfollowed fork, then we
can't do a global wildcard, as that would resume the fork
child. */
- if (is_pending_fork_parent_thread (tp))
- may_global_wildcard_vcont = 0;
+ if (thread_pending_fork_status (tp) != nullptr)
+ may_global_wildcard_vcont = false;
}
+ /* We didn't have any resumed thread pending a vCont resume, so nothing to
+ do. */
+ if (!any_pending_vcont_resume)
+ return;
+
/* Now let's build the vCont packet(s). Actions must be appended
from narrower to wider scopes (thread -> process -> global). If
we end up with too many actions for a single packet vcont_builder
gdb_assert (!thread_is_in_step_over_chain (tp));
+ /* We should never be commit-resuming a thread that has a stop reply.
+ Otherwise, we would end up reporting a stop event for a thread while
+ it is running on the remote target. */
+ remote_state *rs = get_remote_state ();
+ for (const auto &stop_reply : rs->stop_reply_queue)
+ gdb_assert (stop_reply->ptid != tp->ptid);
+
const resumed_pending_vcont_info &info
= remote_thr->resumed_pending_vcont_info ();
/* Now check whether we can send any process-wide wildcard. This is
to avoid sending a global wildcard in the case nothing is
supposed to be resumed. */
- any_process_wildcard = 0;
+ bool any_process_wildcard = false;
for (inferior *inf : all_non_exited_inferiors (this))
{
if (get_remote_inferior (inf)->may_wildcard_vcont)
{
- any_process_wildcard = 1;
+ any_process_wildcard = true;
break;
}
}
vcont_builder.flush ();
}
+/* Implementation of target_has_pending_events. */
+
+bool
+remote_target::has_pending_events ()
+{
+ if (target_can_async_p ())
+ {
+ remote_state *rs = get_remote_state ();
+
+ if (async_event_handler_marked (rs->remote_async_inferior_event_token))
+ return true;
+
+ /* Note that BUFCNT can be negative, indicating sticky
+ error. */
+ if (rs->remote_desc->bufcnt != 0)
+ return true;
+ }
+ return false;
+}
+
\f
/* Non-stop version of target_stop. Uses `vCont;t' to stop a remote
char *p = rs->buf.data ();
char *endp = p + get_remote_packet_size ();
+ /* If any thread that needs to stop was resumed but pending a vCont
+ resume, generate a phony stop_reply. However, first check
+ whether the thread wasn't resumed with a signal. Generating a
+ phony stop in that case would result in losing the signal. */
+ bool needs_commit = false;
+ for (thread_info *tp : all_non_exited_threads (this, ptid))
+ {
+ remote_thread_info *remote_thr = get_remote_thread_info (tp);
+
+ if (remote_thr->get_resume_state ()
+ == resume_state::RESUMED_PENDING_VCONT)
+ {
+ const resumed_pending_vcont_info &info
+ = remote_thr->resumed_pending_vcont_info ();
+ if (info.sig != GDB_SIGNAL_0)
+ {
+ /* This signal must be forwarded to the inferior. We
+ could commit-resume just this thread, but its simpler
+ to just commit-resume everything. */
+ needs_commit = true;
+ break;
+ }
+ }
+ }
+
+ if (needs_commit)
+ commit_resumed ();
+ else
+ for (thread_info *tp : all_non_exited_threads (this, ptid))
+ {
+ remote_thread_info *remote_thr = get_remote_thread_info (tp);
+
+ if (remote_thr->get_resume_state ()
+ == resume_state::RESUMED_PENDING_VCONT)
+ {
+ remote_debug_printf ("Enqueueing phony stop reply for thread pending "
+ "vCont-resume (%d, %ld, %s)", tp->ptid.pid(),
+ tp->ptid.lwp (),
+ pulongest (tp->ptid.tid ()));
+
+ /* Check that the thread wasn't resumed with a signal.
+ Generating a phony stop would result in losing the
+ signal. */
+ const resumed_pending_vcont_info &info
+ = remote_thr->resumed_pending_vcont_info ();
+ gdb_assert (info.sig == GDB_SIGNAL_0);
+
+ stop_reply *sr = new stop_reply ();
+ sr->ptid = tp->ptid;
+ sr->rs = rs;
+ sr->ws.set_stopped (GDB_SIGNAL_0);
+ sr->arch = tp->inf->gdbarch;
+ sr->stop_reason = TARGET_STOPPED_BY_NO_REASON;
+ sr->watch_data_address = 0;
+ sr->core = 0;
+ this->push_stop_reply (sr);
+
+ /* Pretend that this thread was actually resumed on the
+ remote target, then stopped. If we leave it in the
+ RESUMED_PENDING_VCONT state and the commit_resumed
+ method is called while the stop reply is still in the
+ queue, we'll end up reporting a stop event to the core
+ for that thread while it is running on the remote
+ target... that would be bad. */
+ remote_thr->set_resumed ();
+ }
+ }
+
/* FIXME: This supports_vCont_probed check is a workaround until
packet_support is per-connection. */
if (packet_support (PACKET_vCont) == PACKET_SUPPORT_UNKNOWN
if (ptid.is_pid ())
/* All (-1) threads of process. */
- nptid = ptid_t (ptid.pid (), -1, 0);
+ nptid = ptid_t (ptid.pid (), -1);
else
{
/* Small optimization: if we already have a stop reply for
rs->ctrlc_pending_p = 1;
/* If the inferior is stopped already, but the core didn't know
- about it yet, just ignore the request. The cached wait status
+ about it yet, just ignore the request. The pending stop events
will be collected in remote_wait. */
- if (rs->cached_wait_status)
+ if (stop_reply_queue_length () > 0)
return;
/* Send interrupt_sequence to remote target. */
gdb_stdtarg->flush ();
}
-struct stop_reply : public notif_event
-{
- ~stop_reply ();
-
- /* The identifier of the thread about this event */
- ptid_t ptid;
-
- /* The remote state this event is associated with. When the remote
- connection, represented by a remote_state object, is closed,
- all the associated stop_reply events should be released. */
- struct remote_state *rs;
-
- struct target_waitstatus ws;
-
- /* The architecture associated with the expedited registers. */
- gdbarch *arch;
-
- /* Expedited registers. This makes remote debugging a bit more
- efficient for those targets that provide critical registers as
- part of their normal status mechanism (as another roundtrip to
- fetch them is avoided). */
- std::vector<cached_reg_t> regcache;
-
- enum target_stop_reason stop_reason;
-
- CORE_ADDR watch_data_address;
-
- int core;
-};
-
/* Return the length of the stop reply queue. */
int
/* Kind can be TARGET_WAITKIND_IGNORE if we have meanwhile discarded
the notification. It was left in the queue because we need to
acknowledge it and pull the rest of the notifications out. */
- if (stop_reply->ws.kind != TARGET_WAITKIND_IGNORE)
+ if (stop_reply->ws.kind () != TARGET_WAITKIND_IGNORE)
remote->push_stop_reply (stop_reply);
}
REMOTE_NOTIF_STOP,
};
-/* Determine if THREAD_PTID is a pending fork parent thread. ARG contains
- the pid of the process that owns the threads we want to check, or
- -1 if we want to check all threads. */
-
-static int
-is_pending_fork_parent (struct target_waitstatus *ws, int event_pid,
- ptid_t thread_ptid)
-{
- if (ws->kind == TARGET_WAITKIND_FORKED
- || ws->kind == TARGET_WAITKIND_VFORKED)
- {
- if (event_pid == -1 || event_pid == thread_ptid.pid ())
- return 1;
- }
-
- return 0;
-}
-
-/* Return the thread's pending status used to determine whether the
- thread is a fork parent stopped at a fork event. */
-
-static struct target_waitstatus *
-thread_pending_fork_status (struct thread_info *thread)
-{
- if (thread->suspend.waitstatus_pending_p)
- return &thread->suspend.waitstatus;
- else
- return &thread->pending_follow;
-}
-
-/* Determine if THREAD is a pending fork parent thread. */
-
-static int
-is_pending_fork_parent_thread (struct thread_info *thread)
-{
- struct target_waitstatus *ws = thread_pending_fork_status (thread);
- int pid = -1;
-
- return is_pending_fork_parent (ws, pid, thread->ptid);
-}
-
/* If CONTEXT contains any fork child threads that have not been
reported yet, remove them from the CONTEXT list. If such a
thread exists it is because we are stopped at a fork catchpoint
void
remote_target::remove_new_fork_children (threads_listing_context *context)
{
- int pid = -1;
struct notif_client *notif = ¬if_client_stop;
/* For any threads stopped at a fork event, remove the corresponding
fork child threads from the CONTEXT list. */
for (thread_info *thread : all_non_exited_threads (this))
{
- struct target_waitstatus *ws = thread_pending_fork_status (thread);
+ const target_waitstatus *ws = thread_pending_fork_status (thread);
+
+ if (ws == nullptr)
+ continue;
- if (is_pending_fork_parent (ws, pid, thread->ptid))
- context->remove_thread (ws->value.related_pid);
+ context->remove_thread (ws->child_ptid ());
}
/* Check for any pending fork events (not reported or processed yet)
CONTEXT list as well. */
remote_notif_get_pending_events (notif);
for (auto &event : get_remote_state ()->stop_reply_queue)
- if (event->ws.kind == TARGET_WAITKIND_FORKED
- || event->ws.kind == TARGET_WAITKIND_VFORKED
- || event->ws.kind == TARGET_WAITKIND_THREAD_EXITED)
- context->remove_thread (event->ws.value.related_pid);
+ if (event->ws.kind () == TARGET_WAITKIND_FORKED
+ || event->ws.kind () == TARGET_WAITKIND_VFORKED
+ || event->ws.kind () == TARGET_WAITKIND_THREAD_EXITED)
+ context->remove_thread (event->ws.child_ptid ());
}
-/* Check whether any event pending in the vStopped queue would prevent
- a global or process wildcard vCont action. Clear
- *may_global_wildcard if we can't do a global wildcard (vCont;c),
- and clear the event inferior's may_wildcard_vcont flag if we can't
- do a process-wide wildcard resume (vCont;c:pPID.-1). */
+/* Check whether any event pending in the vStopped queue would prevent a
+ global or process wildcard vCont action. Set *may_global_wildcard to
+ false if we can't do a global wildcard (vCont;c), and clear the event
+ inferior's may_wildcard_vcont flag if we can't do a process-wide
+ wildcard resume (vCont;c:pPID.-1). */
void
remote_target::check_pending_events_prevent_wildcard_vcont
- (int *may_global_wildcard)
+ (bool *may_global_wildcard)
{
struct notif_client *notif = ¬if_client_stop;
remote_notif_get_pending_events (notif);
for (auto &event : get_remote_state ()->stop_reply_queue)
{
- if (event->ws.kind == TARGET_WAITKIND_NO_RESUMED
- || event->ws.kind == TARGET_WAITKIND_NO_HISTORY)
+ if (event->ws.kind () == TARGET_WAITKIND_NO_RESUMED
+ || event->ws.kind () == TARGET_WAITKIND_NO_HISTORY)
continue;
- if (event->ws.kind == TARGET_WAITKIND_FORKED
- || event->ws.kind == TARGET_WAITKIND_VFORKED)
- *may_global_wildcard = 0;
-
- struct inferior *inf = find_inferior_ptid (this, event->ptid);
+ if (event->ws.kind () == TARGET_WAITKIND_FORKED
+ || event->ws.kind () == TARGET_WAITKIND_VFORKED)
+ *may_global_wildcard = false;
/* This may be the first time we heard about this process.
Regardless, we must not do a global wildcard resume, otherwise
we'd resume this process too. */
- *may_global_wildcard = 0;
- if (inf != NULL)
- get_remote_inferior (inf)->may_wildcard_vcont = false;
+ *may_global_wildcard = false;
+ if (event->ptid != null_ptid)
+ {
+ inferior *inf = find_inferior_ptid (this, event->ptid);
+ if (inf != NULL)
+ get_remote_inferior (inf)->may_wildcard_vcont = false;
+ }
}
}
/* Leave the notification pending, since the server expects that
we acknowledge it with vStopped. But clear its contents, so
that later on when we acknowledge it, we also discard it. */
- reply->ws.kind = TARGET_WAITKIND_IGNORE;
-
- if (remote_debug)
- fprintf_unfiltered (gdb_stdlog,
- "discarded in-flight notification\n");
+ remote_debug_printf
+ ("discarding in-flight notification: ptid: %s, ws: %s\n",
+ reply->ptid.to_string().c_str(),
+ reply->ws.to_string ().c_str ());
+ reply->ws.set_ignore ();
}
/* Discard the stop replies we have already pulled with
{
return event->ptid.pid () == inf->pid;
});
+ for (auto it = iter; it != rs->stop_reply_queue.end (); ++it)
+ remote_debug_printf
+ ("discarding queued stop reply: ptid: %s, ws: %s\n",
+ reply->ptid.to_string().c_str(),
+ reply->ws.to_string ().c_str ());
rs->stop_reply_queue.erase (iter, rs->stop_reply_queue.end ());
}
if (notif_debug)
fprintf_unfiltered (gdb_stdlog,
"notif: discard queued event: 'Stop' in %s\n",
- target_pid_to_str (ptid).c_str ());
+ ptid.to_string ().c_str ());
return result;
}
remote_state *rs = get_remote_state ();
struct stop_reply *r = remote_notif_remove_queued_reply (ptid);
- if (!rs->stop_reply_queue.empty ())
+ if (!rs->stop_reply_queue.empty () && target_can_async_p ())
{
/* There's still at least an event left. */
mark_async_event_handler (rs->remote_async_inferior_event_token);
if (notif_debug)
fprintf_unfiltered (gdb_stdlog,
"notif: push 'Stop' %s to queue %d\n",
- target_pid_to_str (new_event->ptid).c_str (),
+ new_event->ptid.to_string ().c_str (),
int (rs->stop_reply_queue.size ()));
- mark_async_event_handler (rs->remote_async_inferior_event_token);
+ /* Mark the pending event queue only if async mode is currently enabled.
+ If async mode is not currently enabled, then, if it later becomes
+ enabled, and there are events in this queue, we will mark the event
+ token at that point, see remote_target::async. */
+ if (target_is_async_p ())
+ mark_async_event_handler (rs->remote_async_inferior_event_token);
}
/* Returns true if we have a stop reply for PTID. */
remote_state *rs = get_remote_state ();
for (auto &event : rs->stop_reply_queue)
if (ptid == event->ptid
- && event->ws.kind == TARGET_WAITKIND_STOPPED)
+ && event->ws.kind () == TARGET_WAITKIND_STOPPED)
return 1;
return 0;
}
event->ptid = null_ptid;
event->rs = get_remote_state ();
- event->ws.kind = TARGET_WAITKIND_IGNORE;
- event->ws.value.integer = 0;
+ event->ws.set_ignore ();
event->stop_reason = TARGET_STOPPED_BY_NO_REASON;
event->regcache.clear ();
event->core = -1;
{
ULONGEST sysno;
- event->ws.kind = TARGET_WAITKIND_SYSCALL_ENTRY;
p = unpack_varlen_hex (++p1, &sysno);
- event->ws.value.syscall_number = (int) sysno;
+ event->ws.set_syscall_entry ((int) sysno);
}
else if (strprefix (p, p1, "syscall_return"))
{
ULONGEST sysno;
- event->ws.kind = TARGET_WAITKIND_SYSCALL_RETURN;
p = unpack_varlen_hex (++p1, &sysno);
- event->ws.value.syscall_number = (int) sysno;
+ event->ws.set_syscall_return ((int) sysno);
}
else if (strprefix (p, p1, "watch")
|| strprefix (p, p1, "rwatch")
}
else if (strprefix (p, p1, "library"))
{
- event->ws.kind = TARGET_WAITKIND_LOADED;
+ event->ws.set_loaded ();
p = strchrnul (p1 + 1, ';');
}
else if (strprefix (p, p1, "replaylog"))
{
- event->ws.kind = TARGET_WAITKIND_NO_HISTORY;
+ event->ws.set_no_history ();
/* p1 will indicate "begin" or "end", but it makes
no difference for now, so ignore it. */
p = strchrnul (p1 + 1, ';');
event->core = c;
}
else if (strprefix (p, p1, "fork"))
- {
- event->ws.value.related_pid = read_ptid (++p1, &p);
- event->ws.kind = TARGET_WAITKIND_FORKED;
- }
+ event->ws.set_forked (read_ptid (++p1, &p));
else if (strprefix (p, p1, "vfork"))
- {
- event->ws.value.related_pid = read_ptid (++p1, &p);
- event->ws.kind = TARGET_WAITKIND_VFORKED;
- }
+ event->ws.set_vforked (read_ptid (++p1, &p));
else if (strprefix (p, p1, "vforkdone"))
{
- event->ws.kind = TARGET_WAITKIND_VFORK_DONE;
+ event->ws.set_vfork_done ();
p = strchrnul (p1 + 1, ';');
}
else if (strprefix (p, p1, "exec"))
/* Save the pathname for event reporting and for
the next run command. */
- gdb::unique_xmalloc_ptr<char[]> pathname
+ gdb::unique_xmalloc_ptr<char> pathname
((char *) xmalloc (pathlen + 1));
hex2bin (p1, (gdb_byte *) pathname.get (), pathlen);
- pathname[pathlen] = '\0';
+ pathname.get ()[pathlen] = '\0';
/* This is freed during event handling. */
- event->ws.value.execd_pathname = pathname.release ();
- event->ws.kind = TARGET_WAITKIND_EXECD;
+ event->ws.set_execd (std::move (pathname));
/* Skip the registers included in this packet, since
they may be for an architecture different from the
}
else if (strprefix (p, p1, "create"))
{
- event->ws.kind = TARGET_WAITKIND_THREAD_CREATED;
+ event->ws.set_thread_created ();
p = strchrnul (p1 + 1, ';');
}
else
++p;
}
- if (event->ws.kind != TARGET_WAITKIND_IGNORE)
+ if (event->ws.kind () != TARGET_WAITKIND_IGNORE)
break;
/* fall through */
{
int sig;
- event->ws.kind = TARGET_WAITKIND_STOPPED;
sig = (fromhex (buf[1]) << 4) + fromhex (buf[2]);
if (GDB_SIGNAL_FIRST <= sig && sig < GDB_SIGNAL_LAST)
- event->ws.value.sig = (enum gdb_signal) sig;
+ event->ws.set_stopped ((enum gdb_signal) sig);
else
- event->ws.value.sig = GDB_SIGNAL_UNKNOWN;
+ event->ws.set_stopped (GDB_SIGNAL_UNKNOWN);
}
break;
case 'w': /* Thread exited. */
{
ULONGEST value;
- event->ws.kind = TARGET_WAITKIND_THREAD_EXITED;
p = unpack_varlen_hex (&buf[1], &value);
- event->ws.value.integer = value;
+ event->ws.set_thread_exited (value);
if (*p != ';')
error (_("stop reply packet badly formatted: %s"), buf);
event->ptid = read_ptid (++p, NULL);
if (buf[0] == 'W')
{
/* The remote process exited. */
- event->ws.kind = TARGET_WAITKIND_EXITED;
- event->ws.value.integer = value;
+ event->ws.set_exited (value);
}
else
{
/* The remote process exited with a signal. */
- event->ws.kind = TARGET_WAITKIND_SIGNALLED;
if (GDB_SIGNAL_FIRST <= value && value < GDB_SIGNAL_LAST)
- event->ws.value.sig = (enum gdb_signal) value;
+ event->ws.set_signalled ((enum gdb_signal) value);
else
- event->ws.value.sig = GDB_SIGNAL_UNKNOWN;
+ event->ws.set_signalled (GDB_SIGNAL_UNKNOWN);
}
/* If no process is specified, return null_ptid, and let the
}
break;
case 'N':
- event->ws.kind = TARGET_WAITKIND_NO_RESUMED;
+ event->ws.set_no_resumed ();
event->ptid = minus_one_ptid;
break;
}
ptid_t
remote_target::select_thread_for_ambiguous_stop_reply
- (const struct target_waitstatus *status)
+ (const target_waitstatus &status)
{
+ REMOTE_SCOPED_DEBUG_ENTER_EXIT;
+
/* Some stop events apply to all threads in an inferior, while others
only apply to a single thread. */
bool process_wide_stop
- = (status->kind == TARGET_WAITKIND_EXITED
- || status->kind == TARGET_WAITKIND_SIGNALLED);
+ = (status.kind () == TARGET_WAITKIND_EXITED
+ || status.kind () == TARGET_WAITKIND_SIGNALLED);
+
+ remote_debug_printf ("process_wide_stop = %d", process_wide_stop);
thread_info *first_resumed_thread = nullptr;
bool ambiguous = false;
gdb_assert (first_resumed_thread != nullptr);
+ remote_debug_printf ("first resumed thread is %s",
+ pid_to_str (first_resumed_thread->ptid).c_str ());
+ remote_debug_printf ("is this guess ambiguous? = %d", ambiguous);
+
/* Warn if the remote target is sending ambiguous stop replies. */
if (ambiguous)
{
/* If no thread/process was reported by the stub then select a suitable
thread/process. */
if (ptid == null_ptid)
- ptid = select_thread_for_ambiguous_stop_reply (status);
+ ptid = select_thread_for_ambiguous_stop_reply (*status);
gdb_assert (ptid != null_ptid);
- if (status->kind != TARGET_WAITKIND_EXITED
- && status->kind != TARGET_WAITKIND_SIGNALLED
- && status->kind != TARGET_WAITKIND_NO_RESUMED)
+ if (status->kind () != TARGET_WAITKIND_EXITED
+ && status->kind () != TARGET_WAITKIND_SIGNALLED
+ && status->kind () != TARGET_WAITKIND_NO_RESUMED)
{
/* Expedited registers. */
if (!stop_reply->regcache.empty ())
stop_reply->regcache.clear ();
}
- remote_notice_new_inferior (ptid, 0);
+ remote_notice_new_inferior (ptid, false);
remote_thread_info *remote_thr = get_remote_thread_info (this, ptid);
remote_thr->core = stop_reply->core;
remote_thr->stop_reason = stop_reply->stop_reason;
return to the event loop. */
if (options & TARGET_WNOHANG)
{
- status->kind = TARGET_WAITKIND_IGNORE;
+ status->set_ignore ();
return minus_one_ptid;
}
first_remote_resumed_thread (remote_target *target)
{
for (thread_info *tp : all_non_exited_threads (target, minus_one_ptid))
- if (tp->resumed)
+ if (tp->resumed ())
return tp->ptid;
return null_ptid;
}
again:
- status->kind = TARGET_WAITKIND_IGNORE;
- status->value.integer = 0;
+ status->set_ignore ();
stop_reply = queued_stop_reply (ptid);
if (stop_reply != NULL)
- return process_stop_reply (stop_reply, status);
-
- if (rs->cached_wait_status)
- /* Use the cached wait status, but only once. */
- rs->cached_wait_status = 0;
+ {
+ /* None of the paths that push a stop reply onto the queue should
+ have set the waiting_for_stop_reply flag. */
+ gdb_assert (!rs->waiting_for_stop_reply);
+ event_ptid = process_stop_reply (stop_reply, status);
+ }
else
{
- int ret;
- int is_notif;
int forever = ((options & TARGET_WNOHANG) == 0
&& rs->wait_forever_enabled_p);
if (!rs->waiting_for_stop_reply)
{
- status->kind = TARGET_WAITKIND_NO_RESUMED;
+ status->set_no_resumed ();
return minus_one_ptid;
}
_never_ wait for ever -> test on target_is_async_p().
However, before we do that we need to ensure that the caller
knows how to take the target into/out of async mode. */
- ret = getpkt_or_notif_sane (&rs->buf, forever, &is_notif);
+ int is_notif;
+ int ret = getpkt_or_notif_sane (&rs->buf, forever, &is_notif);
/* GDB gets a notification. Return to core as this event is
not interesting. */
if (ret == -1 && (options & TARGET_WNOHANG) != 0)
return minus_one_ptid;
- }
- buf = rs->buf.data ();
+ buf = rs->buf.data ();
- /* Assume that the target has acknowledged Ctrl-C unless we receive
- an 'F' or 'O' packet. */
- if (buf[0] != 'F' && buf[0] != 'O')
- rs->ctrlc_pending_p = 0;
+ /* Assume that the target has acknowledged Ctrl-C unless we receive
+ an 'F' or 'O' packet. */
+ if (buf[0] != 'F' && buf[0] != 'O')
+ rs->ctrlc_pending_p = 0;
- switch (buf[0])
- {
- case 'E': /* Error of some sort. */
- /* We're out of sync with the target now. Did it continue or
- not? Not is more likely, so report a stop. */
- rs->waiting_for_stop_reply = 0;
+ switch (buf[0])
+ {
+ case 'E': /* Error of some sort. */
+ /* We're out of sync with the target now. Did it continue or
+ not? Not is more likely, so report a stop. */
+ rs->waiting_for_stop_reply = 0;
- warning (_("Remote failure reply: %s"), buf);
- status->kind = TARGET_WAITKIND_STOPPED;
- status->value.sig = GDB_SIGNAL_0;
- break;
- case 'F': /* File-I/O request. */
- /* GDB may access the inferior memory while handling the File-I/O
- request, but we don't want GDB accessing memory while waiting
- for a stop reply. See the comments in putpkt_binary. Set
- waiting_for_stop_reply to 0 temporarily. */
- rs->waiting_for_stop_reply = 0;
- remote_fileio_request (this, buf, rs->ctrlc_pending_p);
- rs->ctrlc_pending_p = 0;
- /* GDB handled the File-I/O request, and the target is running
- again. Keep waiting for events. */
- rs->waiting_for_stop_reply = 1;
- break;
- case 'N': case 'T': case 'S': case 'X': case 'W':
- {
- /* There is a stop reply to handle. */
- rs->waiting_for_stop_reply = 0;
+ warning (_("Remote failure reply: %s"), buf);
+ status->set_stopped (GDB_SIGNAL_0);
+ break;
+ case 'F': /* File-I/O request. */
+ /* GDB may access the inferior memory while handling the File-I/O
+ request, but we don't want GDB accessing memory while waiting
+ for a stop reply. See the comments in putpkt_binary. Set
+ waiting_for_stop_reply to 0 temporarily. */
+ rs->waiting_for_stop_reply = 0;
+ remote_fileio_request (this, buf, rs->ctrlc_pending_p);
+ rs->ctrlc_pending_p = 0;
+ /* GDB handled the File-I/O request, and the target is running
+ again. Keep waiting for events. */
+ rs->waiting_for_stop_reply = 1;
+ break;
+ case 'N': case 'T': case 'S': case 'X': case 'W':
+ {
+ /* There is a stop reply to handle. */
+ rs->waiting_for_stop_reply = 0;
- stop_reply
- = (struct stop_reply *) remote_notif_parse (this,
- ¬if_client_stop,
- rs->buf.data ());
+ stop_reply
+ = (struct stop_reply *) remote_notif_parse (this,
+ ¬if_client_stop,
+ rs->buf.data ());
- event_ptid = process_stop_reply (stop_reply, status);
- break;
- }
- case 'O': /* Console output. */
- remote_console_output (buf + 1);
- break;
- case '\0':
- if (rs->last_sent_signal != GDB_SIGNAL_0)
- {
- /* Zero length reply means that we tried 'S' or 'C' and the
- remote system doesn't support it. */
- target_terminal::ours_for_output ();
- printf_filtered
- ("Can't send signals to this remote system. %s not sent.\n",
- gdb_signal_to_name (rs->last_sent_signal));
- rs->last_sent_signal = GDB_SIGNAL_0;
- target_terminal::inferior ();
-
- strcpy (buf, rs->last_sent_step ? "s" : "c");
- putpkt (buf);
+ event_ptid = process_stop_reply (stop_reply, status);
+ break;
+ }
+ case 'O': /* Console output. */
+ remote_console_output (buf + 1);
+ break;
+ case '\0':
+ if (rs->last_sent_signal != GDB_SIGNAL_0)
+ {
+ /* Zero length reply means that we tried 'S' or 'C' and the
+ remote system doesn't support it. */
+ target_terminal::ours_for_output ();
+ printf_filtered
+ ("Can't send signals to this remote system. %s not sent.\n",
+ gdb_signal_to_name (rs->last_sent_signal));
+ rs->last_sent_signal = GDB_SIGNAL_0;
+ target_terminal::inferior ();
+
+ strcpy (buf, rs->last_sent_step ? "s" : "c");
+ putpkt (buf);
+ break;
+ }
+ /* fallthrough */
+ default:
+ warning (_("Invalid remote reply: %s"), buf);
break;
}
- /* fallthrough */
- default:
- warning (_("Invalid remote reply: %s"), buf);
- break;
}
- if (status->kind == TARGET_WAITKIND_NO_RESUMED)
+ if (status->kind () == TARGET_WAITKIND_NO_RESUMED)
return minus_one_ptid;
- else if (status->kind == TARGET_WAITKIND_IGNORE)
+ else if (status->kind () == TARGET_WAITKIND_IGNORE)
{
/* Nothing interesting happened. If we're doing a non-blocking
poll, we're done. Otherwise, go back to waiting. */
else
goto again;
}
- else if (status->kind != TARGET_WAITKIND_EXITED
- && status->kind != TARGET_WAITKIND_SIGNALLED)
+ else if (status->kind () != TARGET_WAITKIND_EXITED
+ && status->kind () != TARGET_WAITKIND_SIGNALLED)
{
if (event_ptid != null_ptid)
record_currthread (rs, event_ptid);
remote_state *rs = get_remote_state ();
/* Start by clearing the flag that asks for our wait method to be called,
- we'll mark it again at the end if needed. */
+ we'll mark it again at the end if needed. If the target is not in
+ async mode then the async token should not be marked. */
if (target_is_async_p ())
clear_async_event_handler (rs->remote_async_inferior_event_token);
+ else
+ gdb_assert (!async_event_handler_marked
+ (rs->remote_async_inferior_event_token));
ptid_t event_ptid;
}
}
-void
-remote_target::files_info ()
-{
- puts_filtered ("Debugging a target over a serial line.\n");
-}
\f
/* Stuff for dealing with the packets which are part of this protocol.
See comment at top of file for details. */
string_file stb;
stb.putstrn (buf, n, '\\');
- return std::move (stb.string ());
-}
-
-/* Display a null-terminated packet on stdout, for debugging, using C
- string notation. */
-
-static void
-print_packet (const char *buf)
-{
- puts_filtered ("\"");
- fputstr_filtered (buf, '"', gdb_stdout);
- puts_filtered ("\"");
+ return stb.release ();
}
int
"and then try again."));
}
- /* We're sending out a new packet. Make sure we don't look at a
- stale cached response. */
- rs->cached_wait_status = 0;
-
/* Copy the packet into buffer BUF2, encapsulating it
and giving it a checksum. */
{
int repeat;
- csum += c;
+ csum += c;
c = readchar (remote_timeout);
csum += c;
repeat = c - ' ' + 3; /* Compute repeat count. */
int timeout;
int val = -1;
- /* We're reading a new response. Make sure we don't look at a
- previously cached response. */
- rs->cached_wait_status = 0;
-
strcpy (buf->data (), "timeout");
if (forever)
return getpkt_or_notif_sane_1 (buf, forever, 1, is_notif);
}
-/* Kill any new fork children of process PID that haven't been
+/* Kill any new fork children of inferior INF that haven't been
processed by follow_fork. */
void
-remote_target::kill_new_fork_children (int pid)
+remote_target::kill_new_fork_children (inferior *inf)
{
remote_state *rs = get_remote_state ();
struct notif_client *notif = ¬if_client_stop;
- /* Kill the fork child threads of any threads in process PID
- that are stopped at a fork event. */
- for (thread_info *thread : all_non_exited_threads (this))
+ /* Kill the fork child threads of any threads in inferior INF that are stopped
+ at a fork event. */
+ for (thread_info *thread : inf->non_exited_threads ())
{
- struct target_waitstatus *ws = &thread->pending_follow;
+ const target_waitstatus *ws = thread_pending_fork_status (thread);
- if (is_pending_fork_parent (ws, pid, thread->ptid))
- {
- int child_pid = ws->value.related_pid.pid ();
- int res;
+ if (ws == nullptr)
+ continue;
- res = remote_vkill (child_pid);
- if (res != 0)
- error (_("Can't kill fork child process %d"), child_pid);
- }
+ int child_pid = ws->child_ptid ().pid ();
+ int res = remote_vkill (child_pid);
+
+ if (res != 0)
+ error (_("Can't kill fork child process %d"), child_pid);
}
/* Check for any pending fork events (not reported or processed yet)
- in process PID and kill those fork child threads as well. */
+ in inferior INF and kill those fork child threads as well. */
remote_notif_get_pending_events (notif);
for (auto &event : rs->stop_reply_queue)
- if (is_pending_fork_parent (&event->ws, pid, event->ptid))
- {
- int child_pid = event->ws.value.related_pid.pid ();
- int res;
+ {
+ if (event->ptid.pid () != inf->pid)
+ continue;
- res = remote_vkill (child_pid);
- if (res != 0)
- error (_("Can't kill fork child process %d"), child_pid);
- }
+ if (!is_fork_status (event->ws.kind ()))
+ continue;
+
+ int child_pid = event->ws.child_ptid ().pid ();
+ int res = remote_vkill (child_pid);
+
+ if (res != 0)
+ error (_("Can't kill fork child process %d"), child_pid);
+ }
}
\f
remote_target::kill ()
{
int res = -1;
- int pid = inferior_ptid.pid ();
+ inferior *inf = find_inferior_pid (this, inferior_ptid.pid ());
struct remote_state *rs = get_remote_state ();
+ gdb_assert (inf != nullptr);
+
if (packet_support (PACKET_vKill) != PACKET_DISABLE)
{
/* If we're stopped while forking and we haven't followed yet,
kill the child task. We need to do this before killing the
parent task because if this is a vfork then the parent will
be sleeping. */
- kill_new_fork_children (pid);
+ kill_new_fork_children (inf);
- res = remote_vkill (pid);
+ res = remote_vkill (inf->pid);
if (res == 0)
{
target_mourn_inferior (inferior_ptid);
error (_("Running \"%s\" on the remote target failed"),
remote_exec_file);
default:
- gdb_assert_not_reached (_("bad switch"));
+ gdb_assert_not_reached ("bad switch");
}
}
{
if (packet_support (PACKET_QSetWorkingDir) != PACKET_DISABLE)
{
- const char *inferior_cwd = get_inferior_cwd ();
+ const std::string &inferior_cwd = current_inferior ()->cwd ();
remote_state *rs = get_remote_state ();
- if (inferior_cwd != NULL)
+ if (!inferior_cwd.empty ())
{
- std::string hexpath = bin2hex ((const gdb_byte *) inferior_cwd,
- strlen (inferior_cwd));
+ std::string hexpath
+ = bin2hex ((const gdb_byte *) inferior_cwd.data (),
+ inferior_cwd.size ());
xsnprintf (rs->buf.data (), get_remote_packet_size (),
"QSetWorkingDir:%s", hexpath.c_str ());
{
std::vector<mem_region> result;
gdb::optional<gdb::char_vector> text
- = target_read_stralloc (current_top_target (), TARGET_OBJECT_MEMORY_MAP, NULL);
+ = target_read_stralloc (current_inferior ()->top_target (),
+ TARGET_OBJECT_MEMORY_MAP, NULL);
if (text)
result = parse_memory_map (text->data ());
return result;
}
-static void
-packet_command (const char *args, int from_tty)
+/* Set of callbacks used to implement the 'maint packet' command. */
+
+struct cli_packet_command_callbacks : public send_remote_packet_callbacks
{
- remote_target *remote = get_current_remote_target ();
+ /* Called before the packet is sent. BUF is the packet content before
+ the protocol specific prefix, suffix, and escaping is added. */
- if (remote == nullptr)
- error (_("command can only be used with remote target"));
+ void sending (gdb::array_view<const char> &buf) override
+ {
+ puts_filtered ("sending: ");
+ print_packet (buf);
+ puts_filtered ("\n");
+ }
- remote->packet_command (args, from_tty);
-}
+ /* Called with BUF, the reply from the remote target. */
+
+ void received (gdb::array_view<const char> &buf) override
+ {
+ puts_filtered ("received: \"");
+ print_packet (buf);
+ puts_filtered ("\"\n");
+ }
+
+private:
+
+ /* Print BUF o gdb_stdout. Any non-printable bytes in BUF are printed as
+ '\x??' with '??' replaced by the hexadecimal value of the byte. */
+
+ static void
+ print_packet (gdb::array_view<const char> &buf)
+ {
+ string_file stb;
+
+ for (int i = 0; i < buf.size (); ++i)
+ {
+ gdb_byte c = buf[i];
+ if (isprint (c))
+ fputc_unfiltered (c, &stb);
+ else
+ fprintf_unfiltered (&stb, "\\x%02x", (unsigned char) c);
+ }
+
+ puts_filtered (stb.string ().c_str ());
+ }
+};
+
+/* See remote.h. */
void
-remote_target::packet_command (const char *args, int from_tty)
+send_remote_packet (gdb::array_view<const char> &buf,
+ send_remote_packet_callbacks *callbacks)
{
- if (!args)
- error (_("remote-packet command requires packet text as argument"));
+ if (buf.size () == 0 || buf.data ()[0] == '\0')
+ error (_("a remote packet must not be empty"));
- puts_filtered ("sending: ");
- print_packet (args);
- puts_filtered ("\n");
- putpkt (args);
+ remote_target *remote = get_current_remote_target ();
+ if (remote == nullptr)
+ error (_("packets can only be sent to a remote target"));
- remote_state *rs = get_remote_state ();
+ callbacks->sending (buf);
- getpkt (&rs->buf, 0);
- puts_filtered ("received: ");
- print_packet (rs->buf.data ());
- puts_filtered ("\n");
+ remote->putpkt_binary (buf.data (), buf.size ());
+ remote_state *rs = remote->get_remote_state ();
+ int bytes = remote->getpkt_sane (&rs->buf, 0);
+
+ if (bytes < 0)
+ error (_("error while fetching packet from remote target"));
+
+ gdb::array_view<const char> view (&rs->buf[0], bytes);
+ callbacks->received (view);
+}
+
+/* Entry point for the 'maint packet' command. */
+
+static void
+cli_packet_command (const char *args, int from_tty)
+{
+ cli_packet_command_callbacks cb;
+ gdb::array_view<const char> view
+ = gdb::make_array_view (args, args == nullptr ? 0 : strlen (args));
+ send_remote_packet (view, &cb);
}
#if 0
this case we treat the remote filesystem as local if the
sysroot is exactly TARGET_SYSROOT_PREFIX and if the stub
does not support vFile:open. */
- if (strcmp (gdb_sysroot, TARGET_SYSROOT_PREFIX) == 0)
+ if (gdb_sysroot == TARGET_SYSROOT_PREFIX)
{
enum packet_support ps = packet_support (PACKET_vFile_open);
"ignoring tp %d cond"), b->number);
}
- if (b->commands || *default_collect)
+ if (b->commands || !default_collect.empty ())
{
size_left = buf.size () - strlen (buf.data ());
error ("%s", err_msg);
encode_source_string (b->number, loc->address,
- "cond", b->cond_string,
+ "cond", b->cond_string.get (),
buf.data () + strlen (buf.data ()),
buf.size () - strlen (buf.data ()));
putpkt (buf.data ());
{
struct remote_state *rs = get_remote_state ();
char *reply;
- struct bp_location *loc;
struct tracepoint *tp = (struct tracepoint *) bp;
size_t size = get_remote_packet_size ();
{
tp->hit_count = 0;
tp->traceframe_usage = 0;
- for (loc = tp->loc; loc; loc = loc->next)
+ for (bp_location *loc : tp->locations ())
{
/* If the tracepoint was never downloaded, don't go asking for
any status. */
remote_target::traceframe_info ()
{
gdb::optional<gdb::char_vector> text
- = target_read_stralloc (current_top_target (), TARGET_OBJECT_TRACEFRAME_INFO,
+ = target_read_stralloc (current_inferior ()->top_target (),
+ TARGET_OBJECT_TRACEFRAME_INFO,
NULL);
if (text)
return parse_traceframe_info (text->data ());
}
}
-/* Read the current thread's btrace configuration from the target and
- store it into CONF. */
+/* Read TP's btrace configuration from the target and store it into CONF. */
static void
-btrace_read_config (struct btrace_config *conf)
+btrace_read_config (thread_info *tp, struct btrace_config *conf)
{
+ /* target_read_stralloc relies on INFERIOR_PTID. */
+ scoped_restore_current_thread restore_thread;
+ switch_to_thread (tp);
+
gdb::optional<gdb::char_vector> xml
- = target_read_stralloc (current_top_target (), TARGET_OBJECT_BTRACE_CONF, "");
+ = target_read_stralloc (current_inferior ()->top_target (),
+ TARGET_OBJECT_BTRACE_CONF, "");
if (xml)
parse_xml_btrace_conf (conf, xml->data ());
}
if (packet_support (PACKET_qXfer_btrace_conf) != PACKET_ENABLE)
return;
- scoped_restore_current_thread restore_thread;
-
for (thread_info *tp : all_non_exited_threads (this))
{
- set_general_thread (tp->ptid);
-
memset (&rs->btrace_config, 0x00, sizeof (struct btrace_config));
- btrace_read_config (&rs->btrace_config);
+ btrace_read_config (tp, &rs->btrace_config);
if (rs->btrace_config.format == BTRACE_FORMAT_NONE)
continue;
/* Enable branch tracing. */
struct btrace_target_info *
-remote_target::enable_btrace (ptid_t ptid, const struct btrace_config *conf)
+remote_target::enable_btrace (thread_info *tp,
+ const struct btrace_config *conf)
{
struct btrace_target_info *tinfo = NULL;
struct packet_config *packet = NULL;
btrace_sync_conf (conf);
+ ptid_t ptid = tp->ptid;
set_general_thread (ptid);
buf += xsnprintf (buf, endbuf - buf, "%s", packet->name);
tracing itself is not impacted. */
try
{
- btrace_read_config (&tinfo->conf);
+ btrace_read_config (tp, &tinfo->conf);
}
catch (const gdb_exception_error &err)
{
}
gdb::optional<gdb::char_vector> xml
- = target_read_stralloc (current_top_target (), TARGET_OBJECT_BTRACE, annex);
+ = target_read_stralloc (current_inferior ()->top_target (),
+ TARGET_OBJECT_BTRACE, annex);
if (!xml)
return BTRACE_ERR_UNKNOWN;
xsnprintf (annex, annex_size, "%x", pid);
}
- filename = target_read_stralloc (current_top_target (),
+ filename = target_read_stralloc (current_inferior ()->top_target (),
TARGET_OBJECT_EXEC_FILE, annex);
return filename ? filename->data () : nullptr;
bool
remote_target::can_async_p ()
{
- struct remote_state *rs = get_remote_state ();
+ /* This flag should be checked in the common target.c code. */
+ gdb_assert (target_async_permitted);
- /* We don't go async if the user has explicitly prevented it with the
- "maint set target-async" command. */
- if (!target_async_permitted)
- return false;
-
- /* We're async whenever the serial device is. */
+ /* We're async whenever the serial device can. */
+ struct remote_state *rs = get_remote_state ();
return serial_can_async_p (rs->remote_desc);
}
bool
remote_target::is_async_p ()
{
- struct remote_state *rs = get_remote_state ();
-
- if (!target_async_permitted)
- /* We only enable async when the user specifically asks for it. */
- return false;
-
/* We're async whenever the serial device is. */
+ struct remote_state *rs = get_remote_state ();
return serial_is_async_p (rs->remote_desc);
}
{
remote_target *remote = get_current_remote_target ();
- if (remote != NULL) /* Have a remote connection. */
- remote->remote_check_symbols ();
+ /* First, check whether the current inferior's process target is a remote
+ target. */
+ if (remote == nullptr)
+ return;
+
+ /* When we are attaching or handling a fork child and the shared library
+ subsystem reads the list of loaded libraries, we receive new objfile
+ events in between each found library. The libraries are read in an
+ undefined order, so if we gave the remote side a chance to look up
+ symbols between each objfile, we might give it an inconsistent picture
+ of the inferior. It could appear that a library A appears loaded but
+ a library B does not, even though library A requires library B. That
+ would present a state that couldn't normally exist in the inferior.
+
+ So, skip these events, we'll give the remote a chance to look up symbols
+ once all the loaded libraries and their symbols are known to GDB. */
+ if (current_inferior ()->in_initial_library_scan)
+ return;
+
+ remote->remote_check_symbols ();
}
/* Pull all the tracepoints defined on the target and create local
value);
}
+/* Implement the "supports_memory_tagging" target_ops method. */
+
+bool
+remote_target::supports_memory_tagging ()
+{
+ return remote_memory_tagging_p ();
+}
+
+/* Create the qMemTags packet given ADDRESS, LEN and TYPE. */
+
+static void
+create_fetch_memtags_request (gdb::char_vector &packet, CORE_ADDR address,
+ size_t len, int type)
+{
+ int addr_size = gdbarch_addr_bit (target_gdbarch ()) / 8;
+
+ std::string request = string_printf ("qMemTags:%s,%s:%s",
+ phex_nz (address, addr_size),
+ phex_nz (len, sizeof (len)),
+ phex_nz (type, sizeof (type)));
+
+ strcpy (packet.data (), request.c_str ());
+}
+
+/* Parse the qMemTags packet reply into TAGS.
+
+ Return true if successful, false otherwise. */
+
+static bool
+parse_fetch_memtags_reply (const gdb::char_vector &reply,
+ gdb::byte_vector &tags)
+{
+ if (reply.empty () || reply[0] == 'E' || reply[0] != 'm')
+ return false;
+
+ /* Copy the tag data. */
+ tags = hex2bin (reply.data () + 1);
+
+ return true;
+}
+
+/* Create the QMemTags packet given ADDRESS, LEN, TYPE and TAGS. */
+
+static void
+create_store_memtags_request (gdb::char_vector &packet, CORE_ADDR address,
+ size_t len, int type,
+ const gdb::byte_vector &tags)
+{
+ int addr_size = gdbarch_addr_bit (target_gdbarch ()) / 8;
+
+ /* Put together the main packet, address and length. */
+ std::string request = string_printf ("QMemTags:%s,%s:%s:",
+ phex_nz (address, addr_size),
+ phex_nz (len, sizeof (len)),
+ phex_nz (type, sizeof (type)));
+ request += bin2hex (tags.data (), tags.size ());
+
+ /* Check if we have exceeded the maximum packet size. */
+ if (packet.size () < request.length ())
+ error (_("Contents too big for packet QMemTags."));
+
+ strcpy (packet.data (), request.c_str ());
+}
+
+/* Implement the "fetch_memtags" target_ops method. */
+
+bool
+remote_target::fetch_memtags (CORE_ADDR address, size_t len,
+ gdb::byte_vector &tags, int type)
+{
+ /* Make sure the qMemTags packet is supported. */
+ if (!remote_memory_tagging_p ())
+ gdb_assert_not_reached ("remote fetch_memtags called with packet disabled");
+
+ struct remote_state *rs = get_remote_state ();
+
+ create_fetch_memtags_request (rs->buf, address, len, type);
+
+ putpkt (rs->buf);
+ getpkt (&rs->buf, 0);
+
+ return parse_fetch_memtags_reply (rs->buf, tags);
+}
+
+/* Implement the "store_memtags" target_ops method. */
+
+bool
+remote_target::store_memtags (CORE_ADDR address, size_t len,
+ const gdb::byte_vector &tags, int type)
+{
+ /* Make sure the QMemTags packet is supported. */
+ if (!remote_memory_tagging_p ())
+ gdb_assert_not_reached ("remote store_memtags called with packet disabled");
+
+ struct remote_state *rs = get_remote_state ();
+
+ create_store_memtags_request (rs->buf, address, len, type, tags);
+
+ putpkt (rs->buf);
+ getpkt (&rs->buf, 0);
+
+ /* Verify if the request was successful. */
+ return packet_check_result (rs->buf.data ()) == PACKET_OK;
+}
+
+/* Return true if remote target T is non-stop. */
+
+bool
+remote_target_is_non_stop_p (remote_target *t)
+{
+ scoped_restore_current_thread restore_thread;
+ switch_to_target_no_thread (t);
+
+ return target_is_non_stop_p ();
+}
+
+#if GDB_SELF_TEST
+
+namespace selftests {
+
+static void
+test_memory_tagging_functions ()
+{
+ remote_target remote;
+
+ struct packet_config *config
+ = &remote_protocol_packets[PACKET_memory_tagging_feature];
+
+ scoped_restore restore_memtag_support_
+ = make_scoped_restore (&config->support);
+
+ /* Test memory tagging packet support. */
+ config->support = PACKET_SUPPORT_UNKNOWN;
+ SELF_CHECK (remote.supports_memory_tagging () == false);
+ config->support = PACKET_DISABLE;
+ SELF_CHECK (remote.supports_memory_tagging () == false);
+ config->support = PACKET_ENABLE;
+ SELF_CHECK (remote.supports_memory_tagging () == true);
+
+ /* Setup testing. */
+ gdb::char_vector packet;
+ gdb::byte_vector tags, bv;
+ std::string expected, reply;
+ packet.resize (32000);
+
+ /* Test creating a qMemTags request. */
+
+ expected = "qMemTags:0,0:0";
+ create_fetch_memtags_request (packet, 0x0, 0x0, 0);
+ SELF_CHECK (strcmp (packet.data (), expected.c_str ()) == 0);
+
+ expected = "qMemTags:deadbeef,10:1";
+ create_fetch_memtags_request (packet, 0xdeadbeef, 16, 1);
+ SELF_CHECK (strcmp (packet.data (), expected.c_str ()) == 0);
+
+ /* Test parsing a qMemTags reply. */
+
+ /* Error reply, tags vector unmodified. */
+ reply = "E00";
+ strcpy (packet.data (), reply.c_str ());
+ tags.resize (0);
+ SELF_CHECK (parse_fetch_memtags_reply (packet, tags) == false);
+ SELF_CHECK (tags.size () == 0);
+
+ /* Valid reply, tags vector updated. */
+ tags.resize (0);
+ bv.resize (0);
+
+ for (int i = 0; i < 5; i++)
+ bv.push_back (i);
+
+ reply = "m" + bin2hex (bv.data (), bv.size ());
+ strcpy (packet.data (), reply.c_str ());
+
+ SELF_CHECK (parse_fetch_memtags_reply (packet, tags) == true);
+ SELF_CHECK (tags.size () == 5);
+
+ for (int i = 0; i < 5; i++)
+ SELF_CHECK (tags[i] == i);
+
+ /* Test creating a QMemTags request. */
+
+ /* Empty tag data. */
+ tags.resize (0);
+ expected = "QMemTags:0,0:0:";
+ create_store_memtags_request (packet, 0x0, 0x0, 0, tags);
+ SELF_CHECK (memcmp (packet.data (), expected.c_str (),
+ expected.length ()) == 0);
+
+ /* Non-empty tag data. */
+ tags.resize (0);
+ for (int i = 0; i < 5; i++)
+ tags.push_back (i);
+ expected = "QMemTags:deadbeef,ff:1:0001020304";
+ create_store_memtags_request (packet, 0xdeadbeef, 255, 1, tags);
+ SELF_CHECK (memcmp (packet.data (), expected.c_str (),
+ expected.length ()) == 0);
+}
+
+} // namespace selftests
+#endif /* GDB_SELF_TEST */
+
void _initialize_remote ();
void
_initialize_remote ()
{
- struct cmd_list_element *cmd;
- const char *cmd_name;
-
/* architecture specific data */
remote_g_packet_data_handle =
gdbarch_data_register_pre_init (remote_g_packet_data_init);
add_target (extended_remote_target_info, extended_remote_target::open);
/* Hook into new objfile notification. */
- gdb::observers::new_objfile.attach (remote_new_objfile);
+ gdb::observers::new_objfile.attach (remote_new_objfile, "remote");
#if 0
init_remote_threadtests ();
Remote protocol specific variables.\n\
Configure various remote-protocol specific variables such as\n\
the packets being used."),
- &remote_set_cmdlist, "set remote ",
+ &remote_set_cmdlist,
0 /* allow-unknown */, &setlist);
add_prefix_cmd ("remote", class_maintenance, show_remote_cmd, _("\
Remote protocol specific variables.\n\
Configure various remote-protocol specific variables such as\n\
the packets being used."),
- &remote_show_cmdlist, "show remote ",
+ &remote_show_cmdlist,
0 /* allow-unknown */, &showlist);
add_cmd ("compare-sections", class_obscure, compare_sections_command, _("\
To compare only read-only loaded sections, specify the -r option."),
&cmdlist);
- add_cmd ("packet", class_maintenance, packet_command, _("\
+ add_cmd ("packet", class_maintenance, cli_packet_command, _("\
Send an arbitrary packet to a remote target.\n\
maintenance packet TEXT\n\
If GDB is talking to an inferior via the GDB serial protocol, then\n\
terminating `#' character and checksum."),
&maintenancelist);
- add_setshow_boolean_cmd ("remotebreak", no_class, &remote_break, _("\
+ set_show_commands remotebreak_cmds
+ = add_setshow_boolean_cmd ("remotebreak", no_class, &remote_break, _("\
Set whether to send break if interrupted."), _("\
Show whether to send break if interrupted."), _("\
If set, a break, instead of a cntrl-c, is sent to the remote target."),
- set_remotebreak, show_remotebreak,
- &setlist, &showlist);
- cmd_name = "remotebreak";
- cmd = lookup_cmd (&cmd_name, setlist, "", NULL, -1, 1);
- deprecate_cmd (cmd, "set remote interrupt-sequence");
- cmd_name = "remotebreak"; /* needed because lookup_cmd updates the pointer */
- cmd = lookup_cmd (&cmd_name, showlist, "", NULL, -1, 1);
- deprecate_cmd (cmd, "show remote interrupt-sequence");
+ set_remotebreak, show_remotebreak,
+ &setlist, &showlist);
+ deprecate_cmd (remotebreak_cmds.set, "set remote interrupt-sequence");
+ deprecate_cmd (remotebreak_cmds.show, "show remote interrupt-sequence");
add_setshow_enum_cmd ("interrupt-sequence", class_support,
interrupt_sequence_modes, &interrupt_sequence_mode,
add_packet_config_cmd (&remote_protocol_packets[PACKET_no_resumed],
"N stop reply", "no-resumed-stop-reply", 0);
+ add_packet_config_cmd (&remote_protocol_packets[PACKET_memory_tagging_feature],
+ "memory-tagging-feature", "memory-tagging-feature", 0);
+
/* Assert that we've registered "set remote foo-packet" commands
for all packet configs. */
{
add_basic_prefix_cmd ("remote", class_files, _("\
Manipulate files on the remote system.\n\
Transfer files to and from the remote target system."),
- &remote_cmdlist, "remote ",
+ &remote_cmdlist,
0 /* allow-unknown */, &cmdlist);
add_cmd ("put", class_files, remote_put_command,
/* Eventually initialize fileio. See fileio.c */
initialize_remote_fileio (&remote_set_cmdlist, &remote_show_cmdlist);
+
+#if GDB_SELF_TEST
+ selftests::register_test ("remote_memory_tagging",
+ selftests::test_memory_tagging_functions);
+#endif
}