Bug 24454 - nat/x86-linux-dregs.c:146: internal-error: void x86_linux_update_debug_registers(lwp_info*): Assertion `lwp_is_stopped (lwp)' failed.
Summary: nat/x86-linux-dregs.c:146: internal-error: void x86_linux_update_debug_regist...
Status: RESOLVED FIXED
Alias: None
Product: gdb
Classification: Unclassified
Component: gdb (show other bugs)
Version: HEAD
: P2 normal
Target Milestone: 8.3.1
Assignee: Not yet assigned to anyone
URL:
Keywords:
: 21051 24553 (view as bug list)
Depends on:
Blocks:
 
Reported: 2019-04-15 11:30 UTC by Tom de Vries
Modified: 2019-09-16 23:50 UTC (History)
4 users (show)

See Also:
Host:
Target:
Build:
Last reconfirmed:


Attachments
0001-gdb-testsuite-Add-vfork-follow-child.exp-test-case.patch (718 bytes, patch)
2019-04-16 11:07 UTC, Tom de Vries
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Tom de Vries 2019-04-15 11:30:54 UTC
[ This is most likely a duplicate of PR21051. Filing it separately to prevent obfuscating original PR. ]

Using gdb build from HEAD of today (2019-04-15):
...
$ git show -s --pretty="%h %s"
8669f96f0d Automatic date update in version.in
...

Terminal 1:
...
$ ./gdb /usr/bin/sleep 
...

Terminal 2:
...
$ ./gdb -p $(ps -u $USER | grep gdb | awk '{print $1}')
...

Terminal 3:
...
$ ./gdb -p $(ps -u $USER | grep gdb | awk '{print $1}' | tail -n 1)
(gdb) continue
Continuing.
...

Terminal 2:
...
(gdb) set follow-fork-mode child
(gdb) continue
Continuing.
...

Terminal 1:
...
(gdb) run
Starting program: /usr/bin/sleep 
warning: Could not trace the inferior process.
Error: 
warning: ptrace: Action is not allowed
...

Terminal 2:
...
[Attaching after Thread 0x7fd9945c9ac0 (LWP 15665) vfork to child process 15722]
[New inferior 2 (process 15722)]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
[Detaching vfork parent process 15665 after child exit]
/data/gdb_versions/devel/src/gdb/nat/x86-linux-dregs.c:146: internal-error: void x86_linux_update_debug_registers(lwp_info*): Assertion `lwp_is_stopped (lwp)' failed.
A problem internal to GDB has been detected,
further debugging may prove unreliable.
Quit this debugging session? (y or n)
...

Terminal 3:
...
^C
Thread 1 "gdb" received signal SIGINT, Interrupt.
0x00007f8c2181807b in poll () from /lib64/libc.so.6
(gdb) bt
#0  0x00007f8c2181807b in poll () from /lib64/libc.so.6
#1  0x000000000061bebb in gdb_wait_for_event (block=1) at gdb/event-loop.c:770
#2  0x000000000061b2c2 in gdb_do_one_event () at gdb/event-loop.c:347
#3  0x00000000008caf14 in gdb_readline_wrapper (
    prompt=0x3363230 "gdb/nat/x86-linux-dregs.c:146: internal-error: void x86_linux_update_debug_registers(lwp_info*): Assertion `lwp_is_stopped (lwp)' failed.\nA problem internal to GDB has bee"...) at gdb/top.c:1004
#4  0x00000000009278c1 in defaulted_query(const char *, char, typedef __va_list_tag __va_list_tag *) (
    ctlstr=0xc66b68 "%s\nQuit this debugging session? ", defchar=0 '\000', args=0x7ffec3aca140)
    at gdb/utils.c:894
#5  0x0000000000927c79 in query (ctlstr=0xc66b68 "%s\nQuit this debugging session? ")
    at gdb/utils.c:986
#6  0x0000000000926a39 in internal_vproblem(internal_problem *, const char *, int, const char *, typedef __va_list_tag __va_list_tag *) (problem=0x11ac940 <internal_error_problem>, 
    file=0xbf4fd8 "gdb/nat/x86-linux-dregs.c", line=146, 
    fmt=0xbf4faa "%s: Assertion `%s' failed.", ap=0x7ffec3aca348) at gdb/utils.c:370
