Blame SOURCES/gdb-rhbz2022177-dprintf-1.patch

b94e32
From FEDORA_PATCHES Mon Sep 17 00:00:00 2001
b94e32
From: Kevin Buettner <kevinb@redhat.com>
b94e32
Date: Wed, 10 Nov 2021 18:52:22 -0700
b94e32
Subject: gdb-rhbz2022177-dprintf-1.patch
b94e32
b94e32
;; Backport fix for dprintf bug (RH BZ 2022177).
b94e32
b94e32
Fix PR 28308 - dprintf breakpoints not working when run from script
b94e32
b94e32
This commit fixes Bug 28308, titled "Strange interactions with
b94e32
dprintf and break/commands":
b94e32
b94e32
Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=28308
b94e32
b94e32
Since creating that bug report, I've found a somewhat simpler way of
b94e32
reproducing the problem.  I've encapsulated it into the GDB test case
b94e32
which I've created along with this bug fix.  The name of the new test
b94e32
is gdb.base/dprintf-execution-x-script.exp, I'll demonstrate the
b94e32
problem using this test case, though for brevity, I've placed all
b94e32
relevant files in the same directory and have renamed the files to all
b94e32
start with 'dp-bug' instead of 'dprintf-execution-x-script'.
b94e32
b94e32
The script file, named dp-bug.gdb, consists of the following commands:
b94e32
b94e32
dprintf increment, "dprintf in increment(), vi=%d\n", vi
b94e32
break inc_vi
b94e32
commands
b94e32
  continue
b94e32
end
b94e32
run
b94e32
b94e32
Note that the final command in this script is 'run'.  When 'run' is
b94e32
instead issued interactively, the  bug does not occur.  So, let's look
b94e32
at the interactive case first in order to see the correct/expected
b94e32
output:
b94e32
b94e32
$ gdb -q -x dp-bug.gdb dp-bug
b94e32
... eliding buggy output which I'll discuss later ...
b94e32
(gdb) run
b94e32
Starting program: /mesquite2/sourceware-git/f34-master/bld/gdb/tmp/dp-bug
b94e32
vi=0
b94e32
dprintf in increment(), vi=0
b94e32
b94e32
Breakpoint 2, inc_vi () at dprintf-execution-x-script.c:26
b94e32
26	in dprintf-execution-x-script.c
b94e32
vi=1
b94e32
dprintf in increment(), vi=1
b94e32
b94e32
Breakpoint 2, inc_vi () at dprintf-execution-x-script.c:26
b94e32
26	in dprintf-execution-x-script.c
b94e32
vi=2
b94e32
dprintf in increment(), vi=2
b94e32
b94e32
Breakpoint 2, inc_vi () at dprintf-execution-x-script.c:26
b94e32
26	in dprintf-execution-x-script.c
b94e32
vi=3
b94e32
[Inferior 1 (process 1539210) exited normally]
b94e32
b94e32
In this run, in which 'run' was issued from the gdb prompt (instead
b94e32
of at the end of the script), there are three dprintf messages along
b94e32
with three 'Breakpoint 2' messages.  This is the correct output.
b94e32
b94e32
Now let's look at the output that I snipped above; this is the output
b94e32
when 'run' is issued from the script loaded via GDB's -x switch:
b94e32
b94e32
$ gdb -q -x dp-bug.gdb dp-bug
b94e32
Reading symbols from dp-bug...
b94e32
Dprintf 1 at 0x40116e: file dprintf-execution-x-script.c, line 38.
b94e32
Breakpoint 2 at 0x40113a: file dprintf-execution-x-script.c, line 26.
b94e32
vi=0
b94e32
dprintf in increment(), vi=0
b94e32
b94e32
Breakpoint 2, inc_vi () at dprintf-execution-x-script.c:26
b94e32
26	dprintf-execution-x-script.c: No such file or directory.
b94e32
vi=1
b94e32
b94e32
Breakpoint 2, inc_vi () at dprintf-execution-x-script.c:26
b94e32
26	in dprintf-execution-x-script.c
b94e32
vi=2
b94e32
b94e32
Breakpoint 2, inc_vi () at dprintf-execution-x-script.c:26
b94e32
26	in dprintf-execution-x-script.c
b94e32
vi=3
b94e32
[Inferior 1 (process 1539175) exited normally]
b94e32
b94e32
In the output shown above, only the first dprintf message is printed.
b94e32
The 2nd and 3rd dprintf messages are missing!  However, all three
b94e32
'Breakpoint 2...' messages are still printed.
b94e32
b94e32
Why does this happen?
b94e32
b94e32
bpstat_do_actions_1() in gdb/breakpoint.c contains the following
b94e32
comment and code near the start of the function:
b94e32
b94e32
  /* Avoid endless recursion if a `source' command is contained
b94e32
     in bs->commands.  */
