Compare commits

...

16 Commits

Author SHA1 Message Date
Pedro Alves
4e18fe9cb3 Document pseudo-terminal and interrupting changes
This documents changes done in previous patches:

 - the fact that on GNU/Linux, GDB creates a pseudo-terminal for the
   inferior instead of juggling terminal settings.

 - That when the inferior and GDB share the terminal, you can't
   interrupt some programs with Ctrl-C.

 - That on GNU/Linux, you may get "Program stopped." instead of
   "Program received SIGINT" in response to Ctrl-C.

 - That run+detach may result in the program dying with SIGHUP.

I was surprised that we do not currently have a node/section
specifically to talk about interrupting programs.  Thus I've added a
new "Interrupting" section under the "Stopping and Continuing"
chapter, with some xrefs to other sections.

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <pedro@palves.net>

	* NEWS: Document pseudo-terminal, "tty /dev/tty" and Ctrl-C/SIGINT
	changes.  Document "set/show debug managed-tty".

gdb/doc/ChangeLog:
yyyy-mm-dd  Pedro Alves  <pedro@palves.net>

	* gdb.texinfo (Input/Output): Document that GDB may start a
	program associated with a pseudo-terminal.  Document "tty
	/dev/tty".  Document "set/show debug managed-tty".
	(Attach): Document what happens on run+detach on systems where GDB
	creates a pseudo-terminal for the inferior.
	(Stopping and Continuing): Add new Interrupting node.
	(Background Execution): Add anchor.
	(Features for Debugging MS Windows PE Executables): Add anchor.

Change-Id: I267a0f9300c7ac4d2e7f14a9ba8eabc1eafcc5a7
2021-06-14 22:20:45 +01:00
Pedro Alves
814fb49ba3 GNU/Linux: Interrupt/Ctrl-C with SIGSTOP instead of SIGINT [PR gdb/9425, PR gdb/14559]
After the "Always put inferiors in their own terminal/session
[gdb/9425, gdb/14559]" change, when a user types "Ctrl-C" while the
inferior is running, GDB is the one who gets the SIGINT, not the
inferior process.  GDB then forwards the SIGINT to the inferior with
target_pass_ctrlc.

That was necessary but not not sufficient to fix PRs gdb/9425,
gdb/14559, because if a program blocks SIGINT with e.g. sigprocmask,
then if GDB sends it a SIGINT, the signal isn't ever delivered to the
process, so ptrace does not intercept it.  You type Ctrl-C, but
nothing happens.  Similarly, if a program uses sigwait to wait for
SIGINT, and the program receives a SIGINT, the SIGINT is _not_
intercepted by ptrace, it goes straight to the inferior.