#7  0x0000000000926ccc in internal_verror (
    file=0xbf4fd8 "gdb/nat/x86-linux-dregs.c", line=146, 
    fmt=0xbf4faa "%s: Assertion `%s' failed.", ap=0x7ffec3aca348) at gdb/utils.c:436
#8  0x0000000000512f45 in internal_error (
    file=0xbf4fd8 "gdb/nat/x86-linux-dregs.c", line=146, 
    fmt=0xbf4faa "%s: Assertion `%s' failed.") at gdb/common/errors.c:55
#9  0x000000000076667c in x86_linux_update_debug_registers (lwp=0x3378ac0)
    at gdb/nat/x86-linux-dregs.c:146
#10 0x000000000076688d in x86_linux_prepare_to_resume (lwp=0x3378ac0)
    at gdb/nat/x86-linux.c:81
#11 0x000000000044c3f6 in x86_linux_nat_target::low_prepare_to_resume (this=0x11bd5b0 <the_amd64_linux_nat_target>, lwp=0x3378ac0) at gdb/x86-linux-nat.h:69
#12 0x00000000006f2661 in detach_one_lwp (lp=0x3378ac0, signo_p=0x0) at gdb/linux-nat.c:1406
#13 0x00000000006f2951 in detach_callback (lp=0x3378ac0) at gdb/linux-nat.c:1464
#14 0x00000000006fa7bc in gdb::function_view<int (lwp_info*)>::bind<int, lwp_info*>(int (*)(lwp_info*))::{lambda(gdb::fv_detail::erased_callable, lwp_info*)#1}::operator()(gdb::fv_detail::erased_callable, lwp_info*) const (__closure=0x0, ecall=..., args#0=0x3378ac0) at gdb/common/function-view.h:284
#15 0x00000000006fa7e3 in gdb::function_view<int (lwp_info*)>::bind<int, lwp_info*>(int (*)(lwp_info*))::{lambda(gdb::fv_detail::erased_callable, lwp_info*)#1}::_FUN(gdb::fv_detail::erased_callable, lwp_info*) () at gdb/common/function-view.h:278
#16 0x00000000006fa676 in gdb::function_view<int (lwp_info*)>::operator()(lwp_info*) const (this=0x7ffec3aca650, args#0=0x3378ac0) at gdb/common/function-view.h:247
#17 0x00000000006f1760 in iterate_over_lwps(ptid_t, gdb::function_view<int (lwp_info*)>) (filter=..., callback=...) at gdb/linux-nat.c:972
#18 0x00000000006f2a8e in linux_nat_target::detach (this=0x11bd5b0 <the_amd64_linux_nat_target>, inf=0x334c5e0, from_tty=0) at gdb/linux-nat.c:1484
#19 0x0000000000709a21 in thread_db_target::detach (this=0x11a1978 <the_thread_db_target>, inf=0x334c5e0, from_tty=0) at gdb/linux-thread-db.c:1364
#20 0x00000000008a9742 in target_detach (inf=0x334c5e0, from_tty=0) at gdb/target.c:2038
#21 0x00000000006b9c44 in handle_vfork_child_exec_or_exit (exec=0) at gdb/infrun.c:985
#22 0x00000000006c16b9 in handle_inferior_event (ecs=0x7ffec3acab80) at gdb/infrun.c:4830
#23 0x00000000006bf204 in fetch_inferior_event (client_data=0x0) at gdb/infrun.c:3748
#24 0x00000000006a6e8c in inferior_event_handler (event_type=INF_REG_EVENT, client_data=0x0) at gdb/inf-loop.c:43
#25 0x00000000006f9218 in handle_target_event (error=0, client_data=0x0) at gdb/linux-nat.c:4355
#26 0x000000000061be10 in handle_file_event (file_ptr=0x5393160, ready_mask=1) at gdb/event-loop.c:732
#27 0x000000000061c3b3 in gdb_wait_for_event (block=0) at gdb/event-loop.c:858
#28 0x000000000061b24e in gdb_do_one_event () at gdb/event-loop.c:322
#29 0x000000000061b2ec in start_event_loop () at gdb/event-loop.c:371
#30 0x000000000071df7f in captured_command_loop () at gdb/main.c:331
#31 0x000000000071f3e2 in captured_main (data=0x7ffec3acaef0) at gdb/main.c:1173
#32 0x000000000071f473 in gdb_main (args=0x7ffec3acaef0) at gdb/main.c:1188
#33 0x000000000040fcde in main (argc=3, argv=0x7ffec3acaff8) at gdb/gdb.c:32
...
Comment 1 Tom de Vries 2019-04-16 09:38:15 UTC
(In reply to Tom de Vries from comment #0)
> [ This is most likely a duplicate of PR21051.

There is one difference though: here we have exec == 0 in handle_vfork_child_exec_or_exit:
...
#21 0x00000000006b9c44 in handle_vfork_child_exec_or_exit (exec=0) at gdb/infrun.c:985
...
while exec == 1 in both examples in PR21051.
Comment 2 Tom de Vries 2019-04-16 10:39:04 UTC
Consider this more minimal test-case, calling vfork from a thread:
...
$ cat test.c
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>

static void *
f (void *arg)
{
  printf("thread created\n");
  if (vfork () == 0)
    printf("vfork child\n");
  else
    printf("vfork parent\n");
  printf("thread exiting\n");
  return NULL;
}
 
int
main (void)
{
  pthread_t tid;
  printf("thread create\n");
  pthread_create (&tid, NULL, f, NULL);
  printf("thread joining\n");
  pthread_join (tid, NULL);
  printf("thread joined\n");
  return 0;
}
...

Compiled like so:
...
$ gcc test.c -lpthread
...

executes without problems:
...
$ ./a.out
thread create
thread joining
thread created
vfork child
thread exiting
vfork parent
thread exiting
thread joined
$ echo $?
0
...

when running with gdb in follow-fork-mode child mode, we run into the assert:
...
$ gdb -batch ./a.out -ex "set follow-fork-mode child" -ex "run"
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
thread create
[New Thread 0x7ffff77fe700 (LWP 7557)]
thread created
thread joining
[Attaching after Thread 0x7ffff77fe700 (LWP 7557) vfork to child process 7558]
[New inferior 2 (process 7558)]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
vfork child
thread exiting
[Detaching vfork parent process 7553 after child exit]
vfork parent
thread exiting
/data/gdb_versions/devel/src/gdb/nat/x86-linux-dregs.c:146: internal-error: void x86_linux_update_debug_registers(lwp_info*): Assertion `lwp_is_stopped (lwp)' failed.
A problem internal to GDB has been detected,
further debugging may prove unreliable.
Quit this debugging session? (y or n) [answered Y; input not from terminal]

This is a bug, please report it.  For instructions, see:
<http://www.gnu.org/software/gdb/bugs/>.

/data/gdb_versions/devel/src/gdb/nat/x86-linux-dregs.c:146: internal-error: void x86_linux_update_debug_registers(lwp_info*): Assertion `lwp_is_stopped (lwp)' failed.
A problem internal to GDB has been detected,
further debugging may prove unreliable.
Create a core file of GDB? (y or n) [answered Y; input not from terminal]
Afgebroken (geheugendump gemaakt)
...
Comment 3 Tom de Vries 2019-04-16 11:07:26 UTC
Created attachment 11742 [details]
0001-gdb-testsuite-Add-vfork-follow-child.exp-test-case.patch

[gdb/testsuite] Add vfork-follow-child.exp test-case

PR gdb/24454

Test-case for PR gdb/24454.

Fails like this:
...
Running src/gdb/testsuite/gdb.threads/vfork-follow-child.exp ...
FAIL: gdb.threads/vfork-follow-child.exp: continue (GDB internal error)
ERROR: Could not resync from internal error (resync count exceeded)

                === gdb Summary ===

nr of expected passes            2
nr of unexpected failures        1
...

---
 gdb/testsuite/gdb.threads/vfork-follow-child.c   | 19 +++++++++++++++++++
 gdb/testsuite/gdb.threads/vfork-follow-child.exp | 21 +++++++++++++++++++++
 2 files changed, 40 insertions(+)
Comment 4 Tom de Vries 2019-04-16 12:52:50 UTC
Tentative patch:
...
diff --git a/gdb/infrun.c b/gdb/infrun.c
index 37713b24fe..80978d5198 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -982,7 +982,14 @@ handle_vfork_child_exec_or_exit (int exec)
                }
            }
 
