public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH v5] gdb/gcore: interrupt all threads before generating the corefile
@ 2023-01-30 16:51 Lancelot SIX
  2023-02-10 14:36 ` [PING] " Lancelot SIX
  2023-02-10 21:46 ` Simon Marchi
  0 siblings, 2 replies; 12+ messages in thread
From: Lancelot SIX @ 2023-01-30 16:51 UTC (permalink / raw)
  To: gdb-patches; +Cc: lsix, simark, Lancelot SIX

Hi, this V5 follows
https://sourceware.org/pipermail/gdb-patches/2022-December/194504.html.

Changes since V4:

- Update gdb/NEWS to fall into the "Changes since GDB 13" section.
- Use "build_executable" instead of "prepare_for_testing" in the test
  to avoid one useless start of GDB.
- Updated the test's description.
- Updated the copyright year to include 2023.

Best,
Lancelot.

---

In non-stop mode, if the user tries to generate a core dump (using the
gcore command) while some threads are running, a non-helpful error
message is shown.

Lets consider the following session as an example (debugging the test
program included in this patch):

    (gdb) set non-stop on
    (gdb) b 37
    (gdb) r
    Thread 1 "gcore-nonstop" hit Breakpoint 1, main () at gcore-nonstop.c:39
    (gdb) info thread
       Id   Target Id                                          Frame
     * 1    Thread 0x7ffff7d7a740 (LWP 431838) "gcore-nonstop" main () at gcore-nonstop.c:39
       2    Thread 0x7ffff7d79640 (LWP 431841) "gcore-nonstop" (running)
    (gdb) gcore
    Couldn't get registers: No such process.

The reported error ("No such process") does not help the user understand
what happens.  This is due to the fact that we cannot access the
registers of a running thread.  Even if we ignore this error, generating
a core dump while any thread might update memory would most likely
result in a core file with an inconsistent view of the process' memory.

To solve this, this patch proposes to change the gcore command so it
first stops all running threads (from the current inferior) before
generating the corefile, and then resumes them in their previous state.

To achieve this, this patch exposes the restart_threads function in infrun.h
(used to be local to infrun.c).  We also allow the first parameter
(event_thread) to be nullptr as it is possible that the gcore command is
called while all threads are running, in which case we want all threads
to be restarted at the end of the procedure.

When testing this patch against gdbserver, it appears that using
stop_all_threads / restart_threads was not compatible with all-stop
targets.  For those targets, we need to call target_stop_and_wait /
target_resume.  The gcore_command has code to handle both
configurations.  I could not use a RAII-like object to have a cleaner
way to restore the state at the end as the restore procedure could
throw.  Instead, the procedure keeps track of which method was used to
interrupt threads so the appropriate method can be used to restore their
state.

Tested on x86_64 on navite GDB and the native-extended-gdbserver board.
---
 gdb/NEWS                                 |  8 +++
 gdb/doc/gdb.texinfo                      |  5 ++
 gdb/gcore.c                              | 30 +++++++++
 gdb/infrun.c                             | 16 ++---
 gdb/infrun.h                             |  9 +++
 gdb/testsuite/gdb.base/gcore-nonstop.c   | 44 +++++++++++++
 gdb/testsuite/gdb.base/gcore-nonstop.exp | 79 ++++++++++++++++++++++++
 7 files changed, 180 insertions(+), 11 deletions(-)
 create mode 100644 gdb/testsuite/gdb.base/gcore-nonstop.c
 create mode 100644 gdb/testsuite/gdb.base/gcore-nonstop.exp

diff --git a/gdb/NEWS b/gdb/NEWS
index 445d28efed9..7f78cbc9008 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -38,6 +38,14 @@ maintenance print record-instruction [ N ]
   prints how GDB would undo the N-th previous instruction, and if N is
   positive, it prints how GDB will redo the N-th following instruction.
 
+* Changed commands
+
+gcore
+generate-core-file
+  GDB now ensures that all threads of the current inferior are stopped
+  before generating a core dump.  At the end of the command, threads are
+  restored to their previous state.
+
 * MI changes
 
 ** mi now reports 'no-history' as a stop reason when hitting the end of the
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 03033c7f9e3..c637d6eb114 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -13693,6 +13693,11 @@ Produce a core dump of the inferior process.  The optional argument
 specified, the file name defaults to @file{core.@var{pid}}, where
 @var{pid} is the inferior process ID.
 
+@value{GDBN} ensures that all threads of the current inferior are
+stopped while generating the core dump.  If any of the inferior's threads
+are running when executing this command, @value{GDBN} stops the threads
+and resumes them when the command is done.
+
 Note that this command is implemented only for some systems (as of
 this writing, @sc{gnu}/Linux, FreeBSD, Solaris, and S390).
 
diff --git a/gdb/gcore.c b/gdb/gcore.c
index 973abadb013..939737d83a1 100644
--- a/gdb/gcore.c
+++ b/gdb/gcore.c
@@ -34,6 +34,7 @@
 #include "regset.h"
 #include "gdb_bfd.h"
 #include "readline/tilde.h"
+#include "infrun.h"
 #include <algorithm>
 #include "gdbsupport/gdb_unlinker.h"
 #include "gdbsupport/byte-vector.h"
@@ -131,6 +132,28 @@ gcore_command (const char *args, int from_tty)
   if (!target_has_execution ())
     noprocess ();
 
+  scoped_restore_current_thread restore_current_thread;
+  scoped_disable_commit_resumed disable_commit_resume ("generating coredump");
+  struct inferior *inf = current_inferior ();
+  scoped_finish_thread_state finish_state (inf->process_target (),
+					   ptid_t (inf->pid));
+
+  bool all_stop_was_running = false;
+  if (exists_non_stop_target ())
+    stop_all_threads ("generating coredump", inf);
+  else
+    {
+      all_stop_was_running = any_thread_of_inferior (inf)->executing ();
+
+      if (all_stop_was_running)
+	{
+	  if (!may_stop)
+	    error (_("Cannot stop the target to generate the core dump."));
+
+	  target_stop_and_wait (ptid_t (inf->pid));
+	}
+    }
+
   if (args && *args)
     corefilename.reset (tilde_expand (args));
   else
@@ -161,6 +184,13 @@ gcore_command (const char *args, int from_tty)
     }
 
   gdb_printf ("Saved corefile %s\n", corefilename.get ());
+
+  if (exists_non_stop_target ())
+    restart_threads (nullptr, inf);
+  else if (all_stop_was_running)
+    target_resume (ptid_t (inf->pid), 0, GDB_SIGNAL_0);
+
+  disable_commit_resume.reset_and_commit ();
 }
 
 static enum bfd_architecture
diff --git a/gdb/infrun.c b/gdb/infrun.c
index edfb5ab0a91..0e6abc352aa 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -96,9 +96,6 @@ static void resume (gdb_signal sig);
 
 static void wait_for_inferior (inferior *inf);
 
-static void restart_threads (struct thread_info *event_thread,
-			     inferior *inf = nullptr);
-
 static bool start_step_over (void);
 
 static bool step_over_info_valid_p (void);
@@ -5857,18 +5854,15 @@ handle_inferior_event (struct execution_control_state *ecs)
     }
 }
 
-/* Restart threads back to what they were trying to do back when we
-   paused them (because of an in-line step-over or vfork, for example).
-   The EVENT_THREAD thread is ignored (not restarted).
-
-   If INF is non-nullptr, only resume threads from INF.  */
+/* See infrun.h.  */
 
-static void
+void
 restart_threads (struct thread_info *event_thread, inferior *inf)
 {
   INFRUN_SCOPED_DEBUG_START_END ("event_thread=%s, inf=%d",
-				 event_thread->ptid.to_string ().c_str (),
-				 inf != nullptr ? inf->num : -1);
+				 (event_thread != nullptr
+				  ? event_thread->ptid.to_string ().c_str ()
+				  : "None"), inf != nullptr ? inf->num : -1);
 
   gdb_assert (!step_over_info_valid_p ());
 
diff --git a/gdb/infrun.h b/gdb/infrun.h
index 43fd1b44f5a..f6b04934bad 100644
--- a/gdb/infrun.h
+++ b/gdb/infrun.h
@@ -175,6 +175,15 @@ extern void nullify_last_target_wait_ptid ();
    all threads of all inferiors.  */
 extern void stop_all_threads (const char *reason, inferior *inf = nullptr);
 