b94e32
  if (executing_breakpoint_commands)
b94e32
    return 0;
b94e32
b94e32
  scoped_restore save_executing
b94e32
    = make_scoped_restore (&executing_breakpoint_commands, 1);
b94e32
b94e32
Also, as described by this comment prior to the 'async' field
b94e32
in 'struct ui' in top.h, the main UI starts off in sync mode
b94e32
when processing command line arguments:
b94e32
b94e32
  /* True if the UI is in async mode, false if in sync mode.  If in
b94e32
     sync mode, a synchronous execution command (e.g, "next") does not
b94e32
     return until the command is finished.  If in async mode, then
b94e32
     running a synchronous command returns right after resuming the
b94e32
     target.  Waiting for the command's completion is later done on
b94e32
     the top event loop.  For the main UI, this starts out disabled,
b94e32
     until all the explicit command line arguments (e.g., `gdb -ex
b94e32
     "start" -ex "next"') are processed.  */
b94e32
b94e32
This combination of things, the state of the static global
b94e32
'executing_breakpoint_commands' plus the state of the async
b94e32
field in the main UI causes this behavior.
b94e32
b94e32
This is a backtrace after hitting the dprintf breakpoint for
b94e32
the second time when doing 'run' from the script file, i.e.
b94e32
non-interactively:
b94e32
b94e32
Thread 1 "gdb" hit Breakpoint 3, bpstat_do_actions_1 (bsp=0x7fffffffc2b8)
b94e32
    at /ironwood1/sourceware-git/f34-master/bld/../../worktree-master/gdb/breakpoint.c:4431
b94e32
4431	  if (executing_breakpoint_commands)
b94e32
b94e32
 #0  bpstat_do_actions_1 (bsp=0x7fffffffc2b8)
b94e32
     at gdb/breakpoint.c:4431
b94e32
 #1  0x00000000004d8bc6 in dprintf_after_condition_true (bs=0x1538090)
b94e32
     at gdb/breakpoint.c:13048
b94e32
 #2  0x00000000004c5caa in bpstat_stop_status (aspace=0x116dbc0, bp_addr=0x40116e, thread=0x137f450, ws=0x7fffffffc718,
b94e32
     stop_chain=0x1538090) at gdb/breakpoint.c:5498
b94e32
 #3  0x0000000000768d98 in handle_signal_stop (ecs=0x7fffffffc6f0)
b94e32
     at gdb/infrun.c:6172
b94e32
 #4  0x00000000007678d3 in handle_inferior_event (ecs=0x7fffffffc6f0)
b94e32
     at gdb/infrun.c:5662
b94e32
 #5  0x0000000000763cd5 in fetch_inferior_event ()
b94e32
     at gdb/infrun.c:4060
b94e32
 #6  0x0000000000746d7d in inferior_event_handler (event_type=INF_REG_EVENT)
b94e32
     at gdb/inf-loop.c:41
b94e32
 #7  0x00000000007a702f in handle_target_event (error=0, client_data=0x0)
b94e32
     at gdb/linux-nat.c:4207
b94e32
 #8  0x0000000000b8cd6e in gdb_wait_for_event (block=block@entry=0)
