binutils-gdb/gdb/gdbserver/target.c
Pedro Alves e671cd59d7 Per-inferior target_terminal state, fix PR gdb/13211, more
In my multi-target branch I ran into problems with GDB's terminal
handling that exist in master as well, with multi-inferior debugging.

This patch adds a testcase for said problems
(gdb.multi/multi-term-settings.exp), fixes the problems, fixes PR
gdb/13211 as well (and adds a testcase for that too,
gdb.base/interrupt-daemon.exp).

The basis of the problem I ran into is the following.  Consider a
scenario where you have:

 - inferior 1 - started with "attach", process is running on some
   other terminal.

 - inferior 2 - started with "run", process is sharing gdb's terminal.

In this scenario, when you stop/resume both inferiors, you want GDB to
save/restore the terminal settings of inferior 2, the one that is
sharing GDB's terminal.  I.e., you want inferior 2 to "own" the
terminal (in target_terminal::is_ours/target_terminal::is_inferior
sense).

Unfortunately, that's not what you get currently.  Because GDB doesn't
know whether an attached inferior is actually sharing GDB's terminal,
it tries to save/restore its settings anyway, ignoring errors.  In
this case, this is pointless, because inferior 1 is running on a
different terminal, but GDB doesn't know better.

And then, because it is only possible to have the terminal settings of
a single inferior be in effect at a time, or make one inferior/pgrp be
the terminal's foreground pgrp (aka, only one inferior can "own" the
terminal, ignoring fork children here), if GDB happens to try to
restore the terminal settings of inferior 1 first, then GDB never
restores the terminal settings of inferior 2.