Now that the Ctrl-C results in a SIGINT sent to GDB instead of the
inferior, we can make GDB interrupt the program any other way we like.
This patch makes non-stop-capable ports interrupt the program with
stop_all_threads / target_stop (i.e., SIGSTOP) instead of
target_pass_ctrlc (i.e., SIGINT), which always works -- SIGSTOP can't
be blocked/ignored.  (In the future GDB may even switch to
PTRACE_INTERRUPT on Linux, though that's a project of its own.)

Another advantage here is with multi-target -- currently, because GDB
relies on Ctrl-C stopping one thread, and then stopping all other
threads in reaction to that stop, target_pass_ctrlc tries to find one
inferior with a thread that is running, in any target.  If the
selected target for some reason fails to process the Ctrl-C request,
then the Ctrl-C ends up lost.  The mechanism implemented in this patch
is different -- we never have to pick a thread, inferior or target --
we're going to stop everything, so we end up in stop_all_threads.

For non-stop, the patch preserves the current behavior of only
stopping one thread in reaction to Ctrl-C, so it can still happen that
the thread that GDB selects to stop disappears and the Ctrl-C ends up
being lost.  However, because now GDB always sees the SIGINT first, we
can change how Ctrl-C behaves there too.  We could even make it
configurable -- for example, it could be reasonable that Ctrl-C simply
drops the CLI back to the prompt, without stopping anything at all.
That might be interesting for "set observer-mode on", at least.

This commit has a user-visible behavior change in all-stop mode --
when you interrupt the program with Ctrl-C, instead of:

  Thread 1 "threads" received signal SIGINT, Interrupt.

You'll get:

  Thread 1 "threads" stopped.

Which is what you already got with the "interrupt" command in non-stop
mode.

If you really want to pass a SIGINT to the program, you can then issue:

  (gdb) signal SIGINT

This commit also adjusts the testsuite to cope with that output
alternative.

With this change, the gdb.base/sigint-sigwait.exp and
gdb.base/sigint-masked-out.exp testcases now pass cleanly on
GNU/Linux, on both native debugging and gdbserver + "maint set
target-non-stop on", so the kfails are adjusted accordingly.

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <pedro@palves.net>

	PR gdb/9425
	PR gdb/14559
	* event-top.c (default_quit_handler): Mark infrun event-loop
	handler with mark_infrun_async_event_handler_ctrl_c instead of
	passing the Ctrl-C to the target directly with target_pass_ctrlc.
	* infcmd.c (interrupt_target_1): On non-stop targets, Mark infrun
	event-loop handler with
	mark_infrun_async_event_handler_interrupt_all instead of using
	target_interrupt.
	* infrun.c (interrupt_all_requested, switch_to_stop_thread)
	(sync_interrupt_all)
	(mark_infrun_async_event_handler_interrupt_all)
	(mark_infrun_async_event_handler_ctrl_c): New.
	(infrun_async_inferior_event_handler): Handle Ctrl-C/interrupt
	requests.
	* infrun.h (mark_infrun_async_event_handler_interrupt_all)
	(mark_infrun_async_event_handler_ctrl_c): Declare.
	* linux-nat.c (wait_for_signal): Don't handle Ctrl-C here.
	(linux_nat_wait_1): Handle it here, by marking the infrun event
	handler, and returning TARGET_WAITKIND_IGNORE with the quit flag
	still set.
	* target.c (maybe_pass_ctrlc): New.
	(target_terminal::inferior, target_terminal::restore_inferior):
	Use it.
	(target_pass_ctrlc): Ass there's no non-stop target pushed.

gdb/testsuite/ChangeLog:
yyyy-mm-dd  Pedro Alves  <pedro@palves.net>

	PR gdb/9425
	PR gdb/14559
	* gdb.base/bp-cmds-continue-ctrl-c.exp: Expect "stopped" in
	alternative to "signal SIGINT".
	* gdb.base/interrupt-daemon-attach.exp: Likewise.
	* gdb.base/interrupt-daemon.exp: Likewise.
	* gdb.base/interrupt-noterm.exp: Likewise.
	* gdb.base/interrupt.exp: Likewise.
	* gdb.base/random-signal.exp: Likewise.
	* gdb.base/range-stepping.exp: Likewise.
	* gdb.gdb/selftest.exp: Likewise.
	* gdb.mi/new-ui-mi-sync.exp: Likewise.
	* gdb.multi/multi-target-interrupt.exp: Likewise.
	* gdb.multi/multi-target-no-resumed.exp: Likewise.
	* gdb.multi/multi-term-settings.exp: Likewise.
	* gdb.server/reconnect-ctrl-c.exp: Likewise.
	* gdb.threads/async.exp: Likewise.
	* gdb.threads/continue-pending-status.exp: Likewise.
	* gdb.threads/leader-exit.exp: Likewise.
	* gdb.threads/manythreads.exp: Likewise.
	* gdb.threads/pthreads.exp: Likewise.
	* gdb.threads/schedlock.exp: Likewise.
	* gdb.threads/sigthread.exp: Likewise.

	* lib/gdb.exp (can_interrupt_blocked_sigint): New.
	* gdb.base/sigint-masked-out.exp (test_ctrl_c)
	(test_interrupt_cmd): Use can_interrupt_blocked_sigint, and don't
	kfail if true.
	* gdb.base/sigint-sigwait.exp (test_ctrl_c, test_interrupt_cmd):
	Likewise.

Change-Id: I83c1b6a20deea1f1909156adde1d60b8f6f2629b
2021-06-14 22:20:45 +01:00
Pedro Alves
86a5798736 convert previous_inferior_ptid to strong reference to thread_info
While working on a patch, I spotted a regression in the
gdb.multi/multi-target-no-resumed.exp.exp testcase.  Debugging the
issue, I realized that the problem was related to how I was using
previous_inferior_ptid to look up the thread the user had last
selected.  The problem is that previous_inferior_ptid alone doesn't
tell you which target that ptid is from, and I was just always using
the current target, which was incorrect.  Two different targets may
have threads with the same ptid.

I decided to fix this by replacing previous_inferior_ptid with a
strong reference to the thread, called previous_thread.

A new update_previous_thread function is added can be used to updated
previous_thread from inferior_ptid.

This must be called in several places that really want to get rid of
previous_thread thread, and reset the thread id counter, otherwise we
get regressions like these:

  (gdb) info threads -gid
    Id   GId  Target Id                               Frame
 - * 1    1    Thread 2974541.2974541 "tids-gid-reset" main () at src/gdb/testsuite/gdb.multi/tids-gid-reset.c:21
 - (gdb) PASS: gdb.multi/tids-gid-reset.exp: single-inferior: after restart: info threads -gid
 + * 1    2    Thread 2958361.2958361 "tids-gid-reset" main () at src/gdb/testsuite/gdb.multi/tids-gid-reset.c:21
 + (gdb) FAIL: gdb.multi/tids-gid-reset.exp: single-inferior: after restart: info threads -gid

and:

  Core was generated by `build/gdb/testsuite/outputs/gdb.reverse/sigall-precsave/si'.
  Program terminated with signal SIGTRAP, Trace/breakpoint trap.
  #0  gen_ABRT () at src/gdb/testsuite/gdb.reverse/sigall-reverse.c:398
  398      kill (getpid (), SIGABRT);
 +[Current thread is 1 (LWP 2662066)]
  Restored records from core file build/gdb/testsuite/outputs/gdb.reverse/sigall-precsave/sigall.precsave.
  #0  gen_ABRT () at src/gdb/testsuite/gdb.reverse/sigall-reverse.c:398
  398      kill (getpid (), SIGABRT);

  continue
  Continuing.

 -Program received signal SIGABRT, Aborted.
 +Thread 1 received signal SIGABRT, Aborted.
  0x00007ffff7dfd55b in kill () at ../sysdeps/unix/syscall-template.S:78
  78     ../sysdeps/unix/syscall-template.S: No such file or directory.
 -(gdb) PASS: gdb.reverse/sigall-precsave.exp: sig-test-1: get signal ABRT
 +(gdb) FAIL: gdb.reverse/sigall-precsave.exp: sig-test-1: get signal ABRT

I.e., GDB was failing to restart the thread counter back to 1, because
the previous_thread thread was being help due to the strong reference.

Tested on GNU/Linux native, gdbserver and gdbserver + "maint set
target-non-stop on".

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <pedro@palves.net>

	* infcmd.c (kill_command, detach_command, disconnect_command):
	Call update_previous_thread.
	* infrun.c (previous_inferior_ptid): Delete.
	(previous_thread): New.
	(update_previous_thread): New.
	(proceed, init_wait_for_inferior): Call update_previous_thread.
	(normal_stop): Adjust to compare previous_thread and
	inferior_thread.  Call update_previous_thread.
	* infrun.h (update_previous_thread): Declare.
	* target.c (target_pre_inferior, target_preopen): Call
	update_previous_thread.

Change-Id: I4f06dd81f00848bb7d6d26a94ff091e2a4e646d9
2021-06-14 22:20:45 +01:00
Pedro Alves
de89924a63 exists_non_stop_target: Avoid flushing frames
A following patch adds an exists_non_stop_target call in the
target_terminal routines, and that surprisingly caused a weird
regression / GDB crash:

 $ make check RUNTESTFLAGS="--target_board=native-extended-gdbserver" TESTS="gdb.base/signest.exp"
 ...
 configuring for gdbserver local testing (extended-remote)
 Using src/gdb/testsuite/config/extended-gdbserver.exp as tool-and-target-specific interface file.
 Running src/gdb/testsuite/gdb.base/signest.exp ...
 ERROR: GDB process no longer exists

Debugging the core, we see infinite recursion:

 (top-gdb) bt 20
 #0  0x0000561d6a1bfeff in frame_unwind_arch (next_frame=0x561d6b19f9c0) at src/gdb/frame.c:2950
 #1  0x0000561d6a1bfeb8 in get_frame_arch (this_frame=0x561d6b19f9c0) at src/gdb/frame.c:2939
 #2  0x0000561d6a1b989f in frame_unwind_find_by_frame (this_frame=0x561d6b19f9c0, this_cache=0x561d6b19f9d8) at src/gdb/frame-unwind.c:174
 #3  0x0000561d6a1bff04 in frame_unwind_arch (next_frame=0x561d6b19f9c0) at src/gdb/frame.c:2950
 #4  0x0000561d6a1bfeb8 in get_frame_arch (this_frame=0x561d6b19f9c0) at src/gdb/frame.c:2939
 #5  0x0000561d6a1b989f in frame_unwind_find_by_frame (this_frame=0x561d6b19f9c0, this_cache=0x561d6b19f9d8) at src/gdb/frame-unwind.c:174
 #6  0x0000561d6a1bff04 in frame_unwind_arch (next_frame=0x561d6b19f9c0) at src/gdb/frame.c:2950
 #7  0x0000561d6a1bfeb8 in get_frame_arch (this_frame=0x561d6b19f9c0) at src/gdb/frame.c:2939
 #8  0x0000561d6a1b989f in frame_unwind_find_by_frame (this_frame=0x561d6b19f9c0, this_cache=0x561d6b19f9d8) at src/gdb/frame-unwind.c:174
 #9  0x0000561d6a1bff04 in frame_unwind_arch (next_frame=0x561d6b19f9c0) at src/gdb/frame.c:2950
 #10 0x0000561d6a1bfeb8 in get_frame_arch (this_frame=0x561d6b19f9c0) at src/gdb/frame.c:2939
 #11 0x0000561d6a1b989f in frame_unwind_find_by_frame (this_frame=0x561d6b19f9c0, this_cache=0x561d6b19f9d8) at src/gdb/frame-unwind.c:174
 #12 0x0000561d6a1bff04 in frame_unwind_arch (next_frame=0x561d6b19f9c0) at src/gdb/frame.c:2950
 #13 0x0000561d6a1bfeb8 in get_frame_arch (this_frame=0x561d6b19f9c0) at src/gdb/frame.c:2939
 #14 0x0000561d6a1b989f in frame_unwind_find_by_frame (this_frame=0x561d6b19f9c0, this_cache=0x561d6b19f9d8) at src/gdb/frame-unwind.c:174
 #15 0x0000561d6a1bff04 in frame_unwind_arch (next_frame=0x561d6b19f9c0) at src/gdb/frame.c:2950
 #16 0x0000561d6a1bfeb8 in get_frame_arch (this_frame=0x561d6b19f9c0) at src/gdb/frame.c:2939
 #17 0x0000561d6a1b989f in frame_unwind_find_by_frame (this_frame=0x561d6b19f9c0, this_cache=0x561d6b19f9d8) at src/gdb/frame-unwind.c:174
 #18 0x0000561d6a1bff04 in frame_unwind_arch (next_frame=0x561d6b19f9c0) at src/gdb/frame.c:2950
 #19 0x0000561d6a1bfeb8 in get_frame_arch (this_frame=0x561d6b19f9c0) at src/gdb/frame.c:2939
 (More stack frames follow...)

 (top-gdb) bt -30
 #157054 0x0000561d6a1bfeb8 in get_frame_arch (this_frame=0x561d6b19f9c0) at src/gdb/frame.c:2939
 #157055 0x0000561d6a1b989f in frame_unwind_find_by_frame (this_frame=0x561d6b19f9c0, this_cache=0x561d6b19f9d8) at src/gdb/frame-unwind.c:174
 #157056 0x0000561d6a1bff04 in frame_unwind_arch (next_frame=0x561d6b19f9c0) at src/gdb/frame.c:2950
 #157057 0x0000561d6a1bfeb8 in get_frame_arch (this_frame=0x561d6b19f9c0) at src/gdb/frame.c:2939
 #157058 0x0000561d6a1b989f in frame_unwind_find_by_frame (this_frame=0x561d6b19f9c0, this_cache=0x561d6b19f9d8) at src/gdb/frame-unwind.c:174
 #157059 0x0000561d6a1bff04 in frame_unwind_arch (next_frame=0x561d6b19f9c0) at src/gdb/frame.c:2950
 #157060 0x0000561d6a1bbc65 in frame_unwind_pc (this_frame=0x561d6b19f9c0) at src/gdb/frame.c:970
 #157061 0x0000561d6a1bf54c in get_frame_pc (frame=0x561d6b19fa90) at src/gdb/frame.c:2625
 #157062 0x0000561d6a1bf63e in get_frame_address_in_block (this_frame=0x561d6b19fa90) at src/gdb/frame.c:2655
 #157063 0x0000561d6a0cae7f in dwarf2_frame_cache (this_frame=0x561d6b19fa90, this_cache=0x561d6b19faa8) at src/gdb/dwarf2/frame.c:1010
 #157064 0x0000561d6a0cb928 in dwarf2_frame_this_id (this_frame=0x561d6b19fa90, this_cache=0x561d6b19faa8, this_id=0x561d6b19faf0) at src/gdb/dwarf2/frame.c:1227
 #157065 0x0000561d6a1baf72 in compute_frame_id (fi=0x561d6b19fa90) at src/gdb/frame.c:588
 #157066 0x0000561d6a1bb16e in get_frame_id (fi=0x561d6b19fa90) at src/gdb/frame.c:636
 #157067 0x0000561d6a1bb224 in get_stack_frame_id (next_frame=0x561d6b19fa90) at src/gdb/frame.c:650
 #157068 0x0000561d6a26ecd0 in insert_hp_step_resume_breakpoint_at_frame (return_frame=0x561d6b19fa90) at src/gdb/infrun.c:7809
 #157069 0x0000561d6a26b88a in handle_signal_stop (ecs=0x7ffc67022830) at src/gdb/infrun.c:6428
 #157070 0x0000561d6a269d81 in handle_inferior_event (ecs=0x7ffc67022830) at src/gdb/infrun.c:5741
 #157071 0x0000561d6a265bd0 in fetch_inferior_event () at src/gdb/infrun.c:4120
 #157072 0x0000561d6a244c24 in inferior_event_handler (event_type=INF_REG_EVENT) at src/gdb/inf-loop.c:41
 #157073 0x0000561d6a435cc4 in remote_async_serial_handler (scb=0x561d6b4a8990, context=0x561d6b4a4c48) at src/gdb/remote.c:14403
 #157074 0x0000561d6a460bc5 in run_async_handler_and_reschedule (scb=0x561d6b4a8990) at src/gdb/ser-base.c:138
 #157075 0x0000561d6a460cae in fd_event (error=0, context=0x561d6b4a8990) at src/gdb/ser-base.c:189
 #157076 0x0000561d6a76a191 in handle_file_event (file_ptr=0x561d6b233ae0, ready_mask=1) at src/gdbsupport/event-loop.cc:575
 #157077 0x0000561d6a76a743 in gdb_wait_for_event (block=1) at src/gdbsupport/event-loop.cc:701
 #157078 0x0000561d6a7694ee in gdb_do_one_event () at src/gdbsupport/event-loop.cc:237
 #157079 0x0000561d6a2df16b in start_event_loop () at src/gdb/main.c:421
 #157080 0x0000561d6a2df2b6 in captured_command_loop () at src/gdb/main.c:481
 #157081 0x0000561d6a2e0d16 in captured_main (data=0x7ffc67022bd0) at src/gdb/main.c:1353
 #157082 0x0000561d6a2e0da8 in gdb_main (args=0x7ffc67022bd0) at src/gdb/main.c:1370
 #157083 0x0000561d69eb3d82 in main (argc=13, argv=0x7ffc67022cf8, envp=0x7ffc67022d68) at src/gdb/gdb.c:33

This was caused by exists_non_stop_target flushing the frame cache via
scoped_restore_current_thread/switch_to_thread, while we're in the
middle of unwinding.

Fix this by making exists_non_stop_target only switch the inferior,
like done in target_pass_ctrlc.

The annota1.exp change is necessary because we'd get a regression
otherwise:

  @@ -238,8 +238,6 @@ Continuing.

   \032\032breakpoints-invalid

  -\032\032frames-invalid
  -
   \032\032breakpoint 3

   Breakpoint 3,
  @@ -276,7 +274,7 @@ printf.c
   \032\032pre-prompt
   (gdb)
   \032\032prompt
  -PASS: gdb.base/annota1.exp: continue to printf
  +FAIL: gdb.base/annota1.exp: continue to printf

... because this patch avoids flushing the frame cache that lead to
that missing frames-invalid.  We still need to match frames-invalid
because against gdbserver + "maint set target non-stop on", some other
code path flushes the frame cache resulting in the annotation being
emitted anyway.

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <pedro@palves.net>

	* target.c (exists_non_stop_target): Use
	scoped_restore_current_inferior and set_current_inferior instead
	of scoped_restore_current_thread / switch_to_inferior_no_thread.

Change-Id: I8402483ee755e64e54d8b7c4a67c177557f569bd
2021-06-14 22:20:45 +01:00
Pedro Alves
d9bda17252 Always put inferiors in their own terminal/session [gdb/9425, gdb/14559]
Currently, on GNU/Linux, it is not possible to interrupt with Ctrl-C
programs that block or ignore SIGINT, with e.g., sigprocmask or
signal(SIGINT, SIG_IGN).  You type Ctrl-C, but nothing happens.
Similarly, if a program uses sigwait to wait for SIGINT, and the
program receives a SIGINT, the SIGINT is _not_ intercepted by ptrace,
it goes straight to the inferior.  These problems have been known for
years, and recorded in gdb/9425, gdb/14559.

This is a consequence of how GDB implements interrupting programs with
Ctrl-C -- when GDB spawns a new process, it makes the process use the
same terminal as GDB, and then makes the process's process group be
the foreground process group in GDB's terminal.  This means that when
the process is running in the foreground, after e.g. "continue", when
the user types Ctrl-C, the kernel sends a SIGINT to the foreground
process group, which is the inferior.  GDB then intercepts the SIGINT
via ptrace, the same way it intercepts any other signal, stops all the
other threads of the inferior if in all-stop, and presents the
"Program received SIGINT" stop to the user.

This patch paves the way to address gdb/9425, gdb/14559, by turning
Ctrl-C handling around such that the SIGINT always reaches GDB first,
not the inferior.  That is done by making GDB put inferiors in their
own terminal/session created by GDB.  I.e., GDB creates a
pseudo-terminal master/slave pair, makes the inferior run with the
slave as its terminal, and pumps output/input on the master end.
Because the inferior is run with its own session/terminal, GDB is free
to remain as the foreground process in its own terminal, which means
that the Ctrl-C SIGINT always reaches GDB first, instead of reaching
the inferior first, and then GDB reacting to the ptrace-intercepted
SIGINT.  Because GDB gets the SIGINT first, GDB is then free to
handle it by interrupting the program any way it sees fit.  A
following patch will then make GDB interrupt the program with SIGSTOP
instead of SIGINT, which always works even if the inferior
blocks/ignores SIGINT -- SIGSTOP can't be ignored.  (In the future GDB
may even switch to PTRACE_INTERRUPT, though that's a project of its
own.)

Having the inferior in its own terminal also means that GDB is in
control of when inferior output is flushed to the screen.  When
debugging with the CLI, this means that inferior output is now never
interpersed with GDB's output in an unreadable fashion.  This will
also allow fixing the problem of inferior output really messing up the
screen in the TUI, forcing users to Ctrl-L to refresh the screen.
This patch does not address the TUI part, but it shouldn't be too hard
-- I wrote a quick&dirty prototype patch doing that a few years back,
so I know it works.

Implementation wise, here's what is happening:

 - when GDB spawns an inferior, unless the user asked otherwise with
   "tty /dev/tty", GDB creates a pty pair, and makes the slave end the
   inferior's terminal.  Note that starting an inferior on a given
   terminal already exists, given the "tty" command.  GDB records the
   master and slave ends of the pty.

 - GDB registers that new terminal's master end on the event loop.
   When the master is written to, it means the inferior has written
   some output on its terminal.  The event loop wakes up and GDB
   flushes the inferior output to its own terminal / to the screen.

 - When target_terminal state is switched to "inferior", with
   target_tarminal::inferiors(), GDB registers the stdin file
   descriptor on the event loop with a callback that forwards input
   typed on GDB's terminal to the inferior's tty.

 - Similarly, when GDB receives a SIGWINCH signal, meaning GDB's
   terminal was resized, GDB resizes the inferior's terminal too.

 - GDB puts the inferior in its own session, and there's a "session
   leader" process between GDB and the inferior.  The latter is
   because session leaders have special properties, one of which is,
   if they exit, all progresses in the foreground process group in the
   session get a SIGHUP.  If the spawned inferior was the session
   leader itself, if you were debugging an inferior that forks and
   follow to the child, if the parent (the session leader) exits, then
   the child would get a SIGHUP.  Forking twice when launching an
   inferior, and making the first child be the session leader, and the
   second child the inferior avoids that problem.

 - When the inferior exits or is killed, GDB sends a SIGHUP to the
   session leader, waits for the leader to exit and then destroys the
   terminal.  The session leader's SIGHUP handler makes the session
   leader pgrp be the foreground process group and then exits.  This
   sequence is important comparing to just closing the terminal and
   letting the session leader terminate due to the SIGHUP the kernel
   sends, because when the session leader exits, all processes in the
   foreground process group get a SIGHUP, meaning that if the detached
   process was still in the foreground, it would get a SIGHUP, and
   likely die.

 - The gdb.multi/multi-term-settings.exp was adjusted to test for
   shared and not-shared terminal/session.  Without the change, we get
   failures:

    FAIL: gdb.multi/multi-term-settings.exp: inf1_how=run: inf2_how=run: continue (expected SIGTTOU)
    FAIL: gdb.multi/multi-term-settings.exp: inf1_how=run: inf2_how=run: stop with control-c (Quit)

Tested on GNU/Linux native, gdbserver and gdbserver + "maint target
set-non-stop on".  Also build-tested tested on mingw32-w64, Solaris
11, and OpenBSD.

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <pedro@palves.net>

	PR gdb/9425
	PR gdb/14559
	* fork-child.c (child_has_managed_tty_hook): New.
	* inf-ptrace.c (inf_ptrace_me): If we created a managed tty, raise
	SIGSTOP.
	(inf_ptrace_handle_session_leader_fork): New.
	(inf_ptrace_target::create_inferior): Pass it down as
	handle_session_leader_fork callback.
	* inf-ptrace.h (inf_ptrace_target) <handle_session_leader_fork>:
	New virtual method.
	* inferior.h (child_terminal_on_sigwinch): Declare.
	* inflow.c: Include "gdbsupport/event-loop.h",
	"gdbsupport/refcounted-object.h", "gdbsupport/gdb_wait.h",
	"gdbsupport/managed-tty.h".
	(USES_FORK_CHILD): Define, and wrap fork-child.c-related code with
	it.
	(struct run_terminal_info): New.
	(struct terminal_info) <run_terminal>: Now a run_terminal_info.
	<process_group>: Default to -1.
	<save_from_tty>: New method.
	(sigint_ours): Update comments.
	(inferior_thisrun_terminal_pty_fd): New.
	(input_fd_redirected): New.
	(sharing_input_terminal): Adjust.
	(gdb_tcgetattr, gdb_tcsetattr, make_raw, class scoped_raw_termios)
	(child_terminal_flush_from_to, child_terminal_flush_stdout)
	(inferior_stdout_event_handler, inferior_stdin_event_handler): New.
	(child_terminal_inferior): Handle inferiors with gdb-managed ttys.
	(child_terminal_save_inferior): Handle inferiors with gdb-managed
	ttys.  Use save_from_tty.
	(child_terminal_ours_1): Handle inferiors with gdb-managed ttys.
	(terminal_info::~terminal_info): Use delete instead of xfree.
	(child_terminal_on_sigwinc): New.
	(inflow_inferior_exit): Release terminal created by GDB.
	(copy_terminal_info): Assert there's no run_terminal yet in TO
	yet.  Incref run_terminal after copying.
	(child_terminal_info): Handle inferiors with gdb-managed ttys.
	(new_tty_prefork): Allocate pseudo-terminal.
	(created_managed_tty): New.
	(new_tty): Remove __GO32__ and _WIN32 #ifdefs, not needed given
	USES_FORK_CHILD.
	(new_tty_postfork): Handle inferiors with gdb-managed ttys.
	(show_debug_managed_tty): New.
	(_initialize_inflow): Register "set/show debug managed-tty".
	* linux-nat.c (waitpid_sigstop, waitpid_fork)
	(linux_nat_target::handle_session_leader_fork): New.
	* linux-nat.h (linux_nat_target) <handle_session_leader_fork>:
	Declare override.
	* nat/fork-inferior.c: Include
	"gdbsupport/scoped_ignore_sigttou.h", "gdbsupport/managed-tty.h",
	<sys/types.h> and <sys/wait.h>.
	(session_leader_hup): New.
	(fork_inferior): Add handle_session_leader_fork parameter.  If the
	inferior has a gdb-managed tty, don't use vfork, and fork twice,
	with the first fork becoming the session leader.  Call
	handle_session_leader_fork.
	* nat/fork-inferior.h (fork_inferior): Add
	handle_session_leader_fork parameter and update comment.
	(child_has_managed_tty_hook): Declare.
	* terminal.h (created_managed_tty, child_gdb_owns_session):
	Declare.
	* tui/tui-win.c: Include "inferior.h".
	(tui_async_resize_screen): Call child_terminal_on_sigwinch.

gdbsupport/ChangeLog:
yyyy-mm-dd  Pedro Alves  <pedro@palves.net>

	PR gdb/9425
	PR gdb/14559
	* Makefile.am (libgdbsupport_a_SOURCES): Add managed-tty.cc.
	* Makefile.in: Regenerate.
	* managed-tty.cc: New.
	* managed-tty.h: New.

gdbserver/ChangeLog:
yyyy-mm-dd  Pedro Alves  <pedro@palves.net>

	PR gdb/9425
	PR gdb/14559
	* fork-child.cc (child_has_managed_tty_hook): New.

gdb/testsuite/ChangeLog:
yyyy-mm-dd  Pedro Alves  <pedro@palves.net>

	PR gdb/9425
	PR gdb/14559
	* gdb.multi/multi-term-settings.exp (create_inferior): Document
	"run-session", "run-share" and "run-tty" instead of "run" and
	"tty".  Adjust to handle "run-session" vs "run-share".
	(coretest): Adjust to handle "run-session" vs "run-share".
	(how_modes): Use "run-session", "run-share" and "run-tty" instead
	of "run" and "tty".

Change-Id: I2569e189294044891e68a66401b381e4b999b19c
2021-06-14 22:20:25 +01:00
Pedro Alves
9f84272c29 Move scoped_ignore_sigttou to gdbsupport/
A following patch will want to use scoped_ignore_sigttou in code
shared between GDB and GDBserver.  Move it under gdbsupport/.

Note that despite what inflow.h/inflow.c's first line says, inflow.c
is no longer about ptrace, it is about terminal management.  Some
other files were unnecessarily including inflow.h, I guess a leftover
from the days when inflow.c really was about ptrace.  Those inclusions
are simply dropped.

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <pedro@palves.net>

	* Makefile.in (HFILES_NO_SRCDIR): Remove inflow.h.
	* inf-ptrace.c, inflow.c, procfs.c: Don't include "inflow.h".
	* inflow.h: Delete, moved to gdbsupport/ under a different name.
	* ser-unix.c: Don't include "inflow.h".  Include
	"gdbsupport/scoped_ignore_sigttou.h".

gdbsupport/ChangeLog:
yyyy-mm-dd  Pedro Alves  <pedro@palves.net>

	* scoped_ignore_sigttou.h: New file, moved from gdb/ and renamed.

Change-Id: Ie390abf42c3a78bec6d282ad2a63edd3e623559a
2021-06-14 21:53:54 +01:00
Pedro Alves
5cd9932f70 target_terminal::ours_for_output before printing signal received
A following patch will make GDB put spawned inferiors in their own
terminal/session (on GNU/Linux).  In that case, GDB is in control of
when is the inferior's output flushed to the screen.  A sync point is
when target_terminal state goes from inferior -> ours/ours_for_output.

The gdb.multi/multi-term-settings.exp testcase exposed a bug where an
inferior output flush is missing, resulting in this regression:

Good:

 (gdb) PASS: gdb.multi/multi-term-settings.exp: inf1_how=run-share: inf2_how=run-session: info inferiors
 continue
 Continuing.
 pid=1275538, count=0
 pid=1276069, count=0

 Thread 1.1 "multi-term-sett" received signal SIGTTOU, Stopped (tty output).
 [Switching to process 1275538]
 0x00007ffff7ecda14 in __tcsetattr (fd=0, optional_actions=0, termios_p=0x7fffffffd450) at ../sysdeps/unix/sysv/linux/tcsetattr.c:83
 83	../sysdeps/unix/sysv/linux/tcsetattr.c: No such file or directory.
 (gdb) PASS: gdb.multi/multi-term-settings.exp: inf1_how=run-share: inf2_how=run-session: continue (expected SIGTTOU)
 Quit
 (gdb) PASS: gdb.multi/multi-term-settings.exp: inf1_how=run-share: inf2_how=run-session: stop with control-c (Quit)

Bad:

 (gdb) PASS: gdb.multi/multi-term-settings.exp: inf1_how=run-share: inf2_how=run-session: info inferiors
 continue
 Continuing.
 pid=1287638, count=0

 Thread 1.1 "multi-term-sett" received signal SIGTTOU, Stopped (tty output).
 pid=1287663, count=0                      <<<<<< HERE
 [Switching to process 1287638]
 0x00007ffff7ecda14 in __tcsetattr (fd=0, optional_actions=0, termios_p=0x7fffffffd450) at ../sysdeps/unix/sysv/linux/tcsetattr.c:83
 83	../sysdeps/unix/sysv/linux/tcsetattr.c: No such file or directory.
 (gdb) FAIL: gdb.multi/multi-term-settings.exp: inf1_how=run-share: inf2_how=run-session: continue
 Quit
 (gdb) PASS: gdb.multi/multi-term-settings.exp: inf1_how=run-share: inf2_how=run-session: stop with control-c (Quit)


Notice the "<<<<<< HERE" line in the "Bad" output above -- that is
inferior output being printed on the screen _after_ GDB says the
thread stopped...  That's obviously bogus, the output was printed by
the inferior before it was stopped.

The fix is to claim back the terminal for output before printing the
"signal received SIGTTOU" message.

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <pedro@palves.net>

	* infrun.c (normal_stop): Call target_terminal::ours_for_output
	before calling signal_received observers.

Change-Id: Iea106c33b4c585562fc3579ca85d43481fa214f0
2021-06-14 21:53:54 +01:00
Pedro Alves
6579312c44 gdb.mi/mi-logging.exp, don't send input to GDB while the inferior is running
The gdb.mi/mi-logging.exp testcase sends a sequence of execution
commands to the GDB terminal while the inferior is running, like this:

 send_gdb "1002-exec-step\n"
 send_gdb "1003-exec-next\n"

expecting that GDB will consume the "1003-exec-next" intput after the
inferior stops for the 1002-exec-step.

That's a flawed assumption in general, because the inferior itself
could consume the "1003-exec-next" input while it is stepping.

When GDB puts the inferior in its own terminal, while the inferior is
running, GDB marshals input from its terminal to the inferior's
terminal.  The result is that input typed while the inferior is
running never goes to GDB, and so the test fails.

The previous patch addressed issues like this by making the inferior
and GDB share the same terminal for tests that really wanted to test
some aspect of a shared terminal.  For gdb.mi/mi-logging.exp though,
there's really no reason to send input while the program is running,
so the fix here is to stop it from doing so.

While debugging the testcase, I ran into the fact that it reuses the
same log file for more than one [open] sequence, overwriting previous
runs.  The testcase also deletes the log files at the very end, which
makes it impossible to inspect the logs after a failure run.  The
patch addresses those issues as well.

gdb/testsuite/ChangeLog:
yyyy-mm-dd  Pedro Alves  <pedro@palves.net>

	* gdb.mi/mi-logging.exp: Do not reuse log files for different
	runs.  Delete logs at the start of the testcase, not at the
	end.
	(wait_open, gdb_test_file): New procedures.  Use them.

Change-Id: Ife215a82391a020041fd05478bd8dbee6e04d607
2021-06-14 21:53:54 +01:00
Pedro Alves
9d5edd40e6 Make inferior/GDB share terminal in tests that exercise GDB/inferior reading same input
Some testcases exercise some aspect that only makes sense when GDB and
the inferior are sharing the same terminal, meaning GDB and the
inferior are reading from the same input file.

This commit makes sure that continues to be tested even after GDB
changed to put inferiors in their own session/terminal by default, by
issuing "tty /dev/tty".  The tests would fail otherwise.

gdb/testsuite/ChangeLog:
yyyy-mm-dd  Pedro Alves  <pedro@palves.net>

	* gdb.base/annota-input-while-running.exp: Issue "tty /dev/tty"
	before starting program.
	* gdb.base/continue-all-already-running.exp: Likewise.
	* gdb.base/infcall-input.exp: Likewise.

Change-Id: Ia5f9061bf28a5e780194aa75b37b6058de0614ee
2021-06-14 21:53:54 +01:00
Pedro Alves
8db79a51e5 Make inferior/GDB share terminal in tests expecting output after detach
A following patch will make GDB put spawned inferiors in their own
terminal/session.  A consequence of that is that if you do "run", and
then "detach", GDB closes the terminal, so expecting inferior output
after detach no longer works, the inferior is alive, but its output
goes nowhere.

There's only one testcase that expects output out of an inferior after
detach.  Tweak it to output to GDB's terminal instead.

gdb/testsuite/ChangeLog:
yyyy-mm-dd  Pedro Alves  <pedro@palves.net>

	* gdb.threads/process-dies-while-detaching.exp: Issue "tty
	/dev/tty" before starting program.

Change-Id: Ic62bca178295763fb9c47657ee459fe715f7865e
2021-06-14 21:53:28 +01:00
Pedro Alves
68ad0fe169 Special-case "set inferior-tty /dev/tty"
The following patches will make GDB spawn inferiors in their own
session and tty by default.  Meaning, GDB will create and manage a pty
for the inferior instead of the inferior and GDB sharing the same
terminal and GDB having to juggle terminal settings depending on
whether the inferior running or gdb showing the prompt.

For some use cases however, it will still be useful to be able to tell
GDB to spawn the inferior in the same terminal & session as GDB, like
today.

Setting the inferior tty to "/dev/tty" seems like an obvious way to
get that, as /dev/tty is a special file that represents the terminal
for the current process.  This leaves "tty" with no arguments free for
a different behavior.

This patch hardcodes "/dev/tty" in is_gdb_terminal for that reason.

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <pedro@palves.net>

	* inflow.c (is_gdb_terminal): Hardcode "/dev/tty".
	(new_tty): Don't create a tty if is_gdb_terminal is true.
	(new_tty_postfork): Always allocate a run_terminal.
	(create_tty_session): Don't create a session if sharing the
	terminal with GDB.

Change-Id: I4dc7958f823fa4e30c21a2c3fe4d8434a5d5ed40
2021-06-14 21:31:29 +01:00
Pedro Alves
deb9814ee1 Don't check parent pid in gdb.threads/{ia64-sigill,siginfo-threads,watchthreads-reorder}.c
A following patch will make GDB always put spawned inferiors in their
own terminal session.  To do that, GDB forks twice, creating an extra
"session leader" process between gdb and the inferior process.

gdb.threads/{ia64-sigill,siginfo-threads,watchthreads-reorder}.c all
check whether the parent process is GDB.  If it isn't, they just bail,
and the testcase fails.  After the changes mentioned above, this
parent check will fail if GDB is putting inferiors in their own
terminal session, because in that case, the test's parent is the extra
"session leader" process between gdb and the test process.  The tracer
will be the test process's grandparent, not the direct parent.

Since the test programs already check whether there's a ptracer
attached, there's no real need for this parent pid check.  Just remove
it.

gdb/testsuite/ChangeLog:
yyyy-mm-dd  Pedro Alves  <pedro@palves.net>

	* gdb.threads/ia64-sigill.c (main): Don't check whether the parent
	pid is the tracer.
	* gdb.threads/siginfo-threads.c (main): Don't check whether the
	parent pid is the tracer.
	* gdb.threads/watchthreads-reorder.c (main): Don't check whether
	the parent pid is the tracer.

Change-Id: Iea0b06fb93c31bde1a0993c52b3fe8a5f408aec7
2021-06-14 21:31:28 +01:00
Pedro Alves
976f24fd55 Fix gdb.multi/multi-term-settings.exp race
The gdb.multi/multi-term-settings.exp testcase sometimes fails like so:

 Running /home/pedro/gdb/mygit/src/gdb/testsuite/gdb.multi/multi-term-settings.exp ...
 FAIL: gdb.multi/multi-term-settings.exp: inf1_how=attach: inf2_how=attach: stop with control-c (SIGINT)

It's easier to reproduce if you stress the machine at the same time, like e.g.:

  $ stress -c 24

Looking at gdb.log, we see:

 (gdb) attach 60422
 Attaching to program: build/gdb/testsuite/outputs/gdb.multi/multi-term-settings/multi-term-settings, process 60422
 [New Thread 60422.60422]
 Reading symbols from /lib/x86_64-linux-gnu/libc.so.6...
 Reading symbols from /usr/lib/debug//lib/x86_64-linux-gnu/libc-2.31.so...
 Reading symbols from /lib64/ld-linux-x86-64.so.2...
 (No debugging symbols found in /lib64/ld-linux-x86-64.so.2)
 0x00007f2fc2485334 in __GI___clock_nanosleep (clock_id=<optimized out>, clock_id@entry=0, flags=flags@entry=0, req=req@entry=0x7ffe23126940, rem=rem@entry=0x0) at ../sysdeps/unix/sysv/linux/clock_nanosleep.c:78
 78	../sysdeps/unix/sysv/linux/clock_nanosleep.c: No such file or directory.
 (gdb) PASS: gdb.multi/multi-term-settings.exp: inf1_how=attach: inf2_how=attach: inf2: attach
 set schedule-multiple on
 (gdb) PASS: gdb.multi/multi-term-settings.exp: inf1_how=attach: inf2_how=attach: set schedule-multiple on
 info inferiors
   Num  Description       Connection                         Executable
   1    process 60404     1 (extended-remote localhost:2349) build/gdb/testsuite/outputs/gdb.multi/multi-term-settings/multi-term-settings
 * 2    process 60422     1 (extended-remote localhost:2349) build/gdb/testsuite/outputs/gdb.multi/multi-term-settings/multi-term-settings
 (gdb) PASS: gdb.multi/multi-term-settings.exp: inf1_how=attach: inf2_how=attach: info inferiors
 pid=60422, count=46
 pid=60422, count=47
 pid=60422, count=48
 pid=60422, count=49
 pid=60422, count=50
 pid=60422, count=51
 pid=60422, count=52
 pid=60422, count=53
 pid=60422, count=54
 pid=60422, count=55
 pid=60422, count=56
 pid=60422, count=57
 pid=60422, count=58
 pid=60422, count=59
 pid=60422, count=60
 pid=60422, count=61
 pid=60422, count=62
 pid=60422, count=63
 pid=60422, count=64
 pid=60422, count=65
 pid=60422, count=66
 pid=60422, count=67
 pid=60422, count=68
 pid=60422, count=69
 pid=60404, count=54
 pid=60404, count=55
 pid=60404, count=56
 pid=60404, count=57
 pid=60404, count=58
 PASS: gdb.multi/multi-term-settings.exp: inf1_how=attach: inf2_how=attach: continue
 Quit
 (gdb) FAIL: gdb.multi/multi-term-settings.exp: inf1_how=attach: inf2_how=attach: stop with control-c (SIGINT)

If you look at the testcase's sources, you'll see that the intention
is to resumes the program with "continue", wait to see a few of those
"pid=..., count=..." lines, and then interrupt the program with
Ctrl-C.  But somehow, that resulted in GDB printing "Quit", instead of
the Ctrl-C stopping the program with SIGINT.

Here's what is happening:

 #1 - those "pid=..., count=..." lines we see above weren't actually
      output by the inferior after it has been continued (see #1).
      Note that "inf1_how" and "inf2_how" are "attach".  What happened
      is that those "pid=..., count=..." lines were output by the
      inferiors _before_ they were attached to.  We see them at that
      point instead of earlier, because that's where the testcase
      reads from the inferiors' spawn_ids.

 #2 - The testcase mistakenly thinks those "pid=..., count=..." lines
      happened after the continue was processed by GDB, meaning it has
      waited enough, and so sends the Ctrl-C.  GDB hasn't yet passed
      the terminal to the inferior, so the Ctrl-C results in that
      Quit.

The fix here is twofold:

 #1 - flush inferior output right after attaching

 #2 - consume the "Continuing" printed by "continue", indicating the
      inferior has the terminal.  This is the same as done throughout
      the testsuite to handle this exact problem of sending Ctrl-C too
      soon.

gdb/testsuite/ChangeLog:
yyyy-mm-dd  Pedro Alves  <pedro@palves.net>

	* gdb.multi/multi-term-settings.exp (create_inferior): Flush
	inferior output.
	(coretest): Use $gdb_test_name.  After issuing "continue", wait
	for "Continuing".

Change-Id: Iba7671dfe1eee6b98d29cfdb05a1b9aa2f9defb9
2021-06-14 21:31:28 +01:00
Pedro Alves
7a884f7b7f Make gdb.base/long-inferior-output.exp fail fast
A local change I was working on had a bug that made GDB lose inferior
output, which was caught by the gdb.base/long-inferior-output.exp
testcase.  While debugging the problem, I found it useful to have the
testcase fail quickly when it noticed output was missing.

Also, tighten the regexes to make sure that we don't get
spurious/repeated/bogus output between each "this is line number ..."
line.

gdb/testsuite/ChangeLog:
yyyy-mm-dd  Pedro Alves  <pedro@palves.net>

	* gdb.base/long-inferior-output.exp: Don't set "more" when we see
	an unexpected "this is line number" number.

Change-Id: I53e8499bd8fdbf961431a7f2a94d263da6a9f573
2021-06-14 21:31:28 +01:00
Pedro Alves
0d599db17d prefork_hook: Remove 'args' parameter
prefork_hook's 'args' parameter is only used in debug output in
gdbserver.  Remove it.

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <pedro@palves.net>

	* fork-child.c (prefork_hook): Remove 'args' parameter.  All
	callers adjusted.
	* nat/fork-inferior.h (prefork_hook): Remove 'args' parameter.
	All callers adjusted.
	* nat/fork-inferior.c (fork_inferior): Adjust.

gdbserver/fork-child.cc
yyyy-mm-dd  Pedro Alves  <pedro@palves.net>

	* fork-child.cc (prefork_hook): Remove 'args' parameter, and
	references.

Change-Id: Iaf8977af7dd6915c123b0d50ded93395bdafd920
2021-06-14 21:31:27 +01:00
Pedro Alves
2de900d8ff Test interrupting programs that block SIGINT [gdb/9425, gdb/14559]
This adds a couple testcases that exercise interrupting programs that
block SIGINT.  One tests a program that uses sigprocmask to mask out
SIGINT, and the other tests a program that waits for SIGINT with
sigwait.  On GNU/Linux, it is currently impossible to interrupt such a
program with Ctrl-C, and you can't interrupt it with the "interrupt"
command in all-stop mode either.

These currently fail, and are suitably kfailed.  The rest of the
series will fix the fails.

Tested on GNU/Linux native, gdbserver, and gdbserver + "maint set
target-non-stop on".

gdb/testsuite/ChangeLog:
yyyy-mm-dd  Pedro Alves  <pedro@palves.net>

	PR gdb/9425
	PR gdb/14559
	* gdb.base/sigint-masked-out.c: New file.
	* gdb.base/sigint-masked-out.exp: New file.
	* gdb.base/sigint-sigwait.c: New file.
	* gdb.base/sigint-sigwait.exp: New file.

Change-Id: Iddb70c0a2c92550027fccb6f51ecba1292d233c8
2021-06-14 21:31:27 +01:00
62 changed files with 1999 additions and 260 deletions

View File

@@ -1336,7 +1336,6 @@ HFILES_NO_SRCDIR = \
inf-ptrace.h \
infcall.h \
inferior.h \
inflow.h \
inline-frame.h \
interps.h \
jit.h \

View File

@@ -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

View File

@@ -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

View File

@@ -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 ();
}
}
}

View File

@@ -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)
{

View File

@@ -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

View File

@@ -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;

View File

@@ -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 ();
}

View File

@@ -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.

View File

@@ -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 ();

View File

@@ -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 (&current_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);
}

View File

@@ -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;

View File

@@ -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);

View File

@@ -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;

View File

@@ -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

View File

@@ -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 ();

View File

@@ -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"

View File

@@ -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
{

View File

@@ -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;
}

View File

@@ -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. */

View File

@@ -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 {

View File

@@ -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$"

View File

@@ -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"

View File

@@ -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
}

View File

@@ -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

View File

@@ -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"

View File

@@ -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
}
}

View File

@@ -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

View File

@@ -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"
}
}

View File

@@ -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.
}
}
}

View File

@@ -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

View File

@@ -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
}
}

View 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;
}

View 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

View 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));
}
}

View 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

View File

@@ -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 $" {

View File

@@ -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

View File

@@ -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
}
}

View File

@@ -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
}
}

View File

@@ -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
}
}

View File

@@ -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.

View File

@@ -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" {

View File

@@ -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
}
}

View File

@@ -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
}
}

View File

@@ -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

View File

@@ -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
}
}

View File

@@ -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 $" {

View File

@@ -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"

View File

@@ -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 $" {

View File

@@ -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
}
}
}

View File

@@ -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

View File

@@ -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"

View File

@@ -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

View File

@@ -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 {} {

View File

@@ -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

View File

@@ -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)
{

View File

@@ -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 \

View File

@@ -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
View 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
View 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 */

View File

@@ -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 */