b94e32
     at gdbsupport/event-loop.cc:701
b94e32
 #9  0x0000000000b8d032 in gdb_wait_for_event (block=0)
b94e32
     at gdbsupport/event-loop.cc:597
b94e32
 #10 gdb_do_one_event () at gdbsupport/event-loop.cc:212
b94e32
 #11 0x00000000009d19b6 in wait_sync_command_done ()
b94e32
     at gdb/top.c:528
b94e32
 #12 0x00000000009d1a3f in maybe_wait_sync_command_done (was_sync=0)
b94e32
     at gdb/top.c:545
b94e32
 #13 0x00000000009d2033 in execute_command (p=0x7fffffffcb18 "", from_tty=0)
b94e32
     at gdb/top.c:676
b94e32
 #14 0x0000000000560d5b in execute_control_command_1 (cmd=0x13b9bb0, from_tty=0)
b94e32
     at gdb/cli/cli-script.c:547
b94e32
 #15 0x000000000056134a in execute_control_command (cmd=0x13b9bb0, from_tty=0)
b94e32
     at gdb/cli/cli-script.c:717
b94e32
 #16 0x00000000004c3bbe in bpstat_do_actions_1 (bsp=0x137f530)
b94e32
     at gdb/breakpoint.c:4469
b94e32
 #17 0x00000000004c3d40 in bpstat_do_actions ()
b94e32
     at gdb/breakpoint.c:4533
b94e32
 #18 0x00000000006a473a in command_handler (command=0x1399ad0 "run")
b94e32
     at gdb/event-top.c:624
b94e32
 #19 0x00000000009d182e in read_command_file (stream=0x113e540)
b94e32
     at gdb/top.c:443
b94e32
 #20 0x0000000000563697 in script_from_file (stream=0x113e540, file=0x13bb0b0 "dp-bug.gdb")
b94e32
     at gdb/cli/cli-script.c:1642
b94e32
 #21 0x00000000006abd63 in source_gdb_script (extlang=0xc44e80 <extension_language_gdb>, stream=0x113e540,
b94e32
     file=0x13bb0b0 "dp-bug.gdb") at gdb/extension.c:188
b94e32
 #22 0x0000000000544400 in source_script_from_stream (stream=0x113e540, file=0x7fffffffd91a "dp-bug.gdb",
b94e32
     file_to_open=0x13bb0b0 "dp-bug.gdb")
b94e32
     at gdb/cli/cli-cmds.c:692
b94e32
 #23 0x0000000000544557 in source_script_with_search (file=0x7fffffffd91a "dp-bug.gdb", from_tty=1, search_path=0)
b94e32
     at gdb/cli/cli-cmds.c:750
b94e32
 #24 0x00000000005445cf in source_script (file=0x7fffffffd91a "dp-bug.gdb", from_tty=1)
b94e32
     at gdb/cli/cli-cmds.c:759
b94e32
 #25 0x00000000007cf6d9 in catch_command_errors (command=0x5445aa <source_script(char const*, int)>,
b94e32
     arg=0x7fffffffd91a "dp-bug.gdb", from_tty=1, do_bp_actions=false)
b94e32
     at gdb/main.c:523
b94e32
 #26 0x00000000007cf85d in execute_cmdargs (cmdarg_vec=0x7fffffffd1b0, file_type=CMDARG_FILE, cmd_type=CMDARG_COMMAND,
b94e32
     ret=0x7fffffffd18c) at gdb/main.c:615
b94e32
 #27 0x00000000007d0c8e in captured_main_1 (context=0x7fffffffd3f0)
b94e32
     at gdb/main.c:1322
b94e32
 #28 0x00000000007d0eba in captured_main (data=0x7fffffffd3f0)
b94e32
     at gdb/main.c:1343
b94e32
 #29 0x00000000007d0f25 in gdb_main (args=0x7fffffffd3f0)
b94e32
     at gdb/main.c:1368
