From patchwork Thu Oct 6 11:44:13 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Burgess X-Patchwork-Id: 58731 From: aburgess@redhat.com (Andrew Burgess) Date: Thu, 06 Oct 2022 12:44:13 +0100 List-Id: gdb-patches mailing list Subject: [PATCHv2 7/7] gdb: some process_stratum_target should not be shared In-Reply-To: <20221005211505.u2pj6qzkumpf4kle@ubuntu.lan> References: <20220921131200.3983844-1-aburgess@redhat.com> <3ea56551906f0e7815950c3dc98747ce86b6e64d.1664729722.git.aburgess@redhat.com> <20221005211505.u2pj6qzkumpf4kle@ubuntu.lan> Message-ID: <875ygxfa7m.fsf@redhat.com> Lancelot SIX writes: > Hi, > > Thanks for doing this! > > I have a couple of comments below. > > On Sun, Oct 02, 2022 at 06:04:48PM +0100, Andrew Burgess via Gdb-patches wrote: >> When multi-target support was added to GDB, an assumption was made >> that all process_stratum_target sub-classes could be shared by >> multiple inferiors. >> >> For things like the Linux and FreeBSD native targets, as well as the >> remote target, this is absolutely true (or was made true). But some >> targets were never updated to be shareable, for example, the >> core_target, which is used when reading core-files, stores some of its >> state in the program_space, but also, the core-file and the executable >> being debugged are closely related. >> >> As each add-inferior call creates an inferior with a new >> program_space, and doesn't automatically copy the executable, or the >> current core-file, I don't think it really makes sense to "share" >> core_target objects between inferiors. >> >> Consider this session: >> >> $ gdb -q >> (gdb) file test1 >> Reading symbols from test1... >> (gdb) core-file core.test1.433190 >> [New LWP 433190] >> Core was generated by `./test1'. >> Program terminated with signal SIGSEGV, Segmentation fault. >> #0 0x0000000000401111 in foo () at test1.c:6 >> 6 return *global_ptr; >> (gdb) add-inferior >> [New inferior 2] >> Added inferior 2 on connection 1 (core) >> (gdb) info inferiors >> Num Description Connection Executable >> * 1 process 433190 1 (core) /tmp/multi-core/test1 >> 2 1 (core) >> (gdb) info connections >> Num What Description >> * 1 core Local core dump file >> (gdb) inferior 2 >> [Switching to inferior 2 [] ()] >> (gdb) file test2 >> Reading symbols from test2... >> (gdb) core-file core.test2.433203 >> [New LWP 433203] >> Core was generated by `./test2'. >> Program terminated with signal SIGSEGV, Segmentation fault. >> #0 0x0000000000401111 in main () at test2.c:6 >> 6 return *global_ptr; >> (gdb) info inferiors >> Num Description Connection Executable >> 1 process 433190 1 (core) /tmp/multi-core/test1 >> * 2 process 433203 2 (core) /tmp/multi-core/test2 >> (gdb) info connections >> Num What Description >> 1 core Local core dump file >> * 2 core Local core dump file >> (gdb) >> >> After the 'add-inferior' the core_target connection is shared between >> the inferiors. However, as soon as the user sets up the core-file and >> executable in the new inferior a new core connection has been created. >> >> I think this behaviour might be confusing, so I'd like to have GDB not >> initially share the core connection. Instead, when the user tries to >> add the new inferior a warning is given, and the new inferior is >> created without a connection, like this: >> >> $ gdb -q >> (gdb) file test1 >> Reading symbols from test1... >> (gdb) core-file core.test1.433190 >> [New LWP 433190] >> Core was generated by `./test1'. >> Program terminated with signal SIGSEGV, Segmentation fault. >> #0 0x0000000000401111 in foo () at test1.c:6 >> 6 return *global_ptr; >> (gdb) add-inferior >> [New inferior 2] >> warning: can't share connection 1 (core) between inferiors >> Added inferior 2 >> (gdb) info inferiors >> Num Description Connection Executable >> * 1 process 433190 1 (core) /tmp/multi-core/test1 >> 2 >> (gdb) >> >> If the user explicitly asks for the new inferior to be created without >> a connection, then no warning will be given. >> >> At this point the user is free to setup inferior 2 with a different >> executable and core file (or to do anything they like with the >> inferior). >> >> In an earlier version of this patch I had GDB error instead of giving >> a warning. However, the error message ended up being something like: >> >> can't share connection ..... between inferiors, use -no-connection >> option to create an inferior without sharing a connection. >> >> but it seemed better to just create the inferior. >> >> I've updated the docs, and added a NEWS entry for the new warning. In >> the docs for clone-inferior I've added reference to -no-connection, >> which was previously missing. >> --- >> gdb/NEWS | 7 + >> gdb/corelow.c | 5 + >> gdb/doc/gdb.texinfo | 37 ++++- >> gdb/inferior.c | 16 ++ >> gdb/inferior.h | 6 +- >> gdb/target.c | 14 ++ >> gdb/target.h | 8 + >> gdb/testsuite/gdb.multi/multi-core-files-1.c | 37 +++++ >> gdb/testsuite/gdb.multi/multi-core-files-2.c | 31 ++++ >> gdb/testsuite/gdb.multi/multi-core-files.exp | 156 +++++++++++++++++++ >> 10 files changed, 313 insertions(+), 4 deletions(-) >> create mode 100644 gdb/testsuite/gdb.multi/multi-core-files-1.c >> create mode 100644 gdb/testsuite/gdb.multi/multi-core-files-2.c >> create mode 100644 gdb/testsuite/gdb.multi/multi-core-files.exp >> >> diff --git a/gdb/NEWS b/gdb/NEWS >> index a6ea7c9f98f..cb576edd203 100644 >> --- a/gdb/NEWS >> +++ b/gdb/NEWS >> @@ -71,6 +71,13 @@ >> For both /r and /b GDB is now better at using whitespace in order to >> align the disassembled instruction text. >> >> +* The add-inferior, clone-inferior, and MI -add-inferior commands will >> + now give a warning, and create the new inferior without a >> + connection, when the current inferior, at the time the command is >> + given, is a core-file target. The core-file target could never >> + really be shared between inferiors, GDB is now more vocal about what >> + is going on. >> + >> * New commands >> >> maintenance set ignore-prologue-end-flag on|off >> diff --git a/gdb/corelow.c b/gdb/corelow.c >> index 293bc8d4f59..4d8393a3587 100644 >> --- a/gdb/corelow.c >> +++ b/gdb/corelow.c >> @@ -128,6 +128,11 @@ class core_target final : public process_stratum_target >> /* See definition. */ >> void info_proc_mappings (struct gdbarch *gdbarch); >> >> + /* The core_target only works for the inferior in which it was initially >> + opened, and can't be copied to some other inferior's target_stack. */ >> + bool is_shareable () override >> + { return false; } >> + >> private: /* per-core data */ >> >> /* Get rid of the core inferior. */ >> diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo >> index 107df84d108..a558fc4de38 100644 >> --- a/gdb/doc/gdb.texinfo >> +++ b/gdb/doc/gdb.texinfo >> @@ -3358,8 +3358,31 @@ >> remote} command to connect to some other @code{gdbserver} instance, >> use @code{run} to spawn a local program, etc. >> >> +Not all connections can be shared between inferiors. For example, the >> +@code{target core} target is unique for each inferior. That is, >> +multiple inferiors can use @code{target core} at the same time, but >> +each @code{target core} is different. If you try to >> +@code{add-inferior}, and the current inferior is @code{target core}, >> +then @value{GDBN} will give a warning and create the new inferior >> +without a connection, like this: >> + >> +@smallexample >> +(@value{GDBP}) file test1 >> +Reading symbols from test1... >> +(@value{GDBP}) target core core.test1.433190 >> +[New LWP 433190] >> +Core was generated by `./test1'. >> +Program terminated with signal SIGSEGV, Segmentation fault. >> +#0 0x0000000000401111 in foo () at test1.c:6 >> +6 return *global_ptr; >> +(@value{GDBP}) add-inferior >> +[New inferior 2] >> +warning: can't share connection 1 (core) between inferiors >> +Added inferior 2 >> +@end smallexample >> + >> @kindex clone-inferior >> -@item clone-inferior [ -copies @var{n} ] [ @var{infno} ] >> +@item clone-inferior [ -copies @var{n} ] [ -no-connection ] [ @var{infno} ] >> Adds @var{n} inferiors ready to execute the same program as inferior >> @var{infno}; @var{n} defaults to 1, and @var{infno} defaults to the >> number of the current inferior. This command copies the values of the >> @@ -3384,6 +3407,13 @@ >> >> You can now simply switch focus to inferior 2 and run it. >> >> +Like @code{add-inferior}, @code{clone-inferior} shares the connection >> +with the inferior @var{infno}. If the @var{-no-connection} option is >> +given then the new inferior will be created without a connection. If >> +the connection of inferior @var{infno} can't be shared, then >> +@value{GDBN} will give a warning, and the new inferior will be created >> +without a connection. >> + >> @kindex remove-inferiors >> @item remove-inferiors @var{infno}@dots{} >> Removes the inferior or inferiors @var{infno}@dots{}. It is not >> @@ -37786,6 +37816,11 @@ >> @code{gdbserver} instance, use @code{-exec-run} to spawn a local >> program, etc. >> >> +If the connection of the current inferior cannot be shared, e.g.@: the >> +@code{-target-select core} target cannot be shared between inferiors, >> +then @value{GDBN} will give a warning and create the new inferior >> +without a connection. >> + >> The command response always has a field, @var{inferior}, whose value >> is the identifier of the thread group corresponding to the new >> inferior. >> diff --git a/gdb/inferior.c b/gdb/inferior.c >> index a498b9d493c..0868a55593a 100644 >> --- a/gdb/inferior.c >> +++ b/gdb/inferior.c >> @@ -817,6 +817,22 @@ switch_to_inferior_and_push_target (inferior *new_inf, >> symbols. */ >> switch_to_inferior_no_thread (new_inf); >> >> + if (!no_connection && proc_target != nullptr >> + && !proc_target->is_shareable ()) >> + { >> + if (proc_target->connection_string () != nullptr) >> + warning (_("can't share connection %d (%s %s) between inferiors"), >> + proc_target->connection_number, >> + proc_target->shortname (), >> + proc_target->connection_string ()); >> + else >> + warning (_("can't share connection %d (%s) between inferiors"), >> + proc_target->connection_number, >> + proc_target->shortname ()); >> + >> + proc_target = nullptr; >> + } >> + >> /* Reuse the target for new inferior. */ >> if (!no_connection && proc_target != NULL) >> { >> diff --git a/gdb/inferior.h b/gdb/inferior.h >> index 344974c4811..639364e5de3 100644 >> --- a/gdb/inferior.h >> +++ b/gdb/inferior.h >> @@ -762,9 +762,9 @@ extern struct inferior *add_inferior_with_spaces (void); >> /* Print the current selected inferior. */ >> extern void print_selected_inferior (struct ui_out *uiout); >> >> -/* Switch to inferior NEW_INF, a new inferior, and unless >> - NO_CONNECTION is true, push the process_stratum_target of ORG_INF >> - to NEW_INF. */ >> +/* Switch to inferior NEW_INF, a new inferior, and unless NO_CONNECTION is >> + true, or the process_stratum_target of ORG_INF is not shareable, push >> + the process_stratum_target of ORG_INF to NEW_INF. */ >> >> extern void switch_to_inferior_and_push_target >> (inferior *new_inf, bool no_connection, inferior *org_inf); >> diff --git a/gdb/target.c b/gdb/target.c >> index 1e447f604d9..65bc0e7246e 100644 >> --- a/gdb/target.c >> +++ b/gdb/target.c >> @@ -1188,6 +1188,12 @@ target_stack::push (target_ops *t) >> if (m_stack[stratum].get () != nullptr) >> unpush (m_stack[stratum].get ()); >> >> + /* If this target can't be shared, then check that the target doesn't >> + already appear on some other target stack. */ >> + if (!t->is_shareable ()) >> + for (inferior *inf : all_inferiors ()) >> + gdb_assert (!inf->target_is_pushed (t)); >> + >> /* Now add the new one. */ >> m_stack[stratum] = std::move (ref); >> >> @@ -3226,6 +3232,14 @@ target_ops::fileio_readlink (struct inferior *inf, const char *filename, >> >> /* See target.h. */ >> >> +bool >> +target_ops::is_shareable () >> +{ >> + return true; >> +} >> + >> +/* See target.h. */ >> + >> int >> target_fileio_open (struct inferior *inf, const char *filename, >> int flags, int mode, bool warn_if_slow, fileio_error *target_errno) >> diff --git a/gdb/target.h b/gdb/target.h >> index 547ee8a3bbd..30e5085a543 100644 >> --- a/gdb/target.h >> +++ b/gdb/target.h >> @@ -1321,6 +1321,14 @@ struct target_ops >> virtual bool store_memtags (CORE_ADDR address, size_t len, >> const gdb::byte_vector &tags, int type) >> TARGET_DEFAULT_NORETURN (tcomplain ()); >> + >> + /* Return true if this target can be shared on multiple target_stacks, >> + or false if this target should only appear on a single target_stack. >> + When this function returns false multiple separate instances of the >> + same target_ops sub-class can still appear on different >> + target_stacks, but the same concrete instance can only appear on a >> + single target_stack. */ >> + virtual bool is_shareable (); >> }; >> >> /* Deleter for std::unique_ptr. See comments in >> diff --git a/gdb/testsuite/gdb.multi/multi-core-files-1.c b/gdb/testsuite/gdb.multi/multi-core-files-1.c >> new file mode 100644 >> index 00000000000..f996973023e >> --- /dev/null >> +++ b/gdb/testsuite/gdb.multi/multi-core-files-1.c >> @@ -0,0 +1,37 @@ >> +/* This testcase is part of GDB, the GNU debugger. >> + >> + Copyright 2022 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 . */ >> + >> +#include >> + >> +int >> +bar () >> +{ >> + abort (); >> + return 0; >> +} >> + >> +int >> +baz () >> +{ >> + return bar (); >> +} >> + >> +int >> +main () >> +{ >> + return baz (); >> +} >> diff --git a/gdb/testsuite/gdb.multi/multi-core-files-2.c b/gdb/testsuite/gdb.multi/multi-core-files-2.c >> new file mode 100644 >> index 00000000000..fb99e137c3f >> --- /dev/null >> +++ b/gdb/testsuite/gdb.multi/multi-core-files-2.c >> @@ -0,0 +1,31 @@ >> +/* This testcase is part of GDB, the GNU debugger. >> + >> + Copyright 2022 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 . */ >> + >> +#include >> + >> +int >> +foo () >> +{ >> + abort (); >> + return 0; >> +} >> + >> +int >> +main () >> +{ >> + return foo (); >> +} >> diff --git a/gdb/testsuite/gdb.multi/multi-core-files.exp b/gdb/testsuite/gdb.multi/multi-core-files.exp >> new file mode 100644 >> index 00000000000..9cf188b0caa >> --- /dev/null >> +++ b/gdb/testsuite/gdb.multi/multi-core-files.exp >> @@ -0,0 +1,156 @@ >> +# Copyright 2022 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 . >> + >> +# This script runs some basic tests that GDB can support multiple >> +# inferiors each debugging different core files. >> +# >> +# We also check the behaviour of GDB if the user attempts to clone or >> +# duplicate an inferior that is debugging a core file. >> + >> +standard_testfile -1.c -2.c >> + >> +set binfile1 "${binfile}-1" >> +set binfile2 "${binfile}-2" >> + >> +if {[build_executable "build first executable" $binfile1 $srcfile \ >> + debug] == -1} { >> + untested "failed to compile first executable" >> + return -1 >> +} >> + >> +if {[build_executable "build first executable" $binfile2 $srcfile2 \ > > s/first/second/ maybe? > >> + debug] == -1} { >> + untested "failed to compile second executable" >> + return -1 >> +} >> + >> +set corefile1 [core_find $binfile1] >> +set corefile2 [core_find $binfile2] > > I think you should check corefile*. > > On my system (where ulimit -c gives 0), I have: > > WARNING: can't generate a core file - core tests suppressed - check ulimit -c > WARNING: can't generate a core file - core tests suppressed - check ulimit -c > FAIL: gdb.multi/multi-core-files.exp: load core file > FAIL: gdb.multi/multi-core-files.exp: check backtrace in inferior 1 > FAIL: gdb.multi/multi-core-files.exp: add-inferior > FAIL: gdb.multi/multi-core-files.exp: clone-inferior > FAIL: gdb.multi/multi-core-files.exp: interpreter-exec mi "-add-inferior" > FAIL: gdb.multi/multi-core-files.exp: first info inferiors call > FAIL: gdb.multi/multi-core-files.exp: second info inferiors call > FAIL: gdb.multi/multi-core-files.exp: info connections > FAIL: gdb.multi/multi-core-files.exp: Loaded second core file > FAIL: gdb.multi/multi-core-files.exp: check backtrace in inferior 2 > FAIL: gdb.multi/multi-core-files.exp: Loaded first core file into inferior 3 > FAIL: gdb.multi/multi-core-files.exp: check backtrace in inferior 3 > FAIL: gdb.multi/multi-core-files.exp: detach from inferior 3 core file (the program is no longer running) > FAIL: gdb.multi/multi-core-files.exp: detach from inferior 2 core file (the program is no longer running) > FAIL: gdb.multi/multi-core-files.exp: check backtrace in inferior 1 again > FAIL: gdb.multi/multi-core-files.exp: detach from inferior 1 core file (the program is no longer running) > > I think something like this could do: > > if { $corefile1 eq "" || $corefile2 eq "" } { > untested "Can't generate core files" > return > } > Thanks for spotting this. I included the fix you suggested, and confirmed that this resolves the FAILs when 'ulimit -c 0' is in use. I've also fixed all the typos you pointed out. An updated patch is below. Thanks, Andrew --- commit df38705e87e25e24dbca7b04d0ac3a61affe714f Author: Andrew Burgess Date: Thu Sep 29 12:45:26 2022 +0100 List-Id: gdb-patches mailing list gdb: some process_stratum_target should not be shared When multi-target support was added to GDB, an assumption was made that all process_stratum_target sub-classes could be shared by multiple inferiors. For things like the Linux and FreeBSD native targets, as well as the remote target, this is absolutely true (or was made true). But some targets were never updated to be shareable, for example, the core_target, which is used when reading core-files, stores some of its state in the program_space, but also, the core-file and the executable being debugged are closely related. As each add-inferior call creates an inferior with a new program_space, and doesn't automatically copy the executable, or the current core-file, I don't think it really makes sense to "share" core_target objects between inferiors. Consider this session: $ gdb -q (gdb) file test1 Reading symbols from test1... (gdb) core-file core.test1.433190 [New LWP 433190] Core was generated by `./test1'. Program terminated with signal SIGSEGV, Segmentation fault. #0 0x0000000000401111 in foo () at test1.c:6 6 return *global_ptr; (gdb) add-inferior [New inferior 2] Added inferior 2 on connection 1 (core) (gdb) info inferiors Num Description Connection Executable * 1 process 433190 1 (core) /tmp/multi-core/test1 2 1 (core) (gdb) info connections Num What Description * 1 core Local core dump file (gdb) inferior 2 [Switching to inferior 2 [] ()] (gdb) file test2 Reading symbols from test2... (gdb) core-file core.test2.433203 [New LWP 433203] Core was generated by `./test2'. Program terminated with signal SIGSEGV, Segmentation fault. #0 0x0000000000401111 in main () at test2.c:6 6 return *global_ptr; (gdb) info inferiors Num Description Connection Executable 1 process 433190 1 (core) /tmp/multi-core/test1 * 2 process 433203 2 (core) /tmp/multi-core/test2 (gdb) info connections Num What Description 1 core Local core dump file * 2 core Local core dump file (gdb) After the 'add-inferior' the core_target connection is shared between the inferiors. However, as soon as the user sets up the core-file and executable in the new inferior a new core connection has been created. I think this behaviour might be confusing, so I'd like to have GDB not initially share the core connection. Instead, when the user tries to add the new inferior a warning is given, and the new inferior is created without a connection, like this: $ gdb -q (gdb) file test1 Reading symbols from test1... (gdb) core-file core.test1.433190 [New LWP 433190] Core was generated by `./test1'. Program terminated with signal SIGSEGV, Segmentation fault. #0 0x0000000000401111 in foo () at test1.c:6 6 return *global_ptr; (gdb) add-inferior [New inferior 2] warning: can't share connection 1 (core) between inferiors Added inferior 2 (gdb) info inferiors Num Description Connection Executable * 1 process 433190 1 (core) /tmp/multi-core/test1 2 (gdb) If the user explicitly asks for the new inferior to be created without a connection, then no warning will be given. At this point the user is free to setup inferior 2 with a different executable and core file (or to do anything they like with the inferior). In an earlier version of this patch I had GDB error instead of giving a warning. However, the error message ended up being something like: can't share connection ..... between inferiors, use -no-connection option to create an inferior without sharing a connection. but it seemed better to just create the inferior. I've updated the docs, and added a NEWS entry for the new warning. In the docs for clone-inferior I've added reference to -no-connection, which was previously missing. diff --git a/gdb/NEWS b/gdb/NEWS index a6ea7c9f98f..cb576edd203 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -71,6 +71,13 @@ For both /r and /b GDB is now better at using whitespace in order to align the disassembled instruction text. +* The add-inferior, clone-inferior, and MI -add-inferior commands will + now give a warning, and create the new inferior without a + connection, when the current inferior, at the time the command is + given, is a core-file target. The core-file target could never + really be shared between inferiors, GDB is now more vocal about what + is going on. + * New commands maintenance set ignore-prologue-end-flag on|off diff --git a/gdb/corelow.c b/gdb/corelow.c index 293bc8d4f59..4d8393a3587 100644 --- a/gdb/corelow.c +++ b/gdb/corelow.c @@ -128,6 +128,11 @@ class core_target final : public process_stratum_target /* See definition. */ void info_proc_mappings (struct gdbarch *gdbarch); + /* The core_target only works for the inferior in which it was initially + opened, and can't be copied to some other inferior's target_stack. */ + bool is_shareable () override + { return false; } + private: /* per-core data */ /* Get rid of the core inferior. */ diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 107df84d108..a5009c85e09 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -3358,8 +3358,31 @@ remote} command to connect to some other @code{gdbserver} instance, use @code{run} to spawn a local program, etc. +Not all connections can be shared between inferiors. For example, the +@code{target core} target is unique for each inferior. That is, +multiple inferiors can use @code{target core} at the same time, but +each @code{target core} is different. If you try to +@code{add-inferior}, and the current inferior is @code{target core}, +then @value{GDBN} will give a warning and create the new inferior +without a connection, like this: + +@smallexample +(@value{GDBP}) file test1 +Reading symbols from test1... +(@value{GDBP}) target core core.test1.433190 +[New LWP 433190] +Core was generated by `./test1'. +Program terminated with signal SIGSEGV, Segmentation fault. +#0 0x0000000000401111 in foo () at test1.c:6 +6 return *global_ptr; +(@value{GDBP}) add-inferior +[New inferior 2] +warning: can't share connection 1 (core) between inferiors +Added inferior 2 +@end smallexample + @kindex clone-inferior -@item clone-inferior [ -copies @var{n} ] [ @var{infno} ] +@item clone-inferior [ -copies @var{n} ] [ -no-connection ] [ @var{infno} ] Adds @var{n} inferiors ready to execute the same program as inferior @var{infno}; @var{n} defaults to 1, and @var{infno} defaults to the number of the current inferior. This command copies the values of the @@ -3384,6 +3407,13 @@ You can now simply switch focus to inferior 2 and run it. +Like @code{add-inferior}, @code{clone-inferior} shares the connection +with the inferior @var{infno}. If the @var{-no-connection} option is +given, then the new inferior will be created without a connection. If +the connection of inferior @var{infno} can't be shared, then +@value{GDBN} will give a warning, and the new inferior will be created +without a connection. + @kindex remove-inferiors @item remove-inferiors @var{infno}@dots{} Removes the inferior or inferiors @var{infno}@dots{}. It is not @@ -37786,6 +37816,11 @@ @code{gdbserver} instance, use @code{-exec-run} to spawn a local program, etc. +If the connection of the current inferior cannot be shared, e.g.@: the +@code{-target-select core} target cannot be shared between inferiors, +then @value{GDBN} will give a warning and create the new inferior +without a connection. + The command response always has a field, @var{inferior}, whose value is the identifier of the thread group corresponding to the new inferior. diff --git a/gdb/inferior.c b/gdb/inferior.c index 26b242ed1bc..ccc4d451e2f 100644 --- a/gdb/inferior.c +++ b/gdb/inferior.c @@ -817,6 +817,22 @@ switch_to_inferior_and_push_target (inferior *new_inf, symbols. */ switch_to_inferior_no_thread (new_inf); + if (!no_connection && proc_target != nullptr + && !proc_target->is_shareable ()) + { + if (proc_target->connection_string () != nullptr) + warning (_("can't share connection %d (%s %s) between inferiors"), + proc_target->connection_number, + proc_target->shortname (), + proc_target->connection_string ()); + else + warning (_("can't share connection %d (%s) between inferiors"), + proc_target->connection_number, + proc_target->shortname ()); + + proc_target = nullptr; + } + /* Reuse the target for new inferior. */ if (!no_connection && proc_target != NULL) { diff --git a/gdb/inferior.h b/gdb/inferior.h index ab6a209a448..7aa5f157034 100644 --- a/gdb/inferior.h +++ b/gdb/inferior.h @@ -762,9 +762,9 @@ extern struct inferior *add_inferior_with_spaces (void); /* Print the current selected inferior. */ extern void print_selected_inferior (struct ui_out *uiout); -/* Switch to inferior NEW_INF, a new inferior, and unless - NO_CONNECTION is true, push the process_stratum_target of ORG_INF - to NEW_INF. */ +/* Switch to inferior NEW_INF, a new inferior, and unless NO_CONNECTION is + true, or the process_stratum_target of ORG_INF is not shareable, push + the process_stratum_target of ORG_INF to NEW_INF. */ extern void switch_to_inferior_and_push_target (inferior *new_inf, bool no_connection, inferior *org_inf); diff --git a/gdb/target.c b/gdb/target.c index 1e447f604d9..65bc0e7246e 100644 --- a/gdb/target.c +++ b/gdb/target.c @@ -1188,6 +1188,12 @@ target_stack::push (target_ops *t) if (m_stack[stratum].get () != nullptr) unpush (m_stack[stratum].get ()); + /* If this target can't be shared, then check that the target doesn't + already appear on some other target stack. */ + if (!t->is_shareable ()) + for (inferior *inf : all_inferiors ()) + gdb_assert (!inf->target_is_pushed (t)); + /* Now add the new one. */ m_stack[stratum] = std::move (ref); @@ -3226,6 +3232,14 @@ target_ops::fileio_readlink (struct inferior *inf, const char *filename, /* See target.h. */ +bool +target_ops::is_shareable () +{ + return true; +} + +/* See target.h. */ + int target_fileio_open (struct inferior *inf, const char *filename, int flags, int mode, bool warn_if_slow, fileio_error *target_errno) diff --git a/gdb/target.h b/gdb/target.h index 547ee8a3bbd..30e5085a543 100644 --- a/gdb/target.h +++ b/gdb/target.h @@ -1321,6 +1321,14 @@ struct target_ops virtual bool store_memtags (CORE_ADDR address, size_t len, const gdb::byte_vector &tags, int type) TARGET_DEFAULT_NORETURN (tcomplain ()); + + /* Return true if this target can be shared on multiple target_stacks, + or false if this target should only appear on a single target_stack. + When this function returns false multiple separate instances of the + same target_ops sub-class can still appear on different + target_stacks, but the same concrete instance can only appear on a + single target_stack. */ + virtual bool is_shareable (); }; /* Deleter for std::unique_ptr. See comments in diff --git a/gdb/testsuite/gdb.multi/multi-core-files-1.c b/gdb/testsuite/gdb.multi/multi-core-files-1.c new file mode 100644 index 00000000000..f996973023e --- /dev/null +++ b/gdb/testsuite/gdb.multi/multi-core-files-1.c @@ -0,0 +1,37 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2022 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 . */ + +#include + +int +bar () +{ + abort (); + return 0; +} + +int +baz () +{ + return bar (); +} + +int +main () +{ + return baz (); +} diff --git a/gdb/testsuite/gdb.multi/multi-core-files-2.c b/gdb/testsuite/gdb.multi/multi-core-files-2.c new file mode 100644 index 00000000000..fb99e137c3f --- /dev/null +++ b/gdb/testsuite/gdb.multi/multi-core-files-2.c @@ -0,0 +1,31 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2022 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 . */ + +#include + +int +foo () +{ + abort (); + return 0; +} + +int +main () +{ + return foo (); +} diff --git a/gdb/testsuite/gdb.multi/multi-core-files.exp b/gdb/testsuite/gdb.multi/multi-core-files.exp new file mode 100644 index 00000000000..22a56c07ceb --- /dev/null +++ b/gdb/testsuite/gdb.multi/multi-core-files.exp @@ -0,0 +1,160 @@ +# Copyright 2022 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 . + +# This script runs some basic tests that GDB can support multiple +# inferiors each debugging different core files. +# +# We also check the behaviour of GDB if the user attempts to clone or +# duplicate an inferior that is debugging a core file. + +standard_testfile -1.c -2.c + +set binfile1 "${binfile}-1" +set binfile2 "${binfile}-2" + +if {[build_executable "build first executable" $binfile1 $srcfile \ + debug] == -1} { + untested "failed to compile first executable" + return -1 +} + +if {[build_executable "build second executable" $binfile2 $srcfile2 \ + debug] == -1} { + untested "failed to compile second executable" + return -1 +} + +set corefile1 [core_find $binfile1] +set corefile2 [core_find $binfile2] +if { $corefile1 == "" || $corefile2 == "" } { + untested "Can't generate core files" + return +} + +# Start GDB, and load the first executable and corefile into the first +# inferior. +clean_restart ${binfile1} +gdb_test "core-file $corefile1" "Program terminated with .*" \ + "load core file" +gdb_test "bt" "bar \\(\\) at .*" \ + "check backtrace in inferior 1" + +# Try to use add-inferior and clone-inferior to create new +# inferiors. In both cases this will try to share the core_target +# between inferior 1 and the new inferior. As the core_target can't +# be shared we should get a warning, and the inferior should be +# created without a connection. +gdb_test "add-inferior" \ + [multi_line \ + "\\\[New inferior 2\\\]" \ + "warning: can't share connection 1 \\(core\\) between inferiors" \ + "Added inferior 2"] +gdb_test "clone-inferior" \ + [multi_line \ + "\\\[New inferior 3\\\]" \ + "warning: can't share connection 1 \\(core\\) between inferiors" \ + "Added inferior 3"] + +# Check the MI -add-inferior command. Do this using interpreter-exec. +# We're not doing a full MI test here, just checking this one command. +gdb_test "interpreter-exec mi \"-add-inferior\"" \ + [multi_line \ + "~\"\\\[New inferior 4\\\]..\"" \ + "&\"warning: can't share connection 1 \\(core\\) between inferiors..\"" \ + "~\"Added inferior 4..\"" \ + "\\^done,inferior=\"\[^\"\]+\""] + +# Now check that none of the new inferiors have a connection. +gdb_test "info inferiors" \ + [multi_line \ + "\\*\\s+1\\s+\[^\r\n\]+\\s+1 \\(core\\)\\s+\[^\r\n\]+.*" \ + "\\s+2\\s+\\s+" \ + "\\s+3\\s+\\s+\[^\r\n\]+" \ + "\\s+4\\s+\\s+"] \ + "first info inferiors call" + +# Now use add-inferior and clone-inferior but this time with the +# -no-connection option, this should avoid issuing the warning. We +# also use interpreter-exec to test the MI version of this command. +gdb_test "add-inferior -no-connection" \ + [multi_line \ + "\\\[New inferior 5\\\]" \ + "Added inferior 5"] +gdb_test "clone-inferior -no-connection" \ + [multi_line \ + "\\\[New inferior 6\\\]" \ + "Added inferior 6"] +gdb_test "interpreter-exec mi \"-add-inferior --no-connection\"" \ + "\\\[New inferior 7\\\].*Added inferior 7.*" + +# Now check that none of the new inferiors have a connection. +gdb_test "info inferiors" \ + [multi_line \ + "\\*\\s+1\\s+\[^\r\n\]+\\s+1 \\(core\\)\\s+\[^\r\n\]+.*" \ + "\\s+2\\s+\\s+" \ + "\\s+3\\s+\\s+\[^\r\n\]+" \ + "\\s+4\\s+\\s+" \ + "\\s+5\\s+\\s+" \ + "\\s+6\\s+\\s+\[^\r\n\]+" \ + "\\s+7\\s+\\s+"] \ + "second info inferiors call" + +# Check after all the new inferiors have been created that we still +# only have a single connection. +gdb_test "info connections" \ + "\\*\\s+1\\s+\\s+core\\s+Local core dump file\\s*" + +# Now switch to inferior 2 and load the second executable and core +# file. Check the backtrace for the presence of function 'foo', this +# indicates we are seeing the correct core file. +gdb_test "inferior 2" "Switching to inferior 2 .*" +gdb_test "file $binfile2" \ + "Reading symbols from .*" \ + "Loaded second test binary" +gdb_test "core-file $corefile2" \ + "Program terminated with signal SIGABRT, Aborted.*" \ + "Loaded second core file" +gdb_test "bt" "foo \\(\\) at .*" \ + "check backtrace in inferior 2" + +# Switch to inferior 3, this one was cloned from inferior 1, so is +# already debugging the first binary file. Check its backtrace for +# 'bar', which indicates we are debugging the correct core file. +gdb_test "inferior 3" "Switching to inferior 3 .*" +gdb_test "core-file $corefile1" \ + "Program terminated with signal SIGABRT, Aborted.*" \ + "Loaded first core file into inferior 3" +gdb_test "bt" "bar \\(\\) at .*" \ + "check backtrace in inferior 3" + +# Detach from some of the core files and delete some of the inferiors. +gdb_test "detach" "No core file now\\." \ + "detach from inferior 3 core file" +gdb_test "inferior 2" "Switching to inferior 2 .*" \ + "switch back to inferior 2" +gdb_test_no_output "remove-inferiors 3 4" + +# Now detach in inferior 2, and delete the inferior. +gdb_test "detach" "No core file now\\." \ + "detach from inferior 2 core file" +gdb_test "inferior 1" "Switching to inferior 1 .*" \ + "switch back to inferior 1" +gdb_test_no_output "remove-inferiors 2" + +# Finally, check that inferior 1 backtrace is still working. +gdb_test "bt" "bar \\(\\) at .*" \ + "check backtrace in inferior 1 again" +gdb_test "detach" "No core file now\\." \ + "detach from inferior 1 core file"