gdb/infrun: disable pagination in fetch_inferior_event
authorTankut Baris Aktemur <tankut.baris.aktemur@intel.com>
Fri, 30 Oct 2020 07:13:57 +0000 (08:13 +0100)
committerTankut Baris Aktemur <tankut.baris.aktemur@intel.com>
Fri, 30 Oct 2020 07:15:20 +0000 (08:15 +0100)
commitb78b3a297b981e2c8492823e2c54436e2451c2fa
tree59d3172eef410e43451791ab649671b968ed4ea6
parentd70bdd3cc4cfdfd30bed44a271ff80f98b96eebf
gdb/infrun: disable pagination in fetch_inferior_event

Having pagination enabled when handling an inferior event gives the
user an option to quit, which causes early exit in GDB's flow and may
lead to half-baked state.  For instance, here is a case where we quit
in the middle of handling an inferior exit:

  $ gdb ./a.out
  Reading symbols from ./a.out...
  (gdb) set height 2
  (gdb) run
  Starting program: ./a.out
  --Type <RET> for more, q to quit, c to continue without paging--q
  Quit
  Couldn't get registers: No such process.
  (gdb) set height unlimited
  Couldn't get registers: No such process.
  (gdb) info threads
    Id   Target Id         Frame
  * 1    process 27098     Couldn't get registers: No such process.
  Couldn't get registers: No such process.
  (gdb)

Or suppose having a multi-threaded program like below:

  static void *
  fun (void *dummy)
  {
    int a = 1; /* break-here */
    return NULL;
  }

  int
  main (void)
  {
    pthread_t thread;
    pthread_create (&thread, NULL, fun, NULL);
    pthread_join (thread, NULL);

    return 0;
  }

If we define a breakpoint at line "break-here", we expect only Thread
2 to hit it.

  $ gdb ./a.out
  Reading symbols from ./a.out...
  (gdb) break 7
  Breakpoint 1 at 0x1182: file mt.c, line 7.
  (gdb) set height 2
  (gdb) run
  Starting program: ./a.out
  [Thread debugging using libthread_db enabled]
  Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
  [New Thread 0x7ffff77c4700 (LWP 23048)]
  --Type <RET> for more, q to quit, c to continue without paging--q
  Quit
  (gdb) set height unlimited
  (gdb) info thread
    Id   Target Id                                 Frame
  * 1    Thread 0x7ffff7fe3740 (LWP 23044) "a.out" 0x00007ffff7bbed2d in ...
    2    Thread 0x7ffff77c4700 (LWP 23048) "a.out" fun (dummy=0x0) at mt.c:7
  (gdb)

The prompt for continuation was triggered because Thread 2 hit the
breakpoint.  (If we had hit 'c', we were going to see that stop event,
but we didn't.)  The context did not switch to Thread 2.  GDB also did
not execute several other things it would normally do in
infrun.c:normal_stop after outputting "[Switching to Thread ...]" (but
it seems harmless in this case).  If we 'continue' at this state, both
threads run until termination, and we don't see the breakpoint hit
event ever.

Here is another related and more complicated scenario that leads to a
GDB crash.  Create two inferiors, one sitting on top of a native
target, and the other on a remote target, so that we have a
multi-target setting, like so:

  (gdb) i inferiors
    Num  Description       Connection     Executable
    1    process 13786     1 (native)     a.out
  * 2    process 13806     2 (remote ...) target:a.out

Next, resume both inferiors to run until termination:

  (gdb) set schedule-multiple on
  (gdb) set height 2
  (gdb) continue
  Continuing.
  --Type <RET> for more, q to quit, c to continue without paging--[Inferior 2 (process 13806) exited normally]

  terminate called after throwing an instance of 'gdb_exception_error'
  Aborted

Here, GDB first received a termination event from Inferior 1.  GDB
attempted to print this event, triggering a "prompt for continue", and
GDB started polling for events, hoping to get an input from the user.
However, the exit event from Inferior 2 was received instead.  So, GDB
started processing an exit event while being in the middle of
processing another exit event.  It was not ready for this situation
and eventually crashed.

To address these cases, temporarily disable pagination in
fetch_inferior_event.  This doesn't affect commands like 'info
threads', 'backtrace', or 'thread apply'.

Regression-tested on X86_64 Linux.

gdb/ChangeLog:
2020-10-30  Tankut Baris Aktemur  <tankut.baris.aktemur@intel.com>

* infrun.c (fetch_inferior_event): Temporarily disable pagination.

gdb/testsuite/ChangeLog:
2020-10-30  Tankut Baris Aktemur  <tankut.baris.aktemur@intel.com>

* gdb.base/paginate-after-ctrl-c-running.exp: Update with no pagination
behavior.
* gdb.base/paginate-bg-execution.exp: Ditto.
* gdb.base/paginate-inferior-exit.exp: Ditto.
* gdb.base/double-prompt-target-event-error.c: Remove.
* gdb.base/double-prompt-target-event-error.exp: Remove.
gdb/ChangeLog
gdb/infrun.c
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.base/double-prompt-target-event-error.c [deleted file]
gdb/testsuite/gdb.base/double-prompt-target-event-error.exp [deleted file]
gdb/testsuite/gdb.base/paginate-after-ctrl-c-running.exp
gdb/testsuite/gdb.base/paginate-bg-execution.exp
gdb/testsuite/gdb.base/paginate-inferior-exit.exp