+/* Restart threads back to what they were trying to do back when we
+   paused them (because of an in-line step-over or vfork, for example).
+   The EVENT_THREAD thread, if non-nullptr, is ignored (not restarted).
+
+   If INF is non-nullptr, only resume threads from INF.  */
+
+extern void restart_threads (struct thread_info *event_thread,
+			     inferior *inf = nullptr);
+
 extern void prepare_for_detach (void);
 
 extern void fetch_inferior_event ();
diff --git a/gdb/testsuite/gdb.base/gcore-nonstop.c b/gdb/testsuite/gdb.base/gcore-nonstop.c
new file mode 100644
index 00000000000..a96c011ad14
--- /dev/null
+++ b/gdb/testsuite/gdb.base/gcore-nonstop.c
@@ -0,0 +1,44 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2022-2023 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 <pthread.h>
+
+static pthread_barrier_t barrier;
+
+static void *
+worker_func (void *ignored)
+{
+  pthread_barrier_wait (&barrier);
+  return NULL;
+}
+
+int
+main (void)
+{
+  pthread_t worker_thread;
+  pthread_barrier_init (&barrier, NULL, 2);
+
+  pthread_create (&worker_thread, NULL, worker_func, NULL);
+
+  /* Break here.  */
+
+  pthread_barrier_wait (&barrier);
+  pthread_join (worker_thread, NULL);
+  pthread_barrier_destroy (&barrier);
+
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.base/gcore-nonstop.exp b/gdb/testsuite/gdb.base/gcore-nonstop.exp
new file mode 100644
index 00000000000..172dd760e73
--- /dev/null
+++ b/gdb/testsuite/gdb.base/gcore-nonstop.exp
@@ -0,0 +1,79 @@
+# Copyright 2022-2023 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/>.
+
+# Check that in non-stop mode, if some threads are running when the user
+# launches the "gcore" command, GDB suspends all threads, generates the core
+# file and resumes threads which where running before the "gcore" command
+# got issued.  If running threads were not stopped, GDB would report errors
+# when trying to capture the thread's state.
+
+standard_testfile
+
+if { [build_executable "failed to build" \
+	${testfile} ${srcfile} {pthreads debug}] } {
+    return
+}
+
+save_vars { GDBFLAGS } {
+    append GDBFLAGS " -ex \"set non-stop on\""
+    clean_restart ${binfile}
+}
+
+set lineno [gdb_get_line_number "Break here"]
+if { ![runto $lineno] } {
+    return
+}
+
+# We should be stopped in thread 1 while thread 2 is running.
+gdb_test_sequence "info threads" "info threads" {
+    {Id\s+Target Id\s+Frame}
+    {\*\s+1[^\n]*\n}
+    {\s+2\s+[^\n]*\(running\)[^\n]*\n}
+}
+
+set th1_pc ""
+gdb_test_multiple "p/x \$pc" "fetch thread 1 PC" {
+  -wrap -re "$::decimal = ($::hex)" {
+    set th1_pc $expect_out(1,string)
+    pass $gdb_test_name
+  }
+}
+
+set corefile [standard_output_file "corefile"]
+if {![gdb_gcore_cmd $corefile "generate corefile"]} {
+  # gdb_gcore_cmd issues a "UNSUPPORTED".
+  return
+}
+
+# After the core file is generated, thread 2 should be back running
+# and thread 1 should still be selected.
+gdb_test_sequence "info threads" "correct thread selection after gcore" {
+    {Id\s+Target Id\s+Frame}
+    {\*\s+1[^\n]*\n}
+    {\s+2\s+[^\n]*\(running\)[^\n]*\n}
+}
+
+# Thread 1 is at the same PC it was before calling the gcore command.
+gdb_test "p/x \$pc" "\\\$$::decimal = $th1_pc" "thread 1 unchanged"
+
+clean_restart $binfile
+gdb_test "core-file $corefile" "Core was generated by.*" "load corefile"
+
+# The core file has the 2 threads.
+gdb_test_sequence "info threads" "threads in corefile" {
+    {Id\s+Target Id\s+Frame}
+    {\s+1\s+Thread[^\n]*\n}
+    {\s+2\s+Thread[^\n]*\n}
+}

base-commit: 5867ab870b8aa36ae490ec6e4e8e4c55be11ccf1
-- 
2.34.1


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PING] [PATCH v5] gdb/gcore: interrupt all threads before generating the corefile
  2023-01-30 16:51 [PATCH v5] gdb/gcore: interrupt all threads before generating the corefile Lancelot SIX
@ 2023-02-10 14:36 ` Lancelot SIX
  2023-02-10 21:46 ` Simon Marchi
  1 sibling, 0 replies; 12+ messages in thread
From: Lancelot SIX @ 2023-02-10 14:36 UTC (permalink / raw)
  To: gdb-patches; +Cc: lsix, simark

Hi,

Kindly pinking.

Best,
Lancelot.

