forked from Imagelibrary/binutils-gdb
Compare commits
16 Commits
binutils-2
...
users/palv
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4e18fe9cb3 | ||
|
|
814fb49ba3 | ||
|
|
86a5798736 | ||
|
|
de89924a63 | ||
|
|
d9bda17252 | ||
|
|
9f84272c29 | ||
|
|
5cd9932f70 | ||
|
|
6579312c44 | ||
|
|
9d5edd40e6 | ||
|
|
8db79a51e5 | ||
|
|
68ad0fe169 | ||
|
|
deb9814ee1 | ||
|
|
976f24fd55 | ||
|
|
7a884f7b7f | ||
|
|
0d599db17d | ||
|
|
2de900d8ff |
@@ -1336,7 +1336,6 @@ HFILES_NO_SRCDIR = \
|
||||
inf-ptrace.h \
|
||||
infcall.h \
|
||||
inferior.h \
|
||||
inflow.h \
|
||||
inline-frame.h \
|
||||
interps.h \
|
||||
jit.h \
|
||||
|
||||
23
gdb/NEWS
23
gdb/NEWS
@@ -6,6 +6,25 @@
|
||||
* The 'set disassembler-options' command now supports specifying options
|
||||
for the ARC target.
|
||||
|
||||
* On GNU/Linux, by default GDB now starts programs associated with a
|
||||
pseudo-terminal slave device created and managed by GDB, instead of
|
||||
having the inferior use the same terminal as GDB directly. GDB
|
||||
takes care of forwarding input and output to and from GDB's terminal
|
||||
at appropriate times, so this is largely transparent.
|
||||
|
||||
In addition, by default, Ctrl-C no longer stops the program with
|
||||
SIGINT. Instead you'll see for example:
|
||||
|
||||
Thread 1 "main" stopped.
|
||||
|
||||
With these changes it is now possible to interrupt programs that
|
||||
block or ignore the SIGINT signal (with e.g., the sigprocmask
|
||||
function), or use the sigwait function to wait for SIGINT signal.
|
||||
|
||||
You can use the "tty /dev/tty" command to restore the previous
|
||||
behavior of spawning programs associated with the same terminal as
|
||||
GDB.
|
||||
|
||||
* GDB now supports general memory tagging functionality if the underlying
|
||||
architecture supports the proper primitives and hooks. Currently this is
|
||||
enabled only for AArch64 MTE.
|
||||
@@ -84,6 +103,10 @@ set debug event-loop
|
||||
show debug event-loop
|
||||
Control the display of debug output about GDB's event loop.
|
||||
|
||||
set debug managed-tty
|
||||
show debug managed-tty
|
||||
Control the display of debug output about GDB-managed terminals.
|
||||
|
||||
set print memory-tag-violations
|
||||
show print memory-tag-violations
|
||||
Control whether to display additional information about memory tag violations
|
||||
|
||||
@@ -2983,11 +2983,27 @@ current working directory of the debuggee.
|
||||
@cindex redirection
|
||||
@cindex i/o
|
||||
@cindex terminal
|
||||
By default, the program you run under @value{GDBN} does input and output to
|
||||
the same terminal that @value{GDBN} uses. @value{GDBN} switches the terminal
|
||||
to its own terminal modes to interact with you, but it records the terminal
|
||||
modes your program was using and switches back to them when you continue
|
||||
running your program.
|
||||
By default, the program you run under @value{GDBN} does (or
|
||||
@value{GDBN} provides the illusion that it does) input and output to
|
||||
the same terminal that @value{GDBN} uses.
|
||||
|
||||
Depending on the operating system and configuration, either:
|
||||
|
||||
@itemize
|
||||
|
||||
@item
|
||||
@value{GDBN} switches the terminal to its own terminal modes to
|
||||
interact with you, but it records the terminal modes your program was
|
||||
using and switches back to them when you continue running your
|
||||
program. This is the default on most systems.
|
||||
|
||||
@item
|
||||
@value{GDBN} creates a pseudo-terminal, sets your program to use it
|
||||
for standard input and standard output, and forwards input and output
|
||||
to and from @value{GDBN}'s terminal at appropriate times. This is the
|
||||
default on GNU/Linux.
|
||||
|
||||
@end itemize
|
||||
|
||||
@table @code
|
||||
@kindex info terminal
|
||||
@@ -3023,6 +3039,30 @@ directs that processes started with subsequent @code{run} commands
|
||||
default to do input and output on the terminal @file{/dev/ttyb} and have
|
||||
that as their controlling terminal.
|
||||
|
||||
On some operating systems, by default, @value{GDBN} creates a
|
||||
pseudo-terminal, and sets your program to use it for standard input
|
||||
and standard output. @value{GDBN} takes care of forwarding input and
|
||||
output to and from @value{GDBN}'s terminal at appropriate times, so
|
||||
this is largely transparent.
|
||||
|
||||
On such systems, in some cases, like for example if you need to run
|
||||
your program and then detach it, and you want the program to remain
|
||||
associated with a terminal, you may prefer that @value{GDBN} starts
|
||||
your program using the same device for standard input and output as
|
||||
@value{GDBN} is using.
|
||||
|
||||
Specifying the special @file{/dev/tty} file as input and output device
|
||||
is interpreted as a request for doing I/O to the same terminal that
|
||||
@value{GDBN} uses directly, skipping creation of an intermediary
|
||||
pseudo-terminal on systems where @value{GDBN} creates one by default.
|
||||
|
||||
Note that on GNU/Linux at least, a consequence of using the same
|
||||
terminal as @value{GDBN} directly is that programs that block or
|
||||
ignore the @code{SIGINT} signal (with e.g., the @code{sigprocmask}
|
||||
function), or use the @code{sigwait} function to wait for
|
||||
@code{SIGINT} signals can not be interrupted by typing the interrupt
|
||||
character (often @kbd{Ctrl-c}).
|
||||
|
||||
An explicit redirection in @code{run} overrides the @code{tty} command's
|
||||
effect on the input/output device, but not its effect on the controlling
|
||||
terminal.
|
||||
@@ -3050,6 +3090,21 @@ restores the default behavior, which is to use the same terminal as
|
||||
Show the current tty for the program being debugged.
|
||||
@end table
|
||||
|
||||
The management of @value{GDBN}-created terminals can be inspected
|
||||
using:
|
||||
|
||||
@table @code
|
||||
@kindex set debug managed-tty
|
||||
@item set debug managed-tty @r{[}on|off@r{]}
|
||||
Set whether to print debug output about @value{GDBN}-managed
|
||||
terminals.
|
||||
|
||||
@kindex show debug managed-tty
|
||||
@item show debug managed-tty
|
||||
Show whether the debug output about @value{GDBN}-managed terminals is
|
||||
printed.
|
||||
@end table
|
||||
|
||||
@node Attach
|
||||
@section Debugging an Already-running Process
|
||||
@kindex attach
|
||||
@@ -3132,6 +3187,16 @@ things; you can control whether or not you need to confirm by using the
|
||||
@code{set confirm} command (@pxref{Messages/Warnings, ,Optional Warnings and
|
||||
Messages}).
|
||||
|
||||
On systems where @value{GDBN} creates a pseudo-terminal for spawned
|
||||
inferiors, detaching from a process that was started by @value{GDBN}
|
||||
(for example with the @code{run} command) results in the process
|
||||
losing its terminal right after detaching, because @value{GDBN}
|
||||
destroys the pseudo-terminal device pair. If you prefer, you can use
|
||||
the @code{tty} command command to instruct @value{GDBN} to start your
|
||||
program doing I/O to the same terminal that @value{GDBN} uses or to
|
||||
some other terminal. @xref{Input/Output, ,Your Program's Input and
|
||||
Output}.
|
||||
|
||||
@node Kill Process
|
||||
@section Killing the Child Process
|
||||
|
||||
@@ -4224,6 +4289,7 @@ running or not, what process it is, and why it stopped.
|
||||
@menu
|
||||
* Breakpoints:: Breakpoints, watchpoints, and catchpoints
|
||||
* Continuing and Stepping:: Resuming execution
|
||||
* Interrupting:: Interrupting execution
|
||||
* Skipping Over Functions and Files::
|
||||
Skipping over functions and files
|
||||
* Signals:: Signals
|
||||
@@ -6376,6 +6442,45 @@ default is @code{on}.
|
||||
|
||||
@end table
|
||||
|
||||
@node Interrupting
|
||||
@section Interrupting
|
||||
|
||||
You can stop the program while it is running in the foreground by
|
||||
pressing the interrupt character (often @kbd{Ctrl-c}). If the program
|
||||
is executing in the background (@pxref{Background Execution}), you can
|
||||
use the @code{interrupt} command (@pxref{interrupt}).
|
||||
|
||||
Depending on operating system and configuration, this results in
|
||||
interrupting the program with either a @code{SIGINT} signal:
|
||||
|
||||
@smallexample
|
||||
Program received signal SIGINT, Interrupt.
|
||||
@end smallexample
|
||||
|
||||
@noindent
|
||||
or plainly suspending the program:
|
||||
|
||||
@smallexample
|
||||
Program stopped.
|
||||
@end smallexample
|
||||
|
||||
The terminal the program was started with affects whether you get the
|
||||
former or the latter. @xref{Input/Output, ,Your Program's Input and
|
||||
Output}.
|
||||
|
||||
On systems where interrupting the program results in a plain
|
||||
suspension instead of the program receiving a @code{SIGINT} signal,
|
||||
you can still pass a @code{SIGINT} signal to the program after it
|
||||
stops, using either the @code{signal SIGINT} or @code{queue-signal
|
||||
SIGINT} commands. @xref{Signaling,,Giving your Program a Signal}.
|
||||
|
||||
The remote target supports a number of options to configure how the
|
||||
remote program is interrupted. @xref{Remote Configuration}.
|
||||
|
||||
@value{GDBN} on MS-Windows supports @kbd{C-@key{BREAK}} as an
|
||||
alternative interrupt key sequence. @xref{interrupt debuggee on
|
||||
MS-Windows}.
|
||||
|
||||
@node Skipping Over Functions and Files
|
||||
@section Skipping Over Functions and Files
|
||||
@cindex skipping over functions and files
|
||||
@@ -7090,6 +7195,7 @@ using the @code{interrupt} command.
|
||||
|
||||
@table @code
|
||||
@kindex interrupt
|
||||
@anchor{interrupt}
|
||||
@item interrupt
|
||||
@itemx interrupt -a
|
||||
|
||||
@@ -24219,6 +24325,7 @@ DLLs with and without symbolic debugging information.
|
||||
|
||||
@cindex Ctrl-BREAK, MS-Windows
|
||||
@cindex interrupt debuggee on MS-Windows
|
||||
@anchor{interrupt debuggee on MS-Windows}
|
||||
MS-Windows programs that call @code{SetConsoleMode} to switch off the
|
||||
special meaning of the @samp{Ctrl-C} keystroke cannot be interrupted
|
||||
by typing @kbd{C-c}. For this reason, @value{GDBN} on MS-Windows
|
||||
|
||||
@@ -1002,7 +1002,15 @@ default_quit_handler (void)
|
||||
if (target_terminal::is_ours ())
|
||||
quit ();
|
||||
else
|
||||
target_pass_ctrlc ();
|
||||
{
|
||||
/* Let the even loop handle the quit/interrupt. In some
|
||||
modes (e.g., "set non-stop off" + "maint set
|
||||
target-non-stop on"), it's not safe to request an
|
||||
interrupt right now, as we may be in the middle of
|
||||
handling some other event, and target_stop changes infrun
|
||||
state. */
|
||||
mark_infrun_async_event_handler_ctrl_c ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ static struct ui *saved_ui = NULL;
|
||||
/* See nat/fork-inferior.h. */
|
||||
|
||||
void
|
||||
prefork_hook (const char *args)
|
||||
prefork_hook ()
|
||||
{
|
||||
gdb_assert (saved_ui == NULL);
|
||||
/* Retain a copy of our UI, since the child will replace this value
|
||||
@@ -73,6 +73,14 @@ prefork_hook (const char *args)
|
||||
|
||||
/* See nat/fork-inferior.h. */
|
||||
|
||||
bool
|
||||
child_has_managed_tty_hook ()
|
||||
{
|
||||
return created_managed_tty ();
|
||||
}
|
||||
|
||||
/* See nat/fork-inferior.h. */
|
||||
|
||||
void
|
||||
postfork_hook (pid_t pid)
|
||||
{
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
#include "defs.h"
|
||||
#include "command.h"
|
||||
#include "inferior.h"
|
||||
#include "inflow.h"
|
||||
#include "terminal.h"
|
||||
#include "gdbcore.h"
|
||||
#include "regcache.h"
|
||||
@@ -62,6 +61,26 @@ inf_ptrace_me (void)
|
||||
/* "Trace me, Dr. Memory!" */
|
||||
if (ptrace (PT_TRACE_ME, 0, (PTRACE_TYPE_ARG3) 0, 0) < 0)
|
||||
trace_start_error_with_name ("ptrace");
|
||||
if (created_managed_tty ())
|
||||
{
|
||||
/* We're about to fork again, so that this child remains as
|
||||
session leader, and the grandchild becomes the real inferior.
|
||||
Let GDB grab control of this child, and enable tracing the
|
||||
grandchild fork. */
|
||||
raise (SIGSTOP);
|
||||
}
|
||||
}
|
||||
|
||||
/* fork_inferior handle_session_leader_fork hook. Dispatches to
|
||||
inf_ptrace_target. */
|
||||
|
||||
static pid_t
|
||||
inf_ptrace_handle_session_leader_fork (pid_t sl_pid)
|
||||
{
|
||||
auto *proc_target = current_inferior ()->process_target ();
|
||||
auto *ptrace_targ = static_cast<inf_ptrace_target *> (proc_target);
|
||||
|
||||
return ptrace_targ->handle_session_leader_fork (sl_pid);
|
||||
}
|
||||
|
||||
/* Start a new inferior Unix child process. EXEC_FILE is the file to
|
||||
@@ -89,7 +108,8 @@ inf_ptrace_target::create_inferior (const char *exec_file,
|
||||
}
|
||||
|
||||
pid_t pid = fork_inferior (exec_file, allargs, env, inf_ptrace_me, NULL,
|
||||
NULL, NULL, NULL);
|
||||
NULL, NULL, NULL,
|
||||
inf_ptrace_handle_session_leader_fork);
|
||||
|
||||
ptid_t ptid (pid);
|
||||
/* We have something that executes now. We'll be running through
|
||||
|
||||
@@ -44,6 +44,14 @@ struct inf_ptrace_target : public inf_child_target
|
||||
void create_inferior (const char *, const std::string &,
|
||||
char **, int) override;
|
||||
|
||||
/* Targets that support putting the inferior in its own gdb-managed
|
||||
terminal must override this method. */
|
||||
virtual pid_t handle_session_leader_fork (pid_t sl_pid)
|
||||
{
|
||||
gdb_assert_not_reached ("handle_session_leader_fork called");
|
||||
return -1;
|
||||
}
|
||||
|
||||
void mourn_inferior () override;
|
||||
|
||||
bool thread_alive (ptid_t ptid) override;
|
||||
|
||||
39
gdb/infcmd.c
39
gdb/infcmd.c
@@ -2401,6 +2401,8 @@ kill_command (const char *arg, int from_tty)
|
||||
|
||||
target_kill ();
|
||||
|
||||
update_previous_thread ();
|
||||
|
||||
if (print_inferior_events)
|
||||
printf_unfiltered (_("[Inferior %d (%s) killed]\n"),
|
||||
infnum, pid_str.c_str ());
|
||||
@@ -2756,6 +2758,8 @@ detach_command (const char *args, int from_tty)
|
||||
|
||||
target_detach (current_inferior (), from_tty);
|
||||
|
||||
update_previous_thread ();
|
||||
|
||||
/* The current inferior process was just detached successfully. Get
|
||||
rid of breakpoints that no longer make sense. Note we don't do
|
||||
this within target_detach because that is also used when
|
||||
@@ -2794,6 +2798,7 @@ disconnect_command (const char *args, int from_tty)
|
||||
target_disconnect (args, from_tty);
|
||||
no_shared_libraries (NULL, from_tty);
|
||||
init_thread_list ();
|
||||
update_previous_thread ();
|
||||
if (deprecated_detach_hook)
|
||||
deprecated_detach_hook ();
|
||||
}
|
||||
@@ -2841,7 +2846,39 @@ interrupt_target_1 (bool all_threads)
|
||||
stop_current_target_threads_ns (inferior_ptid);
|
||||
}
|
||||
else
|
||||
target_interrupt ();
|
||||
{
|
||||
if (exists_non_stop_target ())
|
||||
{
|
||||
/* Ignore the interrupt request if everything is already
|
||||
stopped. */
|
||||
auto any_resumed = [] ()
|
||||
{
|
||||
for (thread_info *thr : all_non_exited_threads ())
|
||||
{
|
||||
if (thr->executing)
|
||||
return true;
|
||||
if (thr->suspend.waitstatus_pending_p)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
if (any_resumed ())
|
||||
{
|
||||
/* Stop all threads, and report one single stop for all
|
||||
threads. Since the "interrupt" command works
|
||||
asynchronously on all other modes (non-stop or true
|
||||
all-stop + stopping with SIGINT), i.e., the command
|
||||
finishes and GDB prints the prompt before the target
|
||||
actually stops, make this mode work the same, by
|
||||
deferring the actual synchronous stopping work to the
|
||||
event loop. */
|
||||
mark_infrun_async_event_handler_interrupt_all ();
|
||||
}
|
||||
}
|
||||
else
|
||||
target_interrupt ();
|
||||
}
|
||||
|
||||
disable_commit_resumed.reset_and_commit ();
|
||||
}
|
||||
|
||||
@@ -182,6 +182,10 @@ extern void child_pass_ctrlc (struct target_ops *self);
|
||||
|
||||
extern void child_interrupt (struct target_ops *self);
|
||||
|
||||
/* Called when we get a SIGWINCH, to manage the sizes of inferior
|
||||
terminals created by GDB. */
|
||||
extern void child_terminal_on_sigwinch ();
|
||||
|
||||
/* From fork-child.c */
|
||||
|
||||
/* Helper function to call STARTUP_INFERIOR with PID and NUM_TRAPS.
|
||||
|
||||
729
gdb/inflow.c
729
gdb/inflow.c
@@ -29,12 +29,16 @@
|
||||
#include <fcntl.h>
|
||||
#include "gdbsupport/gdb_select.h"
|
||||
|
||||
#include "inflow.h"
|
||||
#include "gdbcmd.h"
|
||||
#ifdef HAVE_TERMIOS_H
|
||||
#include <termios.h>
|
||||
#endif
|
||||
#include "gdbsupport/job-control.h"
|
||||
#include "gdbsupport/scoped_ignore_sigttou.h"
|
||||
#include "gdbsupport/event-loop.h"
|
||||
#include "gdbsupport/refcounted-object.h"
|
||||
#include "gdbsupport/gdb_wait.h"
|
||||
#include "gdbsupport/managed-tty.h"
|
||||
|
||||
#ifdef HAVE_SYS_IOCTL_H
|
||||
#include <sys/ioctl.h>
|
||||
@@ -44,6 +48,14 @@
|
||||
#define O_NOCTTY 0
|
||||
#endif
|
||||
|
||||
/* Defined as 1 if the native target uses fork-child.c to spawn
|
||||
processes. */
|
||||
#if !defined(__GO32__) && !defined(_WIN32)
|
||||
# define USES_FORK_CHILD 1
|
||||
#else
|
||||
# define USES_FORK_CHILD 0
|
||||
#endif
|
||||
|
||||
static void pass_signal (int);
|
||||
|
||||
static void child_terminal_ours_1 (target_terminal_state);
|
||||
@@ -52,6 +64,25 @@ static void child_terminal_ours_1 (target_terminal_state);
|
||||
|
||||
static struct serial *stdin_serial;
|
||||
|
||||
/* "run terminal" terminal info. This is info about the terminal we
|
||||
give to the inferior when it is started. This is refcounted
|
||||
because it is potentially shared between multiple inferiors -- a
|
||||
fork child is associated with the same terminal as its parent. */
|
||||
|
||||
struct run_terminal_info : public refcounted_object
|
||||
{
|
||||
/* The name of the tty (from the `tty' command) that we gave to the
|
||||
inferior when it was started. */
|
||||
gdb::unique_xmalloc_ptr<char> ttyname;
|
||||
|
||||
/* The file descriptor of the master end of the pty created for the
|
||||
inferior. -1 if no terminal was created by GDB. */
|
||||
int pty_fd = -1;
|
||||
|
||||
/* The PID of the terminal's session leader. */
|
||||
pid_t session_leader = -1;
|
||||
};
|
||||
|
||||
/* Terminal related info we need to keep track of. Each inferior
|
||||
holds an instance of this structure --- we save it whenever the
|
||||
corresponding inferior stops, and restore it to the terminal when
|
||||
@@ -63,9 +94,10 @@ struct terminal_info
|
||||
|
||||
terminal_info &operator= (const terminal_info &) = default;
|
||||
|
||||
/* The name of the tty (from the `tty' command) that we gave to the
|
||||
inferior when it was started. */
|
||||
char *run_terminal = nullptr;
|
||||
/* Info about the tty that we gave to the inferior when it was
|
||||
started. This is potentially shared between multiple
|
||||
inferiors. */
|
||||
run_terminal_info *run_terminal = nullptr;
|
||||
|
||||
/* TTY state. We save it whenever the inferior stops, and restore
|
||||
it when it resumes in the foreground. */
|
||||
@@ -85,11 +117,26 @@ struct terminal_info
|
||||
inf2's pgrp in the foreground instead of inf1's (which would be
|
||||
problematic since it would be left stopped: Ctrl-C wouldn't work,
|
||||
for example). */
|
||||
pid_t process_group = 0;
|
||||
pid_t process_group = -1;
|
||||
#endif
|
||||
|
||||
/* fcntl flags. Saved and restored just like ttystate. */
|
||||
int tflags = 0;
|
||||
|
||||
/* Save terminal settings from TTY_SERIAL. */
|
||||
void save_from_tty (serial *tty_serial)
|
||||
{
|
||||
xfree (this->ttystate);
|
||||
this->ttystate = serial_get_tty_state (tty_serial);
|
||||
|
||||
#ifdef HAVE_TERMIOS_H
|
||||
this->process_group = tcgetpgrp (tty_serial->fd);
|
||||
#endif
|
||||
|
||||
#ifdef F_GETFL
|
||||
this->tflags = fcntl (tty_serial->fd, F_GETFL, 0);
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
/* Our own tty state, which we restore every time we need to deal with
|
||||
@@ -108,9 +155,10 @@ static serial_ttystate initial_gdb_ttystate;
|
||||
|
||||
static struct terminal_info *get_inflow_inferior_data (struct inferior *);
|
||||
|
||||
/* While the inferior is running, we want SIGINT and SIGQUIT to go to the
|
||||
inferior only. If we have job control, that takes care of it. If not,
|
||||
we save our handlers in these two variables and set SIGINT and SIGQUIT
|
||||
/* While the inferior is running, and the inferior is sharing the same
|
||||
terminal as GDB, we want SIGINT and SIGQUIT to go to the inferior
|
||||
only. If we have job control, that takes care of it. If not, we
|
||||
save our handlers in these two variables and set SIGINT and SIGQUIT
|
||||
to SIG_IGN. */
|
||||
|
||||
static sighandler_t sigint_ours;
|
||||
@@ -118,6 +166,8 @@ static sighandler_t sigint_ours;
|
||||
static sighandler_t sigquit_ours;
|
||||
#endif
|
||||
|
||||
#if USES_FORK_CHILD
|
||||
|
||||
/* The name of the tty (from the `tty' command) that we're giving to
|
||||
the inferior when starting it up. This is only (and should only
|
||||
be) used as a transient global by new_tty_prefork,
|
||||
@@ -125,6 +175,18 @@ static sighandler_t sigquit_ours;
|
||||
fork_inferior, while forking a new child. */
|
||||
static const char *inferior_thisrun_terminal;
|
||||
|
||||
#if GDB_MANAGED_TERMINALS
|
||||
|
||||
/* The file descriptor of the master end of the pty that we're giving
|
||||
to the inferior when starting it up, iff we created the terminal
|
||||
ourselves. This is set by new_tty_prefork, and like
|
||||
INFERIOR_THISRUN_TERMINAL, is transient. */
|
||||
static int inferior_thisrun_terminal_pty_fd = -1;
|
||||
|
||||
#endif /* GDB_MANAGED_TERMINALS */
|
||||
|
||||
#endif /* USES_FORK_CHILD */
|
||||
|
||||
/* Track who owns GDB's terminal (is it GDB or some inferior?). While
|
||||
target_terminal::is_ours() etc. tracks the core's intention and is
|
||||
independent of the target backend, this tracks the actual state of
|
||||
@@ -137,6 +199,11 @@ static const char *inferior_thisrun_terminal;
|
||||
different terminal). */
|
||||
static target_terminal_state gdb_tty_state = target_terminal_state::is_ours;
|
||||
|
||||
/* True if stdin is redirected. As long as this is true, any input
|
||||
typed in GDB's terminal is forwarded to the foreground inferior's
|
||||
gdb-managed terminal. See inferior_stdin_event_handler. */
|
||||
static bool input_fd_redirected = false;
|
||||
|
||||
/* See terminal.h. */
|
||||
|
||||
void
|
||||
@@ -222,6 +289,11 @@ is_gdb_terminal (const char *tty)
|
||||
struct stat other_tty;
|
||||
int res;
|
||||
|
||||
/* Users can explicitly set the inferior tty to "/dev/tty" to mean
|
||||
"the GDB terminal". */
|
||||
if (strcmp (tty, "/dev/tty") == 0)
|
||||
return TRIBOOL_TRUE;
|
||||
|
||||
res = stat (tty, &other_tty);
|
||||
if (res == -1)
|
||||
return TRIBOOL_UNKNOWN;
|
||||
@@ -304,7 +376,7 @@ sharing_input_terminal (inferior *inf)
|
||||
positive we just end up trying to save/restore terminal
|
||||
settings when we didn't need to or we actually can't. */
|
||||
if (tinfo->run_terminal != NULL)
|
||||
res = is_gdb_terminal (tinfo->run_terminal);
|
||||
res = is_gdb_terminal (tinfo->run_terminal->ttyname.get ());
|
||||
|
||||
/* If we still can't determine, assume yes. */
|
||||
if (res == TRIBOOL_UNKNOWN)
|
||||
@@ -314,6 +386,199 @@ sharing_input_terminal (inferior *inf)
|
||||
return res == TRIBOOL_TRUE;
|
||||
}
|
||||
|
||||
#if GDB_MANAGED_TERMINALS
|
||||
|
||||
/* Wrappers around tcgetattr/tcsetattr to log errors. We don't throw
|
||||
on error instead because an error here is most likely caused by
|
||||
stdin having been closed (e.g., GDB lost its terminal), and we may
|
||||
be called while handling/printing exceptions. E.g., from
|
||||
target_target::ours_for_output() before printing an exception
|
||||
message. */
|
||||
|
||||
static int
|
||||
gdb_tcgetattr (int fd, struct termios *termios)
|
||||
{
|
||||
if (tcgetattr (fd, termios) != 0)
|
||||
{
|
||||
managed_tty_debug_printf (_("tcgetattr(fd=%d) failed: %d (%s)\n"),
|
||||
fd, errno, safe_strerror (errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* See gdb_tcgetattr. */
|
||||
|
||||
static int
|
||||
gdb_tcsetattr (int fd, int optional_actions, struct termios *termios)
|
||||
{
|
||||
if (tcsetattr (fd, optional_actions, termios) != 0)
|
||||
{
|
||||
managed_tty_debug_printf (_("tcsetattr(fd=%d) failed: %d (%s)\n"),
|
||||
fd, errno, safe_strerror (errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Disable echo, canonical mode, and \r\n -> \n translation. Leave
|
||||
ISIG, since we want to grab Ctrl-C before the inferior sees it. If
|
||||
CLEAR_OFLAG is true, also clear the output modes, otherwise, leave
|
||||
them unmodified. */
|
||||
|
||||
static void
|
||||
make_raw (struct termios *termios, bool clear_oflag)
|
||||
{
|
||||
termios->c_iflag &= ~(INLCR | IGNCR | ICRNL);
|
||||
if (clear_oflag)
|
||||
termios->c_oflag = 0;
|
||||
termios->c_lflag &= ~(ECHO | ICANON);
|
||||
termios->c_cflag &= ~CSIZE;
|
||||
termios->c_cflag |= CLOCAL | CS8;
|
||||
termios->c_cc[VMIN] = 0;
|
||||
termios->c_cc[VTIME] = 0;
|
||||
}
|
||||
|
||||
/* RAII class to temporarily set the terminal to raw mode, with
|
||||
`oflag` cleared. See make_raw. */
|
||||
|
||||
class scoped_raw_termios
|
||||
{
|
||||
public:
|
||||
scoped_raw_termios ()
|
||||
{
|
||||
if (gdb_tcgetattr (STDIN_FILENO, &m_saved_termios) == 0)
|
||||
{
|
||||
m_saved_termios_p = true;
|
||||
|
||||
struct termios raw_termios = m_saved_termios;
|
||||
make_raw (&raw_termios, true);
|
||||
gdb_tcsetattr (STDIN_FILENO, TCSADRAIN, &raw_termios);
|
||||
}
|
||||
}
|
||||
|
||||
~scoped_raw_termios ()
|
||||
{
|
||||
if (m_saved_termios_p)
|
||||
gdb_tcsetattr (STDIN_FILENO, TCSADRAIN, &m_saved_termios);
|
||||
}
|
||||
|
||||
private:
|
||||
/* The saved termios data. */
|
||||
struct termios m_saved_termios;
|
||||
|
||||
/* True iff M_SAVED_TERMIOS is valid. */
|
||||
bool m_saved_termios_p;
|
||||
};
|
||||
|
||||
/* Flush input/output from READ_FD to WRITE_FD. WHAT is used for
|
||||
logging purposes. */
|
||||
|
||||
static void
|
||||
child_terminal_flush_from_to (int read_fd, int write_fd, const char *what)
|
||||
{
|
||||
char buf[1024];
|
||||
|
||||
gdb::optional<scoped_raw_termios> save_termios;
|
||||
save_termios.emplace ();
|
||||
|
||||
while (1)
|
||||
{
|
||||
int r = read (read_fd, buf, sizeof (buf));
|
||||
if (r <= 0)
|
||||
{
|
||||
/* Restore terminal state before printing debug output or
|
||||
warnings. */
|
||||
save_termios.reset ();
|
||||
|
||||
if (r == 0)
|
||||
;
|
||||
else if (r == -1 && errno == EAGAIN)
|
||||
;
|
||||
else if (r == -1 && errno == EIO)
|
||||
{
|
||||
managed_tty_debug_printf (_("%s: bad read: closed?\n"),
|
||||
what);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Unexpected. */
|
||||
warning (_("%s: bad read: %d: (%d) %s"), what, r,
|
||||
errno, safe_strerror (errno));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const char *p = buf;
|
||||
|
||||
while (r > 0)
|
||||
{
|
||||
int w = write (write_fd, p, r);
|
||||
if (w == -1 && errno == EAGAIN)
|
||||
continue;
|
||||
else if (w <= 0)
|
||||
{
|
||||
int err = errno;
|
||||
|
||||
/* Restore terminal state before printing the
|
||||
warning. */
|
||||
save_termios.reset ();
|
||||
|
||||
warning (_("%s: bad write: %d: (%d) %s"), what, r,
|
||||
err, safe_strerror (err));
|
||||
return;
|
||||
}
|
||||
|
||||
r -= w;
|
||||
p += w;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Flush inferior terminal output to GDB's stdout. Used when the
|
||||
inferior is associated with a terminal created and managed by
|
||||
GDB. */
|
||||
|
||||
static void
|
||||
child_terminal_flush_stdout (run_terminal_info *run_terminal)
|
||||
{
|
||||
gdb_assert (run_terminal->pty_fd != -1);
|
||||
child_terminal_flush_from_to (run_terminal->pty_fd, STDOUT_FILENO,
|
||||
"stdout");
|
||||
}
|
||||
|
||||
/* Event handler associated with the inferior's terminal pty. Used
|
||||
when the inferior is associated with a terminal created and managed
|
||||
by GDB. Whenever the inferior writes to its terminal, the event
|
||||
loop calls this handler, which then flushes inferior terminal
|
||||
output to GDB's stdout. */
|
||||
|
||||
static void
|
||||
inferior_stdout_event_handler (int error, gdb_client_data client_data)
|
||||
{
|
||||
run_terminal_info *run_terminal = (run_terminal_info *) client_data;
|
||||
child_terminal_flush_stdout (run_terminal);
|
||||
}
|
||||
|
||||
/* Event handler associated with stdin. Used when the inferior is
|
||||
associated with a terminal created and managed by GDB. Whenever
|
||||
the user types on GDB's terminal, the event loop calls this
|
||||
handler, which then flushes user input to the inferior's terminal
|
||||
input. */
|
||||
|
||||
static void
|
||||
inferior_stdin_event_handler (int error, gdb_client_data client_data)
|
||||
{
|
||||
run_terminal_info *run_terminal = (run_terminal_info *) client_data;
|
||||
gdb_assert (run_terminal->pty_fd != -1);
|
||||
child_terminal_flush_from_to (STDIN_FILENO, run_terminal->pty_fd,
|
||||
"stdin");
|
||||
}
|
||||
|
||||
#endif /* GDB_MANAGED_TERMINALS */
|
||||
|
||||
/* Put the inferior's terminal settings into effect. This is
|
||||
preparation for starting or resuming the inferior. */
|
||||
|
||||
@@ -333,24 +598,11 @@ child_terminal_inferior (struct target_ops *self)
|
||||
terminal_info *tinfo = get_inflow_inferior_data (inf);
|
||||
|
||||
if (gdb_has_a_terminal ()
|
||||
&& tinfo->ttystate != NULL
|
||||
&& sharing_input_terminal (inf))
|
||||
&& tinfo->ttystate != nullptr
|
||||
&& ((tinfo->run_terminal != nullptr
|
||||
&& tinfo->run_terminal->pty_fd != -1)
|
||||
|| sharing_input_terminal (inf)))
|
||||
{
|
||||
int result;
|
||||
|
||||
/* Ignore SIGTTOU since it will happen when we try to set the
|
||||
terminal's state (if gdb_tty_state is currently
|
||||
ours_for_output). */
|
||||
scoped_ignore_sigttou ignore_sigttou;
|
||||
|
||||
#ifdef F_GETFL
|
||||
result = fcntl (0, F_SETFL, tinfo->tflags);
|
||||
OOPSY ("fcntl F_SETFL");
|
||||
#endif
|
||||
|
||||
result = serial_set_tty_state (stdin_serial, tinfo->ttystate);
|
||||
OOPSY ("setting tty state");
|
||||
|
||||
if (!job_control)
|
||||
{
|
||||
sigint_ours = signal (SIGINT, SIG_IGN);
|
||||
@@ -359,37 +611,85 @@ child_terminal_inferior (struct target_ops *self)
|
||||
#endif
|
||||
}
|
||||
|
||||
if (job_control)
|
||||
/* Ignore SIGTTOU since it will happen when we try to set the
|
||||
terminal's state (if gdb_tty_state is currently
|
||||
ours_for_output). */
|
||||
scoped_ignore_sigttou ignore_sigttou;
|
||||
|
||||
#if GDB_MANAGED_TERMINALS
|
||||
if (tinfo->run_terminal != nullptr
|
||||
&& tinfo->run_terminal->pty_fd != -1)
|
||||
{
|
||||
#ifdef HAVE_TERMIOS_H
|
||||
/* If we can't tell the inferior's actual process group,
|
||||
then restore whatever was the foreground pgrp the last
|
||||
time the inferior was running. See also comments
|
||||
describing terminal_state::process_group. */
|
||||
#ifdef HAVE_GETPGID
|
||||
result = tcsetpgrp (0, getpgid (inf->pid));
|
||||
#else
|
||||
result = tcsetpgrp (0, tinfo->process_group);
|
||||
#endif
|
||||
if (result == -1)
|
||||
/* Set stdin to raw (see make_raw) so we can later marshal
|
||||
unadulterated input to the inferior's terminal, but leave
|
||||
the output flags intact. Importantly, we don't want to
|
||||
disable \n -> \r\n translation on output, mainly to avoid
|
||||
the staircase effect in debug logging all over the code
|
||||
base while terminal_inferior is in effect. */
|
||||
struct termios termios;
|
||||
|
||||
if (gdb_tcgetattr (STDIN_FILENO, &termios) == 0)
|
||||
{
|
||||
make_raw (&termios, false);
|
||||
gdb_tcsetattr (STDIN_FILENO, TCSADRAIN, &termios);
|
||||
}
|
||||
|
||||
/* Register our stdin-forwarder handler in the event
|
||||
loop. */
|
||||
add_file_handler (0, inferior_stdin_event_handler,
|
||||
tinfo->run_terminal,
|
||||
string_printf ("stdin-forward-%d", inf->num),
|
||||
true);
|
||||
|
||||
input_fd_redirected = true;
|
||||
}
|
||||
else
|
||||
#endif /* GDB_MANAGED_TERMINALS */
|
||||
{
|
||||
int result;
|
||||
|
||||
#ifdef F_GETFL
|
||||
result = fcntl (0, F_SETFL, tinfo->tflags);
|
||||
OOPSY ("fcntl F_SETFL");
|
||||
#endif
|
||||
|
||||
result = serial_set_tty_state (stdin_serial, tinfo->ttystate);
|
||||
OOPSY ("setting tty state");
|
||||
|
||||
if (job_control)
|
||||
{
|
||||
#ifdef HAVE_TERMIOS_H
|
||||
/* If we can't tell the inferior's actual process group,
|
||||
then restore whatever was the foreground pgrp the
|
||||
last time the inferior was running. See also
|
||||
comments describing
|
||||
terminal_state::process_group. */
|
||||
#ifdef HAVE_GETPGID
|
||||
result = tcsetpgrp (0, getpgid (inf->pid));
|
||||
#else
|
||||
result = tcsetpgrp (0, tinfo->process_group);
|
||||
#endif
|
||||
if (result == -1)
|
||||
{
|
||||
#if 0
|
||||
/* This fails if either GDB has no controlling terminal,
|
||||
e.g., running under 'setsid(1)', or if the inferior
|
||||
is not attached to GDB's controlling terminal. E.g.,
|
||||
if it called setsid to create a new session or used
|
||||
the TIOCNOTTY ioctl, or simply if we've attached to a
|
||||
process running on another terminal and we couldn't
|
||||
tell whether it was sharing GDB's terminal (and so
|
||||
assumed yes). */
|
||||
fprintf_unfiltered
|
||||
(gdb_stderr,
|
||||
"[tcsetpgrp failed in child_terminal_inferior: %s]\n",
|
||||
safe_strerror (errno));
|
||||
/* This fails if either GDB has no controlling
|
||||
terminal, e.g., running under 'setsid(1)', or if
|
||||
the inferior is not attached to GDB's controlling
|
||||
terminal. E.g., if it called setsid to create a
|
||||
new session or used the TIOCNOTTY ioctl, or
|
||||
simply if we've attached to a process running on
|
||||
another terminal and we couldn't tell whether it
|
||||
was sharing GDB's terminal (and so assumed
|
||||
yes). */
|
||||
fprintf_unfiltered
|
||||
(gdb_stderr,
|
||||
"[tcsetpgrp failed in child_terminal_inferior: %s]\n",
|
||||
safe_strerror (errno));
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
gdb_tty_state = target_terminal_state::is_inferior;
|
||||
}
|
||||
@@ -439,21 +739,27 @@ child_terminal_save_inferior (struct target_ops *self)
|
||||
inferior *inf = current_inferior ();
|
||||
terminal_info *tinfo = get_inflow_inferior_data (inf);
|
||||
|
||||
#if GDB_MANAGED_TERMINALS
|
||||
run_terminal_info *run_terminal = tinfo->run_terminal;
|
||||
if (run_terminal != nullptr && run_terminal->pty_fd != -1)
|
||||
{
|
||||
/* The inferior has its own terminal, so there are no settings
|
||||
to save. However, do flush inferior output -- usually we'll
|
||||
be grabbing the terminal in reaction to an inferior stop, and
|
||||
it's only logical to print inferior output before we announce
|
||||
the stop, since the inferior printed it before it
|
||||
stopped. */
|
||||
child_terminal_flush_stdout (run_terminal);
|
||||
return;
|
||||
}
|
||||
#endif /* GDB_MANAGED_TERMINALS */
|
||||
|
||||
/* No need to save/restore if the inferior is not sharing GDB's
|
||||
tty. */
|
||||
if (!sharing_input_terminal (inf))
|
||||
return;
|
||||
|
||||
xfree (tinfo->ttystate);
|
||||
tinfo->ttystate = serial_get_tty_state (stdin_serial);
|
||||
|
||||
#ifdef HAVE_TERMIOS_H
|
||||
tinfo->process_group = tcgetpgrp (0);
|
||||
#endif
|
||||
|
||||
#ifdef F_GETFL
|
||||
tinfo->tflags = fcntl (0, F_GETFL, 0);
|
||||
#endif
|
||||
tinfo->save_from_tty (stdin_serial);
|
||||
}
|
||||
|
||||
/* Switch terminal state to DESIRED_STATE, either is_ours, or
|
||||
@@ -476,27 +782,37 @@ child_terminal_ours_1 (target_terminal_state desired_state)
|
||||
terminal's pgrp. */
|
||||
scoped_ignore_sigttou ignore_sigttou;
|
||||
|
||||
/* Set tty state to our_ttystate. */
|
||||
serial_set_tty_state (stdin_serial, our_terminal_info.ttystate);
|
||||
|
||||
/* If we only want output, then leave the inferior's pgrp in the
|
||||
foreground, so that Ctrl-C/Ctrl-Z reach the inferior
|
||||
directly. */
|
||||
/* If we only want output, then:
|
||||
- if the inferior is sharing GDB's session, leave the
|
||||
inferior's pgrp in the foreground, so that Ctrl-C/Ctrl-Z
|
||||
reach the inferior directly.
|
||||
- if the inferior has its own session, leave stdin
|
||||
forwarding to the inferior. */
|
||||
if (job_control && desired_state == target_terminal_state::is_ours)
|
||||
{
|
||||
if (input_fd_redirected)
|
||||
{
|
||||
delete_file_handler (0);
|
||||
input_fd_redirected = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef HAVE_TERMIOS_H
|
||||
result = tcsetpgrp (0, our_terminal_info.process_group);
|
||||
result = tcsetpgrp (0, our_terminal_info.process_group);
|
||||
#if 0
|
||||
/* This fails on Ultrix with EINVAL if you run the testsuite
|
||||
in the background with nohup, and then log out. GDB never
|
||||
used to check for an error here, so perhaps there are other
|
||||
such situations as well. */
|
||||
if (result == -1)
|
||||
fprintf_unfiltered (gdb_stderr,
|
||||
"[tcsetpgrp failed in child_terminal_ours: %s]\n",
|
||||
safe_strerror (errno));
|
||||
/* This fails on Ultrix with EINVAL if you run the
|
||||
testsuite in the background with nohup, and then log
|
||||
out. GDB never used to check for an error here, so
|
||||
perhaps there are other such situations as well. */
|
||||
if (result == -1)
|
||||
fprintf_unfiltered (gdb_stderr,
|
||||
"[tcsetpgrp failed in child_terminal_ours: %s]\n",
|
||||
safe_strerror (errno));
|
||||
#endif
|
||||
#endif /* termios */
|
||||
}
|
||||
}
|
||||
|
||||
if (!job_control && desired_state == target_terminal_state::is_ours)
|
||||
@@ -603,7 +919,7 @@ static const struct inferior_key<terminal_info> inflow_inferior_data;
|
||||
|
||||
terminal_info::~terminal_info ()
|
||||
{
|
||||
xfree (run_terminal);
|
||||
delete run_terminal;
|
||||
xfree (ttystate);
|
||||
}
|
||||
|
||||
@@ -622,17 +938,108 @@ get_inflow_inferior_data (struct inferior *inf)
|
||||
return info;
|
||||
}
|
||||
|
||||
/* This is a "inferior_exit" observer. Releases the TERMINAL_INFO member
|
||||
of the inferior structure. This field is private to inflow.c, and
|
||||
its type is opaque to the rest of GDB. PID is the target pid of
|
||||
the inferior that is about to be removed from the inferior
|
||||
list. */
|
||||
#ifdef TIOCGWINSZ
|
||||
|
||||
/* See inferior.h. */
|
||||
|
||||
void
|
||||
child_terminal_on_sigwinch ()
|
||||
{
|
||||
struct winsize size;
|
||||
|
||||
if (ioctl (0, TIOCGWINSZ, &size) == -1)
|
||||
return;
|
||||
|
||||
/* For each inferior that is connected to a terminal that we
|
||||
created, resize the inferior's terminal to match GDB's. */
|
||||
for (inferior *inf : all_inferiors ())
|
||||
{
|
||||
terminal_info *info = inflow_inferior_data.get (inf);
|
||||
if (info != nullptr
|
||||
&& info->run_terminal != nullptr
|
||||
&& info->run_terminal->pty_fd != -1)
|
||||
ioctl (info->run_terminal->pty_fd, TIOCSWINSZ, &size);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* This is an "inferior_exit" observer. Releases the TERMINAL_INFO
|
||||
member of the inferior structure. This field is private to
|
||||
inflow.c, and its type is opaque to the rest of GDB. If INF's
|
||||
terminal is a GDB-managed terminal, then destroy it. */
|
||||
|
||||
static void
|
||||
inflow_inferior_exit (struct inferior *inf)
|
||||
{
|
||||
inf->terminal_state = target_terminal_state::is_ours;
|
||||
inflow_inferior_data.clear (inf);
|
||||
|
||||
terminal_info *info = inflow_inferior_data.get (inf);
|
||||
if (info != nullptr)
|
||||
{
|
||||
/* Release the terminal created by GDB, if there's one. This
|
||||
closes the session leader process. */
|
||||
if (info->run_terminal != nullptr)
|
||||
{
|
||||
run_terminal_info *run_terminal = info->run_terminal;
|
||||
|
||||
/* The terminal may be used by other processes (e.g., if the
|
||||
inferior forked). Only actually destroy the terminal if
|
||||
the refcount reaches 0. */
|
||||
run_terminal->decref ();
|
||||
if (run_terminal->refcount () == 0)
|
||||
{
|
||||
#if GDB_MANAGED_TERMINALS
|
||||
if (run_terminal->pty_fd != -1)
|
||||
{
|
||||
/* Flush any pending output and close the pty. */
|
||||
delete_file_handler (run_terminal->pty_fd);
|
||||
child_terminal_flush_stdout (run_terminal);
|
||||
|
||||
/* Explicitly send a SIGHUP instead of just closing
|
||||
the terminal and letting the kernel send it,
|
||||
because we want the session leader to have a
|
||||
chance to put itself in the foreground, so that
|
||||
its children, if any (e.g., we're detaching),
|
||||
don't get a SIGHUP too. */
|
||||
kill (run_terminal->session_leader, SIGHUP);
|
||||
|
||||
/* The session leader should exit in reaction to
|
||||
SIGHUP. */
|
||||
managed_tty_debug_printf (_("reaping session leader "
|
||||
"for inf %d (sid=%d)\n"),
|
||||
inf->num,
|
||||
(int) run_terminal->session_leader);
|
||||
|
||||
int status;
|
||||
int res = waitpid (run_terminal->session_leader,
|
||||
&status, 0);
|
||||
if (res == -1)
|
||||
warning (_("unexpected waitstatus "
|
||||
"reaping session leader for inf %d (sid=%d): "
|
||||
"res=-1, errno=%d (%s)"),
|
||||
inf->num, (int) run_terminal->session_leader,
|
||||
errno, safe_strerror (errno));
|
||||
else if (res != run_terminal->session_leader
|
||||
|| !WIFEXITED (status)
|
||||
|| WEXITSTATUS (status) != 0)
|
||||
warning (_("unexpected waitstatus "
|
||||
"reaping session leader for inf %d (sid=%d): "
|
||||
"res=%d, status=0x%x"),
|
||||
inf->num, (int) run_terminal->session_leader,
|
||||
res, status);
|
||||
|
||||
/* We can now close the terminal. */
|
||||
close (run_terminal->pty_fd);
|
||||
}
|
||||
#endif /* GDB_MANAGED_TERMINALS */
|
||||
delete run_terminal;
|
||||
}
|
||||
info->run_terminal = nullptr;
|
||||
}
|
||||
|
||||
inflow_inferior_data.clear (inf);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@@ -643,14 +1050,12 @@ copy_terminal_info (struct inferior *to, struct inferior *from)
|
||||
tinfo_to = get_inflow_inferior_data (to);
|
||||
tinfo_from = get_inflow_inferior_data (from);
|
||||
|
||||
xfree (tinfo_to->run_terminal);
|
||||
gdb_assert (tinfo_to->run_terminal == nullptr);
|
||||
xfree (tinfo_to->ttystate);
|
||||
|
||||
*tinfo_to = *tinfo_from;
|
||||
|
||||
if (tinfo_from->run_terminal)
|
||||
tinfo_to->run_terminal
|
||||
= xstrdup (tinfo_from->run_terminal);
|
||||
if (tinfo_from->run_terminal != nullptr)
|
||||
tinfo_from->run_terminal->incref ();
|
||||
|
||||
if (tinfo_from->ttystate)
|
||||
tinfo_to->ttystate
|
||||
@@ -697,6 +1102,25 @@ child_terminal_info (struct target_ops *self, const char *args, int from_tty)
|
||||
inf = current_inferior ();
|
||||
tinfo = get_inflow_inferior_data (inf);
|
||||
|
||||
/* child_terminal_save_inferior doesn't bother with saving terminal
|
||||
settings if the inferior isn't sharing the terminal with GDB, so
|
||||
refresh them now. Note that if the inferior _is_ sharing a
|
||||
terminal with GDB, then we must not refresh settings now, as that
|
||||
would be reading GDB's terminal settings, not the inferiors. */
|
||||
|
||||
serial *term_serial
|
||||
= (tinfo->run_terminal->pty_fd != -1
|
||||
? serial_fdopen (tinfo->run_terminal->pty_fd)
|
||||
: stdin_serial);
|
||||
SCOPE_EXIT
|
||||
{
|
||||
if (term_serial != stdin_serial)
|
||||
serial_un_fdopen (term_serial);
|
||||
};
|
||||
|
||||
if (!sharing_input_terminal (inf))
|
||||
tinfo->save_from_tty (term_serial);
|
||||
|
||||
printf_filtered (_("Inferior's terminal status "
|
||||
"(currently saved by GDB):\n"));
|
||||
|
||||
@@ -760,9 +1184,12 @@ child_terminal_info (struct target_ops *self, const char *args, int from_tty)
|
||||
printf_filtered ("Process group = %d\n", (int) tinfo->process_group);
|
||||
#endif
|
||||
|
||||
serial_print_tty_state (stdin_serial, tinfo->ttystate, gdb_stdout);
|
||||
serial_print_tty_state (term_serial, tinfo->ttystate, gdb_stdout);
|
||||
}
|
||||
|
||||
|
||||
#if USES_FORK_CHILD
|
||||
|
||||
/* NEW_TTY_PREFORK is called before forking a new child process,
|
||||
so we can record the state of ttys in the child to be formed.
|
||||
TTYNAME is null if we are to share the terminal with gdb;
|
||||
@@ -775,12 +1202,56 @@ child_terminal_info (struct target_ops *self, const char *args, int from_tty)
|
||||
void
|
||||
new_tty_prefork (const char *ttyname)
|
||||
{
|
||||
/* Save the name for later, for determining whether we and the child
|
||||
are sharing a tty. */
|
||||
inferior_thisrun_terminal = ttyname;
|
||||
/* Save the name and fd for later, for determining whether we and
|
||||
the child are sharing a tty. */
|
||||
|
||||
if (ttyname != nullptr)
|
||||
{
|
||||
inferior_thisrun_terminal = ttyname;
|
||||
inferior_thisrun_terminal_pty_fd = -1;
|
||||
}
|
||||
#if GDB_MANAGED_TERMINALS
|
||||
else
|
||||
{
|
||||
/* Open an unused pty master device. */
|
||||
int pty_fd = posix_openpt (O_RDWR | O_NONBLOCK | O_CLOEXEC | O_NOCTTY);
|
||||
if (pty_fd == -1)
|
||||
perror_with_name ("posix_openpt");
|
||||
|
||||
/* Grant access to the slave tty. */
|
||||
if (grantpt (pty_fd) == -1)
|
||||
{
|
||||
int err = errno;
|
||||
close (pty_fd);
|
||||
errno = err;
|
||||
perror_with_name ("grantpt");
|
||||
}
|
||||
|
||||
/* Unlock the pty master/slave pair. */
|
||||
if (unlockpt (pty_fd) == -1)
|
||||
{
|
||||
close (pty_fd);
|
||||
perror_with_name ("unlockpt");
|
||||
}
|
||||
|
||||
inferior_thisrun_terminal = ptsname (pty_fd);
|
||||
inferior_thisrun_terminal_pty_fd = pty_fd;
|
||||
|
||||
if (initial_gdb_ttystate != nullptr)
|
||||
{
|
||||
serial *pty_fd_serial = serial_fdopen (pty_fd);
|
||||
|
||||
int result = serial_set_tty_state (pty_fd_serial,
|
||||
initial_gdb_ttystate);
|
||||
gdb_assert (result != -1);
|
||||
OOPSY ("setting tty state");
|
||||
|
||||
serial_un_fdopen (pty_fd_serial);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if !defined(__GO32__) && !defined(_WIN32)
|
||||
/* If RESULT, assumed to be the return value from a system call, is
|
||||
negative, print the error message indicated by errno and exit.
|
||||
MSG should identify the operation that failed. */
|
||||
@@ -793,14 +1264,21 @@ check_syscall (const char *msg, int result)
|
||||
_exit (1);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* See terminal.h. */
|
||||
|
||||
bool
|
||||
created_managed_tty ()
|
||||
{
|
||||
return inferior_thisrun_terminal_pty_fd != -1;
|
||||
}
|
||||
|
||||
void
|
||||
new_tty (void)
|
||||
new_tty ()
|
||||
{
|
||||
if (inferior_thisrun_terminal == 0)
|
||||
if (inferior_thisrun_terminal == nullptr
|
||||
|| is_gdb_terminal (inferior_thisrun_terminal))
|
||||
return;
|
||||
#if !defined(__GO32__) && !defined(_WIN32)
|
||||
int tty;
|
||||
|
||||
#ifdef TIOCNOTTY
|
||||
@@ -849,12 +1327,13 @@ new_tty (void)
|
||||
|
||||
if (tty > 2)
|
||||
close (tty);
|
||||
#endif /* !go32 && !win32 */
|
||||
}
|
||||
|
||||
/* NEW_TTY_POSTFORK is called after forking a new child process, and
|
||||
adding it to the inferior table, to store the TTYNAME being used by
|
||||
the child, or null if it sharing the terminal with gdb. */
|
||||
the child, or null if it sharing the terminal with gdb. If the
|
||||
child is using a terminal created by GDB, the corresponding pty
|
||||
master fd is stored. */
|
||||
|
||||
void
|
||||
new_tty_postfork (void)
|
||||
@@ -862,17 +1341,40 @@ new_tty_postfork (void)
|
||||
/* Save the name for later, for determining whether we and the child
|
||||
are sharing a tty. */
|
||||
|
||||
if (inferior_thisrun_terminal)
|
||||
struct inferior *inf = current_inferior ();
|
||||
struct terminal_info *tinfo = get_inflow_inferior_data (inf);
|
||||
auto *run_terminal = new run_terminal_info ();
|
||||
tinfo->run_terminal = run_terminal;
|
||||
run_terminal->incref ();
|
||||
|
||||
if (inferior_thisrun_terminal != nullptr)
|
||||
{
|
||||
struct inferior *inf = current_inferior ();
|
||||
struct terminal_info *tinfo = get_inflow_inferior_data (inf);
|
||||
run_terminal->ttyname = make_unique_xstrdup (inferior_thisrun_terminal);
|
||||
run_terminal->pty_fd = inferior_thisrun_terminal_pty_fd;
|
||||
if (run_terminal->pty_fd != -1)
|
||||
{
|
||||
run_terminal->session_leader = getsid (inf->pid);
|
||||
gdb_assert (run_terminal->session_leader != -1);
|
||||
}
|
||||
|
||||
tinfo->run_terminal = xstrdup (inferior_thisrun_terminal);
|
||||
if (run_terminal->pty_fd != -1)
|
||||
{
|
||||
add_file_handler (run_terminal->pty_fd,
|
||||
inferior_stdout_event_handler, run_terminal,
|
||||
string_printf ("pty_fd-%s",
|
||||
run_terminal->ttyname.get ()),
|
||||
true);
|
||||
}
|
||||
}
|
||||
else
|
||||
run_terminal->ttyname = make_unique_xstrdup ("/dev/tty");
|
||||
|
||||
inferior_thisrun_terminal = NULL;
|
||||
inferior_thisrun_terminal = nullptr;
|
||||
inferior_thisrun_terminal_pty_fd = -1;
|
||||
}
|
||||
|
||||
#endif /* USES_FORK_CHILD */
|
||||
|
||||
|
||||
/* Call set_sigint_trap when you need to pass a signal on to an attached
|
||||
process when handling SIGINT. */
|
||||
@@ -927,7 +1429,9 @@ create_tty_session (void)
|
||||
#ifdef HAVE_SETSID
|
||||
pid_t ret;
|
||||
|
||||
if (!job_control || inferior_thisrun_terminal == 0)
|
||||
if (!job_control
|
||||
|| inferior_thisrun_terminal == nullptr
|
||||
|| is_gdb_terminal (inferior_thisrun_terminal))
|
||||
return 0;
|
||||
|
||||
ret = setsid ();
|
||||
@@ -953,6 +1457,16 @@ initialize_stdin_serial (void)
|
||||
stdin_serial = serial_fdopen (0);
|
||||
}
|
||||
|
||||
/* "show" callback for "set debug managed-tty". */
|
||||
|
||||
static void
|
||||
show_debug_managed_tty (ui_file *file, int from_tty,
|
||||
cmd_list_element *c, const char *value)
|
||||
{
|
||||
fprintf_filtered (file, _("Debugging of GDB-managed terminals is %s.\n"),
|
||||
value);
|
||||
}
|
||||
|
||||
void _initialize_inflow ();
|
||||
void
|
||||
_initialize_inflow ()
|
||||
@@ -960,6 +1474,13 @@ _initialize_inflow ()
|
||||
add_info ("terminal", info_terminal_command,
|
||||
_("Print inferior's saved terminal status."));
|
||||
|
||||
add_setshow_boolean_cmd
|
||||
("managed-tty", class_maintenance, &debug_managed_tty,
|
||||
_("Set debugging of GDB-managed terminals."),
|
||||
_("Show debugging of GDB-managed terminals."),
|
||||
_("When non-zero, GDB-managed terminals specific debugging is enabled."),
|
||||
nullptr, show_debug_managed_tty, &setdebuglist, &showdebuglist);
|
||||
|
||||
/* OK, figure out whether we have job control. */
|
||||
have_job_control ();
|
||||
|
||||
|
||||
179
gdb/infrun.c
179
gdb/infrun.c
@@ -143,8 +143,18 @@ show_step_stop_if_no_debug (struct ui_file *file, int from_tty,
|
||||
/* proceed and normal_stop use this to notify the user when the
|
||||
inferior stopped in a different thread than it had been running
|
||||
in. */
|
||||
static thread_info_ref previous_thread;
|
||||
|
||||
static ptid_t previous_inferior_ptid;
|
||||
/* See infrun.h. */
|
||||
|
||||
void
|
||||
update_previous_thread ()
|
||||
{
|
||||
if (inferior_ptid == null_ptid)
|
||||
previous_thread = nullptr;
|
||||
else
|
||||
previous_thread = thread_info_ref::new_reference (inferior_thread ());
|
||||
}
|
||||
|
||||
/* If set (default for legacy reasons), when following a fork, GDB
|
||||
will detach from one of the fork branches, child or parent.
|
||||
@@ -3072,7 +3082,7 @@ proceed (CORE_ADDR addr, enum gdb_signal siggnal)
|
||||
}
|
||||
|
||||
/* We'll update this if & when we switch to a new thread. */
|
||||
previous_inferior_ptid = inferior_ptid;
|
||||
update_previous_thread ();
|
||||
|
||||
regcache = get_current_regcache ();
|
||||
gdbarch = regcache->arch ();
|
||||
@@ -3336,7 +3346,7 @@ init_wait_for_inferior (void)
|
||||
|
||||
nullify_last_target_wait_ptid ();
|
||||
|
||||
previous_inferior_ptid = inferior_ptid;
|
||||
update_previous_thread ();
|
||||
}
|
||||
|
||||
|
||||
@@ -8537,7 +8547,10 @@ normal_stop (void)
|
||||
update_thread_list ();
|
||||
|
||||
if (last.kind == TARGET_WAITKIND_STOPPED && stopped_by_random_signal)
|
||||
gdb::observers::signal_received.notify (inferior_thread ()->suspend.stop_signal);
|
||||
{
|
||||
target_terminal::ours_for_output ();
|
||||
gdb::observers::signal_received.notify (inferior_thread ()->suspend.stop_signal);
|
||||
}
|
||||
|
||||
/* As with the notification of thread events, we want to delay
|
||||
notifying the user that we've switched thread context until
|
||||
@@ -8556,11 +8569,11 @@ normal_stop (void)
|
||||
after this event is handled, so we're not really switching, only
|
||||
informing of a stop. */
|
||||
if (!non_stop
|
||||
&& previous_inferior_ptid != inferior_ptid
|
||||
&& target_has_execution ()
|
||||
&& last.kind != TARGET_WAITKIND_SIGNALLED
|
||||
&& last.kind != TARGET_WAITKIND_EXITED
|
||||
&& last.kind != TARGET_WAITKIND_NO_RESUMED)
|
||||
&& last.kind != TARGET_WAITKIND_NO_RESUMED
|
||||
&& target_has_execution ()
|
||||
&& previous_thread != inferior_thread ())
|
||||
{
|
||||
SWITCH_THRU_ALL_UIS ()
|
||||
{
|
||||
@@ -8569,7 +8582,8 @@ normal_stop (void)
|
||||
target_pid_to_str (inferior_ptid).c_str ());
|
||||
annotate_thread_changed ();
|
||||
}
|
||||
previous_inferior_ptid = inferior_ptid;
|
||||
|
||||
update_previous_thread ();
|
||||
}
|
||||
|
||||
if (last.kind == TARGET_WAITKIND_NO_RESUMED)
|
||||
@@ -9426,13 +9440,158 @@ static const struct internalvar_funcs siginfo_funcs =
|
||||
NULL
|
||||
};
|
||||
|
||||
/* Callback for infrun's target events source. This is marked when a
|
||||
thread has a pending status to process. */
|
||||
/* True if the user used the "interrupt" command and we want to handle
|
||||
the interruption via the event loop instead of immediately. This
|
||||
is so that "interrupt" always stops the program asynchronously in
|
||||
all the different execution modes. In particular, in "set non-stop
|
||||
off" + "maint set target-non-stop on" mode, we want to
|
||||
synchronously stop all threads with stop_all_threads, so we delay
|
||||
doing that to the event loop, so that "interrupt" presents a prompt
|
||||
immediately, and then presents the stop afterwards, just like what
|
||||
happens in non-stop mode, or if the target is in true all-stop mode
|
||||
and the interrupting is done by sending a SIGINT to the inferior
|
||||
process. */
|
||||
static bool interrupt_all_requested = false;
|
||||
|
||||
/* Pick the thread to report the stop on and to switch to it. */
|
||||
|
||||
static void
|
||||
switch_to_stop_thread ()
|
||||
{
|
||||
thread_info *stop_thr = nullptr;
|
||||
|
||||
if (previous_thread != nullptr && previous_thread->state == THREAD_RUNNING)
|
||||
stop_thr = previous_thread.get ();
|
||||
else
|
||||
{
|
||||
for (thread_info *thr : all_non_exited_threads ())
|
||||
if (thr->state == THREAD_RUNNING)
|
||||
{
|
||||
stop_thr = thr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
gdb_assert (stop_thr != nullptr);
|
||||
|
||||
switch_to_thread (stop_thr);
|
||||
}
|
||||
|
||||
/* Synchronously stop all threads, saving interesting events as
|
||||
pending events, and present a normal stop on one of the threads.
|
||||
Preference is given to the "previous thread", which was the thread
|
||||
that the user last resumed. This is used in "set non-stop off" +
|
||||
"maint set target-non-stop on" mode to stop the target in response
|
||||
to Ctrl-C or the "interrupt" command. */
|
||||
|
||||
static void
|
||||
sync_interrupt_all ()
|
||||
{
|
||||
/* Events are always processed with the main UI as current UI. This
|
||||
way, warnings, debug output, etc. are always consistently sent to
|
||||
the main console. */
|
||||
scoped_restore save_ui = make_scoped_restore (¤t_ui, main_ui);
|
||||
|
||||
/* Exposed by gdb.base/paginate-after-ctrl-c-running.exp. */
|
||||
|
||||
/* Temporarily disable pagination. Otherwise, the user would be
|
||||
given an option to press 'q' to quit, which would cause an early
|
||||
exit and could leave GDB in a half-baked state. */
|
||||
scoped_restore save_pagination
|
||||
= make_scoped_restore (&pagination_enabled, false);
|
||||
|
||||
scoped_disable_commit_resumed disable_commit_resumed ("stopping for ctrl-c");
|
||||
|
||||
gdb_assert (!non_stop);
|
||||
|
||||
/* Stop all threads before picking which one to present the stop on
|
||||
-- this is safer than the other way around because otherwise the
|
||||
thread we pick could exit just while we try to stop it. */
|
||||
stop_all_threads ();
|
||||
|
||||
switch_to_stop_thread ();
|
||||
|
||||
target_waitstatus ws;
|
||||
ws.kind = TARGET_WAITKIND_STOPPED;
|
||||
ws.value.sig = GDB_SIGNAL_0;
|
||||
set_last_target_status (current_inferior ()->process_target (),
|
||||
inferior_ptid, ws);
|
||||
stopped_by_random_signal = true;
|
||||
stop_print_frame = true;
|
||||
normal_stop ();
|
||||
|
||||
inferior_event_handler (INF_EXEC_COMPLETE);
|
||||
|
||||
/* If a UI was in sync execution mode, and now isn't, restore its
|
||||
prompt (a synchronous execution command has finished, and we're
|
||||
ready for input). */
|
||||
all_uis_check_sync_execution_done ();
|
||||
}
|
||||
|
||||
/* See infrun.h. */
|
||||
|
||||
void
|
||||
mark_infrun_async_event_handler_interrupt_all ()
|
||||
{
|
||||
mark_infrun_async_event_handler ();
|
||||
interrupt_all_requested = true;
|
||||
}
|
||||
|
||||
/* See infrun.h. */
|
||||
|
||||
void
|
||||
mark_infrun_async_event_handler_ctrl_c ()
|
||||
{
|
||||
mark_infrun_async_event_handler ();
|
||||
set_quit_flag ();
|
||||
}
|
||||
|
||||
/* Callback for infrun's target events source. This is marked either
|
||||
when a thread has a pending status to process, or a target
|
||||
interrupt was requested, either with Ctrl-C or the "interrupt"
|
||||
command and target is in non-stop mode. */
|
||||
|
||||
static void
|
||||
infrun_async_inferior_event_handler (gdb_client_data data)
|
||||
{
|
||||
/* Handle a Ctrl-C while the inferior has the terminal, or an
|
||||
"interrupt" cmd request. */
|
||||
if ((!target_terminal::is_ours () && check_quit_flag ())
|
||||
|| interrupt_all_requested)
|
||||
{
|
||||
interrupt_all_requested = false;
|
||||
|
||||
if (exists_non_stop_target ())
|
||||
{
|
||||
if (non_stop)
|
||||
{
|
||||
/* Stop one thread, like it would happen if we were
|
||||
stopping with SIGINT sent to the foreground
|
||||
process. */
|
||||
switch_to_stop_thread ();
|
||||
interrupt_target_1 (false);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Stop all threads, and report one single stop for all
|
||||
threads. */
|
||||
sync_interrupt_all ();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Pass a Ctrl-C request to the target. Usually this means
|
||||
sending a SIGINT to the inferior process. */
|
||||
target_pass_ctrlc ();
|
||||
}
|
||||
|
||||
/* Don't clear the event handler yet -- there may be pending
|
||||
events to process. */
|
||||
return;
|
||||
}
|
||||
|
||||
clear_async_event_handler (infrun_async_inferior_event_token);
|
||||
|
||||
inferior_event_handler (INF_REG_EVENT);
|
||||
}
|
||||
|
||||
|
||||
16
gdb/infrun.h
16
gdb/infrun.h
@@ -87,6 +87,10 @@ enum exec_direction_kind
|
||||
/* The current execution direction. */
|
||||
extern enum exec_direction_kind execution_direction;
|
||||
|
||||
/* Call this to point 'previous_thread' at the thread returned by
|
||||
inferior_thread, or at nullptr, if there's no selected thread. */
|
||||
extern void update_previous_thread ();
|
||||
|
||||
extern void start_remote (int from_tty);
|
||||
|
||||
/* Clear out all variables saying what to do when inferior is
|
||||
@@ -251,6 +255,18 @@ extern void infrun_async (int enable);
|
||||
loop. */
|
||||
extern void mark_infrun_async_event_handler (void);
|
||||
|
||||
/* Like mark_infrun_async_event_handler, and ask the event loop to
|
||||
stop all threads, in response to an "interrupt" command. */
|
||||
extern void mark_infrun_async_event_handler_interrupt_all ();
|
||||
|
||||
/* Like mark_infrun_async_event_handler, and ask the event loop to
|
||||
handle a "Ctrl-C" interruption request. In some modes (e.g., "set
|
||||
non-stop off" + "maint set target-non-stop on"), we interrupt the
|
||||
target with target_stop, and it's not safe to use that right away,
|
||||
as we may be in the middle of handling some other event, and
|
||||
target_stop changes infrun state. */
|
||||
extern void mark_infrun_async_event_handler_ctrl_c ();
|
||||
|
||||
/* The global chain of threads that need to do a step-over operation
|
||||
to get past e.g., a breakpoint. */
|
||||
extern struct thread_info *global_thread_step_over_chain_head;
|
||||
|
||||
115
gdb/linux-nat.c
115
gdb/linux-nat.c
@@ -1075,6 +1075,82 @@ linux_nat_post_attach_wait (ptid_t ptid, int *signalled)
|
||||
return status;
|
||||
}
|
||||
|
||||
/* Wait for a SIGSTOP out of PID. */
|
||||
|
||||
static void
|
||||
waitpid_sigstop (pid_t pid)
|
||||
{
|
||||
int status;
|
||||
int res = waitpid (pid, &status, 0);
|
||||
if (res == -1)
|
||||
perror_with_name (_("waiting for child"));
|
||||
else if (res != pid)
|
||||
error (_("wait returned unexpected PID %d"), res);
|
||||
else if (!WIFSTOPPED (status) || WSTOPSIG (status) != SIGSTOP)
|
||||
error (_("wait returned unexpected status 0x%x"), status);
|
||||
}
|
||||
|
||||
/* Wait for a fork event out of PID. */
|
||||
|
||||
static void
|
||||
waitpid_fork (pid_t pid)
|
||||
{
|
||||
int status;
|
||||
int res = waitpid (pid, &status, 0);
|
||||
if (res == -1)
|
||||
perror_with_name (_("waiting for child"));
|
||||
else if (res != pid)
|
||||
error (_("wait returned unexpected PID %d"), res);
|
||||
else if (!WIFSTOPPED (status))
|
||||
error (_("wait returned unexpected status 0x%x"), status);
|
||||
else
|
||||
{
|
||||
int event = linux_ptrace_get_extended_event (status);
|
||||
if (event != PTRACE_EVENT_FORK)
|
||||
error (_("wait returned unexpected status 0x%x"), status);
|
||||
}
|
||||
}
|
||||
|
||||
pid_t
|
||||
linux_nat_target::handle_session_leader_fork (pid_t sl_pid)
|
||||
{
|
||||
/* The first fork child is the session leader. In turn its fork
|
||||
child (i.e., GDB's granchild) is the inferior we want to debug.
|
||||
Enable tracefork in order to trace the grandchild, and get its
|
||||
pid. */
|
||||
|
||||
waitpid_sigstop (sl_pid);
|
||||
|
||||
linux_enable_event_reporting (sl_pid, PTRACE_O_TRACEFORK);
|
||||
ptrace (PTRACE_CONT, sl_pid, (PTRACE_TYPE_ARG3) 1, 0);
|
||||
|
||||
/* We should see a fork event now, for the second fork. */
|
||||
waitpid_fork (sl_pid);
|
||||
|
||||
/* Extract the grandchild's pid. This is the final inferior
|
||||
process. */
|
||||
unsigned long inf_pid;
|
||||
|
||||
if (ptrace (PTRACE_GETEVENTMSG, sl_pid, 0, &inf_pid) == -1)
|
||||
perror_with_name (_("getting event message"));
|
||||
|
||||
/* The new child has a pending SIGSTOP. We can't affect it until it
|
||||
hits the SIGSTOP, but we're already attached. */
|
||||
waitpid_sigstop (inf_pid);
|
||||
|
||||
/* We don't need to continue debugging the session leader. It's
|
||||
simpler to just detach from it. */
|
||||
ptrace (PTRACE_DETACH, sl_pid, (PTRACE_TYPE_ARG3) 1, 0);
|
||||
|
||||
/* Resume the grandchild / inferior. Disable event reporting to
|
||||
avoid confusing startup_inferior with extra events as the
|
||||
inferior goes through the shell. */
|
||||
linux_disable_event_reporting (inf_pid);
|
||||
ptrace (PTRACE_CONT, inf_pid, (PTRACE_TYPE_ARG3) 1, 0);
|
||||
|
||||
return inf_pid;
|
||||
}
|
||||
|
||||
void
|
||||
linux_nat_target::create_inferior (const char *exec_file,
|
||||
const std::string &allargs,
|
||||
@@ -2066,19 +2142,6 @@ wait_for_signal ()
|
||||
{
|
||||
linux_nat_debug_printf ("about to sigsuspend");
|
||||
sigsuspend (&suspend_mask);
|
||||
|
||||
/* If the quit flag is set, it means that the user pressed Ctrl-C
|
||||
and we're debugging a process that is running on a separate
|
||||
terminal, so we must forward the Ctrl-C to the inferior. (If the
|
||||
inferior is sharing GDB's terminal, then the Ctrl-C reaches the
|
||||
inferior directly.) We must do this here because functions that
|
||||
need to block waiting for a signal loop forever until there's an
|
||||
event to report before returning back to the event loop. */
|
||||
if (!target_terminal::is_ours ())
|
||||
{
|
||||
if (check_quit_flag ())
|
||||
target_pass_ctrlc ();
|
||||
}
|
||||
}
|
||||
|
||||
/* Wait for LP to stop. Returns the wait status, or 0 if the LWP has
|
||||
@@ -3241,8 +3304,32 @@ linux_nat_wait_1 (ptid_t ptid, struct target_waitstatus *ourstatus,
|
||||
/* We shouldn't end up here unless we want to try again. */
|
||||
gdb_assert (lp == NULL);
|
||||
|
||||
/* Block until we get an event reported with SIGCHLD. */
|
||||
/* Block until we get an event reported with SIGCHLD or a SIGINT
|
||||
interrupt. */
|
||||
wait_for_signal ();
|
||||
|
||||
/* If the quit flag is set, it means that the user pressed
|
||||
Ctrl-C and we're debugging a process that is running on a
|
||||
separate terminal, so we must forward the Ctrl-C to the
|
||||
inferior. (If the inferior is sharing GDB's terminal, then
|
||||
the Ctrl-C reaches the inferior directly.) If we were
|
||||
interrupted by Ctrl-C, return back to the event loop and let
|
||||
it handle interrupting the target (or targets). */
|
||||
|
||||
if (!target_terminal::is_ours () && check_quit_flag ())
|
||||
{
|
||||
mark_infrun_async_event_handler_ctrl_c ();
|
||||
|
||||
linux_nat_debug_printf ("exit (quit flag)");
|
||||
|
||||
/* If we got a SIGCHLD, need to end up here again. */
|
||||
async_file_mark ();
|
||||
|
||||
ourstatus->kind = TARGET_WAITKIND_IGNORE;
|
||||
|
||||
restore_child_signals_mask (&prev_mask);
|
||||
return minus_one_ptid;
|
||||
}
|
||||
}
|
||||
|
||||
gdb_assert (lp);
|
||||
|
||||
@@ -40,6 +40,8 @@ public:
|
||||
void create_inferior (const char *, const std::string &,
|
||||
char **, int) override;
|
||||
|
||||
pid_t handle_session_leader_fork (pid_t sl_pid) override;
|
||||
|
||||
void attach (const char *, int) override;
|
||||
|
||||
void detach (inferior *, int) override;
|
||||
|
||||
@@ -27,7 +27,11 @@
|
||||
#include "gdbsupport/pathstuff.h"
|
||||
#include "gdbsupport/signals-state-save-restore.h"
|
||||
#include "gdbsupport/gdb_tilde_expand.h"
|
||||
#include "gdbsupport/scoped_ignore_sigttou.h"
|
||||
#include "gdbsupport/managed-tty.h"
|
||||
#include <vector>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
extern char **environ;
|
||||
|
||||
@@ -81,6 +85,33 @@ private:
|
||||
std::string m_storage;
|
||||
};
|
||||
|
||||
#if GDB_MANAGED_TERMINALS
|
||||
|
||||
/* SIGHUP handler for the session leader processes. GDB sends this
|
||||
explicitly, though it'll also be called if GDB crashes and the
|
||||
terminal is abruptly closed. */
|
||||
|
||||
static void
|
||||
session_leader_hup (int sig)
|
||||
{
|
||||
scoped_ignore_sigttou ignore_sigttou;
|
||||
|
||||
/* We put the inferior (a child of the session leader) in the
|
||||
foreground, so by default, on detach, if we did nothing else, the
|
||||
inferior would get a SIGHUP when the terminal is closed by GDB.
|
||||
That SIGHUP would likely kill the inferior. To avoid it, we put
|
||||
the session leader in the foreground before the terminal is
|
||||
closed. Only processes in the foreground process group get the
|
||||
automatic SIGHUP, so the detached process doesn't get it. */
|
||||
int res = tcsetpgrp (0, getpid ());
|
||||
if (res == -1)
|
||||
trace_start_error (_("tcsetpgrp failed in session leader\n"));
|
||||
|
||||
_exit (0);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* Create argument vector for straight call to execvp. Breaks up
|
||||
ALLARGS into an argument vector suitable for passing to execvp and
|
||||
stores it in M_ARGV. E.g., on "run a b c d" this routine would get
|
||||
@@ -271,7 +302,8 @@ fork_inferior (const char *exec_file_arg, const std::string &allargs,
|
||||
void (*pre_trace_fun) (),
|
||||
const char *shell_file_arg,
|
||||
void (*exec_fun)(const char *file, char * const *argv,
|
||||
char * const *env))
|
||||
char * const *env),
|
||||
pid_t (*handle_session_leader_fork) (pid_t sl_pid))
|
||||
{
|
||||
pid_t pid;
|
||||
/* Set debug_fork then attach to the child while it sleeps, to debug. */
|
||||
@@ -317,7 +349,7 @@ fork_inferior (const char *exec_file_arg, const std::string &allargs,
|
||||
|
||||
/* Perform any necessary actions regarding to TTY before the
|
||||
fork/vfork call. */
|
||||
prefork_hook (allargs.c_str ());
|
||||
prefork_hook ();
|
||||
|
||||
/* It is generally good practice to flush any possible pending stdio
|
||||
output prior to doing a fork, to avoid the possibility of both
|
||||
@@ -354,7 +386,7 @@ fork_inferior (const char *exec_file_arg, const std::string &allargs,
|
||||
actually be a call to fork(2) due to the fact that autoconf will
|
||||
``#define vfork fork'' on certain platforms. */
|
||||
#if !(defined(__UCLIBC__) && defined(HAS_NOMMU))
|
||||
if (pre_trace_fun || debug_fork)
|
||||
if (pre_trace_fun || debug_fork || child_has_managed_tty_hook ())
|
||||
pid = fork ();
|
||||
else
|
||||
#endif
|
||||
@@ -405,6 +437,85 @@ fork_inferior (const char *exec_file_arg, const std::string &allargs,
|
||||
|
||||
restore_original_signals_state ();
|
||||
|
||||
/* Fork again so that the resulting inferior process is not the
|
||||
session leader. This makes it possible for the inferior to
|
||||
exit without killing its own children. If we instead let the
|
||||
inferior process be the session leader, when it exits, it'd
|
||||
cause a SIGHUP to be sent to all processes in its session
|
||||
(i.e., it's children). The code is disabled on no-MMU
|
||||
machines because those can't do fork, only vfork. In theory
|
||||
we could make this work with vfork by making the session
|
||||
leader process exec a helper process, probably gdb itself in
|
||||
a special mode (e.g., something like
|
||||
exec /proc/self/exe --session-leader-for PID
|
||||
*/
|
||||
#if GDB_MANAGED_TERMINALS
|
||||
if (child_has_managed_tty_hook ())
|
||||
{
|
||||
/* Fork again, to make sure the inferior is not the new
|
||||
session's leader. */
|
||||
pid_t child2 = fork ();
|
||||
if (child2 != 0)
|
||||
{
|
||||
/* This is the parent / session leader process. It just
|
||||
stays around until GDB closes the terminal. */
|
||||
|
||||
/* Gracefully handle SIGHUP. */
|
||||
signal (SIGHUP, session_leader_hup);
|
||||
|
||||
managed_tty_debug_printf
|
||||
(_("session-leader (sid=%d): waiting for child pid=%d exit\n"),
|
||||
(int) getpid (), (int) child2);
|
||||
|
||||
/* Reap the child/inferior exit status. */
|
||||
int status;
|
||||
int res = waitpid (child2, &status, 0);
|
||||
|
||||
managed_tty_debug_printf (_("session-leader (sid=%d): "
|
||||
"wait for child pid=%d returned: "
|
||||
"res=%d, waitstatus=0x%x\n"),
|
||||
(int) getpid (), child2, res, status);
|
||||
|
||||
if (res == -1)
|
||||
warning (_("session-leader (sid=%d): unexpected waitstatus "
|
||||
"reaping child pid=%d: "
|
||||
"res=-1, errno=%d (%s)"),
|
||||
(int) getpid (), child2, errno, safe_strerror (errno));
|
||||
else if (res != child2)
|
||||
warning (_("session-leader (sid=%d): unexpected waitstatus "
|
||||
"reaping child pid=%d: "
|
||||
"res=%d, status=0x%x"),
|
||||
(int) getpid (), child2, res, status);
|
||||
|
||||
/* Don't exit yet. While our direct child is gone,
|
||||
there may still be grandchildren attached to our
|
||||
session. We'll exit when our parent (GDB) closes the
|
||||
pty, killing us with SIGHUP. */
|
||||
while (1)
|
||||
pause ();
|
||||
}
|
||||
else
|
||||
{
|
||||
/* This is the child / final inferior process. */
|
||||
|
||||
int res;
|
||||
|
||||
/* Run the inferior in its own process group, and make
|
||||
it the session's foreground pgrp. */
|
||||
|
||||
res = gdb_setpgid ();
|
||||
if (res == -1)
|
||||
trace_start_error (_("setpgid failed in grandchild"));
|
||||
|
||||
scoped_ignore_sigttou ignore_sigttou;
|
||||
|
||||
res = tcsetpgrp (0, getpid ());
|
||||
if (res == -1)
|
||||
trace_start_error (_("tcsetpgrp failed in grandchild\n"));
|
||||
}
|
||||
}
|
||||
#endif /* GDB_MANAGED_TERMINALS */
|
||||
|
||||
/* There is no execlpe call, so we have to set the environment
|
||||
for our child in the global variable. If we've vforked, this
|
||||
clobbers the parent, but environ is restored a few lines down
|
||||
@@ -434,6 +545,9 @@ fork_inferior (const char *exec_file_arg, const std::string &allargs,
|
||||
/* Restore our environment in case a vforked child clob'd it. */
|
||||
environ = save_our_env;
|
||||
|
||||
if (child_has_managed_tty_hook () && handle_session_leader_fork != nullptr)
|
||||
pid = handle_session_leader_fork (pid);
|
||||
|
||||
postfork_hook (pid);
|
||||
|
||||
/* Now that we have a child process, make it our target, and
|
||||
|
||||
@@ -31,12 +31,15 @@ struct process_stratum_target;
|
||||
implementations. */
|
||||
#define START_INFERIOR_TRAPS_EXPECTED 1
|
||||
|
||||
/* Start an inferior Unix child process and sets inferior_ptid to its
|
||||
pid. EXEC_FILE is the file to run. ALLARGS is a string containing
|
||||
the arguments to the program. ENV is the environment vector to
|
||||
pass. SHELL_FILE is the shell file, or NULL if we should pick
|
||||
one. EXEC_FUN is the exec(2) function to use, or NULL for the default
|
||||
one. */
|
||||
/* Start an inferior Unix child process and return its pid. EXEC_FILE_ARG
|
||||
is the file to run. ALLARGS is a string containing the arguments
|
||||
to the program. ENV is the environment vector to pass.
|
||||
SHELL_FILE_ARG is the shell file, or NULL if we should pick one.
|
||||
EXEC_FUN is the exec(2) function to use, or NULL for the default
|
||||
one. HANDLE_SESSION_LEADER_FORK is the function to use to debug
|
||||
the session leader process across the double-fork and extract the final
|
||||
inferior PID if the inferior is using a gdb-managed terminal, or
|
||||
NULL otherwise. */
|
||||
|
||||
/* This function is NOT reentrant. Some of the variables have been
|
||||
made static to ensure that they survive the vfork call. */
|
||||
@@ -48,7 +51,9 @@ extern pid_t fork_inferior (const char *exec_file_arg,
|
||||
const char *shell_file_arg,
|
||||
void (*exec_fun) (const char *file,
|
||||
char * const *argv,
|
||||
char * const *env));
|
||||
char * const *env),
|
||||
pid_t (*handle_session_leader_fork) (pid_t sl_pid)
|
||||
= nullptr);
|
||||
|
||||
/* Accept NTRAPS traps from the inferior.
|
||||
|
||||
@@ -58,10 +63,9 @@ extern ptid_t startup_inferior (process_stratum_target *proc_target,
|
||||
struct target_waitstatus *mystatus,
|
||||
ptid_t *myptid);
|
||||
|
||||
/* Perform any necessary tasks before a fork/vfork takes place. ARGS
|
||||
is a string containing all the arguments received by the inferior.
|
||||
This function is mainly used by fork_inferior. */
|
||||
extern void prefork_hook (const char *args);
|
||||
/* Perform any necessary tasks before a fork/vfork takes place. This
|
||||
function is mainly used by fork_inferior. */
|
||||
extern void prefork_hook ();
|
||||
|
||||
/* Perform any necessary tasks after a fork/vfork takes place. This
|
||||
function is mainly used by fork_inferior. */
|
||||
@@ -71,6 +75,10 @@ extern void postfork_hook (pid_t pid);
|
||||
place. This function is mainly used by fork_inferior. */
|
||||
extern void postfork_child_hook ();
|
||||
|
||||
/* True if the inferior child has a terminal created and managed by
|
||||
GDB or GDBserver. */
|
||||
extern bool child_has_managed_tty_hook ();
|
||||
|
||||
/* Flush both stdout and stderr. This function needs to be
|
||||
implemented differently on GDB and GDBserver. */
|
||||
extern void gdb_flush_out_err ();
|
||||
|
||||
@@ -40,7 +40,6 @@
|
||||
#include <signal.h>
|
||||
#include <ctype.h>
|
||||
#include "gdb_bfd.h"
|
||||
#include "inflow.h"
|
||||
#include "auxv.h"
|
||||
#include "procfs.h"
|
||||
#include "observable.h"
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
#include "gdbcmd.h"
|
||||
#include "gdbsupport/filestuff.h"
|
||||
#include <termios.h>
|
||||
#include "inflow.h"
|
||||
#include "gdbsupport/scoped_ignore_sigttou.h"
|
||||
|
||||
struct hardwire_ttystate
|
||||
{
|
||||
|
||||
40
gdb/target.c
40
gdb/target.c
@@ -927,6 +927,21 @@ target_terminal::init (void)
|
||||
m_terminal_state = target_terminal_state::is_ours;
|
||||
}
|
||||
|
||||
/* Called after switching the terminal to the inferior. If the user
|
||||
hit C-c before, pretend that it was hit right here. Non-stop
|
||||
targets however are more complicated though, as interruption with
|
||||
"set non-stop off" + "maint set target-non-stop on" wants to stop
|
||||
all threads individually with target_stop (and synchronously wait
|
||||
for the stops), so we let the event loop handle it, when it's
|
||||
recursion-safe to do so. */
|
||||
|
||||
static void
|
||||
maybe_pass_ctrlc ()
|
||||
{
|
||||
if (!exists_non_stop_target () && check_quit_flag ())
|
||||
target_pass_ctrlc ();
|
||||
}
|
||||
|
||||
/* See target/target.h. */
|
||||
|
||||
void
|
||||
@@ -961,8 +976,7 @@ target_terminal::inferior (void)
|
||||
|
||||
/* If the user hit C-c before, pretend that it was hit right
|
||||
here. */
|
||||
if (check_quit_flag ())
|
||||
target_pass_ctrlc ();
|
||||
maybe_pass_ctrlc ();
|
||||
}
|
||||
|
||||
/* See target/target.h. */
|
||||
@@ -998,8 +1012,7 @@ target_terminal::restore_inferior (void)
|
||||
|
||||
/* If the user hit C-c before, pretend that it was hit right
|
||||
here. */
|
||||
if (check_quit_flag ())
|
||||
target_pass_ctrlc ();
|
||||
maybe_pass_ctrlc ();
|
||||
}
|
||||
|
||||
/* Switch terminal state to DESIRED_STATE, either is_ours, or
|
||||
@@ -2505,6 +2518,8 @@ target_pre_inferior (int from_tty)
|
||||
|
||||
current_inferior ()->highest_thread_num = 0;
|
||||
|
||||
update_previous_thread ();
|
||||
|
||||
agent_capability_invalidate ();
|
||||
}
|
||||
|
||||
@@ -2533,6 +2548,9 @@ target_preopen (int from_tty)
|
||||
error (_("Program not killed."));
|
||||
}
|
||||
|
||||
/* Release reference to old previous thread. */
|
||||
update_previous_thread ();
|
||||
|
||||
/* Calling target_kill may remove the target from the stack. But if
|
||||
it doesn't (which seems like a win for UDI), remove it now. */
|
||||
/* Leave the exec target, though. The user may be switching from a
|
||||
@@ -3792,6 +3810,9 @@ target_interrupt ()
|
||||
void
|
||||
target_pass_ctrlc (void)
|
||||
{
|
||||
/* Non-stop targets interrupt programs with target_stop instead. */
|
||||
gdb_assert (!exists_non_stop_target ());
|
||||
|
||||
/* Pass the Ctrl-C to the first target that has a thread
|
||||
running. */
|
||||
for (inferior *inf : all_inferiors ())
|
||||
@@ -4390,11 +4411,18 @@ exists_non_stop_target ()
|
||||
if (target_is_non_stop_p ())
|
||||
return true;
|
||||
|
||||
scoped_restore_current_thread restore_thread;
|
||||
/* We can get here quite deep in core code, e.g., from
|
||||
target_terminal::inferior(). Avoid switching thread context or
|
||||
anything that would communicate with the target (e.g., to fetch
|
||||
registers), or flushing e.g., the frame cache, as we may end up
|
||||
called from within the frame building code. We just switch
|
||||
inferior in order to be able to call through the
|
||||
target_stack. */
|
||||
scoped_restore_current_inferior restore_inferior;
|
||||
|
||||
for (inferior *inf : all_inferiors ())
|
||||
{
|
||||
switch_to_inferior_no_thread (inf);
|
||||
set_current_inferior (inf);
|
||||
if (target_is_non_stop_p ())
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -27,6 +27,9 @@ extern void new_tty (void);
|
||||
|
||||
extern void new_tty_postfork (void);
|
||||
|
||||
/* Returns true if new_tty_prefork created a GDB-managed terminal. */
|
||||
extern bool created_managed_tty ();
|
||||
|
||||
extern void copy_terminal_info (struct inferior *to, struct inferior *from);
|
||||
|
||||
/* Exchange the terminal info and state between inferiors A and B. */
|
||||
|
||||
@@ -22,6 +22,10 @@ if {[prepare_for_testing "failed to prepare" $testfile $srcfile debug] == -1} {
|
||||
return -1
|
||||
}
|
||||
|
||||
# This testcase only makes sense when GDB and the inferior are reading
|
||||
# from the same input file (aka sharing the terminal's input buffer).
|
||||
gdb_test_no_output "tty /dev/tty"
|
||||
|
||||
# Because runto_main doesn't know how to handle the prompt with annotations,
|
||||
# run to main before we set the annotation level.
|
||||
if ![runto_main] then {
|
||||
|
||||
@@ -24,6 +24,7 @@ if ![target_can_use_run_cmd] {
|
||||
}
|
||||
|
||||
set breakpoints_invalid "\r\n\032\032breakpoints-invalid\r\n"
|
||||
set frames_invalid "\r\n\032\032frames-invalid\r\n"
|
||||
|
||||
#
|
||||
# test running programs
|
||||
@@ -226,7 +227,7 @@ gdb_test_multiple "break printf" "break printf" {
|
||||
#
|
||||
# get to printf
|
||||
#
|
||||
set pat_begin "\r\n\032\032post-prompt\r\nContinuing.\r\n\r\n\032\032starting\r\n\r\n\032\032frames-invalid\r\n${breakpoints_invalid}\r\n\032\032frames-invalid\r\n"
|
||||
set pat_begin "\r\n\032\032post-prompt\r\nContinuing.\r\n\r\n\032\032starting\r\n${frames_invalid}${breakpoints_invalid}(${frames_invalid})?"
|
||||
set pat_adjust "warning: Breakpoint 3 address previously adjusted from $hex to $hex.\r\n"
|
||||
set pat_end "\r\n\032\032breakpoint 3\r\n\r\nBreakpoint 3, \r\n\032\032frame-begin 0 $hex\r\n\r\n(\032\032frame-address\r\n$hex\r\n\032\032frame-address-end\r\n in \r\n)*.*\032\032frame-function-name\r\n.*printf(@.*)?\r\n\032\032frame-args\r\n.*\032\032frame-end\r\n\r\n\032\032stopped\r\n$gdb_prompt$"
|
||||
|
||||
|
||||
@@ -87,6 +87,9 @@ proc do_test {} {
|
||||
-re "Program received signal SIGINT.*\r\n$gdb_prompt $" {
|
||||
send_log "$internal_pass (SIGINT)\n"
|
||||
}
|
||||
-re "Program stopped.*\r\n$gdb_prompt $" {
|
||||
send_log "$internal_pass (stopped)\n"
|
||||
}
|
||||
-re "Quit\r\n$gdb_prompt $" {
|
||||
send_log "$internal_pass (Quit)\n"
|
||||
|
||||
|
||||
@@ -20,6 +20,9 @@ standard_testfile
|
||||
|
||||
save_vars { GDBFLAGS } {
|
||||
set GDBFLAGS "$GDBFLAGS -ex \"set non-stop on\""
|
||||
# This test only makes sense when GDB and the inferior are reading
|
||||
# from the same input file / sharing the terminal.
|
||||
append GDBFLAGS " -ex \"tty /dev/tty\""
|
||||
if { [prepare_for_testing "failed to prepare" ${testfile} $srcfile] } {
|
||||
return -1
|
||||
}
|
||||
|
||||
@@ -23,10 +23,20 @@ if [target_info exists gdb,cannot_call_functions] {
|
||||
continue
|
||||
}
|
||||
|
||||
if {[prepare_for_testing "failed to prepare" $testfile $srcfile debug]} {
|
||||
if {[build_executable "failed to compile" $testfile $srcfile debug]} {
|
||||
return -1
|
||||
}
|
||||
|
||||
save_vars { GDBFLAGS } {
|
||||
# This test only makes sense when GDB and the inferior are reading
|
||||
# from the same input file / sharing the terminal. If we instead
|
||||
# let GDB put the inferior in its own session, then while the
|
||||
# inferior is running in the foreground, input would be redirected
|
||||
# to the inferior, and GDB would never see that input.
|
||||
append GDBFLAGS " -ex \"tty /dev/tty\""
|
||||
clean_restart $binfile
|
||||
}
|
||||
|
||||
if ![runto_main] then {
|
||||
fail "couldn't run to main"
|
||||
return -1
|
||||
|
||||
@@ -81,7 +81,7 @@ proc do_test {} {
|
||||
}
|
||||
|
||||
after 500 {send_gdb "\003"}
|
||||
gdb_test "" "(Program|Thread .*) received signal SIGINT.*" \
|
||||
gdb_test "" "(Program|Thread .*) (received signal SIGINT|stopped).*" \
|
||||
"stop with control-c"
|
||||
|
||||
remote_exec host "kill -9 $child_pid"
|
||||
|
||||
@@ -53,7 +53,7 @@ proc do_test {} {
|
||||
|
||||
set test "ctrl-c stops process"
|
||||
gdb_test_multiple "" $test {
|
||||
-re "received signal SIGINT.*\r\n$gdb_prompt $" {
|
||||
-re "(received signal SIGINT|stopped).*\r\n$gdb_prompt $" {
|
||||
pass $test
|
||||
}
|
||||
}
|
||||
@@ -79,7 +79,7 @@ proc do_test {} {
|
||||
|
||||
set test "interrupt cmd stops process"
|
||||
gdb_test_multiple "" $test {
|
||||
-re "received signal SIGINT" {
|
||||
-re "(received signal SIGINT|stopped)" {
|
||||
pass $test
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ gdb_test_multiple $test $test {
|
||||
|
||||
set test "inferior received SIGINT"
|
||||
gdb_test_multiple "" $test {
|
||||
-re "\r\nProgram received signal SIGINT.*" {
|
||||
-re "\r\nProgram (received signal SIGINT|stopped).*" {
|
||||
# This appears after the prompt, which was already consumed
|
||||
# above.
|
||||
pass $test
|
||||
|
||||
@@ -82,7 +82,7 @@ if ![file exists $binfile] then {
|
||||
send_gdb "\003"
|
||||
set msg "send_gdb control C"
|
||||
gdb_test_multiple "" $msg {
|
||||
-re "Program received signal SIGINT.*$gdb_prompt $" {
|
||||
-re "Program (received signal SIGINT|stopped).*$gdb_prompt $" {
|
||||
pass $msg
|
||||
}
|
||||
}
|
||||
@@ -166,7 +166,7 @@ if ![file exists $binfile] then {
|
||||
set msg "Send Control-C, second time"
|
||||
send_gdb "\003"
|
||||
gdb_test_multiple "" "$msg" {
|
||||
-re "Program received signal SIGINT.*$gdb_prompt $" {
|
||||
-re "Program (received signal SIGINT|stopped).*$gdb_prompt $" {
|
||||
pass "$msg"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,21 +47,46 @@ if { ![runto_main] } then {
|
||||
set printing_done_line [gdb_get_line_number "printing done"]
|
||||
gdb_test "break $printing_done_line" ".*" "set breakpoint after printing"
|
||||
|
||||
send_gdb "continue\n"
|
||||
gdb_test_multiple "continue" "" {
|
||||
-re "Continuing\.\r\n" {
|
||||
pass $gdb_test_name
|
||||
}
|
||||
}
|
||||
|
||||
set expected_lines 3000
|
||||
set more 1
|
||||
set i 0
|
||||
while {$more} {
|
||||
set more 0
|
||||
|
||||
# Make sure that we don't get spurious/repeated/bogus output
|
||||
# between each "this is line number ..." line, with an anchor.
|
||||
# But consume any output that precedes the first line, because
|
||||
# when testing against GDBserver, we'll get:
|
||||
#
|
||||
# continue
|
||||
# Continuing.
|
||||
# PASS: gdb.base/long-inferior-output.exp: continue
|
||||
# Remote debugging from host ::1, port 40044
|
||||
# this is line number 0
|
||||
# ...
|
||||
if {$i != 0} {
|
||||
set anchor "^"
|
||||
} else {
|
||||
set anchor ""
|
||||
}
|
||||
|
||||
gdb_expect {
|
||||
-i $inferior_spawn_id
|
||||
-ex "this is line number $i" {
|
||||
-re "${anchor}this is line number $i\r\n" {
|
||||
incr i
|
||||
if {$i != $expected_lines} {
|
||||
if {$i != $expected_lines} {
|
||||
set more 1
|
||||
}
|
||||
}
|
||||
-re "this is line number \[^\r\n\]*\r\n" {
|
||||
# If we see this, we've lost output.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -47,7 +47,8 @@ proc do_test {} {
|
||||
# For this to work we must be sure to consume the "Continuing."
|
||||
# message first, or GDB's signal handler may not be in place.
|
||||
after 500 {send_gdb "\003"}
|
||||
gdb_test "" "Program received signal SIGINT.*" "stop with control-c"
|
||||
gdb_test "" "Program (received signal SIGINT|stopped).*" \
|
||||
"stop with control-c"
|
||||
}
|
||||
|
||||
# With native debugging and "run" (with job control), the ctrl-c
|
||||
|
||||
@@ -193,7 +193,7 @@ if ![target_info exists gdb,nointerrupts] {
|
||||
incr vcont_r_counter
|
||||
exp_continue
|
||||
}
|
||||
-re "Program received signal SIGINT.*$gdb_prompt $" {
|
||||
-re "Program (received signal SIGINT|stopped).*$gdb_prompt $" {
|
||||
pass $test
|
||||
}
|
||||
}
|
||||
|
||||
43
gdb/testsuite/gdb.base/sigint-masked-out.c
Normal file
43
gdb/testsuite/gdb.base/sigint-masked-out.c
Normal file
@@ -0,0 +1,43 @@
|
||||
/* This testcase is part of GDB, the GNU debugger.
|
||||
|
||||
Copyright 2021 Free Software Foundation, Inc.
|
||||
|
||||
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 <unistd.h>
|
||||
#include <signal.h>
|
||||
|
||||
static void
|
||||
done ()
|
||||
{
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
int i;
|
||||
sigset_t sigs;
|
||||
|
||||
alarm (30);
|
||||
|
||||
sigfillset (&sigs);
|
||||
sigprocmask (SIG_SETMASK, &sigs, NULL);
|
||||
|
||||
done ();
|
||||
|
||||
while (1)
|
||||
sleep (1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
109
gdb/testsuite/gdb.base/sigint-masked-out.exp
Normal file
109
gdb/testsuite/gdb.base/sigint-masked-out.exp
Normal file
@@ -0,0 +1,109 @@
|
||||
# Copyright 2021 Free Software Foundation, Inc.
|
||||
#
|
||||
# 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/>.
|
||||
|
||||
# Make sure that we can interrupt an inferior that has all signals
|
||||
# masked out, including SIGINT, with both Ctrl-C and the "interrupt"
|
||||
# command.
|
||||
|
||||
standard_testfile
|
||||
|
||||
if {[build_executable "failed to build" $testfile $srcfile {debug}]} {
|
||||
return -1
|
||||
}
|
||||
|
||||
# Test interrupting with Ctrl-C.
|
||||
|
||||
proc_with_prefix test_ctrl_c {} {
|
||||
global binfile
|
||||
global gdb_prompt
|
||||
|
||||
clean_restart $binfile
|
||||
|
||||
if ![runto "done"] {
|
||||
fail "can't run to done function"
|
||||
return
|
||||
}
|
||||
|
||||
set can_interrupt [can_interrupt_blocked_sigint]
|
||||
|
||||
gdb_test_multiple "continue" "" {
|
||||
-re "Continuing" {
|
||||
pass $gdb_test_name
|
||||
}
|
||||
}
|
||||
|
||||
after 200
|
||||
|
||||
send_gdb "\003"
|
||||
|
||||
gdb_test_multiple "" "ctrl-c stops process" {
|
||||
-timeout 5
|
||||
-re -wrap "(received signal SIGINT|stopped).*" {
|
||||
pass $gdb_test_name
|
||||
}
|
||||
timeout {
|
||||
if {!$can_interrupt} {
|
||||
setup_kfail "gdb/9425" *-*-*
|
||||
}
|
||||
fail "$gdb_test_name (timeout)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Test interrupting with the "interrupt" command.
|
||||
|
||||
proc_with_prefix test_interrupt_cmd {} {
|
||||
global binfile
|
||||
global gdb_prompt
|
||||
|
||||
clean_restart $binfile
|
||||
|
||||
if ![runto "done"] {
|
||||
fail "can't run to done function"
|
||||
return
|
||||
}
|
||||
|
||||
set can_interrupt [can_interrupt_blocked_sigint]
|
||||
|
||||
gdb_test_multiple "continue&" "" {
|
||||
-re "Continuing\\.\r\n$gdb_prompt " {
|
||||
pass $gdb_test_name
|
||||
}
|
||||
}
|
||||
|
||||
after 200
|
||||
|
||||
gdb_test_multiple "interrupt" "" {
|
||||
-re "$gdb_prompt " {
|
||||
pass $gdb_test_name
|
||||
}
|
||||
}
|
||||
|
||||
gdb_test_multiple "" "interrupt cmd stops process" {
|
||||
-timeout 5
|
||||
-re "(received signal SIGINT|stopped)" {
|
||||
pass $gdb_test_name
|
||||
}
|
||||
timeout {
|
||||
if {!$can_interrupt} {
|
||||
setup_kfail "gdb/14559" *-*-*
|
||||
}
|
||||
fail "$gdb_test_name (timeout)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
test_ctrl_c
|
||||
test_interrupt_cmd
|
||||
80
gdb/testsuite/gdb.base/sigint-sigwait.c
Normal file
80
gdb/testsuite/gdb.base/sigint-sigwait.c
Normal file
@@ -0,0 +1,80 @@
|
||||
/* This testcase is part of GDB, the GNU debugger.
|
||||
|
||||
Copyright 2021 Free Software Foundation, Inc.
|
||||
|
||||
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 <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static void
|
||||
handle_sigint (int signo)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
done ()
|
||||
{
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
sigset_t signal_set;
|
||||
struct sigaction s;
|
||||
|
||||
alarm (30);
|
||||
|
||||
s.sa_flags = SA_RESTART;
|
||||
s.sa_handler = handle_sigint;
|
||||
|
||||
if (sigaction (SIGINT, &s, NULL) < 0)
|
||||
{
|
||||
perror ("<%s> Error sigaction: ");
|
||||
exit (2);
|
||||
}
|
||||
|
||||
if (sigfillset (&signal_set) < 0)
|
||||
{
|
||||
perror ("<main> Error sigfillset(): ");
|
||||
exit (2);
|
||||
}
|
||||
|
||||
done ();
|
||||
|
||||
while (1)
|
||||
{
|
||||
int sig;
|
||||
|
||||
/* Wait for queued signals. .*/
|
||||
if (sigwait (&signal_set, &sig) != 0)
|
||||
{
|
||||
perror ("<main> Error sigwait(): ");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
/* Process signal. */
|
||||
if (sig == SIGINT)
|
||||
{
|
||||
printf ("Terminating.\n");
|
||||
exit (0);
|
||||
}
|
||||
|
||||
printf ("Unhandled signal [%s]\n", strsignal (sig));
|
||||
}
|
||||
}
|
||||
113
gdb/testsuite/gdb.base/sigint-sigwait.exp
Normal file
113
gdb/testsuite/gdb.base/sigint-sigwait.exp
Normal file
@@ -0,0 +1,113 @@
|
||||
# Copyright 2021 Free Software Foundation, Inc.
|
||||
#
|
||||
# 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/>.
|
||||
|
||||
# Make sure that we can interrupt an inferior that has all signals
|
||||
# masked out, including SIGINT, and then waits for signals with
|
||||
# sigwait. Test interrupting with both Ctrl-C and the "interrupt"
|
||||
# command.
|
||||
|
||||
standard_testfile
|
||||
|
||||
if {[build_executable "failed to build" $testfile $srcfile {debug}]} {
|
||||
return -1
|
||||
}
|
||||
|
||||
# Test interrupting with Ctrl-C.
|
||||
|
||||
proc_with_prefix test_ctrl_c {} {
|
||||
global binfile
|
||||
global gdb_prompt
|
||||
|
||||
clean_restart $binfile
|
||||
|
||||
if ![runto "done"] {
|
||||
fail "can't run to done function"
|
||||
return
|
||||
}
|
||||
|
||||
set can_interrupt [can_interrupt_blocked_sigint]
|
||||
|
||||
gdb_test_multiple "continue" "" {
|
||||
-re "Continuing" {
|
||||
pass $gdb_test_name
|
||||
}
|
||||
}
|
||||
|
||||
after 200
|
||||
|
||||
send_gdb "\003"
|
||||
|
||||
global exited_normally_re
|
||||
|
||||
gdb_test_multiple "" "ctrl-c stops process" {
|
||||
-re -wrap "(received signal SIGINT|stopped).*" {
|
||||
pass $gdb_test_name
|
||||
}
|
||||
-re -wrap "Inferior.*exited normally.*" {
|
||||
if {!$can_interrupt} {
|
||||
setup_kfail "gdb/9425" *-*-*
|
||||
}
|
||||
fail "$gdb_test_name (the program exited)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Test interrupting with the "interrupt" command.
|
||||
|
||||
proc_with_prefix test_interrupt_cmd {} {
|
||||
global binfile
|
||||
global gdb_prompt
|
||||
|
||||
clean_restart $binfile
|
||||
|
||||
if ![runto "done"] {
|
||||
fail "can't run to done function"
|
||||
return
|
||||
}
|
||||
|
||||
set can_interrupt [can_interrupt_blocked_sigint]
|
||||
|
||||
gdb_test_multiple "continue&" "" {
|
||||
-re "Continuing\\.\r\n$gdb_prompt " {
|
||||
pass $gdb_test_name
|
||||
}
|
||||
}
|
||||
|
||||
after 200
|
||||
|
||||
gdb_test_multiple "interrupt" "" {
|
||||
-re "$gdb_prompt " {
|
||||
pass $gdb_test_name
|
||||
}
|
||||
}
|
||||
|
||||
global exited_normally_re
|
||||
|
||||
gdb_test_multiple "" "interrupt cmd stops process" {
|
||||
-timeout 5
|
||||
-re "(received signal SIGINT|stopped)" {
|
||||
pass $gdb_test_name
|
||||
}
|
||||
-re "Inferior.*exited normally" {
|
||||
if {!$can_interrupt} {
|
||||
setup_kfail "gdb/14559" *-*-*
|
||||
}
|
||||
fail "$gdb_test_name (the program exited)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
test_ctrl_c
|
||||
test_interrupt_cmd
|
||||
@@ -97,7 +97,7 @@ proc test_with_self { } {
|
||||
send_gdb "\003"
|
||||
# "Thread 1" is displayed iff Guile support is linked in.
|
||||
gdb_expect {
|
||||
-re "(Thread .*|Program) received signal SIGINT.*$gdb_prompt $" {
|
||||
-re "(Thread .*|Program) (received signal SIGINT|stopped).*$gdb_prompt $" {
|
||||
pass "$description"
|
||||
}
|
||||
-re ".*$gdb_prompt $" {
|
||||
@@ -119,7 +119,7 @@ proc test_with_self { } {
|
||||
set description "send ^C to child process again"
|
||||
send_gdb "\003"
|
||||
gdb_expect {
|
||||
-re "(Thread .*|Program) received signal SIGINT.*$gdb_prompt $" {
|
||||
-re "(Thread .*|Program) (received signal SIGINT|stopped).*$gdb_prompt $" {
|
||||
pass "$description"
|
||||
}
|
||||
-re ".*$gdb_prompt $" {
|
||||
|
||||
@@ -30,9 +30,13 @@ if {[mi_runto_main] < 0} {
|
||||
return -1
|
||||
}
|
||||
|
||||
set milogfile [standard_output_file "milog.txt"]
|
||||
set milogfile1 [standard_output_file "milog1.txt"]
|
||||
set milogfile2 [standard_output_file "milog2.txt"]
|
||||
|
||||
mi_gdb_test "-gdb-set logging file $milogfile" ".*"
|
||||
remote_file host delete $milogfile1
|
||||
remote_file host delete $milogfile2
|
||||
|
||||
mi_gdb_test "-gdb-set logging file $milogfile1" ".*"
|
||||
|
||||
mi_gdb_test "-gdb-set logging overwrite on" ".*"
|
||||
|
||||
@@ -44,7 +48,7 @@ mi_next "logged next"
|
||||
|
||||
mi_gdb_test "-gdb-set logging off" ".*" "logging off"
|
||||
|
||||
set chan [open $milogfile]
|
||||
set chan [open $milogfile1]
|
||||
set logcontent [read $chan]
|
||||
close $chan
|
||||
|
||||
@@ -58,26 +62,84 @@ if [regexp "\\^done\[\r\n\]+$mi_log_prompt\\^running\[\r\n\]+\\*running,thread-i
|
||||
|
||||
# Now try the redirect, which writes into the file only.
|
||||
|
||||
mi_gdb_test "-gdb-set logging redirect on" ".*" "redirect logging on"
|
||||
mi_gdb_test "-gdb-set logging file $milogfile2" ".*"
|
||||
|
||||
# Since all output will be going into the file, just keep sending commands
|
||||
# and don't expect anything to appear until logging is turned off.
|
||||
mi_gdb_test "999-gdb-set logging redirect on" ".*" "redirect logging on"
|
||||
|
||||
send_gdb "1001-gdb-set logging on\n"
|
||||
|
||||
# We can't open the file right away, because we're not waiting for
|
||||
# GDB's "^done" -- we could end up opening the file before GDB creates
|
||||
# it. So spin waiting until the file exists.
|
||||
|
||||
proc wait_open {filename} {
|
||||
set ticks [expr 10 * $::timeout]
|
||||
set t 0
|
||||
|
||||
while {![file exists $filename]} {
|
||||
after 100
|
||||
|
||||
incr t
|
||||
if {$t == $ticks} {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
return [open $filename]
|
||||
}
|
||||
|
||||
set chan [wait_open $milogfile2]
|
||||
|
||||
# Read GDB output in channel/file CHAN, expect the result. PATTERN is
|
||||
# the pattern to match for a PASS.
|
||||
|
||||
proc gdb_test_file {chan pattern test} {
|
||||
global timeout
|
||||
|
||||
verbose -log "gdb_test_file: begin"
|
||||
|
||||
set ticks [expr 10 * $timeout]
|
||||
set t 0
|
||||
set logcontent ""
|
||||
|
||||
while {1} {
|
||||
set r [read $chan]
|
||||
append logcontent $r
|
||||
|
||||
send_log -- "$r"
|
||||
|
||||
if [regexp "$pattern" $logcontent] {
|
||||
pass "$test"
|
||||
break
|
||||
} else {
|
||||
incr t
|
||||
if {$t == $ticks} {
|
||||
fail "$test (timeout)"
|
||||
break
|
||||
}
|
||||
after 100
|
||||
}
|
||||
}
|
||||
|
||||
verbose -log "gdb_test_file: end"
|
||||
}
|
||||
|
||||
gdb_test_file $chan \
|
||||
"1001\\^done\[\r\n\]+$mi_log_prompt" \
|
||||
"redirect log file contents, set logging on"
|
||||
|
||||
send_gdb "1002-exec-step\n"
|
||||
gdb_test_file $chan \
|
||||
"1002\\^running\[\r\n\]+\\*running,thread-id=\"all\"\[\r\n\]+$mi_log_prompt\\*stopped,reason=\"end-stepping-range\",.*\[\r\n\]+$mi_log_prompt" \
|
||||
"redirect log file contents, exec-step"
|
||||
|
||||
send_gdb "1003-exec-next\n"
|
||||
gdb_test_file $chan \
|
||||
"1003\\^running\[\r\n\]+\\*running,thread-id=\"all\"\[\r\n\]+$mi_log_prompt\\*stopped,reason=\"end-stepping-range\",.*\[\r\n\]+$mi_log_prompt" \
|
||||
"redirect log file contents, exec-next"
|
||||
|
||||
mi_gdb_test "1004-gdb-set logging off" ".*" "redirect logging off"
|
||||
|
||||
set chan [open $milogfile]
|
||||
set logcontent [read $chan]
|
||||
close $chan
|
||||
|
||||
if [regexp "1001\\^done\[\r\n\]+$mi_log_prompt.*1002\\^running\[\r\n\]+\\*running,thread-id=\"all\"\[\r\n\]+$mi_log_prompt\\*stopped,reason=\"end-stepping-range\",.*\[\r\n\]+$mi_log_prompt.*1003\\^running\[\r\n\]+\\*running,thread-id=\"all\"\[\r\n\]+$mi_log_prompt\\*stopped,reason=\"end-stepping-range\",.*\[\r\n\]+$mi_log_prompt" $logcontent] {
|
||||
pass "redirect log file contents"
|
||||
} else {
|
||||
fail "redirect log file contents"
|
||||
}
|
||||
mi_gdb_test "1004-gdb-set logging off" ".*" "redirect logging off"
|
||||
|
||||
# Now try enabling a redirect while GDB is already logging. This used
|
||||
# to crash GDB.
|
||||
@@ -94,5 +156,3 @@ with_test_prefix "redirect while already logging" {
|
||||
}
|
||||
|
||||
mi_gdb_exit
|
||||
|
||||
remote_file host delete $milogfile
|
||||
|
||||
@@ -92,7 +92,7 @@ proc do_test {sync_command} {
|
||||
gdb_test_multiple "interrupt" "$message" {
|
||||
-re "$gdb_prompt " {
|
||||
gdb_test_multiple "" "$message" {
|
||||
-re "received signal SIGINT" {
|
||||
-re "(received signal SIGINT|stopped)" {
|
||||
pass $message
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ proc test_ctrlc {} {
|
||||
|
||||
set msg "send_gdb control C"
|
||||
gdb_test_multiple "" $msg {
|
||||
-re "received signal SIGINT.*$gdb_prompt $" {
|
||||
-re "(received signal SIGINT|stopped).*$gdb_prompt $" {
|
||||
pass $msg
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ proc test_no_resumed_infs {inf_A inf_B} {
|
||||
# Now stop the program (all targets).
|
||||
send_gdb "\003"
|
||||
gdb_test_multiple "" "send_gdb control C" {
|
||||
-re "received signal SIGINT.*$gdb_prompt $" {
|
||||
-re "(received signal SIGINT|stopped).*$gdb_prompt $" {
|
||||
pass $gdb_test_name
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,9 +48,18 @@ set attach_pid2 [spawn_id_get_pid $attach_spawn_id2]
|
||||
# Create inferior WHICH_INF. INF_HOW is how to create it. This can
|
||||
# be one of:
|
||||
#
|
||||
# - "run" - for "run" command.
|
||||
# - "tty" - for "run" + "tty TTY".
|
||||
# - "attach" - for attaching to an existing process.
|
||||
# - "run-session" - for "run" command, GDB puts inferior in its own
|
||||
# session and gives its own controlling terminal,
|
||||
# on systems that support it (e.g., GNU/Linux) (the
|
||||
# default).
|
||||
#
|
||||
# - "run-share" - for "run" command, GDB and inferior share terminal
|
||||
# ("set inferior-tty /dev/tty").
|
||||
#
|
||||
# - "run-tty" - for "run" + "tty TTY".
|
||||
#
|
||||
# - "attach" - for attaching to an existing process.
|
||||
|
||||
proc create_inferior {which_inf inf_how} {
|
||||
global binfile
|
||||
|
||||
@@ -69,12 +78,22 @@ proc create_inferior {which_inf inf_how} {
|
||||
}
|
||||
}
|
||||
|
||||
if {$inf_how == "run"} {
|
||||
if {$inf_how == "run-session"} {
|
||||
# Clear inferior-tty back to the default, in case we're
|
||||
# running the testsuite with "set inferior-tty /dev/tty"
|
||||
# forced.
|
||||
gdb_test_no_output "set inferior-tty"
|
||||
if [my_runto_main] {
|
||||
global inferior_spawn_id
|
||||
return $inferior_spawn_id
|
||||
}
|
||||
} elseif {$inf_how == "tty"} {
|
||||
} elseif {$inf_how == "run-share"} {
|
||||
gdb_test_no_output "set inferior-tty /dev/tty"
|
||||
if [my_runto_main] {
|
||||
global inferior_spawn_id
|
||||
return $inferior_spawn_id
|
||||
}
|
||||
} elseif {$inf_how == "run-tty"} {
|
||||
# Create the new PTY for the inferior.
|
||||
spawn -pty
|
||||
set inf_spawn_id $spawn_id
|
||||
@@ -95,6 +114,22 @@ proc create_inferior {which_inf inf_how} {
|
||||
if {[gdb_test "attach $testpid" \
|
||||
"Attaching to program: .*, process $testpid.*(in|at).*" \
|
||||
"attach"] == 0} {
|
||||
|
||||
# The program is now stopped, but if testing against
|
||||
# gdbserver, then the inferior's output emmitted before it
|
||||
# stopped isn't flushed unless we explicitly do so,
|
||||
# because it is on a different spawn_id. Do it now, to
|
||||
# avoid confusing tests further below.
|
||||
gdb_test_multiple "" "flush inferior output" {
|
||||
-timeout 1
|
||||
-i $test_spawn_id -re "pid=" {
|
||||
exp_continue
|
||||
}
|
||||
timeout {
|
||||
pass $gdb_test_name
|
||||
}
|
||||
}
|
||||
|
||||
return $test_spawn_id
|
||||
}
|
||||
} else {
|
||||
@@ -145,8 +180,21 @@ proc coretest {inf1_how inf2_how} {
|
||||
# "run" makes each inferior be a process group leader. When we
|
||||
# run both inferiors in GDB's terminal/session, only one can end
|
||||
# up as the terminal's foreground process group, so it's expected
|
||||
# that the other receives a SIGTTOU.
|
||||
set expect_ttou [expr {$inf1_how == "run" && $inf2_how == "run"}]
|
||||
# that the other receives a SIGTTOU. Similarly, if we run one
|
||||
# inferior in GDB's terminal, and another in its own GDB-created
|
||||
# terminal/session, then if we resume the latter first, GDB won't
|
||||
# put the former in the foreground either (since GDB must stay in
|
||||
# the foreground to forward input and catch Ctrl-C).
|
||||
|
||||
# Only the native target knows how to put the inferior in its own
|
||||
# terminal/session.
|
||||
set can_per_inf_session [gdb_is_target_native]
|
||||
|
||||
set expect_ttou [expr {(($inf1_how == "run-share"
|
||||
|| (!$can_per_inf_session
|
||||
&& $inf1_how == "run-session"))
|
||||
&& ($inf2_how == "run-share"
|
||||
|| $inf2_how == "run-session"))}]
|
||||
|
||||
global gdb_prompt
|
||||
|
||||
@@ -179,9 +227,9 @@ proc coretest {inf1_how inf2_how} {
|
||||
uplevel 1 {
|
||||
if {$count1 >= 3 && $count2 >= 3} {
|
||||
if $expect_ttou {
|
||||
fail "$test (expected SIGTTOU)"
|
||||
fail "$gdb_test_name (expected SIGTTOU)"
|
||||
} else {
|
||||
pass $test
|
||||
pass $gdb_test_name
|
||||
}
|
||||
} else {
|
||||
exp_continue
|
||||
@@ -195,8 +243,20 @@ proc coretest {inf1_how inf2_how} {
|
||||
set count1 0
|
||||
set count2 0
|
||||
|
||||
set test "continue"
|
||||
gdb_test_multiple $test $test {
|
||||
# We're going to interrupt with Ctrl-C. For this to work we must
|
||||
# be sure to consume the "Continuing." message first, or GDB may
|
||||
# still own the terminal. Also, note that in the attach case, we
|
||||
# flushed inferior output right after attaching, so that we're
|
||||
# sure that the "pid=" lines we see are emitted by the inferior
|
||||
# after it is continued, instead of having been emitted before it
|
||||
# was attached to.
|
||||
gdb_test_multiple "continue" "continue, hand over terminal" {
|
||||
-re "Continuing" {
|
||||
pass $gdb_test_name
|
||||
}
|
||||
}
|
||||
|
||||
gdb_test_multiple "" "continue" {
|
||||
-i $infs_spawn_ids -re "pid=$pid1, count=" {
|
||||
incr count1
|
||||
pass_or_exp_continue
|
||||
@@ -207,9 +267,9 @@ proc coretest {inf1_how inf2_how} {
|
||||
}
|
||||
-i $gdb_spawn_id -re "received signal SIGTTOU.*$gdb_prompt " {
|
||||
if $expect_ttou {
|
||||
pass "$test (expected SIGTTOU)"
|
||||
pass "$gdb_test_name (expected SIGTTOU)"
|
||||
} else {
|
||||
fail "$test (SIGTTOU)"
|
||||
fail "$gdb_test_name (SIGTTOU)"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -218,7 +278,7 @@ proc coretest {inf1_how inf2_how} {
|
||||
if {$expect_ttou} {
|
||||
gdb_test "" "Quit" "stop with control-c (Quit)"
|
||||
} else {
|
||||
gdb_test "" "received signal SIGINT.*" "stop with control-c (SIGINT)"
|
||||
gdb_test "" "(received signal SIGINT|stopped).*" "stop with control-c (SIGINT)"
|
||||
}
|
||||
|
||||
# Useful for debugging in case the Ctrl-C above fails.
|
||||
@@ -228,11 +288,11 @@ proc coretest {inf1_how inf2_how} {
|
||||
}
|
||||
}
|
||||
|
||||
set how_modes {"run" "attach" "tty"}
|
||||
set how_modes {"run-session" "run-share" "run-tty" "attach"}
|
||||
|
||||
foreach_with_prefix inf1_how $how_modes {
|
||||
foreach_with_prefix inf2_how $how_modes {
|
||||
if {($inf1_how == "tty" || $inf2_how == "tty")
|
||||
if {($inf1_how == "run-tty" || $inf2_how == "run-tty")
|
||||
&& [target_info gdb_protocol] == "extended-remote"} {
|
||||
# No way to specify the inferior's tty in the remote
|
||||
# protocol.
|
||||
|
||||
@@ -49,7 +49,7 @@ with_test_prefix "preparation" {
|
||||
gdb_test "disconnect" ".*"
|
||||
}
|
||||
|
||||
# Connect, continue, send Ctrl-C and expect a SIGINT stop.
|
||||
# Connect, continue, send Ctrl-C and expect a stop.
|
||||
|
||||
proc connect_continue_ctrl_c {} {
|
||||
global gdbserver_protocol gdbserver_gdbport
|
||||
@@ -67,7 +67,7 @@ proc connect_continue_ctrl_c {} {
|
||||
}
|
||||
|
||||
after 1000 {send_gdb "\003"}
|
||||
gdb_test "" "Program received signal SIGINT.*" "stop with control-c"
|
||||
gdb_test "" "Program (received signal SIGINT|stopped).*" "stop with control-c"
|
||||
}
|
||||
|
||||
with_test_prefix "first" {
|
||||
|
||||
@@ -80,7 +80,7 @@ proc test_current_thread {expected_thr} {
|
||||
gdb_test_multiple $test $test {
|
||||
-re "^interrupt\r\n$gdb_prompt " {
|
||||
gdb_test_multiple "" $test {
|
||||
-re "Thread .* received signal SIGINT, Interrupt\\." {
|
||||
-re "Thread .* (received signal SIGINT, Interrupt|stopped)\\." {
|
||||
pass $test
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ for {set i 0} {$i < $attempts} {incr i} {
|
||||
|
||||
set msg "caught interrupt"
|
||||
gdb_test_multiple "" $msg {
|
||||
-re "Thread .* received signal SIGINT.*$gdb_prompt $" {
|
||||
-re "Thread .* (received signal SIGINT|stopped).*$gdb_prompt $" {
|
||||
pass $msg
|
||||
}
|
||||
}
|
||||
|
||||
@@ -292,11 +292,6 @@ main (int argc, char **argv)
|
||||
fprintf (stderr, "The testcase must be run by GDB!\n");
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
if (tracer != getppid ())
|
||||
{
|
||||
fprintf (stderr, "The testcase parent must be our GDB tracer!\n");
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
/* SIGCONT our debugger in the case of our crash as we would deadlock
|
||||
|
||||
@@ -55,7 +55,7 @@ send_gdb "\003"
|
||||
|
||||
set test "caught interrupt"
|
||||
gdb_test_multiple "" $test {
|
||||
-re "Thread .* received signal SIGINT.*$gdb_prompt $" {
|
||||
-re "Thread .* (received signal SIGINT|stopped).*$gdb_prompt $" {
|
||||
pass $test
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ gdb_test_multiple "continue" "first continue" {
|
||||
# we don't lose GDB's output while we do it.
|
||||
remote_expect host 1 { timeout { } }
|
||||
|
||||
# Send a Ctrl-C and wait for the SIGINT.
|
||||
# Send a Ctrl-C and wait for the stop.
|
||||
|
||||
proc interrupt_and_wait { message } {
|
||||
global gdb_prompt
|
||||
@@ -70,7 +70,7 @@ proc interrupt_and_wait { message } {
|
||||
-re "\\\[\[^\]\]* exited\\\]\r\n" {
|
||||
exp_continue
|
||||
}
|
||||
-re " received signal SIGINT.*$gdb_prompt $" {
|
||||
-re " (received signal SIGINT|stopped).*$gdb_prompt $" {
|
||||
pass "$message"
|
||||
}
|
||||
-re "$gdb_prompt $" {
|
||||
|
||||
@@ -215,6 +215,7 @@ proc test_detach {multi_process cmd} {
|
||||
global binfile
|
||||
|
||||
clean_restart ${binfile}
|
||||
gdb_test_no_output "tty /dev/tty"
|
||||
|
||||
if ![runto_main] {
|
||||
fail "can't run to main"
|
||||
@@ -240,6 +241,7 @@ proc test_detach_watch {multi_process cmd} {
|
||||
global binfile decimal
|
||||
|
||||
clean_restart ${binfile}
|
||||
gdb_test_no_output "tty /dev/tty"
|
||||
|
||||
if ![runto_main] {
|
||||
fail "can't run to main"
|
||||
@@ -276,6 +278,7 @@ proc test_detach_killed_outside {multi_process cmd} {
|
||||
global binfile
|
||||
|
||||
clean_restart ${binfile}
|
||||
gdb_test_no_output "tty /dev/tty"
|
||||
|
||||
if ![runto_main] {
|
||||
fail "can't run to main"
|
||||
|
||||
@@ -204,7 +204,7 @@ proc check_control_c {} {
|
||||
send_gdb "\003"
|
||||
set description "Stopped with a ^C"
|
||||
gdb_expect {
|
||||
-re "Thread .* received signal SIGINT.*$gdb_prompt $" {
|
||||
-re "Thread .* (received signal SIGINT|stopped).*$gdb_prompt $" {
|
||||
pass $description
|
||||
}
|
||||
-re "Quit.*$gdb_prompt $" {
|
||||
|
||||
@@ -68,14 +68,9 @@ proc stop_process { description } {
|
||||
# For this to work we must be sure to consume the "Continuing."
|
||||
# message first, or GDB's signal handler may not be in place.
|
||||
after 1000 {send_gdb "\003"}
|
||||
gdb_expect {
|
||||
-re "Thread .* received signal SIGINT.*$gdb_prompt $"
|
||||
{
|
||||
pass $description
|
||||
}
|
||||
timeout
|
||||
{
|
||||
fail "$description (timeout)"
|
||||
gdb_test_multiple "" $description {
|
||||
-re "Thread .* (received signal SIGINT|stopped).*$gdb_prompt $" {
|
||||
pass $description
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -376,11 +376,6 @@ main (int argc, char **argv)
|
||||
fprintf (stderr, "The testcase must be run by GDB!\n");
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
if (tracer != getppid ())
|
||||
{
|
||||
fprintf (stderr, "The testcase parent must be our GDB tracer!\n");
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
/* SIGCONT our debugger in the case of our crash as we would deadlock
|
||||
|
||||
@@ -49,4 +49,4 @@ after 500 {send_gdb "\003"}
|
||||
|
||||
# Make sure we do not get an internal error from hitting Control-C
|
||||
# while many signals are flying back and forth.
|
||||
gdb_test "" "Thread .* received signal SIGINT.*" "stop with control-c"
|
||||
gdb_test "" "Thread .* (received signal SIGINT|stopped).*" "stop with control-c"
|
||||
|
||||
@@ -301,11 +301,6 @@ main (int argc, char **argv)
|
||||
fprintf (stderr, "The testcase must be run by GDB!\n");
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
if (tracer != getppid ())
|
||||
{
|
||||
fprintf (stderr, "The testcase parent must be our GDB tracer!\n");
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
/* SIGCONT our debugger in the case of our crash as we would deadlock
|
||||
|
||||
@@ -2870,6 +2870,25 @@ gdb_caching_proc supports_memtag {
|
||||
return 0
|
||||
}
|
||||
|
||||
# Return true if the target is able to interrupt a program that blocks
|
||||
# SIGINT.
|
||||
proc can_interrupt_blocked_sigint {} {
|
||||
if {[istarget *-*-linux*]} {
|
||||
gdb_test_multiple "maint show target-non-stop" "" {
|
||||
-re "(is|currently) on.*$::gdb_prompt $" {
|
||||
}
|
||||
-re "(is|currently) off.*$::gdb_prompt $" {
|
||||
# GDBserver still tries to interrupt programs with
|
||||
# SIGINT.
|
||||
return 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Assume possible.
|
||||
return 1
|
||||
}
|
||||
|
||||
# Return 1 if the target supports hardware single stepping.
|
||||
|
||||
proc can_hardware_single_step {} {
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#include "gdbsupport/event-loop.h"
|
||||
#include "gdbcmd.h"
|
||||
#include "async-event.h"
|
||||
#include "inferior.h"
|
||||
|
||||
#include "tui/tui.h"
|
||||
#include "tui/tui-io.h"
|
||||
@@ -565,6 +566,8 @@ tui_async_resize_screen (gdb_client_data arg)
|
||||
}
|
||||
tui_redisplay_readline ();
|
||||
}
|
||||
|
||||
child_terminal_on_sigwinch ();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -42,14 +42,9 @@ restore_old_foreground_pgrp (void)
|
||||
/* See nat/fork-inferior.h. */
|
||||
|
||||
void
|
||||
prefork_hook (const char *args)
|
||||
prefork_hook ()
|
||||
{
|
||||
client_state &cs = get_client_state ();
|
||||
if (debug_threads)
|
||||
{
|
||||
debug_printf ("args: %s\n", args);
|
||||
debug_flush ();
|
||||
}
|
||||
|
||||
#ifdef SIGTTOU
|
||||
signal (SIGTTOU, SIG_DFL);
|
||||
@@ -63,6 +58,14 @@ prefork_hook (const char *args)
|
||||
|
||||
/* See nat/fork-inferior.h. */
|
||||
|
||||
bool
|
||||
child_has_managed_tty_hook ()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/* See nat/fork-inferior.h. */
|
||||
|
||||
void
|
||||
postfork_hook (pid_t pid)
|
||||
{
|
||||
|
||||
@@ -56,6 +56,7 @@ libgdbsupport_a_SOURCES = \
|
||||
gdb_wait.cc \
|
||||
gdb_vecs.cc \
|
||||
job-control.cc \
|
||||
managed-tty.cc \
|
||||
netstuff.cc \
|
||||
new-op.cc \
|
||||
pathstuff.cc \
|
||||
|
||||
@@ -153,12 +153,13 @@ am_libgdbsupport_a_OBJECTS = agent.$(OBJEXT) btrace-common.$(OBJEXT) \
|
||||
fileio.$(OBJEXT) filestuff.$(OBJEXT) format.$(OBJEXT) \
|
||||
gdb-dlfcn.$(OBJEXT) gdb_tilde_expand.$(OBJEXT) \
|
||||
gdb_wait.$(OBJEXT) gdb_vecs.$(OBJEXT) job-control.$(OBJEXT) \
|
||||
netstuff.$(OBJEXT) new-op.$(OBJEXT) pathstuff.$(OBJEXT) \
|
||||
print-utils.$(OBJEXT) ptid.$(OBJEXT) rsp-low.$(OBJEXT) \
|
||||
run-time-clock.$(OBJEXT) safe-strerror.$(OBJEXT) \
|
||||
scoped_mmap.$(OBJEXT) search.$(OBJEXT) signals.$(OBJEXT) \
|
||||
signals-state-save-restore.$(OBJEXT) tdesc.$(OBJEXT) \
|
||||
thread-pool.$(OBJEXT) xml-utils.$(OBJEXT) $(am__objects_1)
|
||||
managed-tty.$(OBJEXT) netstuff.$(OBJEXT) new-op.$(OBJEXT) \
|
||||
pathstuff.$(OBJEXT) print-utils.$(OBJEXT) ptid.$(OBJEXT) \
|
||||
rsp-low.$(OBJEXT) run-time-clock.$(OBJEXT) \
|
||||
safe-strerror.$(OBJEXT) scoped_mmap.$(OBJEXT) search.$(OBJEXT) \
|
||||
signals.$(OBJEXT) signals-state-save-restore.$(OBJEXT) \
|
||||
tdesc.$(OBJEXT) thread-pool.$(OBJEXT) xml-utils.$(OBJEXT) \
|
||||
$(am__objects_1)
|
||||
libgdbsupport_a_OBJECTS = $(am_libgdbsupport_a_OBJECTS)
|
||||
AM_V_P = $(am__v_P_@AM_V@)
|
||||
am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
|
||||
@@ -379,6 +380,7 @@ libgdbsupport_a_SOURCES = \
|
||||
gdb_wait.cc \
|
||||
gdb_vecs.cc \
|
||||
job-control.cc \
|
||||
managed-tty.cc \
|
||||
netstuff.cc \
|
||||
new-op.cc \
|
||||
pathstuff.cc \
|
||||
@@ -484,6 +486,7 @@ distclean-compile:
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gdb_vecs.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gdb_wait.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/job-control.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/managed-tty.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/netstuff.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/new-op.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pathstuff.Po@am__quote@
|
||||
|
||||
22
gdbsupport/managed-tty.cc
Normal file
22
gdbsupport/managed-tty.cc
Normal file
@@ -0,0 +1,22 @@
|
||||
/* Support for GDB-managed terminals.
|
||||
Copyright (C) 2021 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GDB.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "gdbsupport/common-defs.h"
|
||||
#include "gdbsupport/managed-tty.h"
|
||||
|
||||
bool debug_managed_tty = false;
|
||||
42
gdbsupport/managed-tty.h
Normal file
42
gdbsupport/managed-tty.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/* Support for GDB-managed terminals.
|
||||
|
||||
Copyright (C) 2021 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GDB.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef MANAGED_TTY_H
|
||||
#define MANAGED_TTY_H
|
||||
|
||||
/* Using host-dependent code here is fine, because the support for
|
||||
creating terminals for inferiors is meant to be used with
|
||||
child/native targets. Note we disable the feature on no-MMU
|
||||
architectures for now because those can't use fork, see
|
||||
fork-inferior.c. */
|
||||
#if (defined (__linux__) \
|
||||
&& !(defined (__UCLIBC__) && defined (HAS_NOMMU)))
|
||||
# define GDB_MANAGED_TERMINALS 1
|
||||
#else
|
||||
# define GDB_MANAGED_TERMINALS 0
|
||||
#endif
|
||||
|
||||
extern bool debug_managed_tty;
|
||||
|
||||
/* Print a "managed-tty" debug statement. */
|
||||
|
||||
#define managed_tty_debug_printf(fmt, ...) \
|
||||
debug_prefixed_printf_cond (debug_managed_tty, "managed-tty",fmt, ##__VA_ARGS__)
|
||||
|
||||
#endif /* MANAGED_TTY_H */
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Low level interface to ptrace, for GDB when running under Unix.
|
||||
/* Support for signoring SIGTTOU.
|
||||
|
||||
Copyright (C) 2003-2021 Free Software Foundation, Inc.
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef INFLOW_H
|
||||
#define INFLOW_H
|
||||
#ifndef SCOPED_IGNORE_SIGTTOU_H
|
||||
#define SCOPED_IGNORE_SIGTTOU_H
|
||||
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
@@ -53,4 +53,4 @@ private:
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif /* inflow.h */
|
||||
#endif /* SCOPED_IGNORE_SIGTTOU_H */
|
||||
Reference in New Issue
Block a user