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

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