On 30/01/2023 16:51, Lancelot SIX wrote:
> Hi, this V5 follows
> https://sourceware.org/pipermail/gdb-patches/2022-December/194504.html.
> 
> Changes since V4:
> 
> - Update gdb/NEWS to fall into the "Changes since GDB 13" section.
> - Use "build_executable" instead of "prepare_for_testing" in the test
>    to avoid one useless start of GDB.
> - Updated the test's description.
> - Updated the copyright year to include 2023.
> 
> Best,
> Lancelot.
> 
> ---
> 
> In non-stop mode, if the user tries to generate a core dump (using the
> gcore command) while some threads are running, a non-helpful error
> message is shown.
> 
> Lets consider the following session as an example (debugging the test
> program included in this patch):
> 
>      (gdb) set non-stop on
>      (gdb) b 37
>      (gdb) r
>      Thread 1 "gcore-nonstop" hit Breakpoint 1, main () at gcore-nonstop.c:39
>      (gdb) info thread
>         Id   Target Id                                          Frame
>       * 1    Thread 0x7ffff7d7a740 (LWP 431838) "gcore-nonstop" main () at gcore-nonstop.c:39
>         2    Thread 0x7ffff7d79640 (LWP 431841) "gcore-nonstop" (running)
>      (gdb) gcore
>      Couldn't get registers: No such process.
> 
> The reported error ("No such process") does not help the user understand
> what happens.  This is due to the fact that we cannot access the
> registers of a running thread.  Even if we ignore this error, generating
> a core dump while any thread might update memory would most likely
> result in a core file with an inconsistent view of the process' memory.
> 
> To solve this, this patch proposes to change the gcore command so it
> first stops all running threads (from the current inferior) before
> generating the corefile, and then resumes them in their previous state.
> 
> To achieve this, this patch exposes the restart_threads function in infrun.h
> (used to be local to infrun.c).  We also allow the first parameter
> (event_thread) to be nullptr as it is possible that the gcore command is
> called while all threads are running, in which case we want all threads
> to be restarted at the end of the procedure.
> 
> When testing this patch against gdbserver, it appears that using
> stop_all_threads / restart_threads was not compatible with all-stop
> targets.  For those targets, we need to call target_stop_and_wait /
> target_resume.  The gcore_command has code to handle both
> configurations.  I could not use a RAII-like object to have a cleaner
> way to restore the state at the end as the restore procedure could
> throw.  Instead, the procedure keeps track of which method was used to
> interrupt threads so the appropriate method can be used to restore their
> state.
> 
> Tested on x86_64 on navite GDB and the native-extended-gdbserver board.
> ---
>   gdb/NEWS                                 |  8 +++
>   gdb/doc/gdb.texinfo                      |  5 ++
>   gdb/gcore.c                              | 30 +++++++++
>   gdb/infrun.c                             | 16 ++---
>   gdb/infrun.h                             |  9 +++
>   gdb/testsuite/gdb.base/gcore-nonstop.c   | 44 +++++++++++++
>   gdb/testsuite/gdb.base/gcore-nonstop.exp | 79 ++++++++++++++++++++++++
>   7 files changed, 180 insertions(+), 11 deletions(-)
>   create mode 100644 gdb/testsuite/gdb.base/gcore-nonstop.c
>   create mode 100644 gdb/testsuite/gdb.base/gcore-nonstop.exp
> 
> diff --git a/gdb/NEWS b/gdb/NEWS
> index 445d28efed9..7f78cbc9008 100644
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -38,6 +38,14 @@ maintenance print record-instruction [ N ]
>     prints how GDB would undo the N-th previous instruction, and if N is
>     positive, it prints how GDB will redo the N-th following instruction.
>   
> +* Changed commands
> +
> +gcore
> +generate-core-file
> +  GDB now ensures that all threads of the current inferior are stopped
> +  before generating a core dump.  At the end of the command, threads are
> +  restored to their previous state.
> +
>   * MI changes
>   
>   ** mi now reports 'no-history' as a stop reason when hitting the end of the
> diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
> index 03033c7f9e3..c637d6eb114 100644
> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo
> @@ -13693,6 +13693,11 @@ Produce a core dump of the inferior process.  The optional argument
>   specified, the file name defaults to @file{core.@var{pid}}, where
>   @var{pid} is the inferior process ID.
>   
> +@value{GDBN} ensures that all threads of the current inferior are
> +stopped while generating the core dump.  If any of the inferior's threads
> +are running when executing this command, @value{GDBN} stops the threads
> +and resumes them when the command is done.
> +
>   Note that this command is implemented only for some systems (as of
>   this writing, @sc{gnu}/Linux, FreeBSD, Solaris, and S390).
>   
> diff --git a/gdb/gcore.c b/gdb/gcore.c
> index 973abadb013..939737d83a1 100644
> --- a/gdb/gcore.c
> +++ b/gdb/gcore.c
> @@ -34,6 +34,7 @@
>   #include "regset.h"
>   #include "gdb_bfd.h"
>   #include "readline/tilde.h"
> +#include "infrun.h"
>   #include <algorithm>
>   #include "gdbsupport/gdb_unlinker.h"
>   #include "gdbsupport/byte-vector.h"
> @@ -131,6 +132,28 @@ gcore_command (const char *args, int from_tty)
>     if (!target_has_execution ())
>       noprocess ();
>   
> +  scoped_restore_current_thread restore_current_thread;
> +  scoped_disable_commit_resumed disable_commit_resume ("generating coredump");
> +  struct inferior *inf = current_inferior ();
> +  scoped_finish_thread_state finish_state (inf->process_target (),
> +					   ptid_t (inf->pid));
> +
> +  bool all_stop_was_running = false;
> +  if (exists_non_stop_target ())
> +    stop_all_threads ("generating coredump", inf);
> +  else
> +    {
> +      all_stop_was_running = any_thread_of_inferior (inf)->executing ();
> +
> +      if (all_stop_was_running)
> +	{
> +	  if (!may_stop)
> +	    error (_("Cannot stop the target to generate the core dump."));
> +
> +	  target_stop_and_wait (ptid_t (inf->pid));
> +	}
> +    }
> +
>     if (args && *args)
>       corefilename.reset (tilde_expand (args));
>     else
> @@ -161,6 +184,13 @@ gcore_command (const char *args, int from_tty)
>       }
>   
>     gdb_printf ("Saved corefile %s\n", corefilename.get ());
> +
> +  if (exists_non_stop_target ())
> +    restart_threads (nullptr, inf);
> +  else if (all_stop_was_running)
> +    target_resume (ptid_t (inf->pid), 0, GDB_SIGNAL_0);
> +
> +  disable_commit_resume.reset_and_commit ();
>   }
>   
>   static enum bfd_architecture
> diff --git a/gdb/infrun.c b/gdb/infrun.c
> index edfb5ab0a91..0e6abc352aa 100644
> --- a/gdb/infrun.c
> +++ b/gdb/infrun.c
> @@ -96,9 +96,6 @@ static void resume (gdb_signal sig);
>   
>   static void wait_for_inferior (inferior *inf);
>   
> -static void restart_threads (struct thread_info *event_thread,
> -			     inferior *inf = nullptr);
> -
>   static bool start_step_over (void);
>   
>   static bool step_over_info_valid_p (void);
> @@ -5857,18 +5854,15 @@ handle_inferior_event (struct execution_control_state *ecs)
>       }
>   }
>   
> -/* Restart threads back to what they were trying to do back when we
> -   paused them (because of an in-line step-over or vfork, for example).
> -   The EVENT_THREAD thread is ignored (not restarted).
> -
> -   If INF is non-nullptr, only resume threads from INF.  */
> +/* See infrun.h.  */
>   
> -static void
> +void
>   restart_threads (struct thread_info *event_thread, inferior *inf)
>   {
>     INFRUN_SCOPED_DEBUG_START_END ("event_thread=%s, inf=%d",
> -				 event_thread->ptid.to_string ().c_str (),
> -				 inf != nullptr ? inf->num : -1);
> +				 (event_thread != nullptr
> +				  ? event_thread->ptid.to_string ().c_str ()
> +				  : "None"), inf != nullptr ? inf->num : -1);
>   
>     gdb_assert (!step_over_info_valid_p ());
>   
> diff --git a/gdb/infrun.h b/gdb/infrun.h
> index 43fd1b44f5a..f6b04934bad 100644
> --- a/gdb/infrun.h
> +++ b/gdb/infrun.h
> @@ -175,6 +175,15 @@ extern void nullify_last_target_wait_ptid ();
>      all threads of all inferiors.  */
>   extern void stop_all_threads (const char *reason, inferior *inf = nullptr);
>   
> +/* Restart threads back to what they were trying to do back when we
> +   paused them (because of an in-line step-over or vfork, for example).
> +   The EVENT_THREAD thread, if non-nullptr, is ignored (not restarted).
> +
> +   If INF is non-nullptr, only resume threads from INF.  */
> +
> +extern void restart_threads (struct thread_info *event_thread,
> +			     inferior *inf = nullptr);
> +
>   extern void prepare_for_detach (void);
>   
>   extern void fetch_inferior_event ();
> diff --git a/gdb/testsuite/gdb.base/gcore-nonstop.c b/gdb/testsuite/gdb.base/gcore-nonstop.c
> new file mode 100644
> index 00000000000..a96c011ad14
> --- /dev/null
> +++ b/gdb/testsuite/gdb.base/gcore-nonstop.c
> @@ -0,0 +1,44 @@
> +/* This testcase is part of GDB, the GNU debugger.
> +
> +   Copyright 2022-2023 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 <pthread.h>
> +
> +static pthread_barrier_t barrier;
> +
> +static void *
> +worker_func (void *ignored)
> +{
> +  pthread_barrier_wait (&barrier);
> +  return NULL;
> +}
> +
> +int
> +main (void)
> +{
> +  pthread_t worker_thread;
> +  pthread_barrier_init (&barrier, NULL, 2);
> +
> +  pthread_create (&worker_thread, NULL, worker_func, NULL);
> +
> +  /* Break here.  */
> +
> +  pthread_barrier_wait (&barrier);
> +  pthread_join (worker_thread, NULL);
> +  pthread_barrier_destroy (&barrier);
> +
> +  return 0;
> +}
> diff --git a/gdb/testsuite/gdb.base/gcore-nonstop.exp b/gdb/testsuite/gdb.base/gcore-nonstop.exp
> new file mode 100644
> index 00000000000..172dd760e73
> --- /dev/null
> +++ b/gdb/testsuite/gdb.base/gcore-nonstop.exp
> @@ -0,0 +1,79 @@
> +# Copyright 2022-2023 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/>.
> +
> +# Check that in non-stop mode, if some threads are running when the user
> +# launches the "gcore" command, GDB suspends all threads, generates the core
> +# file and resumes threads which where running before the "gcore" command
> +# got issued.  If running threads were not stopped, GDB would report errors
> +# when trying to capture the thread's state.
> +
> +standard_testfile
> +
> +if { [build_executable "failed to build" \
> +	${testfile} ${srcfile} {pthreads debug}] } {
> +    return
> +}
> +
> +save_vars { GDBFLAGS } {
> +    append GDBFLAGS " -ex \"set non-stop on\""
> +    clean_restart ${binfile}
> +}
> +
> +set lineno [gdb_get_line_number "Break here"]
> +if { ![runto $lineno] } {
> +    return
> +}
> +
> +# We should be stopped in thread 1 while thread 2 is running.
> +gdb_test_sequence "info threads" "info threads" {
> +    {Id\s+Target Id\s+Frame}
> +    {\*\s+1[^\n]*\n}
> +    {\s+2\s+[^\n]*\(running\)[^\n]*\n}
> +}
> +
> +set th1_pc ""
> +gdb_test_multiple "p/x \$pc" "fetch thread 1 PC" {
> +  -wrap -re "$::decimal = ($::hex)" {
> +    set th1_pc $expect_out(1,string)
> +    pass $gdb_test_name
> +  }
> +}
> +
> +set corefile [standard_output_file "corefile"]
> +if {![gdb_gcore_cmd $corefile "generate corefile"]} {
> +  # gdb_gcore_cmd issues a "UNSUPPORTED".
> +  return
> +}
> +
> +# After the core file is generated, thread 2 should be back running
> +# and thread 1 should still be selected.
> +gdb_test_sequence "info threads" "correct thread selection after gcore" {
> +    {Id\s+Target Id\s+Frame}
> +    {\*\s+1[^\n]*\n}
> +    {\s+2\s+[^\n]*\(running\)[^\n]*\n}
> +}
> +
> +# Thread 1 is at the same PC it was before calling the gcore command.
> +gdb_test "p/x \$pc" "\\\$$::decimal = $th1_pc" "thread 1 unchanged"
> +
> +clean_restart $binfile
> +gdb_test "core-file $corefile" "Core was generated by.*" "load corefile"
> +
> +# The core file has the 2 threads.
> +gdb_test_sequence "info threads" "threads in corefile" {
> +    {Id\s+Target Id\s+Frame}
> +    {\s+1\s+Thread[^\n]*\n}
> +    {\s+2\s+Thread[^\n]*\n}
> +}
> 
> base-commit: 5867ab870b8aa36ae490ec6e4e8e4c55be11ccf1

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v5] gdb/gcore: interrupt all threads before generating the corefile
  2023-01-30 16:51 [PATCH v5] gdb/gcore: interrupt all threads before generating the corefile Lancelot SIX
  2023-02-10 14:36 ` [PING] " Lancelot SIX
@ 2023-02-10 21:46 ` Simon Marchi
  2023-03-22 16:35   ` [PATCH v6 0/3] interrupt all threads to generate core in non-stop targets Lancelot SIX
  1 sibling, 1 reply; 12+ messages in thread
From: Simon Marchi @ 2023-02-10 21:46 UTC (permalink / raw)
  To: Lancelot SIX, gdb-patches; +Cc: lsix

On 1/30/23 11:51, Lancelot SIX wrote:
> Hi, this V5 follows
> https://sourceware.org/pipermail/gdb-patches/2022-December/194504.html.
> 
> Changes since V4:
> 
> - Update gdb/NEWS to fall into the "Changes since GDB 13" section.
> - Use "build_executable" instead of "prepare_for_testing" in the test
>   to avoid one useless start of GDB.
> - Updated the test's description.
> - Updated the copyright year to include 2023.
> 
> Best,
> Lancelot.

Hi Lancelot,

It seems like this version does not address the comment I made on v4,
the "what if a thread is doing an inline step" when we call this.  Maybe
another way to look at it is, if the inferior was resumed with schedlock
on.  Only one thread is "executing", and any_thread_of_inferior could
fall on a non-executing thread.  Also, we'd want to end in the same
state as where we started.  Can you check that case (with schedlock)?  I
have the feeling that it's not handled.

Simon

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH v6 0/3] interrupt all threads to generate core in non-stop targets
  2023-02-10 21:46 ` Simon Marchi