-         target_detach (inf->vfork_parent, 0);
+         /* Now that the vfork child has terminated, make sure during detach
+            that we no longer consider the vfork parent to be a vfork parent,
+            but just a regular process that we're detaching from.  */
+         struct inferior *to_detach = inf->vfork_parent;
+         inf->vfork_parent->vfork_child = NULL;
+         inf->vfork_parent = NULL;
+
+         target_detach (to_detach, 0);
 
          /* Put it back.  */
          inf->pspace = pspace;
...
Comment 5 Tom de Vries 2019-04-16 15:07:47 UTC
(In reply to Tom de Vries from comment #4)
> Tentative patch:

Submitted: https://sourceware.org/ml/gdb-patches/2019-04/msg00271.html
Comment 6 cvs-commit@gcc.gnu.org 2019-04-18 16:11:08 UTC
The master branch has been updated by Pedro Alves <palves@sourceware.org>:

https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=b73715df01e6e9b3de5a49cd7bf4170deef48461

commit b73715df01e6e9b3de5a49cd7bf4170deef48461
Author: Tom de Vries <tdevries@suse.de>
Date:   Thu Apr 18 17:05:43 2019 +0100

    [gdb] Handle vfork in thread with follow-fork-mode child
    
    When debugging any of the testcases added by this commit, which do a
    vfork in a thread with "set follow-fork-mode child" + "set
    detach-on-fork on", we run into this assertion:
    
    ...
    src/gdb/nat/x86-linux-dregs.c:146: internal-error: \
      void x86_linux_update_debug_registers(lwp_info*): \
      Assertion `lwp_is_stopped (lwp)' failed.
    ...
    
    The assert is caused by the following: the vfork-child exit or exec
    event is handled by handle_vfork_child_exec_or_exit, which calls
    target_detach to detach from the vfork parent.  During target_detach
    we call linux_nat_target::detach, which:
    
    #1 - stops all the threads
    #2 - waits for all the threads to be stopped
    #3 - detaches all the threads
    
    However, during the second step we run into this code in
    stop_wait_callback:
    
    ...
      /* If this is a vfork parent, bail out, it is not going to report
         any SIGSTOP until the vfork is done with.  */
      if (inf->vfork_child != NULL)
        return 0;
    ...
    
    and we don't wait for the threads to be stopped, which results in this
    assert in x86_linux_update_debug_registers triggering during the third
    step:
    
    ...
      gdb_assert (lwp_is_stopped (lwp));
    ...
    
    The fix is to reset the vfork parent's vfork_child field before
    calling target_detach in handle_vfork_child_exec_or_exit.  There's
    already similar code for the other paths handled by
    handle_vfork_child_exec_or_exit, so this commit refactors the code a
    bit so that all paths share the same code.
    
    The new tests cover both a vfork child exiting, and a vfork child
    execing, since both cases would trigger the assertion.
    
    The new testcases also exercise following the vfork children with "set
    detach-on-fork off", since it doesn't seem to be tested anywhere.
    
    Tested on x86_64-linux, using native and native-gdbserver.
    
    gdb/ChangeLog:
    2019-04-18  Tom de Vries  <tdevries@suse.de>
    	    Pedro Alves  <palves@redhat.com>
    
    	PR gdb/24454
    	* infrun.c (handle_vfork_child_exec_or_exit): Reset vfork parent's
    	vfork_child field before calling target_detach.
    
    gdb/testsuite/ChangeLog:
    2019-04-18  Tom de Vries  <tdevries@suse.de>
    	    Pedro Alves  <palves@redhat.com>
    
    	PR gdb/24454
    	* gdb.threads/vfork-follow-child-exec.c: New file.
    	* gdb.threads/vfork-follow-child-exec.exp: New file.
    	* gdb.threads/vfork-follow-child-exit.c: New file.
    	* gdb.threads/vfork-follow-child-exit.exp: New file.
Comment 7 Tom de Vries 2019-04-18 16:13:32 UTC
Patch with test-cases committed, marking resolved-fixed.
Comment 8 Tom de Vries 2019-04-18 16:15:24 UTC
*** Bug 21051 has been marked as a duplicate of this bug. ***
Comment 9 Gary Benson 2019-05-20 13:34:13 UTC
*** Bug 24553 has been marked as a duplicate of this bug. ***
Comment 10 Tom de Vries 2019-07-23 15:05:53 UTC
proposed 8.3 backport: https://sourceware.org/ml/gdb-patches/2019-07/
Comment 11 cvs-commit@gcc.gnu.org 2019-08-15 22:32:29 UTC
The gdb-8.3-branch branch has been updated by Tom de Vries <vries@sourceware.org>:

https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=ee479c89ed9ecd446eccbbdb0b8786c6526f8906

commit ee479c89ed9ecd446eccbbdb0b8786c6526f8906
Author: Tom de Vries <tdevries@suse.de>
Date:   Fri Aug 16 00:31:48 2019 +0200

    [gdb] Handle vfork in thread with follow-fork-mode child
    
    [ Backport of master commit b73715df01. ]
    
    When debugging any of the testcases added by this commit, which do a
    vfork in a thread with "set follow-fork-mode child" + "set
    detach-on-fork on", we run into this assertion:
    
    ...
    src/gdb/nat/x86-linux-dregs.c:146: internal-error: \
      void x86_linux_update_debug_registers(lwp_info*): \
      Assertion `lwp_is_stopped (lwp)' failed.
    ...
    
    The assert is caused by the following: the vfork-child exit or exec
    event is handled by handle_vfork_child_exec_or_exit, which calls
    target_detach to detach from the vfork parent.  During target_detach
    we call linux_nat_target::detach, which:
    
    However, during the second step we run into this code in
    stop_wait_callback:
    
    ...
      /* If this is a vfork parent, bail out, it is not going to report
         any SIGSTOP until the vfork is done with.  */
      if (inf->vfork_child != NULL)
        return 0;
    ...
    
    and we don't wait for the threads to be stopped, which results in this
    assert in x86_linux_update_debug_registers triggering during the third
    step:
    
    ...
      gdb_assert (lwp_is_stopped (lwp));
    ...
    
    The fix is to reset the vfork parent's vfork_child field before
    calling target_detach in handle_vfork_child_exec_or_exit.  There's
    already similar code for the other paths handled by
    handle_vfork_child_exec_or_exit, so this commit refactors the code a
    bit so that all paths share the same code.
    
    The new tests cover both a vfork child exiting, and a vfork child
    execing, since both cases would trigger the assertion.
    
    The new testcases also exercise following the vfork children with "set
    detach-on-fork off", since it doesn't seem to be tested anywhere.
    
    Tested on x86_64-linux, using native and native-gdbserver.
    
    gdb/ChangeLog:
    2019-04-18  Tom de Vries  <tdevries@suse.de>
    	    Pedro Alves  <palves@redhat.com>
    
    	PR gdb/24454
    	* infrun.c (handle_vfork_child_exec_or_exit): Reset vfork parent's
    	vfork_child field before calling target_detach.
    
    gdb/testsuite/ChangeLog:
    2019-04-18  Tom de Vries  <tdevries@suse.de>
    	    Pedro Alves  <palves@redhat.com>
    
    	PR gdb/24454
    	* gdb.threads/vfork-follow-child-exec.c: New file.
    	* gdb.threads/vfork-follow-child-exec.exp: New file.
    	* gdb.threads/vfork-follow-child-exit.c: New file.
    	* gdb.threads/vfork-follow-child-exit.exp: New file.
Comment 12 Joel Brobecker 2019-09-16 23:50:00 UTC
changing the target milestone because the patch was backported there as well.