This patch fixes that and a few things more along the way:

 - Moves enum target_terminal::terminal_state out of the
   target_terminal class (it's currently private) and makes it a
   scoped enum so that it can be easily used elsewhere.

 - Replaces the inflow.c:terminal_is_ours boolean with a
   target_terminal_state variable.  This allows distinguishing is_ours
   and is_ours_for_output states.  This allows finally making
   child_terminal_ours_1 do something with its "output_only"
   parameter.

 - Makes each inferior have its own copy of the
   is_ours/is_ours_for_output/is_inferior state.

 - Adds a way for GDB to tell whether the inferior is sharing GDB's
   terminal.  Works best on Linux and Solaris; the fallback works just
   as well as currently.

 - With that, we can remove the inf->attach_flag tests from
   child_terminal_inferior/child_terminal_ours.

 - Currently target_ops.to_ours is responsible for both saving the
   current inferior's terminal state, and restoring gdb's state.
   Because each inferior has its own terminal state (possibly handled
   by different targets in a multi-target world, even), we need to
   split the inferior-saving part from the gdb-restoring part.  The
   patch adds a new target_ops.to_save_inferior target method for
   that.

 - Adds a new target_terminal::save_inferior() function, so that
   sequences like:

     scoped_restore_terminal_state save_state;
     target_terminal::ours_for_output ();

   ... restore back inferiors that were
   target_terminal_state::is_inferior before back to is_inferior, and
   leaves inferiors that were is_ours alone.

 - Along the way, this adds a default implementation of
   target_pass_ctrlc to inflow.c (for inf-child.c), that handles
   passing the Ctrl-C to a process running on GDB's terminal or to
   some other process otherwise.

 - Similarly, adds a new target default implementation of
   target_interrupt, for the "interrupt" command.  The current
   implementation of this hook in inf-ptrace.c kills the whole process
   group, but that's incorrect/undesirable because we may not be
   attached to all processes in the process group.  And also, it's
   incorrect because inferior_process_group() doesn't really return
   the inferior's real process group id if the inferior is not a
   process group leader...  This is the cause of PR gdb/13211 [1],
   which this patch fixes.  While at it, that target method's "ptid"
   parameter is eliminated, because it's not really used.

 - A new test is included that exercises and fixes PR gdb/13211, and
   also fixes a GDB issue reported on stackoverflow that I ran into
   while working on this [2].  The problem is similar to PR gdb/13211,
   except that it also triggers with Ctrl-C.  When debugging a daemon
   (i.e., a process that disconnects from the controlling terminal and
   is not a process group leader, then Ctrl-C doesn't work, you just
   can't interrupt the inferior at all, resulting in a hung debug
   session.  The problem is that since the inferior is no longer
   associated with gdb's session / controlling terminal, then trying
   to put the inferior in the foreground fails.  And so Ctrl-C never
   reaches the inferior directly.  pass_signal is only used when the
   inferior is attached, but that is not the case here.  This is fixed
   by the new child_pass_ctrlc.  Without the fix, the new
   interrupt-daemon.exp testcase fails with timeout waiting for a
   SIGINT that never arrives.

[1] PR gdb/13211 - Async / Process group and interrupt not working
https://sourceware.org/bugzilla/show_bug.cgi?id=13211

[2] GDB not reacting Ctrl-C when after fork() and setsid()
https://stackoverflow.com/questions/46101292/gdb-not-reacting-ctrl-c-when-after-fork-and-setsid

Note this patch does _not_ fix:

 - PR gdb/14559 - The 'interrupt' command does not work if sigwait is in use
   https://sourceware.org/bugzilla/show_bug.cgi?id=14559

 - PR gdb/9425 - When using "sigwait" GDB doesn't trap SIGINT. Ctrl+C terminates program when should break gdb.
   https://sourceware.org/bugzilla/show_bug.cgi?id=9425

The only way to fix that that I know of (without changing the kernel)
is to make GDB put inferiors in a separate session (create a
pseudo-tty master/slave pair, make the inferior run with the slave as
its terminal, and have gdb pump output/input on the master end).

gdb/ChangeLog:
2018-01-30  Pedro Alves  <palves@redhat.com>

	PR gdb/13211
	* config.in, configure: Regenerate.
	* configure.ac: Check for getpgid.
	* go32-nat.c (go32_pass_ctrlc): New.
	(go32_target): Install it.
	* inf-child.c (inf_child_target): Install
	child_terminal_save_inferior, child_pass_ctrlc and
	child_interrupt.
	* inf-ptrace.c (inf_ptrace_interrupt): Delete.
	(inf_ptrace_target): No longer install it.
	* infcmd.c (interrupt_target_1): Adjust.
	* inferior.h (child_terminal_save_inferior, child_pass_ctrlc)
	(child_interrupt): Declare.
	(inferior::terminal_state): New.
	* inflow.c (struct terminal_info): Update comments.
	(inferior_process_group): Delete.
	(terminal_is_ours): Delete.
	(gdb_tty_state): New.
	(child_terminal_init): Adjust.
	(is_gdb_terminal, sharing_input_terminal_1)
	(sharing_input_terminal): New functions.
	(child_terminal_inferior): Adjust.  Use sharing_input_terminal.
	Set the process's actual process group in the foreground if
	possible.  Handle is_ours_for_output/is_ours distinction.  Don't
	mark terminal as the inferior's if not sharing GDB's terminal.
	Don't check attach_flag.
	(child_terminal_ours_for_output, child_terminal_ours): Adjust to
	pass down a target_terminal_state.
	(child_terminal_save_inferior): New, factored out from ...
	(child_terminal_ours_1): ... this.  Handle
	target_terminal_state::is_ours_for_output.
	(child_interrupt, child_pass_ctrlc): New.
	(inflow_inferior_exit): Clear the inferior's terminal_state.
	(copy_terminal_info): Copy the inferior's terminal state.
	(_initialize_inflow): Remove reference to terminal_is_ours.
	* inflow.h (inferior_process_group): Delete.
	* nto-procfs.c (nto_handle_sigint, procfs_interrupt): Adjust.
	* procfs.c (procfs_target): Don't install procfs_interrupt.
	(procfs_interrupt): Delete.
	* remote.c (remote_serial_quit_handler): Adjust.
	(remote_interrupt): Remove ptid parameter.  Adjust.
	* target-delegates.c: Regenerate.
	* target.c: Include "terminal.h".
	(target_terminal::terminal_state): Rename to ...
	(target_terminal::m_terminal_state): ... this.
	(target_terminal::init): Adjust.
	(target_terminal::inferior): Adjust to per-inferior
	terminal_state.
	(target_terminal::restore_inferior, target_terminal_is_ours_kind): New.
	(target_terminal::ours, target_terminal::ours_for_output): Use
	target_terminal_is_ours_kind.
	(target_interrupt): Remove ptid parameter.  Adjust.
	(default_target_pass_ctrlc): Adjust.
	* target.h (target_ops::to_terminal_save_inferior): New field.
	(target_ops::to_interrupt): Remove ptid_t parameter.
	(target_interrupt): Remove ptid_t parameter.  Update comment.
	(target_pass_ctrlc): Update comment.
	* target/target.h (target_terminal_state): New scoped enum,
	factored out of ...
	(target_terminal::terminal_state): ... here.
	(target_terminal::inferior): Update comments.
	(target_terminal::restore_inferior): New.
	(target_terminal::is_inferior, target_terminal::is_ours)
	(target_terminal::is_ours_for_output): Adjust.
	(target_terminal::scoped_restore_terminal_state): Adjust to
	rename, and call restore_inferior() instead of inferior().
	(target_terminal::scoped_restore_terminal_state::m_state): Change
	type.
	(target_terminal::terminal_state): Rename to ...
	(target_terminal::m_terminal_state): ... this and change type.

gdb/gdbserver/ChangeLog:
2018-01-30  Pedro Alves  <palves@redhat.com>

	PR gdb/13211
	* target.c (target_terminal::terminal_state): Rename to ...
	(target_terminal::m_terminal_state): ... this.

gdb/testsuite/ChangeLog:
2018-01-30  Pedro Alves  <palves@redhat.com>

	PR gdb/13211
	* gdb.base/interrupt-daemon.c: New.
	* gdb.base/interrupt-daemon.exp: New.
	* gdb.multi/multi-term-settings.c: New.
	* gdb.multi/multi-term-settings.exp: New.
2018-01-30 14:55:18 +00:00

408 lines
9.3 KiB
C

/* Target operations for the remote server for GDB.
Copyright (C) 2002-2018 Free Software Foundation, Inc.
Contributed by MontaVista Software.
This file is part of GDB.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include "server.h"
#include "tracepoint.h"
struct target_ops *the_target;
int
set_desired_thread ()
{
thread_info *found = find_thread_ptid (general_thread);
current_thread = found;
return (current_thread != NULL);
}
/* The thread that was current before prepare_to_access_memory was
called. done_accessing_memory uses this to restore the previous
selected thread. */
static ptid_t prev_general_thread;
/* See target.h. */
int
prepare_to_access_memory (void)
{
/* The first thread found. */
struct thread_info *first = NULL;
/* The first stopped thread found. */
struct thread_info *stopped = NULL;
/* The current general thread, if found. */
struct thread_info *current = NULL;
/* Save the general thread value, since prepare_to_access_memory could change
it. */
prev_general_thread = general_thread;
if (the_target->prepare_to_access_memory != NULL)
{
int res;
res = the_target->prepare_to_access_memory ();
if (res != 0)
return res;
}
for_each_thread (prev_general_thread.pid (), [&] (thread_info *thread)
{
if (mythread_alive (thread->id))
{
if (stopped == NULL && the_target->thread_stopped != NULL
&& thread_stopped (thread))
stopped = thread;
if (first == NULL)
first = thread;
if (current == NULL && prev_general_thread == thread->id)
current = thread;
}
});
/* The thread we end up choosing. */
struct thread_info *thread;
/* Prefer a stopped thread. If none is found, try the current
thread. Otherwise, take the first thread in the process. If
none is found, undo the effects of
target->prepare_to_access_memory() and return error. */
if (stopped != NULL)
thread = stopped;
else if (current != NULL)
thread = current;
else if (first != NULL)
thread = first;
else
{
done_accessing_memory ();
return 1;
}
current_thread = thread;
general_thread = ptid_of (thread);
return 0;
}
/* See target.h. */
void
done_accessing_memory (void)
{
if (the_target->done_accessing_memory != NULL)
the_target->done_accessing_memory ();
/* Restore the previous selected thread. */
general_thread = prev_general_thread;
switch_to_thread (general_thread);
}
int
read_inferior_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len)
{
int res;
res = (*the_target->read_memory) (memaddr, myaddr, len);
check_mem_read (memaddr, myaddr, len);
return res;
}
/* See target/target.h. */
int
target_read_memory (CORE_ADDR memaddr, gdb_byte *myaddr, ssize_t len)
{
return read_inferior_memory (memaddr, myaddr, len);
}
/* See target/target.h. */
int
target_read_uint32 (CORE_ADDR memaddr, uint32_t *result)
{
return read_inferior_memory (memaddr, (gdb_byte *) result, sizeof (*result));
}
int
write_inferior_memory (CORE_ADDR memaddr, const unsigned char *myaddr,
int len)
{
/* Lacking cleanups, there is some potential for a memory leak if the
write fails and we go through error(). Make sure that no more than
one buffer is ever pending by making BUFFER static. */
static unsigned char *buffer = 0;
int res;
if (buffer != NULL)
free (buffer);
buffer = (unsigned char *) xmalloc (len);
memcpy (buffer, myaddr, len);
check_mem_write (memaddr, buffer, myaddr, len);
res = (*the_target->write_memory) (memaddr, buffer, len);
free (buffer);
buffer = NULL;
return res;
}
/* See target/target.h. */
int
target_write_memory (CORE_ADDR memaddr, const gdb_byte *myaddr, ssize_t len)
{
return write_inferior_memory (memaddr, myaddr, len);
}
ptid_t
mywait (ptid_t ptid, struct target_waitstatus *ourstatus, int options,
int connected_wait)
{
ptid_t ret;
if (connected_wait)
server_waiting = 1;
ret = target_wait (ptid, ourstatus, options);
/* We don't expose _LOADED events to gdbserver core. See the
`dlls_changed' global. */
if (ourstatus->kind == TARGET_WAITKIND_LOADED)
ourstatus->kind = TARGET_WAITKIND_STOPPED;
/* If GDB is connected through TCP/serial, then GDBserver will most
probably be running on its own terminal/console, so it's nice to
print there why is GDBserver exiting. If however, GDB is
connected through stdio, then there's no need to spam the GDB
console with this -- the user will already see the exit through
regular GDB output, in that same terminal. */
if (!remote_connection_is_stdio ())
{
if (ourstatus->kind == TARGET_WAITKIND_EXITED)
fprintf (stderr,
"\nChild exited with status %d\n", ourstatus->value.integer);
else if (ourstatus->kind == TARGET_WAITKIND_SIGNALLED)
fprintf (stderr, "\nChild terminated with signal = 0x%x (%s)\n",
gdb_signal_to_host (ourstatus->value.sig),
gdb_signal_to_name (ourstatus->value.sig));
}
if (connected_wait)
server_waiting = 0;
return ret;
}
/* See target/target.h. */
void
target_stop_and_wait (ptid_t ptid)
{
struct target_waitstatus status;
int was_non_stop = non_stop;
struct thread_resume resume_info;
resume_info.thread = ptid;
resume_info.kind = resume_stop;
resume_info.sig = GDB_SIGNAL_0;
(*the_target->resume) (&resume_info, 1);
non_stop = 1;
mywait (ptid, &status, 0, 0);
non_stop = was_non_stop;
}
/* See target/target.h. */
ptid_t
target_wait (ptid_t ptid, struct target_waitstatus *status, int options)
{
return (*the_target->wait) (ptid, status, options);
}
/* See target/target.h. */
void
target_mourn_inferior (ptid_t ptid)
{
(*the_target->mourn) (find_process_pid (ptid_get_pid (ptid)));
}
/* See target/target.h. */
void
target_continue_no_signal (ptid_t ptid)
{
struct thread_resume resume_info;
resume_info.thread = ptid;
resume_info.kind = resume_continue;
resume_info.sig = GDB_SIGNAL_0;
(*the_target->resume) (&resume_info, 1);
}
/* See target/target.h. */
void
target_continue (ptid_t ptid, enum gdb_signal signal)
{
struct thread_resume resume_info;
resume_info.thread = ptid;
resume_info.kind = resume_continue;
resume_info.sig = gdb_signal_to_host (signal);
(*the_target->resume) (&resume_info, 1);
}
/* See target/target.h. */
int
target_supports_multi_process (void)
{
return (the_target->supports_multi_process != NULL ?
(*the_target->supports_multi_process) () : 0);
}
int
start_non_stop (int nonstop)
{
if (the_target->start_non_stop == NULL)
{
if (nonstop)
return -1;
else
return 0;
}
return (*the_target->start_non_stop) (nonstop);
}
void
set_target_ops (struct target_ops *target)
{
the_target = XNEW (struct target_ops);
memcpy (the_target, target, sizeof (*the_target));
}
/* Convert pid to printable format. */
const char *
target_pid_to_str (ptid_t ptid)
{
static char buf[80];
if (ptid_equal (ptid, minus_one_ptid))
xsnprintf (buf, sizeof (buf), "<all threads>");
else if (ptid_equal (ptid, null_ptid))
xsnprintf (buf, sizeof (buf), "<null thread>");
else if (ptid_get_tid (ptid) != 0)
xsnprintf (buf, sizeof (buf), "Thread %d.0x%lx",
ptid_get_pid (ptid), ptid_get_tid (ptid));
else if (ptid_get_lwp (ptid) != 0)
xsnprintf (buf, sizeof (buf), "LWP %d.%ld",
ptid_get_pid (ptid), ptid_get_lwp (ptid));
else
xsnprintf (buf, sizeof (buf), "Process %d",
ptid_get_pid (ptid));
return buf;
}
int
kill_inferior (int pid)
{
gdb_agent_about_to_close (pid);
return (*the_target->kill) (pid);
}
/* Target can do hardware single step. */
int
target_can_do_hardware_single_step (void)
{
return 1;
}
/* Default implementation for breakpoint_kind_for_pc.
The default behavior for targets that don't implement breakpoint_kind_for_pc
is to use the size of a breakpoint as the kind. */
int
default_breakpoint_kind_from_pc (CORE_ADDR *pcptr)
{
int size = 0;
gdb_assert (the_target->sw_breakpoint_from_kind != NULL);
(*the_target->sw_breakpoint_from_kind) (0, &size);
return size;
}
/* Define it. */
target_terminal_state target_terminal::m_terminal_state
= target_terminal_state::is_ours;
/* See target/target.h. */
void
target_terminal::init ()
{
/* Placeholder needed because of fork_inferior. Not necessary on
GDBserver. */
}
/* See target/target.h. */
void
target_terminal::inferior ()
{
/* Placeholder needed because of fork_inferior. Not necessary on
GDBserver. */
}
/* See target/target.h. */
void
target_terminal::ours ()
{
/* Placeholder needed because of fork_inferior. Not necessary on
GDBserver. */
}
/* See target/target.h. */
void
target_terminal::ours_for_output (void)
{
/* Placeholder. */
}
/* See target/target.h. */
void
target_terminal::info (const char *arg, int from_tty)
{
/* Placeholder. */
}