@ 2023-03-22 16:35   ` Lancelot SIX
  2023-03-22 16:35     ` [PATCH v6 1/3] gdb: add inferior parameter to target_is_non_stop_p Lancelot SIX
                       ` (2 more replies)
  0 siblings, 3 replies; 12+ messages in thread
From: Lancelot SIX @ 2023-03-22 16:35 UTC (permalink / raw)
  To: gdb-patches; +Cc: lsix, Lancelot SIX

Hi,

Here is a follow-up to
https://sourceware.org/pipermail/gdb-patches/2023-January/196393.html.

Noticeable changes from last revision:

- Added patch #1  and #2 to improve the assertion at the beginning
  of stop-all-threads.

- With all-stop targets, just check if any thread is still marked as
  executing and error-out with a meaningful error message when
  appropriate.  This is not ideal as all-stop is not on par with
  non-stop, but this patch is still overall a step in the right
  direction in my opinion.

  This is because the previous implementation was most likely
  erroneous.  Among other things, It did rely on target_stop_on_wait,
  which internally calls target_wait and discards the resulting
  target_waitstatus.  This does not seem to be the right thing to
  do.

- Adjusted documentation/NEWS to distinguish all-stop/non-stop
  behaviors.


Lancelot SIX (3):
  gdb: add inferior parameter to target_is_non_stop_p
  gdb/infrun: Improve assertion in stop_all_threads
  gdb/gcore: interrupt all threads to generate core in non-stop targets

 gdb/NEWS                                 |  8 +++
 gdb/doc/gdb.texinfo                      |  5 ++
 gdb/gcore.c                              | 20 ++++++
 gdb/infrun.c                             | 21 +++----
 gdb/infrun.h                             |  9 +++
 gdb/target.c                             |  9 ++-
 gdb/target.h                             |  8 ++-
 gdb/testsuite/gdb.base/gcore-nonstop.c   | 44 +++++++++++++
 gdb/testsuite/gdb.base/gcore-nonstop.exp | 79 ++++++++++++++++++++++++
 9 files changed, 187 insertions(+), 16 deletions(-)
 create mode 100644 gdb/testsuite/gdb.base/gcore-nonstop.c
 create mode 100644 gdb/testsuite/gdb.base/gcore-nonstop.exp


base-commit: 6e7eef72164c00d6a5a7b0bce9fa01f5481f33cb
-- 
2.34.1


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH v6 1/3] gdb: add inferior parameter to target_is_non_stop_p
  2023-03-22 16:35   ` [PATCH v6 0/3] interrupt all threads to generate core in non-stop targets Lancelot SIX
