With these two programs: execer.c ---8<--- #include <unistd.h> #include <stdio.h> int main(int argc, char **argv) { printf("I am execer and my argv[1] is: %s\n", argv[1]); execl("./execee", "./execee", "arg-for-execee", NULL); return 0; } --->8--- execee.c ---8<--- #include <stdio.h> int main(int argc, char **argv) { printf("I am execee and my argv[1] is: %s\n", argv[1]); return 0; } --->8--- Let's try to run in GDB: $ ./gdb -q -nx --data-directory=data-directory --args ./execer args-for-execer Reading symbols from ./execer... (gdb) r Starting program: /home/simark/build/binutils-gdb/gdb/execer args-for-execer I am execer and my argv[1] is: args-for-execer process 1479927 is executing new program: /home/simark/build/binutils-gdb/gdb/execee I am execee and my argv[1] is: arg-for-execee [Inferior 1 (process 1479927) exited normally] (gdb) So far so good, the arguments match the program. Let's try to run again: (gdb) r Starting program: /home/simark/build/binutils-gdb/gdb/execee args-for-execer I am execee and my argv[1] is: args-for-execer [Inferior 1 (process 1479957) exited normally] (gdb) Woops, we have executed execee with the arguments for execer. I think this behavior is not very useful. My takeaway is: - When re-running after an exec, GDB executes the last known program, execee in this case. It can be fine in some use cases, where you just want to debug the execee again and again. But it should find out the arguments with which the execee was executed, save them in the inferior, and re-use them when you "run". - In other use cases, the user could want to start debugging from scratch, that means re-running execer. GDB could remember the latest user-specified executable for the inferior. There would then be a setting to decide which executable to run (the latest user-specified executable, execer, or the latest seen executable, execee) It is possible to remember the original executable by using "follow-exec-mode == new", but IMO that's not very practical, the user needs to switch back to inferior 1 to restart. And if they restart multiple times, the new inferiors just pile up. It would make more sense to be able to re-run from the start (execer) using a single inferior.
> It would make more sense to be able to re-run from the start (execer) using a single inferior. Maybe one idea would be to have a notion of a process tree and a "primary" inferior in the tree. Then, inferiors that are created via fork/exec could be automatically removed. That is, "run" would remove the whole tree and restart at the top.
(In reply to Tom Tromey from comment #1) > > It would make more sense to be able to re-run from the start (execer) using a single inferior. > > Maybe one idea would be to have a notion of a process tree and > a "primary" inferior in the tree. Then, inferiors that are created > via fork/exec could be automatically removed. That is, "run" > would remove the whole tree and restart at the top. I like that.
> It is possible to remember the original executable by using "follow-exec-mode == > new", but IMO that's not very practical, the user needs to switch back to > inferior 1 to restart. And if they restart multiple times, the new inferiors > just pile up. It would make more sense to be able to re-run from the start > (execer) using a single inferior. See below. > Maybe one idea would be to have a notion of a process tree and > a "primary" inferior in the tree. Then, inferiors that are created > via fork/exec could be automatically removed. That is, "run" > would remove the whole tree and restart at the top. That was already sort of the idea behind "follow-exec-mode == new". Note how GDB marks fork children inferiors as "removable": child_inf->removable = 1; And then normal_stop removes these inferiors when possible: /* Try to get rid of automatically added inferiors that are no longer needed. Keeping those around slows down things linearly. Note that this never removes the current inferior. */ prune_inferiors (); This was never fully developed -- I mean, for example, there's no way to force an inferior to be non-removable in the CLI.
The master branch has been updated by Simon Marchi <simark@sourceware.org>: https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=2af87c859fe450d4a3a841cf19637a91d53c9486 commit 2af87c859fe450d4a3a841cf19637a91d53c9486 Author: Simon Marchi <simon.marchi@efficios.com> Date: Thu May 13 15:27:55 2021 -0400 gdb: call target_follow_exec when "set follow-exec-mode" is "same" target_follow_exec is currently only called in the "follow-exec-mode == new" branch of follow_exec, not the "follow-exec-mode == same" branch. I think it would make sense to call it regardless of the mode to let targets do some necessary handling. This is needed in the context of rocm-gdb [1], where a target is pushed on top of the linux-nat target. On exec, it needs to do some bookkeeping, close some file descriptors / handles that were related to the process pre-exec and open some new ones for the process post-exec. However, by looking at the only in-tree implementation of target_ops::follow_exec, remote_target::follow_exec, I found that it would be useful for the extended-remote target too, to align its behavior with native debugging (although I think that behavior is not very user-friendly, see PR 27745 [2]). Using two programs, one (let's call it "execer") that execs the other (let's call it "execee"), with native: $ ./gdb -q -nx --data-directory=data-directory ./execer Reading symbols from ./execer... (gdb) r Starting program: /home/simark/build/binutils-gdb/gdb/execer I am execer process 1495622 is executing new program: /home/simark/build/binutils-gdb/gdb/execee I am execee [Inferior 1 (process 1495622) exited normally] (gdb) r Starting program: /home/simark/build/binutils-gdb/gdb/execee I am execee [Inferior 1 (process 1495626) exited normally] And now with gdbserver (some irrelevant output lines removed for brevity): $ ./gdbserver --once --multi :1234 ... $ ./gdb -q -nx --data-directory=data-directory ./execer -ex "set remote exec-file /home/simark/build/binutils-gdb/gdb/execer" -ex "tar ext :1234" Reading symbols from ./execer... Remote debugging using :1234 (gdb) r Starting program: /home/simark/build/binutils-gdb/gdb/execer process 1495724 is executing new program: /home/simark/build/binutils-gdb/gdb/execee [Inferior 1 (process 1495724) exited normally] (gdb) r `target:/home/simark/build/binutils-gdb/gdb/execee' has disappeared; keeping its symbols. Starting program: target:/home/simark/build/binutils-gdb/gdb/execee warning: Build ID mismatch between current exec-file target:/home/simark/build/binutils-gdb/gdb/execee and automatically determined exec-file target:/home/simark/build/binutils-gdb/gdb/execer exec-file-mismatch handling is currently "ask" Reading /home/simark/build/binutils-gdb/gdb/execer from remote target... Load new symbol table from "target:/home/simark/build/binutils-gdb/gdb/execer"? (y or n) When handling the exec, GDB updates the exec-file of the inferior to be the execee. This means that a subsequent "run" will run the execee, not the original executable (execer). remote_target::follow_exec is meant to update the "remote exec-file", which is the file on the remote system that will be executed if you "run" the inferior, to the execee as well. However, this is not called when follow-exec-mode is same, because target_follow_exec is not called in this branch. As a result, GDB thinks the inferior is executing execee but the remote side is really executing execer, hence the mismatch message. By calling target_follow_exec in the "same" branch of the follow_exec function, we ensure that everybody agrees, and we get the same behavior with the extended-remote target as we get with the native target, the execee is executed on the second run: $ ./gdbserver --once --multi :1234 ... $ ./gdb -q -nx --data-directory=data-directory ./execer -ex "set remote exec-file /home/simark/build/binutils-gdb/gdb/execer" -ex "tar ext :1234" Reading symbols from ./execer... Remote debugging using :1234 (gdb) r Starting program: /home/simark/build/binutils-gdb/gdb/execer process 1501445 is executing new program: /home/simark/build/binutils-gdb/gdb/execee [Inferior 1 (process 1501445) exited normally] (gdb) r `target:/home/simark/build/binutils-gdb/gdb/execee' has disappeared; keeping its symbols. Starting program: target:/home/simark/build/binutils-gdb/gdb/execee [Inferior 1 (process 1501447) exited normally] (gdb) This scenario is tested in gdb.base/foll-exec-mode.exp, and in fact this patch fixes the test for me when using --target_board=native-extended-gdbserver. gdb/ChangeLog: * infrun.c (follow_exec): Call target_follow_fork when follow-exec-mode is same. * target.h (target_follow_fork): Improve doc. [1] https://github.com/ROCm-Developer-Tools/ROCgdb [2] https://sourceware.org/bugzilla/show_bug.cgi?id=27745 Change-Id: I4ee84a875e39bf3f8eaf3e6789a4bfe23a2a430e