b94e32
 #30 0x00000000004186dd in main (argc=5, argv=0x7fffffffd508)
b94e32
     at gdb/gdb.c:32
b94e32
b94e32
There are two frames for bpstat_do_actions_1(), one at frame #16 and
b94e32
the other at frame #0.  The one at frame #16 is processing the actions
b94e32
for Breakpoint 2, which is a 'continue'.  The one at frame #0 is attempting
b94e32
to process the dprintf breakpoint action.  However, at this point,
b94e32
the value of 'executing_breakpoint_commands' is 1, forcing an early
b94e32
return, i.e. prior to executing the command(s) associated with the dprintf
b94e32
breakpoint.
b94e32
b94e32
For the sake of comparison, this is what the stack looks like when hitting
b94e32
the dprintf breakpoint for the second time when issuing the 'run'
b94e32
command from the GDB prompt.
b94e32
b94e32
Thread 1 "gdb" hit Breakpoint 3, bpstat_do_actions_1 (bsp=0x7fffffffccd8)
b94e32
    at /ironwood1/sourceware-git/f34-master/bld/../../worktree-master/gdb/breakpoint.c:4431
b94e32
4431	  if (executing_breakpoint_commands)
b94e32
b94e32
 #0  bpstat_do_actions_1 (bsp=0x7fffffffccd8)
b94e32
     at gdb/breakpoint.c:4431
b94e32
 #1  0x00000000004d8bc6 in dprintf_after_condition_true (bs=0x16b0290)
b94e32
     at gdb/breakpoint.c:13048
b94e32
 #2  0x00000000004c5caa in bpstat_stop_status (aspace=0x116dbc0, bp_addr=0x40116e, thread=0x13f0e60, ws=0x7fffffffd138,
b94e32
     stop_chain=0x16b0290) at gdb/breakpoint.c:5498
b94e32
 #3  0x0000000000768d98 in handle_signal_stop (ecs=0x7fffffffd110)
b94e32
     at gdb/infrun.c:6172
b94e32
 #4  0x00000000007678d3 in handle_inferior_event (ecs=0x7fffffffd110)
b94e32
     at gdb/infrun.c:5662
b94e32
 #5  0x0000000000763cd5 in fetch_inferior_event ()
b94e32
     at gdb/infrun.c:4060
b94e32
 #6  0x0000000000746d7d in inferior_event_handler (event_type=INF_REG_EVENT)
b94e32
     at gdb/inf-loop.c:41
b94e32
 #7  0x00000000007a702f in handle_target_event (error=0, client_data=0x0)
b94e32
     at gdb/linux-nat.c:4207
b94e32
 #8  0x0000000000b8cd6e in gdb_wait_for_event (block=block@entry=0)
b94e32
     at gdbsupport/event-loop.cc:701
b94e32
 #9  0x0000000000b8d032 in gdb_wait_for_event (block=0)
b94e32
     at gdbsupport/event-loop.cc:597
b94e32
 #10 gdb_do_one_event () at gdbsupport/event-loop.cc:212
b94e32
 #11 0x00000000007cf512 in start_event_loop ()
b94e32
     at gdb/main.c:421
b94e32
 #12 0x00000000007cf631 in captured_command_loop ()
b94e32
     at gdb/main.c:481
b94e32
 #13 0x00000000007d0ebf in captured_main (data=0x7fffffffd3f0)
b94e32
     at gdb/main.c:1353
b94e32
 #14 0x00000000007d0f25 in gdb_main (args=0x7fffffffd3f0)
b94e32
     at gdb/main.c:1368
b94e32
 #15 0x00000000004186dd in main (argc=5, argv=0x7fffffffd508)
b94e32
     at gdb/gdb.c:32