@ 2023-03-22 16:35     ` Lancelot SIX
  2023-03-22 16:35     ` [PATCH v6 2/3] gdb/infrun: Improve assertion in stop_all_threads Lancelot SIX
  2023-03-22 16:35     ` [PATCH v6 3/3] gdb/gcore: interrupt all threads to generate core in non-stop targets Lancelot SIX
  2 siblings, 0 replies; 12+ messages in thread
From: Lancelot SIX @ 2023-03-22 16:35 UTC (permalink / raw)
  To: gdb-patches; +Cc: lsix, Lancelot SIX

This patch add a "inferior *inf" parameter to the target_is_non_stop_p
function.  The default value is nullptr so all existing calls remain
valid.

This will be used in the following patch.
---
 gdb/target.c | 9 ++++++++-
 gdb/target.h | 8 +++++---
 2 files changed, 13 insertions(+), 4 deletions(-)

diff --git a/gdb/target.c b/gdb/target.c
index 0cebecfafc3..5e5d1054541 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -4391,8 +4391,15 @@ target_always_non_stop_p (void)
 /* See target.h.  */
 
 bool
-target_is_non_stop_p ()
+target_is_non_stop_p (inferior *inf)
 {
+  gdb::optional<scoped_restore_current_thread> restore_thread;
+  if (inf != nullptr && inf != current_inferior ())
+    {
+      restore_thread.emplace ();
+      switch_to_inferior_no_thread (inf);
+    }
+
   return ((non_stop
 	   || target_non_stop_enabled == AUTO_BOOLEAN_TRUE
 	   || (target_non_stop_enabled == AUTO_BOOLEAN_AUTO
diff --git a/gdb/target.h b/gdb/target.h
index 2dac86c394d..ffa6d95becd 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -1892,10 +1892,12 @@ extern void target_thread_events (int enable);
    non-stop mode is enabled.  */
 extern enum auto_boolean target_non_stop_enabled;
 
-/* Is the target in non-stop mode?  Some targets control the inferior
+/* Is INF's target in non-stop mode?  Some targets control the inferior
    in non-stop mode even with "set non-stop off".  Always true if "set
-   non-stop" is on.  */
-extern bool target_is_non_stop_p ();
+   non-stop" is on.
+
+   If INF is nullptr, check the current inferior.  */
+extern bool target_is_non_stop_p (inferior *inf = nullptr);
 
 /* Return true if at least one inferior has a non-stop target.  */
 extern bool exists_non_stop_target ();
-- 
2.34.1


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH v6 2/3] gdb/infrun: Improve assertion in stop_all_threads
  2023-03-22 16:35   ` [PATCH v6 0/3] interrupt all threads to generate core in non-stop targets Lancelot SIX
  2023-03-22 16:35     ` [PATCH v6 1/3] gdb: add inferior parameter to target_is_non_stop_p Lancelot SIX
@ 2023-03-22 16:35     ` Lancelot SIX
  2023-03-22 16:35     ` [PATCH v6 3/3] gdb/gcore: interrupt all threads to generate core in non-stop targets Lancelot SIX
  2 siblings, 0 replies; 12+ messages in thread
From: Lancelot SIX @ 2023-03-22 16:35 UTC (permalink / raw)
  To: gdb-patches; +Cc: lsix, Lancelot SIX

The stop_all_threads function has an assertion to check that there is a
non-stop target.  However, when called with a non-NULL INF parameter, we
only need INF's target to be non-stop.

This patch updates the assertion.  If INF is non-NULL, check that INF's
target is non-stop, otherwise keep the assertion as before.
---
 gdb/infrun.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/gdb/infrun.c b/gdb/infrun.c
index 5c9babb9104..a509e0d5bef 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -5189,7 +5189,10 @@ stop_all_threads (const char *reason, inferior *inf)
   int pass;
   int iterations = 0;
 
-  gdb_assert (exists_non_stop_target ());
+  if (inf == nullptr)
+    gdb_assert (exists_non_stop_target ());
+  else
+    gdb_assert (target_is_non_stop_p (inf));
 
   INFRUN_SCOPED_DEBUG_START_END ("reason=%s, inf=%d", reason,
 				 inf != nullptr ? inf->num : -1);
-- 
2.34.1


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH v6 3/3] gdb/gcore: interrupt all threads to generate core in non-stop targets
  2023-03-22 16:35   ` [PATCH v6 0/3] interrupt all threads to generate core in non-stop targets Lancelot SIX
  2023-03-22 16:35     ` [PATCH v6 1/3] gdb: add inferior parameter to target_is_non_stop_p Lancelot SIX
  2023-03-22 16:35     ` [PATCH v6 2/3] gdb/infrun: Improve assertion in stop_all_threads Lancelot SIX
