/* Main code for remote server for GDB.
- Copyright (C) 1989-2021 Free Software Foundation, Inc.
+ Copyright (C) 1989-2022 Free Software Foundation, Inc.
This file is part of GDB.
/* Put a stop reply to the stop reply queue. */
static void
-queue_stop_reply (ptid_t ptid, struct target_waitstatus *status)
+queue_stop_reply (ptid_t ptid, const target_waitstatus &status)
{
struct vstop_notif *new_notif = new struct vstop_notif;
new_notif->ptid = ptid;
- new_notif->status = *status;
+ new_notif->status = status;
notif_event_enque (¬if_stop, new_notif);
}
{
struct vstop_notif *vstop = (struct vstop_notif *) event;
- prepare_resume_reply (own_buf, vstop->ptid, &vstop->status);
+ prepare_resume_reply (own_buf, vstop->ptid, vstop->status);
}
/* Helper for in_queued_stop_replies. */
return true;
/* Don't resume fork children that GDB does not know about yet. */
- if ((vstop_event->status.kind == TARGET_WAITKIND_FORKED
- || vstop_event->status.kind == TARGET_WAITKIND_VFORKED)
- && vstop_event->status.value.related_pid.matches (filter_ptid))
+ if ((vstop_event->status.kind () == TARGET_WAITKIND_FORKED
+ || vstop_event->status.kind () == TARGET_WAITKIND_VFORKED)
+ && vstop_event->status.child_ptid ().matches (filter_ptid))
return true;
return false;
/* GDB knows to ignore the first SIGSTOP after attaching to a running
process using the "attach" command, but this is different; it's
just using "target remote". Pretend it's just starting up. */
- if (cs.last_status.kind == TARGET_WAITKIND_STOPPED
- && cs.last_status.value.sig == GDB_SIGNAL_STOP)
- cs.last_status.value.sig = GDB_SIGNAL_TRAP;
+ if (cs.last_status.kind () == TARGET_WAITKIND_STOPPED
+ && cs.last_status.sig () == GDB_SIGNAL_STOP)
+ cs.last_status.set_stopped (GDB_SIGNAL_TRAP);
current_thread->last_resume_kind = resume_stop;
current_thread->last_status = cs.last_status;
error (_("Btrace already enabled."));
current_btrace_conf.format = BTRACE_FORMAT_BTS;
- thread->btrace = target_enable_btrace (thread->id, ¤t_btrace_conf);
+ thread->btrace = target_enable_btrace (thread, ¤t_btrace_conf);
}
/* Handle btrace enabling in Intel Processor Trace format. */
error (_("Btrace already enabled."));
current_btrace_conf.format = BTRACE_FORMAT_PT;
- thread->btrace = target_enable_btrace (thread->id, ¤t_btrace_conf);
+ thread->btrace = target_enable_btrace (thread, ¤t_btrace_conf);
}
/* Handle btrace disabling. */
std::string final_var = hex2str (p);
std::string var_name, var_value;
- if (remote_debug)
- {
- debug_printf (_("[QEnvironmentHexEncoded received '%s']\n"), p);
- debug_printf (_("[Environment variable to be set: '%s']\n"),
- final_var.c_str ());
- debug_flush ();
- }
+ remote_debug_printf ("[QEnvironmentHexEncoded received '%s']", p);
+ remote_debug_printf ("[Environment variable to be set: '%s']",
+ final_var.c_str ());
size_t pos = final_var.find ('=');
if (pos == std::string::npos)
const char *p = own_buf + sizeof ("QEnvironmentUnset:") - 1;
std::string varname = hex2str (p);
- if (remote_debug)
- {
- debug_printf (_("[QEnvironmentUnset received '%s']\n"), p);
- debug_printf (_("[Environment variable to be unset: '%s']\n"),
- varname.c_str ());
- debug_flush ();
- }
+ remote_debug_printf ("[QEnvironmentUnset received '%s']", p);
+ remote_debug_printf ("[Environment variable to be unset: '%s']",
+ varname.c_str ());
our_environ.unset (varname.c_str ());
if (strcmp (own_buf, "QStartNoAckMode") == 0)
{
- if (remote_debug)
- {
- debug_printf ("[noack mode enabled]\n");
- debug_flush ();
- }
+ remote_debug_printf ("[noack mode enabled]");
cs.noack_mode = 1;
write_ok (own_buf);
non_stop = (req != 0);
- if (remote_debug)
- debug_printf ("[%s mode enabled]\n", req_str);
+ remote_debug_printf ("[%s mode enabled]", req_str);
write_ok (own_buf);
return;
unpack_varlen_hex (packet, &setting);
cs.disable_randomization = setting;
- if (remote_debug)
- {
- debug_printf (cs.disable_randomization
- ? "[address space randomization disabled]\n"
- : "[address space randomization enabled]\n");
- }
+ remote_debug_printf (cs.disable_randomization
+ ? "[address space randomization disabled]"
+ : "[address space randomization enabled]");
write_ok (own_buf);
return;
/* Update the flag. */
use_agent = req;
- if (remote_debug)
- debug_printf ("[%s agent]\n", req ? "Enable" : "Disable");
+ remote_debug_printf ("[%s agent]", req ? "Enable" : "Disable");
write_ok (own_buf);
return;
}
cs.report_thread_events = (req == TRIBOOL_TRUE);
- if (remote_debug)
- {
- const char *req_str = cs.report_thread_events ? "enabled" : "disabled";
-
- debug_printf ("[thread events are now %s]\n", req_str);
- }
+ remote_debug_printf ("[thread events are now %s]\n",
+ cs.report_thread_events ? "enabled" : "disabled");
write_ok (own_buf);
return;
return;
}
- if (remote_debug)
- debug_printf (_("[Inferior will %s started with shell]"),
- startup_with_shell ? "be" : "not be");
+ remote_debug_printf ("[Inferior will %s started with shell]",
+ startup_with_shell ? "be" : "not be");
write_ok (own_buf);
return;
{
std::string path = hex2str (p);
- set_inferior_cwd (path.c_str ());
+ remote_debug_printf ("[Set the inferior's current directory to %s]",
+ path.c_str ());
- if (remote_debug)
- debug_printf (_("[Set the inferior's current directory to %s]\n"),
- path.c_str ());
+ set_inferior_cwd (std::move (path));
}
else
{
/* An empty argument means that we should clear out any
previously set cwd for the inferior. */
- set_inferior_cwd (NULL);
+ set_inferior_cwd ("");
- if (remote_debug)
- debug_printf (_("\
-[Unset the inferior's current directory; will use gdbserver's cwd]\n"));
+ remote_debug_printf ("[Unset the inferior's current directory; will "
+ "use gdbserver's cwd]");
}
write_ok (own_buf);
pass signals down without informing GDB. */
if (!non_stop)
{
- if (debug_threads)
- debug_printf ("Forcing non-stop mode\n");
+ threads_debug_printf ("Forcing non-stop mode");
non_stop = true;
the_target->start_non_stop (true);
/* We'll need this after PROCESS has been destroyed. */
int pid = process->pid;
+ /* If this process has an unreported fork child, that child is not known to
+ GDB, so GDB won't take care of detaching it. We must do it here.
+
+ Here, we specifically don't want to use "safe iteration", as detaching
+ another process might delete the next thread in the iteration, which is
+ the one saved by the safe iterator. We will never delete the currently
+ iterated on thread, so standard iteration should be safe. */
+ for (thread_info *thread : all_threads)
+ {
+ /* Only threads that are of the process we are detaching. */
+ if (thread->id.pid () != pid)
+ continue;
+
+ /* Only threads that have a pending fork event. */
+ thread_info *child = target_thread_pending_child (thread);
+ if (child == nullptr)
+ continue;
+
+ process_info *fork_child_process = get_thread_process (child);
+ gdb_assert (fork_child_process != nullptr);
+
+ int fork_child_pid = fork_child_process->pid;
+
+ if (detach_inferior (fork_child_process) != 0)
+ warning (_("Failed to detach fork child %s, child of %s"),
+ target_pid_to_str (ptid_t (fork_child_pid)).c_str (),
+ target_pid_to_str (thread->id).c_str ());
+ }
+
if (detach_inferior (process) != 0)
write_enn (own_buf);
else
/* There is still at least one inferior remaining or
we are in extended mode, so don't terminate gdbserver,
and instead treat this like a normal program exit. */
- cs.last_status.kind = TARGET_WAITKIND_EXITED;
- cs.last_status.value.integer = 0;
+ cs.last_status.set_exited (0);
cs.last_ptid = ptid_t (pid);
- current_thread = NULL;
+ switch_to_thread (nullptr);
}
else
{
{
if (strcmp (mon, "set debug 1") == 0)
{
- debug_threads = 1;
+ debug_threads = true;
monitor_output ("Debug output enabled.\n");
}
else if (strcmp (mon, "set debug 0") == 0)
{
- debug_threads = 0;
+ debug_threads = false;
monitor_output ("Debug output disabled.\n");
}
else if (strcmp (mon, "set debug-hw-points 1") == 0)
}
else if (strcmp (mon, "set remote-debug 1") == 0)
{
- remote_debug = 1;
+ remote_debug = true;
monitor_output ("Protocol debug output enabled.\n");
}
else if (strcmp (mon, "set remote-debug 0") == 0)
{
- remote_debug = 0;
+ remote_debug = false;
monitor_output ("Protocol debug output disabled.\n");
}
else if (strcmp (mon, "set event-loop-debug 1") == 0)
gdb_byte *handle;
bool handle_status = target_thread_handle (ptid, &handle, &handle_len);
+ /* If this is a fork or vfork child (has a fork parent), GDB does not yet
+ know about this process, and must not know about it until it gets the
+ corresponding (v)fork event. Exclude this thread from the list. */
+ if (target_thread_pending_parent (thread) != nullptr)
+ return;
+
write_ptid (ptid_s, ptid);
buffer_xml_printf (buffer, "<thread id=\"%s\"", ptid_s);
{
client_state &cs = get_client_state ();
- scoped_restore save_current_thread
- = make_scoped_restore (¤t_thread);
+ scoped_restore_current_thread restore_thread;
scoped_restore save_current_general_thread
= make_scoped_restore (&cs.general_thread);
if (strcmp ("qSymbol::", own_buf) == 0)
{
- struct thread_info *save_thread = current_thread;
+ scoped_restore_current_thread restore_thread;
/* For qSymbol, GDB only changes the current thread if the
previous current thread was of a different process. So if
exec in a non-leader thread. */
if (current_thread == NULL)
{
- current_thread
+ thread_info *any_thread
= find_any_thread_of_pid (cs.general_thread.pid ());
+ switch_to_thread (any_thread);
/* Just in case, if we didn't find a thread, then bail out
instead of crashing. */
if (current_thread == NULL)
{
write_enn (own_buf);
- current_thread = save_thread;
return;
}
}
if (current_thread != NULL)
the_target->look_up_symbols ();
- current_thread = save_thread;
-
strcpy (own_buf, "OK");
return;
}
cs.last_status = thread->last_status;
cs.last_ptid = thread->id;
- prepare_resume_reply (cs.own_buf, cs.last_ptid, &cs.last_status);
+ prepare_resume_reply (cs.own_buf, cs.last_ptid, cs.last_status);
return 1;
}
return 0;
{
cs.last_ptid = mywait (minus_one_ptid, &cs.last_status, 0, 1);
- if (cs.last_status.kind == TARGET_WAITKIND_NO_RESUMED
+ if (cs.last_status.kind () == TARGET_WAITKIND_NO_RESUMED
&& !report_no_resumed)
{
/* The client does not support this stop reply. At least
return;
}
- if (cs.last_status.kind != TARGET_WAITKIND_EXITED
- && cs.last_status.kind != TARGET_WAITKIND_SIGNALLED
- && cs.last_status.kind != TARGET_WAITKIND_NO_RESUMED)
+ if (cs.last_status.kind () != TARGET_WAITKIND_EXITED
+ && cs.last_status.kind () != TARGET_WAITKIND_SIGNALLED
+ && cs.last_status.kind () != TARGET_WAITKIND_NO_RESUMED)
current_thread->last_status = cs.last_status;
/* From the client's perspective, all-stop mode always stops all
so by now). Tag all threads as "want-stopped", so we don't
resume them implicitly without the client telling us to. */
gdb_wants_all_threads_stopped ();
- prepare_resume_reply (cs.own_buf, cs.last_ptid, &cs.last_status);
+ prepare_resume_reply (cs.own_buf, cs.last_ptid, cs.last_status);
disable_async_io ();
- if (cs.last_status.kind == TARGET_WAITKIND_EXITED
- || cs.last_status.kind == TARGET_WAITKIND_SIGNALLED)
+ if (cs.last_status.kind () == TARGET_WAITKIND_EXITED
+ || cs.last_status.kind () == TARGET_WAITKIND_SIGNALLED)
target_mourn_inferior (cs.last_ptid);
}
}
write_ok (own_buf);
}
else
- prepare_resume_reply (own_buf, cs.last_ptid, &cs.last_status);
+ prepare_resume_reply (own_buf, cs.last_ptid, cs.last_status);
}
else
write_enn (own_buf);
target_create_inferior (program_path.get (), program_args);
- if (cs.last_status.kind == TARGET_WAITKIND_STOPPED)
+ if (cs.last_status.kind () == TARGET_WAITKIND_STOPPED)
{
- prepare_resume_reply (own_buf, cs.last_ptid, &cs.last_status);
+ prepare_resume_reply (own_buf, cs.last_ptid, cs.last_status);
/* In non-stop, sending a resume reply doesn't set the general
thread, but GDB assumes a vRun sets it (this is so GDB can
if (proc != nullptr && kill_inferior (proc) == 0)
{
- cs.last_status.kind = TARGET_WAITKIND_SIGNALLED;
- cs.last_status.value.sig = GDB_SIGNAL_KILL;
+ cs.last_status.set_signalled (GDB_SIGNAL_KILL);
cs.last_ptid = ptid_t (pid);
discard_queued_stop_replies (cs.last_ptid);
write_ok (own_buf);
{
if (target_thread_stopped (thread))
{
- if (debug_threads)
- {
- std::string status_string
- = target_waitstatus_to_string (&thread->last_status);
-
- debug_printf ("Reporting thread %s as already stopped with %s\n",
- target_pid_to_str (thread->id),
- status_string.c_str ());
- }
+ threads_debug_printf
+ ("Reporting thread %s as already stopped with %s",
+ target_pid_to_str (thread->id).c_str (),
+ thread->last_status.to_string ().c_str ());
- gdb_assert (thread->last_status.kind != TARGET_WAITKIND_IGNORE);
+ gdb_assert (thread->last_status.kind () != TARGET_WAITKIND_IGNORE);
/* Pass the last stop reply back to GDB, but don't notify
yet. */
- queue_stop_reply (thread->id, &thread->last_status);
+ queue_stop_reply (thread->id, thread->last_status);
}
}
}
{
thread->last_resume_kind = resume_stop;
- if (thread->last_status.kind == TARGET_WAITKIND_IGNORE)
+ if (thread->last_status.kind () == TARGET_WAITKIND_IGNORE)
{
/* Most threads are stopped implicitly (all-stop); tag that with
signal 0. */
- thread->last_status.kind = TARGET_WAITKIND_STOPPED;
- thread->last_status.value.sig = GDB_SIGNAL_0;
+ thread->last_status.set_stopped (GDB_SIGNAL_0);
}
}
static void
set_pending_status_callback (thread_info *thread)
{
- if (thread->last_status.kind != TARGET_WAITKIND_STOPPED
- || (thread->last_status.value.sig != GDB_SIGNAL_0
+ if (thread->last_status.kind () != TARGET_WAITKIND_STOPPED
+ || (thread->last_status.sig () != GDB_SIGNAL_0
/* A breakpoint, watchpoint or finished step from a previous
GDB run isn't considered interesting for a new GDB run.
If we left those pending, the new GDB could consider them
random SIGTRAPs. This leaves out real async traps. We'd
have to peek into the (target-specific) siginfo to
distinguish those. */
- && thread->last_status.value.sig != GDB_SIGNAL_TRAP))
+ && thread->last_status.sig () != GDB_SIGNAL_TRAP))
thread->status_pending_p = 1;
}
/* Prefer the last thread that reported an event to GDB (even if
that was a GDB_SIGNAL_TRAP). */
- if (cs.last_status.kind != TARGET_WAITKIND_IGNORE
- && cs.last_status.kind != TARGET_WAITKIND_EXITED
- && cs.last_status.kind != TARGET_WAITKIND_SIGNALLED)
+ if (cs.last_status.kind () != TARGET_WAITKIND_IGNORE
+ && cs.last_status.kind () != TARGET_WAITKIND_EXITED
+ && cs.last_status.kind () != TARGET_WAITKIND_SIGNALLED)
thread = find_thread_ptid (cs.last_ptid);
/* If the last event thread is not found for some reason, look
cs.general_thread = thread->id;
set_desired_thread ();
- gdb_assert (tp->last_status.kind != TARGET_WAITKIND_IGNORE);
- prepare_resume_reply (own_buf, tp->id, &tp->last_status);
+ gdb_assert (tp->last_status.kind () != TARGET_WAITKIND_IGNORE);
+ prepare_resume_reply (own_buf, tp->id, tp->last_status);
}
else
strcpy (own_buf, "W00");
gdbserver_version (void)
{
printf ("GNU gdbserver %s%s\n"
- "Copyright (C) 2021 Free Software Foundation, Inc.\n"
+ "Copyright (C) 2022 Free Software Foundation, Inc.\n"
"gdbserver is free software, covered by the "
"GNU General Public License.\n"
"This gdbserver was configured as \"%s\"\n",
*next_arg = NULL;
}
else if (strcmp (*next_arg, "--debug") == 0)
- debug_threads = 1;
+ debug_threads = true;
else if (startswith (*next_arg, "--debug-format="))
{
std::string error_msg
}
}
else if (strcmp (*next_arg, "--remote-debug") == 0)
- remote_debug = 1;
+ remote_debug = true;
else if (strcmp (*next_arg, "--event-loop-debug") == 0)
debug_event_loop = debug_event_loop_kind::ALL;
else if (startswith (*next_arg, "--debug-file="))
}
else
{
- cs.last_status.kind = TARGET_WAITKIND_EXITED;
- cs.last_status.value.integer = 0;
+ cs.last_status.set_exited (0);
cs.last_ptid = minus_one_ptid;
}
if (current_thread != nullptr)
current_process ()->dlls_changed = false;
- if (cs.last_status.kind == TARGET_WAITKIND_EXITED
- || cs.last_status.kind == TARGET_WAITKIND_SIGNALLED)
+ if (cs.last_status.kind () == TARGET_WAITKIND_EXITED
+ || cs.last_status.kind () == TARGET_WAITKIND_SIGNALLED)
was_running = 0;
else
was_running = 1;
if (*dataptr == 'X')
{
/* Conditional expression. */
- if (debug_threads)
- debug_printf ("Found breakpoint condition.\n");
+ threads_debug_printf ("Found breakpoint condition.");
if (!add_breakpoint_condition (bp, &dataptr))
dataptr = strchrnul (dataptr, ';');
}
else if (startswith (dataptr, "cmds:"))
{
dataptr += strlen ("cmds:");
- if (debug_threads)
- debug_printf ("Found breakpoint commands %s.\n", dataptr);
+ threads_debug_printf ("Found breakpoint commands %s.", dataptr);
persist = (*dataptr == '1');
dataptr += 2;
if (add_breakpoint_commands (bp, &dataptr, persist))
running. The traditional protocol will exit instead. */
if (extended_protocol)
{
- cs.last_status.kind = TARGET_WAITKIND_EXITED;
- cs.last_status.value.sig = GDB_SIGNAL_KILL;
+ cs.last_status.set_exited (GDB_SIGNAL_KILL);
return 0;
}
else
{
target_create_inferior (program_path.get (), program_args);
- if (cs.last_status.kind == TARGET_WAITKIND_STOPPED)
+ if (cs.last_status.kind () == TARGET_WAITKIND_STOPPED)
{
/* Stopped at the first instruction of the target
process. */
}
else
{
- cs.last_status.kind = TARGET_WAITKIND_EXITED;
- cs.last_status.value.sig = GDB_SIGNAL_KILL;
+ cs.last_status.set_exited (GDB_SIGNAL_KILL);
}
return 0;
}
void
handle_serial_event (int err, gdb_client_data client_data)
{
- if (debug_threads)
- debug_printf ("handling possible serial event\n");
+ threads_debug_printf ("handling possible serial event");
/* Really handle it. */
if (process_serial_event () < 0)
/* Push a stop notification on the notification queue. */
static void
-push_stop_notification (ptid_t ptid, struct target_waitstatus *status)
+push_stop_notification (ptid_t ptid, const target_waitstatus &status)
{
struct vstop_notif *vstop_notif = new struct vstop_notif;
- vstop_notif->status = *status;
+ vstop_notif->status = status;
vstop_notif->ptid = ptid;
/* Push Stop notification. */
notif_push (¬if_stop, vstop_notif);
handle_target_event (int err, gdb_client_data client_data)
{
client_state &cs = get_client_state ();
- if (debug_threads)
- debug_printf ("handling possible target event\n");
+ threads_debug_printf ("handling possible target event");
cs.last_ptid = mywait (minus_one_ptid, &cs.last_status,
TARGET_WNOHANG, 1);
- if (cs.last_status.kind == TARGET_WAITKIND_NO_RESUMED)
+ if (cs.last_status.kind () == TARGET_WAITKIND_NO_RESUMED)
{
if (gdb_connected () && report_no_resumed)
- push_stop_notification (null_ptid, &cs.last_status);
+ push_stop_notification (null_ptid, cs.last_status);
}
- else if (cs.last_status.kind != TARGET_WAITKIND_IGNORE)
+ else if (cs.last_status.kind () != TARGET_WAITKIND_IGNORE)
{
int pid = cs.last_ptid.pid ();
struct process_info *process = find_process_pid (pid);
int forward_event = !gdb_connected () || process->gdb_detached;
- if (cs.last_status.kind == TARGET_WAITKIND_EXITED
- || cs.last_status.kind == TARGET_WAITKIND_SIGNALLED)
+ if (cs.last_status.kind () == TARGET_WAITKIND_EXITED
+ || cs.last_status.kind () == TARGET_WAITKIND_SIGNALLED)
{
mark_breakpoints_out (process);
target_mourn_inferior (cs.last_ptid);
}
- else if (cs.last_status.kind == TARGET_WAITKIND_THREAD_EXITED)
+ else if (cs.last_status.kind () == TARGET_WAITKIND_THREAD_EXITED)
;
else
{
exit (0);
}
- if (cs.last_status.kind == TARGET_WAITKIND_EXITED
- || cs.last_status.kind == TARGET_WAITKIND_SIGNALLED
- || cs.last_status.kind == TARGET_WAITKIND_THREAD_EXITED)
+ if (cs.last_status.kind () == TARGET_WAITKIND_EXITED
+ || cs.last_status.kind () == TARGET_WAITKIND_SIGNALLED
+ || cs.last_status.kind () == TARGET_WAITKIND_THREAD_EXITED)
;
else
{
inferior, as if it wasn't being traced. */
enum gdb_signal signal;
- if (debug_threads)
- debug_printf ("GDB not connected; forwarding event %d for"
- " [%s]\n",
- (int) cs.last_status.kind,
- target_pid_to_str (cs.last_ptid));
+ threads_debug_printf ("GDB not connected; forwarding event %d for"
+ " [%s]",
+ (int) cs.last_status.kind (),
+ target_pid_to_str (cs.last_ptid).c_str ());
- if (cs.last_status.kind == TARGET_WAITKIND_STOPPED)
- signal = cs.last_status.value.sig;
+ if (cs.last_status.kind () == TARGET_WAITKIND_STOPPED)
+ signal = cs.last_status.sig ();
else
signal = GDB_SIGNAL_0;
target_continue (cs.last_ptid, signal);
}
}
else
- push_stop_notification (cs.last_ptid, &cs.last_status);
+ push_stop_notification (cs.last_ptid, cs.last_status);
}
/* Be sure to not change the selected thread behind GDB's back.