b94e32
b94e32
This relatively short backtrace is due to the current UI's async field
b94e32
being set to 1.
b94e32
b94e32
Yet another thing to be aware of regarding this problem is the
b94e32
difference in the way that commands associated to dprintf breakpoints
b94e32
versus regular breakpoints are handled.  While they both use a command
b94e32
list associated with the breakpoint, regular breakpoints will place
b94e32
the commands to be run on the bpstat chain constructed in
b94e32
bp_stop_status().  These commands are run later on.  For dprintf
b94e32
breakpoints, commands are run via the 'after_condition_true' function
b94e32
pointer directly from bpstat_stop_status().  (The 'commands' field in
b94e32
the bpstat is cleared in dprintf_after_condition_true().  This
b94e32
prevents the dprintf commands from being run again later on when other
b94e32
commands on the bpstat chain are processed.)
b94e32
b94e32
Another thing that I noticed is that dprintf breakpoints are the only
b94e32
type of breakpoint which use 'after_condition_true'.  This suggests
b94e32
that one possible way of fixing this problem, that of making dprintf
b94e32
breakpoints work more like regular breakpoints, probably won't work.
b94e32
(I must admit, however, that my understanding of this code isn't
b94e32
complete enough to say why.  I'll trust that whoever implemented it
b94e32
had a good reason for doing it this way.)
b94e32
b94e32
The comment referenced earlier regarding 'executing_breakpoint_commands'
b94e32
states that the reason for checking this variable is to avoid
b94e32
potential endless recursion when a 'source' command appears in
b94e32
bs->commands.  We know that a dprintf command is constrained to either
b94e32
1) execution of a GDB printf command, 2) an inferior function call of
b94e32
a printf-like function, or 3) execution of an agent-printf command.
b94e32
Therefore, infinite recursion due to a 'source' command cannot happen
b94e32
when executing commands upon hitting a dprintf breakpoint.
b94e32
b94e32
I chose to fix this problem by having dprintf_after_condition_true()
b94e32
directly call execute_control_commands().  This means that it no
b94e32
longer attempts to go through bpstat_do_actions_1() avoiding the
b94e32
infinite recursion check for potential 'source' commands on the
b94e32
command chain.  I think it simplifies this code a little bit too, a
b94e32
definite bonus.
b94e32
b94e32
Summary:
b94e32
b94e32
	* breakpoint.c (dprintf_after_condition_true): Don't call
b94e32
	bpstat_do_actions_1().  Call execute_control_commands()
b94e32
	instead.
b94e32
b94e32
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
b94e32
--- a/gdb/breakpoint.c
b94e32
+++ b/gdb/breakpoint.c
b94e32
@@ -13043,9 +13043,6 @@ dprintf_print_recreate (struct breakpoint *tp, struct ui_file *fp)
b94e32
 static void
b94e32
 dprintf_after_condition_true (struct bpstats *bs)
b94e32
 {
b94e32
-  struct bpstats tmp_bs;
b94e32
-  struct bpstats *tmp_bs_p = &tmp_bs;
b94e32
-
b94e32
   /* dprintf's never cause a stop.  This wasn't set in the
b94e32
      check_status hook instead because that would make the dprintf's
b94e32
      condition not be evaluated.  */
b94e32
@@ -13056,14 +13053,9 @@ dprintf_after_condition_true (struct bpstats *bs)
b94e32
      bpstat_do_actions, if a breakpoint that causes a stop happens to
b94e32
      be set at same address as this dprintf, or even if running the
b94e32
      commands here throws.  */
b94e32
-  tmp_bs.commands = bs->commands;
b94e32
-  bs->commands = NULL;
b94e32
-
b94e32
-  bpstat_do_actions_1 (&tmp_bs_p);
b94e32
-
b94e32
-  /* 'tmp_bs.commands' will usually be NULL by now, but
b94e32
-     bpstat_do_actions_1 may return early without processing the whole
b94e32
-     list.  */
b94e32
+  counted_command_line cmds = std::move (bs->commands);
b94e32
+  gdb_assert (cmds != nullptr);
b94e32
+  execute_control_commands (cmds.get (), 0);
b94e32
 }
b94e32
 
b94e32
 /* The breakpoint_ops structure to be used on static tracepoints with