@ 2023-03-22 16:35     ` Lancelot SIX
  2023-03-22 16:44       ` Eli Zaretskii
  2023-05-10 18:46       ` Lancelot SIX
  2 siblings, 2 replies; 12+ messages in thread
From: Lancelot SIX @ 2023-03-22 16:35 UTC (permalink / raw)
  To: gdb-patches; +Cc: lsix, Lancelot SIX

If the user tries to generate a core dump (using the gcore command)
while some threads are running, a non-helpful error message is shown.

Lets consider the following session as an example (debugging the test
program included in this patch):

    (gdb) set non-stop on
    (gdb) b 37
    (gdb) r
    Thread 1 "gcore-nonstop" hit Breakpoint 1, main () at gcore-nonstop.c:39
    (gdb) info thread
       Id   Target Id                                          Frame
     * 1    Thread 0x7ffff7d7a740 (LWP 431838) "gcore-nonstop" main () at gcore-nonstop.c:39
       2    Thread 0x7ffff7d79640 (LWP 431841) "gcore-nonstop" (running)
    (gdb) gcore
    Couldn't get registers: No such process.

The reported error ("No such process") does not help the user understand
what happens.  This is due to the fact that we cannot access the
registers of a running thread.  Even if we ignore this error, generating
a core dump while any thread might update memory would most likely
result in a core file with an inconsistent view of the process' memory.

This patch proposes to change this.

For non-stop targets change the gcore command so it first stops all
running threads (from the current inferior) before generating the
corefile, and then resumes them in their previous state.  To achieve
this, this patch exposes the restart_threads function in infrun.h (used
to be local to infrun.c).  We also allow the first parameter
(event_thread) to be nullptr as it is possible that the gcore command is
called while all threads are running, in which case we want all threads
to be restarted at the end of the procedure.

Similar change for all-stop targets turns out to be less straight
forward.  For this reason, when dealing with all-stop targets, this
patch proposes to error-out early with a meaningful message.

Tested on x86_64 on native GDB and the native-extended-gdbserver board.
---
 gdb/NEWS                                 |  8 +++
 gdb/doc/gdb.texinfo                      |  5 ++
 gdb/gcore.c                              | 20 ++++++
 gdb/infrun.c                             | 16 ++---
 gdb/infrun.h                             |  9 +++
 gdb/testsuite/gdb.base/gcore-nonstop.c   | 44 +++++++++++++
 gdb/testsuite/gdb.base/gcore-nonstop.exp | 79 ++++++++++++++++++++++++
 7 files changed, 170 insertions(+), 11 deletions(-)
 create mode 100644 gdb/testsuite/gdb.base/gcore-nonstop.c
 create mode 100644 gdb/testsuite/gdb.base/gcore-nonstop.exp

diff --git a/gdb/NEWS b/gdb/NEWS
index cc262f1f8a6..65ac722bd2e 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -91,6 +91,14 @@ show always-read-ctf
    $2 = 1
    (gdb) break func if $_shell("some command") == 0
 
+* Changed commands
+
+gcore
+generate-core-file
+  For non-stop targets, GDB ensures that all threads are stopped before
+  generating a core dump.  At the end of the command, threads are restored
+  to their previous state.
+
 * MI changes
 
 ** mi now reports 'no-history' as a stop reason when hitting the end of the
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 6c811b8be2e..574684d9e28 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -13763,6 +13763,11 @@ Produce a core dump of the inferior process.  The optional argument
 specified, the file name defaults to @file{core.@var{pid}}, where
 @var{pid} is the inferior process ID.
 
+If the current inferior's target is non-stop, @value{GDBN} ensures that
+all the inferior's threads are stopped while generating the core dump.
+@value{GDBN} stops every running threads of the inferior before
+generating the core dump and resumes them when the command is done.
+
 Note that this command is implemented only for some systems (as of
 this writing, @sc{gnu}/Linux, FreeBSD, Solaris, and S390).
 
diff --git a/gdb/gcore.c b/gdb/gcore.c
index 973abadb013..217976ac4e0 100644
--- a/gdb/gcore.c
+++ b/gdb/gcore.c
@@ -34,6 +34,7 @@
 #include "regset.h"
 #include "gdb_bfd.h"
 #include "readline/tilde.h"
+#include "infrun.h"
 #include <algorithm>
 #include "gdbsupport/gdb_unlinker.h"
 #include "gdbsupport/byte-vector.h"
@@ -131,6 +132,20 @@ gcore_command (const char *args, int from_tty)
   if (!target_has_execution ())
     noprocess ();
 
+  scoped_restore_current_thread restore_current_thread;
+  scoped_disable_commit_resumed disable_commit_resume ("generating coredump");
+  struct inferior *inf = current_inferior ();
+  scoped_finish_thread_state finish_state (inf->process_target (),
+					   ptid_t (inf->pid));
+
+  if (target_is_non_stop_p (inf))
+    stop_all_threads ("generating coredump", inf);
+  else
+    for (thread_info *thread : inf->non_exited_threads ())
+      if (thread->executing ())
+	error (_("At least one thread is still running, cannot generate a "
+		 "core dump."));
+
   if (args && *args)
     corefilename.reset (tilde_expand (args));
   else
@@ -161,6 +176,11 @@ gcore_command (const char *args, int from_tty)
     }
 
   gdb_printf ("Saved corefile %s\n", corefilename.get ());
+
+  if (exists_non_stop_target ())
+    restart_threads (nullptr, inf);
+
+  disable_commit_resume.reset_and_commit ();
 }
 
 static enum bfd_architecture
diff --git a/gdb/infrun.c b/gdb/infrun.c
index a509e0d5bef..7364bfa623c 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -97,9 +97,6 @@ static void resume (gdb_signal sig);
 
 static void wait_for_inferior (inferior *inf);
 
-static void restart_threads (struct thread_info *event_thread,
-			     inferior *inf = nullptr);
-
 static bool start_step_over (void);
 
 static bool step_over_info_valid_p (void);
@@ -6008,18 +6005,15 @@ handle_inferior_event (struct execution_control_state *ecs)
     }
 }
 
-/* Restart threads back to what they were trying to do back when we
-   paused them (because of an in-line step-over or vfork, for example).
-   The EVENT_THREAD thread is ignored (not restarted).
-
-   If INF is non-nullptr, only resume threads from INF.  */
+/* See infrun.h.  */
 
-static void
+void
 restart_threads (struct thread_info *event_thread, inferior *inf)
 {
   INFRUN_SCOPED_DEBUG_START_END ("event_thread=%s, inf=%d",
-				 event_thread->ptid.to_string ().c_str (),
-				 inf != nullptr ? inf->num : -1);
+				 (event_thread != nullptr
+				  ? event_thread->ptid.to_string ().c_str ()
+				  : "None"), inf != nullptr ? inf->num : -1);
 
   gdb_assert (!step_over_info_valid_p ());
 
diff --git a/gdb/infrun.h b/gdb/infrun.h
index 5219063586d..4b2a04e503b 100644
--- a/gdb/infrun.h
+++ b/gdb/infrun.h
@@ -182,6 +182,15 @@ extern void nullify_last_target_wait_ptid ();
    all threads of all inferiors.  */
 extern void stop_all_threads (const char *reason, inferior *inf = nullptr);
 
+/* Restart threads back to what they were trying to do back when we
+   paused them (because of an in-line step-over or vfork, for example).
+   The EVENT_THREAD thread, if non-nullptr, is ignored (not restarted).
+
+   If INF is non-nullptr, only resume threads from INF.  */
+
+extern void restart_threads (struct thread_info *event_thread,
+			     inferior *inf = nullptr);
+
 extern void prepare_for_detach (void);
 
 extern void fetch_inferior_event ();
diff --git a/gdb/testsuite/gdb.base/gcore-nonstop.c b/gdb/testsuite/gdb.base/gcore-nonstop.c
new file mode 100644
index 00000000000..a96c011ad14
--- /dev/null
+++ b/gdb/testsuite/gdb.base/gcore-nonstop.c
@@ -0,0 +1,44 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2022-2023 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 <pthread.h>
+
+static pthread_barrier_t barrier;
+
+static void *
+worker_func (void *ignored)
+{
+  pthread_barrier_wait (&barrier);
+  return NULL;
+}
+
+int
+main (void)
+{
+  pthread_t worker_thread;
+  pthread_barrier_init (&barrier, NULL, 2);
+
+  pthread_create (&worker_thread, NULL, worker_func, NULL);
+
+  /* Break here.  */
+
+  pthread_barrier_wait (&barrier);
+  pthread_join (worker_thread, NULL);
+  pthread_barrier_destroy (&barrier);
+
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.base/gcore-nonstop.exp b/gdb/testsuite/gdb.base/gcore-nonstop.exp
new file mode 100644
index 00000000000..172dd760e73
--- /dev/null
+++ b/gdb/testsuite/gdb.base/gcore-nonstop.exp
@@ -0,0 +1,79 @@
+# Copyright 2022-2023 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/>.
+
+# Check that in non-stop mode, if some threads are running when the user
+# launches the "gcore" command, GDB suspends all threads, generates the core
+# file and resumes threads which where running before the "gcore" command
+# got issued.  If running threads were not stopped, GDB would report errors
+# when trying to capture the thread's state.
+
+standard_testfile
+
+if { [build_executable "failed to build" \
+	${testfile} ${srcfile} {pthreads debug}] } {
+    return
+}
+
+save_vars { GDBFLAGS } {
+    append GDBFLAGS " -ex \"set non-stop on\""
+    clean_restart ${binfile}
+}
+
+set lineno [gdb_get_line_number "Break here"]
+if { ![runto $lineno] } {
+    return
+}
+
+# We should be stopped in thread 1 while thread 2 is running.
+gdb_test_sequence "info threads" "info threads" {
+    {Id\s+Target Id\s+Frame}
+    {\*\s+1[^\n]*\n}
+    {\s+2\s+[^\n]*\(running\)[^\n]*\n}
+}
+
+set th1_pc ""
+gdb_test_multiple "p/x \$pc" "fetch thread 1 PC" {
+  -wrap -re "$::decimal = ($::hex)" {
+    set th1_pc $expect_out(1,string)
+    pass $gdb_test_name
+  }
+}
+
+set corefile [standard_output_file "corefile"]
+if {![gdb_gcore_cmd $corefile "generate corefile"]} {
+  # gdb_gcore_cmd issues a "UNSUPPORTED".
+  return
+}
+
+# After the core file is generated, thread 2 should be back running
+# and thread 1 should still be selected.
+gdb_test_sequence "info threads" "correct thread selection after gcore" {
+    {Id\s+Target Id\s+Frame}
+    {\*\s+1[^\n]*\n}
+    {\s+2\s+[^\n]*\(running\)[^\n]*\n}
+}
+
+# Thread 1 is at the same PC it was before calling the gcore command.
+gdb_test "p/x \$pc" "\\\$$::decimal = $th1_pc" "thread 1 unchanged"
+
+clean_restart $binfile
+gdb_test "core-file $corefile" "Core was generated by.*" "load corefile"
+
+# The core file has the 2 threads.
+gdb_test_sequence "info threads" "threads in corefile" {
+    {Id\s+Target Id\s+Frame}
+    {\s+1\s+Thread[^\n]*\n}
+    {\s+2\s+Thread[^\n]*\n}
+}
-- 
2.34.1


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v6 3/3] gdb/gcore: interrupt all threads to generate core in non-stop targets
  2023-03-22 16:35     ` [PATCH v6 3/3] gdb/gcore: interrupt all threads to generate core in non-stop targets Lancelot SIX
@ 2023-03-22 16:44       ` Eli Zaretskii
  2023-03-22 16:59         ` Lancelot SIX
  2023-05-10 18:46       ` Lancelot SIX
  1 sibling, 1 reply; 12+ messages in thread
From: Eli Zaretskii @ 2023-03-22 16:44 UTC (permalink / raw)
  To: Lancelot SIX; +Cc: gdb-patches, lsix

> CC: <lsix@lancelotsix.com>, Lancelot SIX <lancelot.six@amd.com>
> Date: Wed, 22 Mar 2023 16:35:04 +0000
> From: Lancelot SIX via Gdb-patches <gdb-patches@sourceware.org>
> 
> diff --git a/gdb/NEWS b/gdb/NEWS
> index cc262f1f8a6..65ac722bd2e 100644
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -91,6 +91,14 @@ show always-read-ctf
>     $2 = 1
>     (gdb) break func if $_shell("some command") == 0
>  
> +* Changed commands
> +
> +gcore
> +generate-core-file
> +  For non-stop targets, GDB ensures that all threads are stopped before
> +  generating a core dump.  At the end of the command, threads are restored
> +  to their previous state.
> +
>  * MI changes

This part is OK.

> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo
> @@ -13763,6 +13763,11 @@ Produce a core dump of the inferior process.  The optional argument
>  specified, the file name defaults to @file{core.@var{pid}}, where
>  @var{pid} is the inferior process ID.
>  
> +If the current inferior's target is non-stop, @value{GDBN} ensures that

Please add a cross-reference after "non-stop" to where we describe
that mode.

Thanks.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v6 3/3] gdb/gcore: interrupt all threads to generate core in non-stop targets
  2023-03-22 16:44       ` Eli Zaretskii
