public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
From: Lancelot SIX <Lancelot.Six@amd.com>
To: gdb-patches@sourceware.org
Cc: lsix@lancelotsix.com
Subject: Re: [PATCH v6 3/3] gdb/gcore: interrupt all threads to generate core in non-stop targets
Date: Wed, 10 May 2023 19:46:36 +0100	[thread overview]
Message-ID: <dfbbde98-3e95-3782-8e77-be4edc3f8418@amd.com> (raw)
In-Reply-To: <20230322163504.560986-4-lancelot.six@amd.com>

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

  parent reply	other threads:[~2023-05-10 18:46 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
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 [this message]
2023-05-10 19:05         ` Eli Zaretskii
2023-05-10 19:08           ` Lancelot SIX

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=dfbbde98-3e95-3782-8e77-be4edc3f8418@amd.com \
    --to=lancelot.six@amd.com \
    --cc=gdb-patches@sourceware.org \
    --cc=lsix@lancelotsix.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).