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

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