@ 2023-03-22 16:59         ` Lancelot SIX
  0 siblings, 0 replies; 12+ messages in thread
From: Lancelot SIX @ 2023-03-22 16:59 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: gdb-patches, lsix

Hi Eli,

>>
>> +If the current inferior's target is non-stop, @value{GDBN} ensures that
> 
> Please add a cross-reference after "non-stop" to where we describe
> that mode.

I have done the change locally.  This part of the patch is now:

diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 6c811b8be2e..00949b4b950 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -13763,6 +13763,12 @@ Produce a core dump of the inferior process. 
The optional argument
  specified, the file name defaults to @file{core.@var{pid}}, where
  @var{pid} is the inferior process ID.

+If the current inferior's target is non-stop  (@pxref{Non-Stop Mode}),
+@value{GDBN} ensures that all the inferior's threads are stopped while
+generating the core dump.  @value{GDBN} stops every running threads of
+the inferior before generating the core dump and resumes them when the
+command is done.
+
  Note that this command is implemented only for some systems (as of
  this writing, @sc{gnu}/Linux, FreeBSD, Solaris, and S390).

Best,
Lancelot.

> 
> Thanks.
> 
> Reviewed-By: Eli Zaretskii <eliz@gnu.org>

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v6 3/3] gdb/gcore: interrupt all threads to generate core in non-stop targets
  2023-03-22 16:35     ` [PATCH v6 3/3] gdb/gcore: interrupt all threads to generate core in non-stop targets Lancelot SIX
  2023-03-22 16:44       ` Eli Zaretskii
@ 2023-05-10 18:46       ` Lancelot SIX
  2023-05-10 19:05         ` Eli Zaretskii
  1 sibling, 1 reply; 12+ messages in thread
From: Lancelot SIX @ 2023-05-10 18:46 UTC (permalink / raw)
  To: gdb-patches; +Cc: lsix

Hi,

Kindly pinging on this patch.

Best,
Lancelot.

On 22/03/2023 16:35, Lancelot SIX wrote:
> If the user tries to generate a core dump (using the gcore command)
> while some threads are running, a non-helpful error message is shown.
> 
> Lets consider the following session as an example (debugging the test
> program included in this patch):
> 
>      (gdb) set non-stop on
>      (gdb) b 37
>      (gdb) r
>      Thread 1 "gcore-nonstop" hit Breakpoint 1, main () at gcore-nonstop.c:39
>      (gdb) info thread
>         Id   Target Id                                          Frame
>       * 1    Thread 0x7ffff7d7a740 (LWP 431838) "gcore-nonstop" main () at gcore-nonstop.c:39
>         2    Thread 0x7ffff7d79640 (LWP 431841) "gcore-nonstop" (running)
>      (gdb) gcore
>      Couldn't get registers: No such process.
> 
> The reported error ("No such process") does not help the user understand
> what happens.  This is due to the fact that we cannot access the
> registers of a running thread.  Even if we ignore this error, generating
> a core dump while any thread might update memory would most likely
> result in a core file with an inconsistent view of the process' memory.
> 
> This patch proposes to change this.
> 
> For non-stop targets change the gcore command so it first stops all
> running threads (from the current inferior) before generating the
> corefile, and then resumes them in their previous state.  To achieve
> this, this patch exposes the restart_threads function in infrun.h (used
> to be local to infrun.c).  We also allow the first parameter
> (event_thread) to be nullptr as it is possible that the gcore command is
> called while all threads are running, in which case we want all threads
> to be restarted at the end of the procedure.
> 
> Similar change for all-stop targets turns out to be less straight
> forward.  For this reason, when dealing with all-stop targets, this
> patch proposes to error-out early with a meaningful message.
> 
> Tested on x86_64 on native GDB and the native-extended-gdbserver board.
> ---
>   gdb/NEWS                                 |  8 +++
>   gdb/doc/gdb.texinfo                      |  5 ++
>   gdb/gcore.c                              | 20 ++++++
>   gdb/infrun.c                             | 16 ++---
>   gdb/infrun.h                             |  9 +++
>   gdb/testsuite/gdb.base/gcore-nonstop.c   | 44 +++++++++++++
>   gdb/testsuite/gdb.base/gcore-nonstop.exp | 79 ++++++++++++++++++++++++
>   7 files changed, 170 insertions(+), 11 deletions(-)
>   create mode 100644 gdb/testsuite/gdb.base/gcore-nonstop.c
>   create mode 100644 gdb/testsuite/gdb.base/gcore-nonstop.exp
> 
> diff --git a/gdb/NEWS b/gdb/NEWS
> index cc262f1f8a6..65ac722bd2e 100644
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -91,6 +91,14 @@ show always-read-ctf
>      $2 = 1
>      (gdb) break func if $_shell("some command") == 0
>   
> +* Changed commands
> +
> +gcore
> +generate-core-file
> +  For non-stop targets, GDB ensures that all threads are stopped before
> +  generating a core dump.  At the end of the command, threads are restored
> +  to their previous state.
> +
>   * MI changes
>   
>   ** mi now reports 'no-history' as a stop reason when hitting the end of the
> diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
> index 6c811b8be2e..574684d9e28 100644
> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo
> @@ -13763,6 +13763,11 @@ Produce a core dump of the inferior process.  The optional argument
>   specified, the file name defaults to @file{core.@var{pid}}, where
>   @var{pid} is the inferior process ID.
>   
> +If the current inferior's target is non-stop, @value{GDBN} ensures that
> +all the inferior's threads are stopped while generating the core dump.
> +@value{GDBN} stops every running threads of the inferior before
> +generating the core dump and resumes them when the command is done.
> +
>   Note that this command is implemented only for some systems (as of
>   this writing, @sc{gnu}/Linux, FreeBSD, Solaris, and S390).
>   
> diff --git a/gdb/gcore.c b/gdb/gcore.c
> index 973abadb013..217976ac4e0 100644
> --- a/gdb/gcore.c
> +++ b/gdb/gcore.c
> @@ -34,6 +34,7 @@
>   #include "regset.h"
>   #include "gdb_bfd.h"
>   #include "readline/tilde.h"
> +#include "infrun.h"
>   #include <algorithm>
>   #include "gdbsupport/gdb_unlinker.h"
>   #include "gdbsupport/byte-vector.h"
> @@ -131,6 +132,20 @@ gcore_command (const char *args, int from_tty)
>     if (!target_has_execution ())
>       noprocess ();
>   
> +  scoped_restore_current_thread restore_current_thread;
> +  scoped_disable_commit_resumed disable_commit_resume ("generating coredump");
> +  struct inferior *inf = current_inferior ();
> +  scoped_finish_thread_state finish_state (inf->process_target (),
> +					   ptid_t (inf->pid));
> +
> +  if (target_is_non_stop_p (inf))
> +    stop_all_threads ("generating coredump", inf);
> +  else
> +    for (thread_info *thread : inf->non_exited_threads ())
> +      if (thread->executing ())
> +	error (_("At least one thread is still running, cannot generate a "
> +		 "core dump."));
> +
>     if (args && *args)
>       corefilename.reset (tilde_expand (args));
>     else
> @@ -161,6 +176,11 @@ gcore_command (const char *args, int from_tty)
>       }
>   
>     gdb_printf ("Saved corefile %s\n", corefilename.get ());
> +
> +  if (exists_non_stop_target ())
> +    restart_threads (nullptr, inf);
> +
> +  disable_commit_resume.reset_and_commit ();
>   }
>   
>   static enum bfd_architecture
> diff --git a/gdb/infrun.c b/gdb/infrun.c
> index a509e0d5bef..7364bfa623c 100644
> --- a/gdb/infrun.c
> +++ b/gdb/infrun.c
> @@ -97,9 +97,6 @@ static void resume (gdb_signal sig);
>   
>   static void wait_for_inferior (inferior *inf);
>   
> -static void restart_threads (struct thread_info *event_thread,
> -			     inferior *inf = nullptr);
> -
>   static bool start_step_over (void);
>   
>   static bool step_over_info_valid_p (void);
> @@ -6008,18 +6005,15 @@ handle_inferior_event (struct execution_control_state *ecs)
>       }
>   }
>   
> -/* Restart threads back to what they were trying to do back when we
> -   paused them (because of an in-line step-over or vfork, for example).
> -   The EVENT_THREAD thread is ignored (not restarted).
> -
> -   If INF is non-nullptr, only resume threads from INF.  */
> +/* See infrun.h.  */
>   
> -static void
> +void
>   restart_threads (struct thread_info *event_thread, inferior *inf)
>   {
>     INFRUN_SCOPED_DEBUG_START_END ("event_thread=%s, inf=%d",
> -				 event_thread->ptid.to_string ().c_str (),
> -				 inf != nullptr ? inf->num : -1);
> +				 (event_thread != nullptr
> +				  ? event_thread->ptid.to_string ().c_str ()
> +				  : "None"), inf != nullptr ? inf->num : -1);
>   
>     gdb_assert (!step_over_info_valid_p ());
>   
> diff --git a/gdb/infrun.h b/gdb/infrun.h
> index 5219063586d..4b2a04e503b 100644
> --- a/gdb/infrun.h
> +++ b/gdb/infrun.h
> @@ -182,6 +182,15 @@ extern void nullify_last_target_wait_ptid ();
>      all threads of all inferiors.  */
>   extern void stop_all_threads (const char *reason, inferior *inf = nullptr);
>   
> +/* Restart threads back to what they were trying to do back when we
> +   paused them (because of an in-line step-over or vfork, for example).
> +   The EVENT_THREAD thread, if non-nullptr, is ignored (not restarted).
> +
> +   If INF is non-nullptr, only resume threads from INF.  */
> +
> +extern void restart_threads (struct thread_info *event_thread,
> +			     inferior *inf = nullptr);
> +
>   extern void prepare_for_detach (void);
>   
>   extern void fetch_inferior_event ();
> diff --git a/gdb/testsuite/gdb.base/gcore-nonstop.c b/gdb/testsuite/gdb.base/gcore-nonstop.c
> new file mode 100644
> index 00000000000..a96c011ad14
> --- /dev/null
> +++ b/gdb/testsuite/gdb.base/gcore-nonstop.c
> @@ -0,0 +1,44 @@
> +/* This testcase is part of GDB, the GNU debugger.
> +
> +   Copyright 2022-2023 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 <pthread.h>
> +
> +static pthread_barrier_t barrier;
> +
> +static void *
> +worker_func (void *ignored)
> +{
> +  pthread_barrier_wait (&barrier);
> +  return NULL;
> +}
> +
> +int
> +main (void)
> +{
> +  pthread_t worker_thread;
> +  pthread_barrier_init (&barrier, NULL, 2);
> +
> +  pthread_create (&worker_thread, NULL, worker_func, NULL);
> +
> +  /* Break here.  */
> +
> +  pthread_barrier_wait (&barrier);
> +  pthread_join (worker_thread, NULL);
> +  pthread_barrier_destroy (&barrier);
> +
> +  return 0;
> +}
> diff --git a/gdb/testsuite/gdb.base/gcore-nonstop.exp b/gdb/testsuite/gdb.base/gcore-nonstop.exp
> new file mode 100644
> index 00000000000..172dd760e73
> --- /dev/null
> +++ b/gdb/testsuite/gdb.base/gcore-nonstop.exp
> @@ -0,0 +1,79 @@
> +# Copyright 2022-2023 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/>.
> +
> +# Check that in non-stop mode, if some threads are running when the user
> +# launches the "gcore" command, GDB suspends all threads, generates the core
> +# file and resumes threads which where running before the "gcore" command
> +# got issued.  If running threads were not stopped, GDB would report errors
> +# when trying to capture the thread's state.
> +
> +standard_testfile
> +
> +if { [build_executable "failed to build" \
> +	${testfile} ${srcfile} {pthreads debug}] } {
> +    return
> +}
> +
> +save_vars { GDBFLAGS } {
> +    append GDBFLAGS " -ex \"set non-stop on\""
> +    clean_restart ${binfile}
> +}
> +
> +set lineno [gdb_get_line_number "Break here"]
> +if { ![runto $lineno] } {
> +    return
> +}
> +
> +# We should be stopped in thread 1 while thread 2 is running.
> +gdb_test_sequence "info threads" "info threads" {
> +    {Id\s+Target Id\s+Frame}
> +    {\*\s+1[^\n]*\n}
> +    {\s+2\s+[^\n]*\(running\)[^\n]*\n}
> +}
> +
> +set th1_pc ""
> +gdb_test_multiple "p/x \$pc" "fetch thread 1 PC" {
> +  -wrap -re "$::decimal = ($::hex)" {
> +    set th1_pc $expect_out(1,string)
> +    pass $gdb_test_name
> +  }
> +}
> +
> +set corefile [standard_output_file "corefile"]
> +if {![gdb_gcore_cmd $corefile "generate corefile"]} {
> +  # gdb_gcore_cmd issues a "UNSUPPORTED".
> +  return
> +}
> +
> +# After the core file is generated, thread 2 should be back running
> +# and thread 1 should still be selected.
> +gdb_test_sequence "info threads" "correct thread selection after gcore" {
> +    {Id\s+Target Id\s+Frame}
> +    {\*\s+1[^\n]*\n}
> +    {\s+2\s+[^\n]*\(running\)[^\n]*\n}
> +}
> +
> +# Thread 1 is at the same PC it was before calling the gcore command.
> +gdb_test "p/x \$pc" "\\\$$::decimal = $th1_pc" "thread 1 unchanged"
> +
> +clean_restart $binfile
> +gdb_test "core-file $corefile" "Core was generated by.*" "load corefile"
> +
> +# The core file has the 2 threads.
> +gdb_test_sequence "info threads" "threads in corefile" {
> +    {Id\s+Target Id\s+Frame}
> +    {\s+1\s+Thread[^\n]*\n}
> +    {\s+2\s+Thread[^\n]*\n}
> +}

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v6 3/3] gdb/gcore: interrupt all threads to generate core in non-stop targets
  2023-05-10 18:46       ` Lancelot SIX
@ 2023-05-10 19:05         ` Eli Zaretskii
  2023-05-10 19:08           ` Lancelot SIX
  0 siblings, 1 reply; 12+ messages in thread
From: Eli Zaretskii @ 2023-05-10 19:05 UTC (permalink / raw)
  To: Lancelot SIX; +Cc: gdb-patches, lsix

> Date: Wed, 10 May 2023 19:46:36 +0100
> Cc: lsix@lancelotsix.com
> From: Lancelot SIX via Gdb-patches <gdb-patches@sourceware.org>
> 
> Hi,
> 
> Kindly pinging on this patch.

I believe I already reviewed the documentation parts, yes?

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v6 3/3] gdb/gcore: interrupt all threads to generate core in non-stop targets
  2023-05-10 19:05         ` Eli Zaretskii
@ 2023-05-10 19:08           ` Lancelot SIX
  0 siblings, 0 replies; 12+ messages in thread
From: Lancelot SIX @ 2023-05-10 19:08 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: gdb-patches, lsix


>> Date: Wed, 10 May 2023 19:46:36 +0100
>> Cc: lsix@lancelotsix.com
>> From: Lancelot SIX via Gdb-patches <gdb-patches@sourceware.org>
>>
>> Hi,
>>
>> Kindly pinging on this patch.
> 
> I believe I already reviewed the documentation parts, yes?

Hi,

Yes you have. There was just a remark about a missing cross-reference 
which I have since added locally 
(https://sourceware.org/pipermail/gdb-patches/2023-March/198220.html).

I wanted to check if the rest of the series needed other modification 
before sending a full new revision.

Thanks,
Lancelot.

^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2023-05-10 19:08 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-01-30 16:51 [PATCH v5] gdb/gcore: interrupt all threads before generating the corefile Lancelot SIX
2023-02-10 14:36 ` [PING] " Lancelot SIX
2023-02-10 21:46 ` Simon Marchi
2023-03-22 16:35   ` [PATCH v6 0/3] interrupt all threads to generate core in non-stop targets Lancelot SIX
2023-03-22 16:35     ` [PATCH v6 1/3] gdb: add inferior parameter to target_is_non_stop_p Lancelot SIX
2023-03-22 16:35     ` [PATCH v6 2/3] gdb/infrun: Improve assertion in stop_all_threads Lancelot SIX
2023-03-22 16:35     ` [PATCH v6 3/3] gdb/gcore: interrupt all threads to generate core in non-stop targets Lancelot SIX
2023-03-22 16:44       ` Eli Zaretskii
2023-03-22 16:59         ` Lancelot SIX
2023-05-10 18:46       ` Lancelot SIX
2023-05-10 19:05         ` Eli Zaretskii
2023-05-10 19:08           ` Lancelot SIX

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).