* [PATCH] Fix reverse stepping multiple contiguous PC ranges over the line table. @ 2023-04-27 20:59 Carl Love 2023-05-02 14:15 ` Bruno Larsen 2023-05-03 9:53 ` Bruno Larsen 0 siblings, 2 replies; 41+ messages in thread From: Carl Love @ 2023-04-27 20:59 UTC (permalink / raw) To: gdb-patches, Ulrich Weigand, pedro; +Cc: luis.machado, cel, blarsen GDB maintainers: The following patch fixes issues on PowerPC with the reverse-step and reverse-next instructions when there are multiple assignment statements on the same line and when there are multiple function calls on the same line. The commit log below discusses these issues in further depth. The discussion included what the correct operation should be for these commands based on the GDB documentation. The proposed patch at that time changed how the commands worked on other platforms such as X86 in a way they no longer matched the documentation. The issue is the line table contains multiple entries for the same source line. The patch adds a function to search the line table to find the address of the first instruction of a line. When setup up the reverse stepping range, the function is called to make sure the start of the range corresponds to the address of the first instruction for the line. The following patch fixes the execution of the reveres-step and reverse-next commands for both senarios of multiple statements on the same line for PowerPC and aarch64-linux. Unlike the previous patch, it does not change the operation of the commands on other platforms, i.e. X86. The patch adds new test cases for both scenarios to verify they work correctly. The patch has been tested on PowerPC, Intel X86 and aarch64-linux with no new regression failures. Please let me know if the patch is acceptable for mainline. Thanks. Carl --------------------------------------------------------------- Fix reverse stepping multiple contiguous PC ranges over the line table. There are a couple of scenarios where the GDB reverse-step and reverse-next commands do not work correctly. The first scenario consists of multiple assignment statements on the same line. A patch was proposed to address the issue by Luis Machado and briefly discussed on the mailing list in Feb 2021. https://sourceware.org/pipermail/gdb-patches/2021-February/175678.html The discussion was revived by Carl Love with regards to fixing the same issue on PowerPC. https://sourceware.org/pipermail/gdb-patches/2022-March/186463.html The patch was not completed and has been on Carl's to do list for some time. Discussion of a patch to change how the reverse-step and reverse-next commands submitted by Carl Love was started in thread: https://sourceware.org/pipermail/gdb-patches/2023-January/195563.html The patch was withdrawn as it was pointed out the proposed patch would change the intended behavior of the commands as documented in the GDB manual. However, it was pointed out by Pedro Alves <pedro@palves.net> that the reverse-step and reverse-next commands do not work when there are multiple function calls on the same line. This is a second scenario where the commands do not work correctly. The following patch is an extended version of the original patch by Luis Machado to fix the issues in scenario 1 to also address the issues in scenario 2. -------------------------------------------------------- Scenario 1 issue description by Luis Machado: When running GDB's testsuite on aarch64-linux/Ubuntu 20.04 (also spotted on the ppc backend), I noticed some failures in gdb.reverse/solib-precsave.exp and gdb.reverse/solib-reverse.exp. The failure happens around the following code: 38 b[1] = shr2(17); /* middle part two */ 40 b[0] = 6; b[1] = 9; /* generic statement, end part two */ 42 shr1 ("message 1\n"); /* shr1 one */ Normal execution: - step from line 38 will land on line 40. - step from line 40 will land on line 42. Reverse execution: - step from line 42 will land on line 40. - step from line 40 will land on line 40. - step from line 40 will land on line 38. The problem here is that line 40 contains two contiguous but distinct PC ranges in the line table, like so: Line 40 - [0x7ec ~ 0x7f4] Line 40 - [0x7f4 ~ 0x7fc] The two distinct ranges are generated because GCC started outputting source column information, which GDB doesn't take into account at the moment. When stepping forward from line 40, we skip both of these ranges and land on line 42. When stepping backward from line 42, we stop at the start PC of the second (or first, going backwards) range of line 40. This happens because we have this check in infrun.c:process_event_stop_test: /* When stepping backward, stop at beginning of line range (unless it's the function entry point, in which case keep going back to the call point). */ CORE_ADDR stop_pc = ecs->event_thread->stop_pc (); if (stop_pc == ecs->event_thread->control.step_range_start && stop_pc != ecs->stop_func_start && execution_direction == EXEC_REVERSE) end_stepping_range (ecs); else keep_going (ecs); Since we've reached ecs->event_thread->control.step_range_start, we stop stepping backwards. The right thing to do is to look for adjacent PC ranges for the same line, until we notice a line change. Then we take that as the start PC of the range. Another solution I thought about is to merge the contiguous ranges when we are reading the line tables. Though I'm not sure if we really want to process that data as opposed to keeping it as the compiler created, and then working around that. The test case gdb.reverse/map-to-same-line.exp is added to test the fix for the issues in scenario 1. --------------------------------------------------------- Scenario 2 issue described by Pedro Alves: The following explanation of the issue was taken from the gdb mailing list discussion of the withdrawn patch to change the behavior of the reverse-step and reverse-next commands. Specifically, message from Pedro Alves <pedro@palves.net> where he demonstrates the issue where you have multiple function calls on the same source code line: https://sourceware.org/pipermail/gdb-patches/2023-January/196110.html The source line looks like: func1 (); func2 (); so stepping backwards over that line should always stop at the first instruction of the line, not in the middle. Let's simplify this. Here's the full source code of my example: (gdb) list 1 1 void func1 () 2 { 3 } 4 5 void func2 () 6 { 7 } 8 9 int main () 10 { 11 func1 (); func2 (); 12 } Compiled with: $ gcc reverse.c -o reverse -g3 -O0 $ gcc -v ... gcc version 11.3.0 (Ubuntu 11.3.0-1ubuntu1~22.04) Now let's debug it with target record, using current gdb git master (f3d8ae90b236), without your patch: $ gdb ~/reverse GNU gdb (GDB) 14.0.50.20230124-git ... Reading symbols from /home/pedro/reverse... (gdb) start Temporary breakpoint 1 at 0x1147: file reverse.c, line 11. Starting program: /home/pedro/reverse [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". Temporary breakpoint 1, main () at reverse.c:11 11 func1 (); func2 (); (gdb) record (gdb) disassemble /s Dump of assembler code for function main: reverse.c: 10 { 0x000055555555513f <+0>: endbr64 0x0000555555555143 <+4>: push %rbp 0x0000555555555144 <+5>: mov %rsp,%rbp 11 func1 (); func2 (); => 0x0000555555555147 <+8>: mov $0x0,%eax 0x000055555555514c <+13>: call 0x555555555129 <func1> 0x0000555555555151 <+18>: mov $0x0,%eax 0x0000555555555156 <+23>: call 0x555555555134 <func2> 0x000055555555515b <+28>: mov $0x0,%eax 12 } 0x0000555555555160 <+33>: pop %rbp 0x0000555555555161 <+34>: ret End of assembler dump. (gdb) n 12 } So far so good, a "next" stepped over the whole of line 11 and stopped at line 12. Let's confirm where we are now: (gdb) disassemble /s Dump of assembler code for function main: reverse.c: 10 { 0x000055555555513f <+0>: endbr64 0x0000555555555143 <+4>: push %rbp 0x0000555555555144 <+5>: mov %rsp,%rbp 11 func1 (); func2 (); 0x0000555555555147 <+8>: mov $0x0,%eax 0x000055555555514c <+13>: call 0x555555555129 <func1> 0x0000555555555151 <+18>: mov $0x0,%eax 0x0000555555555156 <+23>: call 0x555555555134 <func2> 0x000055555555515b <+28>: mov $0x0,%eax 12 } => 0x0000555555555160 <+33>: pop %rbp 0x0000555555555161 <+34>: ret End of assembler dump. Good, we're at the first instruction of line 12. Now let's undo the "next", with "reverse-next": (gdb) reverse-next 11 func1 (); func2 (); Seemingly stopped at line 11. Let's see exactly where: (gdb) disassemble /s Dump of assembler code for function main: reverse.c: 10 { 0x000055555555513f <+0>: endbr64 0x0000555555555143 <+4>: push %rbp 0x0000555555555144 <+5>: mov %rsp,%rbp 11 func1 (); func2 (); 0x0000555555555147 <+8>: mov $0x0,%eax 0x000055555555514c <+13>: call 0x555555555129 <func1> => 0x0000555555555151 <+18>: mov $0x0,%eax 0x0000555555555156 <+23>: call 0x555555555134 <func2> 0x000055555555515b <+28>: mov $0x0,%eax 12 } 0x0000555555555160 <+33>: pop %rbp 0x0000555555555161 <+34>: ret End of assembler dump. (gdb) And lo, we stopped in the middle of line 11! That is a bug, we should have stepped back all the way to the beginning of the line. The "reverse-next" should have fully undone the prior "next" command. The test cases gdb.reverse/func-map-to-same-line-no-colum-info.exp and gdb.reverse/func-map-to-same-line.exp were added to test the fix for scenario 2 when the binary was compiled with and without line table information. bug: https://sourceware.org/bugzilla/show_bug.cgi?id=28426 Co-authored-by: Luis Machado <luis.machado@arm.com> Co-authored-by: Carl Love <cel@us.ibm.com> --- gdb/infrun.c | 57 +++++++ gdb/symtab.c | 49 ++++++ gdb/symtab.h | 16 ++ .../func-map-to-same-line-no-column-info.exp | 135 ++++++++++++++++ .../gdb.reverse/func-map-to-same-line.c | 36 +++++ .../gdb.reverse/func-map-to-same-line.exp | 123 ++++++++++++++ gdb/testsuite/gdb.reverse/map-to-same-line.c | 58 +++++++ .../gdb.reverse/map-to-same-line.exp | 153 ++++++++++++++++++ 8 files changed, 627 insertions(+) create mode 100644 gdb/testsuite/gdb.reverse/func-map-to-same-line-no-column-info.exp create mode 100644 gdb/testsuite/gdb.reverse/func-map-to-same-line.c create mode 100644 gdb/testsuite/gdb.reverse/func-map-to-same-line.exp create mode 100644 gdb/testsuite/gdb.reverse/map-to-same-line.c create mode 100644 gdb/testsuite/gdb.reverse/map-to-same-line.exp diff --git a/gdb/infrun.c b/gdb/infrun.c index 2f1c6cd694b..59374a05471 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -113,6 +113,9 @@ static struct async_event_handler *infrun_async_inferior_event_token; Starts off as -1, indicating "never enabled/disabled". */ static int infrun_is_async = -1; +static CORE_ADDR update_line_range_start (CORE_ADDR pc, + struct execution_control_state *ecs); + /* See infrun.h. */ void @@ -6768,6 +6771,25 @@ handle_signal_stop (struct execution_control_state *ecs) process_event_stop_test (ecs); } +CORE_ADDR +update_line_range_start (CORE_ADDR pc, struct execution_control_state *ecs) +{ + /* The line table may have multiple entries for the same source code line. + Given the PC, check the line table and return the PC that corresponds + to the line table entry for the source line that PC is in. */ + CORE_ADDR start_line_pc = ecs->event_thread->control.step_range_start; + gdb::optional<CORE_ADDR> real_range_start; + + /* Call find_line_range_start to get smallest address in the + linetable for multiple Line X entries in the line table. */ + real_range_start = find_line_range_start (pc); + + if (real_range_start.has_value ()) + start_line_pc = *real_range_start; + + return start_line_pc; +} + /* Come here when we've got some debug event / signal we can explain (IOW, not a random signal), and test whether it should cause a stop, or whether we should resume the inferior (transparently). @@ -7569,6 +7591,28 @@ process_event_stop_test (struct execution_control_state *ecs) if (stop_pc_sal.is_stmt) { + if (execution_direction == EXEC_REVERSE) + { + /* We are stepping backwards make sure we have reached the + beginning of the line. */ + CORE_ADDR stop_pc = ecs->event_thread->stop_pc (); + CORE_ADDR start_line_pc + = update_line_range_start (stop_pc, ecs); + + if (stop_pc != start_line_pc) + { + /* Have not reached the beginning of the source code line. + Set a step range. Execution should stop in any function + calls we execute back into before reaching the beginning + of the line. */ + ecs->event_thread->control.step_range_start = start_line_pc; + ecs->event_thread->control.step_range_end = stop_pc; + set_step_info (ecs->event_thread, frame, stop_pc_sal); + keep_going (ecs); + return; + } + } + /* We are at the start of a statement. So stop. Note that we don't stop if we step into the middle of a @@ -7631,6 +7675,19 @@ process_event_stop_test (struct execution_control_state *ecs) set_step_info (ecs->event_thread, frame, stop_pc_sal); infrun_debug_printf ("keep going"); + + if (execution_direction == EXEC_REVERSE) + { + CORE_ADDR stop_pc = ecs->event_thread->stop_pc (); + + /* Make sure the stop_pc is set to the beginning of the line. */ + if (stop_pc != ecs->event_thread->control.step_range_start) + { + stop_pc = update_line_range_start (stop_pc, ecs); + ecs->event_thread->control.step_range_start = stop_pc; + } + } + keep_going (ecs); } diff --git a/gdb/symtab.c b/gdb/symtab.c index 27611a34ec4..91d35616eb9 100644 --- a/gdb/symtab.c +++ b/gdb/symtab.c @@ -3282,6 +3282,55 @@ find_pc_line (CORE_ADDR pc, int notcurrent) return sal; } +/* Compare two symtab_and_line entries. Return true if both have + the same line number and the same symtab pointer. That means we + are dealing with two entries from the same line and from the same + source file. + + Return false otherwise. */ + +static bool +sal_line_symtab_matches_p (const symtab_and_line &sal1, + const symtab_and_line &sal2) +{ + return (sal1.line == sal2.line && sal1.symtab == sal2.symtab); +} + +/* See symtah.h. */ + +gdb::optional<CORE_ADDR> +find_line_range_start (CORE_ADDR pc) +{ + struct symtab_and_line current_sal = find_pc_line (pc, 0); + + if (current_sal.line == 0) + return {}; + + struct symtab_and_line prev_sal = find_pc_line (current_sal.pc - 1, 0); + + /* If the previous entry is for a different line, that means we are already + at the entry with the start PC for this line. */ + if (!sal_line_symtab_matches_p (prev_sal, current_sal)) + return current_sal.pc; + + /* Otherwise, keep looking for entries for the same line but with + smaller PC's. */ + bool done = false; + CORE_ADDR prev_pc; + while (!done) + { + prev_pc = prev_sal.pc; + + prev_sal = find_pc_line (prev_pc - 1, 0); + + /* Did we notice a line change? If so, we are done with the search. */ + if (!sal_line_symtab_matches_p (prev_sal, current_sal)) + done = true; + } + + return prev_pc; +} + /* See symtab.h. */ struct symtab * diff --git a/gdb/symtab.h b/gdb/symtab.h index 404d0ab30a8..f54305636da 100644 --- a/gdb/symtab.h +++ b/gdb/symtab.h @@ -2346,6 +2346,22 @@ extern struct symtab_and_line find_pc_line (CORE_ADDR, int); extern struct symtab_and_line find_pc_sect_line (CORE_ADDR, struct obj_section *, int); +/* Given PC, and assuming it is part of a range of addresses that is part of a + line, go back through the linetable and find the starting PC of that + line. + + For example, suppose we have 3 PC ranges for line X: + + Line X - [0x0 - 0x8] + Line X - [0x8 - 0x10] + Line X - [0x10 - 0x18] + + If we call the function with PC == 0x14, we want to return 0x0, as that is + the starting PC of line X, and the ranges are contiguous. +*/ + +extern gdb::optional<CORE_ADDR> find_line_range_start (CORE_ADDR pc); + /* Wrapper around find_pc_line to just return the symtab. */ extern struct symtab *find_pc_line_symtab (CORE_ADDR); diff --git a/gdb/testsuite/gdb.reverse/func-map-to-same-line-no-column-info.exp b/gdb/testsuite/gdb.reverse/func-map-to-same-line-no-column-info.exp new file mode 100644 index 00000000000..20529c90fc2 --- /dev/null +++ b/gdb/testsuite/gdb.reverse/func-map-to-same-line-no-column-info.exp @@ -0,0 +1,135 @@ +# Copyright 2008-2023 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 <http://www.gnu.org/licenses/>. */ + +# This file is part of the GDB testsuite. It tests reverse stepping. +# Lots of code borrowed from "step-test.exp". + +# This test checks to make sure there is no regression failures for +# the reverse-next command when stepping back over two functions in +# the same line. + +if ![supports_reverse] { + return +} + +# This test uses the gcc no-column-info command. +if ![is_c_compiler_gcc] { + unsupported "gcc is required for this test" + return 0 +} + +set srcfile func-map-to-same-line.c +set executable func-map-to-same-line + +set options [list debug additional_flags=-gno-column-info] + +if {[build_executable "failed to prepare" $executable $srcfile $options] == -1}\ + { + return -1 +} + +clean_restart $executable + +runto_main +set target_remote [gdb_is_target_remote] + +if [supports_process_record] { + # Activate process record/replay. + gdb_test_no_output "record" "turn on process record for test1" +} + +# This regression test verifies the reverse-step and reverse-next commands +# work properly when executing backwards thru a source line containing +# two function calls on the same source line, i.e. func1 (); func2 (); +# This test is compiled so the dwarf info not contain the line table +# information. + +# Test 1, reverse-next command +# Set breakpoint at the line after the function calls. +set bp_start_reverse_test [gdb_get_line_number "START REVERSE TEST" $srcfile] +gdb_breakpoint $srcfile:$bp_start_reverse_test temporary + +# Continue to break point for reverse-next test. +# Command definition: reverse-next [count] +# Run backward to the beginning of the previous line executed in the current +# (innermost) stack frame. If the line contains function calls, they will be +# “un-executed” without stopping. Starting from the first line of a function, +# reverse-next will take you back to the caller of that function, before the +# function was called, just as the normal next command would take you from +# the last line of a function back to its return to its caller 2 . + +gdb_continue_to_breakpoint \ + "stopped at command reverse-next test start location" \ + ".*$srcfile:$bp_start_reverse_test\r\n.*" + +# The reverse-next should step all the way back to the beginning of the line, +# i.e. at the beginning of the func1 call. +gdb_test "reverse-next" ".*func1 \\(\\); func2 \\(\\);.*" \ + "reverse-next to line with two functions" + +# A reverse-step should step back and stop at the beginning +# of the previous line b = 2, i.e. not in func1 (). +gdb_test "reverse-step" ".*b = 2;.*" \ + "reverse-step to previous line b = 2" + + +# Setup for test 2 +# Go back to the start of the function +gdb_test "reverse-continue" "a = 1;" "At start of main, setup for test 2" + +# Turn off record to clear logs and turn on again +gdb_test "record stop" "Process record is stopped.*" \ + "turn off process record for test1" +gdb_test_no_output "record" "turn on process record for test2" + +# Delete all breakpoints and catchpoints. +delete_breakpoints + + +# Test 2, reverse-step command +# Set breakpoint at the line after the function calls. +gdb_breakpoint $srcfile:$bp_start_reverse_test temporary + +# Continue to the start of the reverse-step test. +# Command definition: reverse-step [count] +# Run the program backward until control reaches the start of a +# different source line; then stop it, and return control to gdb. +# Like the step command, reverse-step will only stop at the beginning of a +# source line. It “un-executes” the previously executed source line. If the +# previous source line included calls to debuggable functions, reverse-step +# will step (backward) into the called function, stopping at the beginning +# of the last statement in the called function (typically a return +# statement). Also, as with the step command, if non-debuggable functions +# are called, reverse-step will run thru them backward without stopping. + +gdb_continue_to_breakpoint \ + "stopped at command reverse-step test start location" \ + ".*$srcfile:$bp_start_reverse_test\r\n.*" + +# The first reverse step should take us call of func2 (). +gdb_test "reverse-step" ".*}.*" \ + "reverse-step into func2 " + +# The second reverse step should take us into func1 (). +gdb_test "reverse-step" ".*}.*" \ + "reverse-step into func1 " + +# The third reverse step should take us call of func1 (). +gdb_test "reverse-step" ".*func1 \\(\\); func2 \\(\\);.*" \ + "reverse-step to line func1(); func2(), at call for func1 " + +# The fourth reverse step should take us to b = 2 (). +gdb_test "reverse-step" ".*b = 2;.*" \ + "reverse-step to line b = 2 " diff --git a/gdb/testsuite/gdb.reverse/func-map-to-same-line.c b/gdb/testsuite/gdb.reverse/func-map-to-same-line.c new file mode 100644 index 00000000000..e9787ef9ff5 --- /dev/null +++ b/gdb/testsuite/gdb.reverse/func-map-to-same-line.c @@ -0,0 +1,36 @@ +/* Copyright 2008-2023 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 <http://www.gnu.org/licenses/>. + + This test is used to test the reverse-step and reverse-next instruction + execution for a source line that contains multiple function calls. */ + +void +func1 () +{ +} + +void +func2 () +{ +} + +int main () +{ + int a, b; + a = 1; + b = 2; + func1 (); func2 (); + a = a + b; // START REVERSE TEST +} diff --git a/gdb/testsuite/gdb.reverse/func-map-to-same-line.exp b/gdb/testsuite/gdb.reverse/func-map-to-same-line.exp new file mode 100644 index 00000000000..b632a236bbe --- /dev/null +++ b/gdb/testsuite/gdb.reverse/func-map-to-same-line.exp @@ -0,0 +1,123 @@ +# Copyright 2008-2023 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 <http://www.gnu.org/licenses/>. */ + +# This file is part of the GDB testsuite. It tests reverse stepping. +# Lots of code borrowed from "step-test.exp". + +# This test checks to make sure there is no regression failures for +# the reverse-next command when stepping back over two functions in +# the same line. + +if ![supports_reverse] { + return +} + +standard_testfile + +if { [prepare_for_testing "failed to prepare" $testfile $srcfile] } { + return -1 +} + +runto_main +set target_remote [gdb_is_target_remote] + +if [supports_process_record] { + # Activate process record/replay. + gdb_test_no_output "record" "turn on process record for test1" +} + +# This regression test verifies the reverse-step and reverse-next commands +# work properly when executing backwards thru a source line containing +# two function calls on the same source line, i.e. func1 (); func2 (); +# The assumption for this test is the dwarf info contain the column +# information. + +# Test 1, reverse-next command +# Set breakpoint at the line after the function calls. +set bp_start_reverse_test [gdb_get_line_number "START REVERSE TEST" $srcfile] +gdb_breakpoint $srcfile:$bp_start_reverse_test temporary + +# Continue to break point for reverse-next test. +# Command definition: reverse-next [count] +# Run backward to the beginning of the previous line executed in the current +# (innermost) stack frame. If the line contains function calls, they will be +# “un-executed” without stopping. Starting from the first line of a function, +# reverse-next will take you back to the caller of that function, before the +# function was called, just as the normal next command would take you from +# the last line of a function back to its return to its caller 2 . + +gdb_continue_to_breakpoint \ + "stopped at command reverse-next test start location" \ + ".*$srcfile:$bp_start_reverse_test\r\n.*" + +# The reverse-next should step all the way back to the beginning of the line, +# i.e. at the beginning of the func1 call. +gdb_test "reverse-next" ".*func1 \\(\\); func2 \\(\\);.*" \ + "reverse-next to line with two functions" + +# A reverse-step should step back and stop at the beginning +# of the previous line b = 2, i.e. not in func1 (). +gdb_test "reverse-step" ".*b = 2;.*" \ + "reverse-step to previous line b = 2" + + +# Setup for test 2 +# Go back to the start of the function +gdb_test "reverse-continue" "a = 1;" "At start of main, setup for test 2" + +# Turn off record to clear logs and turn on again +gdb_test "record stop" "Process record is stopped.*" \ + "turn off process record for test1" +gdb_test_no_output "record" "turn on process record for test2" + +# Delete all breakpoints and catchpoints. +delete_breakpoints + + +# Test 2, reverse-step command +# Set breakpoint at the line after the function calls. +gdb_breakpoint $srcfile:$bp_start_reverse_test temporary + +# Continue to the start of the reverse-step test. +# Command definition: reverse-step [count] +# Run the program backward until control reaches the start of a +# different source line; then stop it, and return control to gdb. +# Like the step command, reverse-step will only stop at the beginning of a +# source line. It “un-executes” the previously executed source line. If the +# previous source line included calls to debuggable functions, reverse-step +# will step (backward) into the called function, stopping at the beginning +# of the last statement in the called function (typically a return +# statement). Also, as with the step command, if non-debuggable functions +# are called, reverse-step will run thru them backward without stopping. + +gdb_continue_to_breakpoint \ + "stopped at command reverse-step test start location" \ + ".*$srcfile:$bp_start_reverse_test\r\n.*" + +# The first reverse step should take us call of func2 (). +gdb_test "reverse-step" ".*}.*" \ + "reverse-step into func2 " + +# The second reverse step should take us into func1 (). +gdb_test "reverse-step" ".*}.*" \ + "reverse-step into func1 " + +# The third reverse step should take us call of func1 (). +gdb_test "reverse-step" ".*func1 \\(\\); func2 \\(\\);.*" \ + "reverse-step to line func1(); func2(), at call for func1 " + +# The fourth reverse step should take us to b = 2 (). +gdb_test "reverse-step" ".*b = 2;.*" \ + "reverse-step to line b = 2 " diff --git a/gdb/testsuite/gdb.reverse/map-to-same-line.c b/gdb/testsuite/gdb.reverse/map-to-same-line.c new file mode 100644 index 00000000000..f20d778f40e --- /dev/null +++ b/gdb/testsuite/gdb.reverse/map-to-same-line.c @@ -0,0 +1,58 @@ +/* Copyright 2008-2023 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 <http://www.gnu.org/licenses/ >. */ + +/* The purpose of this test is to create a DWARF line table that contains two + or more entries for the same line. When stepping (forwards or backwards), + GDB should step over the entire line and not just a particular entry in the + line table. */ + +int +main () +{ /* TAG: main prologue */ + asm ("main_label: .globl main_label"); + int i = 1, j = 2, k; + float f1 = 2.0, f2 = 4.1, f3; + const char *str_1 = "foo", *str_2 = "bar", *str_3; + + asm ("line1: .globl line1"); + k = i; f3 = f1; str_3 = str_1; /* TAG: line 1 */ + + asm ("line2: .globl line2"); + k = j; f3 = f2; str_3 = str_2; /* TAG: line 2 */ + + asm ("line3: .globl line3"); + k = i; f3 = f1; str_3 = str_1; /* TAG: line 3 */ + + asm ("line4: .globl line4"); + k = j; f3 = f2; str_3 = str_2; /* TAG: line 4 */ + + asm ("line5: .globl line5"); + k = i; f3 = f1; str_3 = str_1; /* TAG: line 5 */ + + asm ("line6: .globl line6"); + k = j; f3 = f2; str_3 = str_2; /* TAG: line 6 */ + + asm ("line7: .globl line7"); + k = i; f3 = f1; str_3 = str_1; /* TAG: line 7 */ + + asm ("line8: .globl line8"); + k = j; f3 = f2; str_3 = str_2; /* TAG: line 8 */ + + asm ("main_return: .globl main_return"); + k = j; f3 = f2; str_3 = str_2; /* TAG: main return */ + + asm ("end_of_sequence: .globl end_of_sequence"); + return 0; /* TAG: main return */ +} diff --git a/gdb/testsuite/gdb.reverse/map-to-same-line.exp b/gdb/testsuite/gdb.reverse/map-to-same-line.exp new file mode 100644 index 00000000000..a01579c2a8d --- /dev/null +++ b/gdb/testsuite/gdb.reverse/map-to-same-line.exp @@ -0,0 +1,153 @@ +# Copyright 2008-2023 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 <http://www.gnu.org/licenses/ >. + +# When stepping (forwards or backwards), GDB should step over the entire line +# and not just a particular entry in the line table. This test was added to +# verify the find_line_range_start function properly sets the step range for a +# line that consists of multiple statements, i.e. multiple entries in the line +# table. This test creates a DWARF line table that contains two entries for +# the same line to do the needed testing. + +load_lib dwarf.exp + +# This test can only be run on targets which support DWARF-2 and use gas. +if {![dwarf2_support]} { + unsupported "dwarf2 support required for this test" + return 0 +} + +if [get_compiler_info] { + return -1 +} + +# The DWARF assembler requires the gcc compiler. +if ![is_c_compiler_gcc] { + unsupported "gcc is required for this test" + return 0 +} + +# This test suitable only for process record-replay +if ![supports_process_record] { + return +} + +standard_testfile .c .S + +if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile}] } { + return -1 +} + +set asm_file [standard_output_file $srcfile2] +Dwarf::assemble $asm_file { + global srcdir subdir srcfile + declare_labels integer_label L + + # Find start address and length of program + lassign [function_range main [list ${srcdir}/${subdir}/$srcfile]] \ + main_start main_len + set main_end "$main_start + $main_len" + + cu {} { + compile_unit { + {language @DW_LANG_C} + {name map-to-same-line.c} + {stmt_list $L DW_FORM_sec_offset} + {low_pc 0 addr} + } { + subprogram { + {external 1 flag} + {name main} + {low_pc $main_start addr} + {high_pc $main_len DW_FORM_data4} + } + } + } + + lines {version 2 default_is_stmt 1} L { + include_dir "${srcdir}/${subdir}" + file_name "$srcfile" 1 + + # Generate the line table program with distinct source lines being + # mapped to the same line entry. Line 1, 5 and 8 contain 1 statement + # each. Line 2 contains 2 statements. Line 3 contains 3 statements. + program { + DW_LNE_set_address $main_start + line [gdb_get_line_number "TAG: main prologue"] + DW_LNS_copy + DW_LNE_set_address line1 + line [gdb_get_line_number "TAG: line 1" ] + DW_LNS_copy + DW_LNE_set_address line2 + line [gdb_get_line_number "TAG: line 2" ] + DW_LNS_copy + DW_LNE_set_address line3 + line [gdb_get_line_number "TAG: line 2" ] + DW_LNS_copy + DW_LNE_set_address line4 + line [gdb_get_line_number "TAG: line 3" ] + DW_LNS_copy + DW_LNE_set_address line5 + line [gdb_get_line_number "TAG: line 3" ] + DW_LNS_copy + DW_LNE_set_address line6 + line [gdb_get_line_number "TAG: line 3" ] + DW_LNS_copy + DW_LNE_set_address line7 + line [gdb_get_line_number "TAG: line 5" ] + DW_LNS_copy + DW_LNE_set_address line8 + line [gdb_get_line_number "TAG: line 8" ] + DW_LNS_copy + DW_LNE_set_address main_return + line [gdb_get_line_number "TAG: main return"] + DW_LNS_copy + DW_LNE_set_address end_of_sequence + DW_LNE_end_sequence + } + } +} + +if { [prepare_for_testing "failed to prepare" ${testfile} \ + [list $srcfile $asm_file] {nodebug} ] } { + return -1 +} + +if ![runto_main] { + return -1 +} + +# Print the line table +gdb_test_multiple "maint info line-table ${testfile}" "" { + -re "\r\n$decimal\[ \t\]+$decimal\[ \t\]+($hex)\[ \t\]+Y\[^\r\n\]*" { + lappend is_stmt $expect_out(1,string) + exp_continue + } + -re -wrap "" { + } +} + +# Activate process record/replay +gdb_test_no_output "record" "turn on process record" + +gdb_test "tbreak main_return" "Temporary breakpoint .*" "breakpoint at return" +gdb_test "continue" "Temporary breakpoint .*" "run to end of main" +gdb_test "display \$pc" ".*pc =.*" "display pc" + +# At this point, GDB has already recorded the execution up until the return +# statement. Reverse-step and test if GDB transitions between lines in the +# expected order. It should reverse-step across lines 8, 5, 3, 2 and 1. +foreach line {8 5 3 2 1} { + gdb_test "reverse-step" ".*TAG: line $line.*" "reverse step to line $line" +} -- 2.37.2 ^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH] Fix reverse stepping multiple contiguous PC ranges over the line table. 2023-04-27 20:59 [PATCH] Fix reverse stepping multiple contiguous PC ranges over the line table Carl Love @ 2023-05-02 14:15 ` Bruno Larsen 2023-05-02 15:40 ` Carl Love 2023-05-11 15:11 ` Simon Marchi 2023-05-03 9:53 ` Bruno Larsen 1 sibling, 2 replies; 41+ messages in thread From: Bruno Larsen @ 2023-05-02 14:15 UTC (permalink / raw) To: Carl Love, gdb-patches, Ulrich Weigand, pedro; +Cc: luis.machado On 27/04/2023 22:59, Carl Love wrote: > GDB maintainers: > > The following patch fixes issues on PowerPC with the reverse-step and > reverse-next instructions when there are multiple assignment statements > on the same line and when there are multiple function calls on the same > line. The commit log below discusses these issues in further depth. > The discussion included what the correct operation should be for these > commands based on the GDB documentation. The proposed patch at that > time changed how the commands worked on other platforms such as X86 in > a way they no longer matched the documentation. > > The issue is the line table contains multiple entries for the same > source line. The patch adds a function to search the line table to > find the address of the first instruction of a line. When setup up the > reverse stepping range, the function is called to make sure the start > of the range corresponds to the address of the first instruction for > the line. > > The following patch fixes the execution of the reveres-step and > reverse-next commands for both senarios of multiple statements on the > same line for PowerPC and aarch64-linux. Unlike the previous patch, it > does not change the operation of the commands on other platforms, i.e. > X86. The patch adds new test cases for both scenarios to verify they > work correctly. > > The patch has been tested on PowerPC, Intel X86 and aarch64-linux with > no new regression failures. > > Please let me know if the patch is acceptable for mainline. Thanks. > > Carl > > --------------------------------------------------------------- > Fix reverse stepping multiple contiguous PC ranges over the line table. > > There are a couple of scenarios where the GDB reverse-step and reverse-next > commands do not work correctly. The first scenario consists of multiple > assignment statements on the same line. A patch was proposed to address the > issue by Luis Machado and briefly discussed on the mailing list in Feb 2021. > > https://sourceware.org/pipermail/gdb-patches/2021-February/175678.html > > The discussion was revived by Carl Love with regards to fixing the same > issue on PowerPC. > > https://sourceware.org/pipermail/gdb-patches/2022-March/186463.html > > The patch was not completed and has been on Carl's to do list for some time. > > Discussion of a patch to change how the reverse-step and reverse-next > commands submitted by Carl Love was started in thread: > > https://sourceware.org/pipermail/gdb-patches/2023-January/195563.html > > The patch was withdrawn as it was pointed out the proposed patch would > change the intended behavior of the commands as documented in the GDB > manual. However, it was pointed out by Pedro Alves <pedro@palves.net> > that the reverse-step and reverse-next commands do not work when there > are multiple function calls on the same line. This is a second scenario > where the commands do not work correctly. > > The following patch is an extended version of the original patch by > Luis Machado to fix the issues in scenario 1 to also address the issues in > scenario 2. > > -------------------------------------------------------- > > Scenario 1 issue description by Luis Machado: > > When running GDB's testsuite on aarch64-linux/Ubuntu 20.04 (also spotted on > the ppc backend), I noticed some failures in gdb.reverse/solib-precsave.exp > and gdb.reverse/solib-reverse.exp. > > The failure happens around the following code: > > 38 b[1] = shr2(17); /* middle part two */ > 40 b[0] = 6; b[1] = 9; /* generic statement, end part two */ > 42 shr1 ("message 1\n"); /* shr1 one */ > > Normal execution: > > - step from line 38 will land on line 40. > - step from line 40 will land on line 42. > > Reverse execution: > - step from line 42 will land on line 40. > - step from line 40 will land on line 40. > - step from line 40 will land on line 38. > > The problem here is that line 40 contains two contiguous but distinct > PC ranges in the line table, like so: > > Line 40 - [0x7ec ~ 0x7f4] > Line 40 - [0x7f4 ~ 0x7fc] > > The two distinct ranges are generated because GCC started outputting source > column information, which GDB doesn't take into account at the moment. > > When stepping forward from line 40, we skip both of these ranges and land on > line 42. When stepping backward from line 42, we stop at the start PC of the > second (or first, going backwards) range of line 40. > > This happens because we have this check in infrun.c:process_event_stop_test: > > /* When stepping backward, stop at beginning of line range > (unless it's the function entry point, in which case > keep going back to the call point). */ > CORE_ADDR stop_pc = ecs->event_thread->stop_pc (); > if (stop_pc == ecs->event_thread->control.step_range_start > && stop_pc != ecs->stop_func_start > && execution_direction == EXEC_REVERSE) > end_stepping_range (ecs); > else > keep_going (ecs); > > Since we've reached ecs->event_thread->control.step_range_start, we stop > stepping backwards. > > The right thing to do is to look for adjacent PC ranges for the same line, > until we notice a line change. Then we take that as the start PC of the > range. > > Another solution I thought about is to merge the contiguous ranges when > we are reading the line tables. Though I'm not sure if we really want to > process that data as opposed to keeping it as the compiler created, and > then working around that. > > The test case gdb.reverse/map-to-same-line.exp is added to test the fix > for the issues in scenario 1. > > --------------------------------------------------------- > > Scenario 2 issue described by Pedro Alves: > > The following explanation of the issue was taken from the gdb mailing list > discussion of the withdrawn patch to change the behavior of the reverse-step > and reverse-next commands. Specifically, message from Pedro Alves > <pedro@palves.net> where he demonstrates the issue where you have multiple > function calls on the same source code line: > > https://sourceware.org/pipermail/gdb-patches/2023-January/196110.html > > The source line looks like: > > func1 (); func2 (); > > so stepping backwards over that line should always stop at the first > instruction of the line, not in the middle. Let's simplify this. > > Here's the full source code of my example: > > (gdb) list 1 > 1 void func1 () > 2 { > 3 } > 4 > 5 void func2 () > 6 { > 7 } > 8 > 9 int main () > 10 { > 11 func1 (); func2 (); > 12 } > > Compiled with: > > $ gcc reverse.c -o reverse -g3 -O0 > $ gcc -v > ... > gcc version 11.3.0 (Ubuntu 11.3.0-1ubuntu1~22.04) > > Now let's debug it with target record, using current gdb git master (f3d8ae90b236), > without your patch: > > $ gdb ~/reverse > GNU gdb (GDB) 14.0.50.20230124-git > ... > Reading symbols from /home/pedro/reverse... > (gdb) start > Temporary breakpoint 1 at 0x1147: file reverse.c, line 11. > Starting program: /home/pedro/reverse > [Thread debugging using libthread_db enabled] > Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". > > Temporary breakpoint 1, main () at reverse.c:11 > 11 func1 (); func2 (); > (gdb) record > > (gdb) disassemble /s > Dump of assembler code for function main: > reverse.c: > 10 { > 0x000055555555513f <+0>: endbr64 > 0x0000555555555143 <+4>: push %rbp > 0x0000555555555144 <+5>: mov %rsp,%rbp > > 11 func1 (); func2 (); > => 0x0000555555555147 <+8>: mov $0x0,%eax > 0x000055555555514c <+13>: call 0x555555555129 <func1> > 0x0000555555555151 <+18>: mov $0x0,%eax > 0x0000555555555156 <+23>: call 0x555555555134 <func2> > 0x000055555555515b <+28>: mov $0x0,%eax > > 12 } > 0x0000555555555160 <+33>: pop %rbp > 0x0000555555555161 <+34>: ret > End of assembler dump. > > (gdb) n > 12 } > > So far so good, a "next" stepped over the whole of line 11 and stopped at line 12. > > Let's confirm where we are now: > > (gdb) disassemble /s > Dump of assembler code for function main: > reverse.c: > 10 { > 0x000055555555513f <+0>: endbr64 > 0x0000555555555143 <+4>: push %rbp > 0x0000555555555144 <+5>: mov %rsp,%rbp > > 11 func1 (); func2 (); > 0x0000555555555147 <+8>: mov $0x0,%eax > 0x000055555555514c <+13>: call 0x555555555129 <func1> > 0x0000555555555151 <+18>: mov $0x0,%eax > 0x0000555555555156 <+23>: call 0x555555555134 <func2> > 0x000055555555515b <+28>: mov $0x0,%eax > > 12 } > => 0x0000555555555160 <+33>: pop %rbp > 0x0000555555555161 <+34>: ret > End of assembler dump. > > Good, we're at the first instruction of line 12. > > Now let's undo the "next", with "reverse-next": > > (gdb) reverse-next > 11 func1 (); func2 (); > > Seemingly stopped at line 11. Let's see exactly where: > > (gdb) disassemble /s > Dump of assembler code for function main: > reverse.c: > 10 { > 0x000055555555513f <+0>: endbr64 > 0x0000555555555143 <+4>: push %rbp > 0x0000555555555144 <+5>: mov %rsp,%rbp > > 11 func1 (); func2 (); > 0x0000555555555147 <+8>: mov $0x0,%eax > 0x000055555555514c <+13>: call 0x555555555129 <func1> > => 0x0000555555555151 <+18>: mov $0x0,%eax > 0x0000555555555156 <+23>: call 0x555555555134 <func2> > 0x000055555555515b <+28>: mov $0x0,%eax > > 12 } > 0x0000555555555160 <+33>: pop %rbp > 0x0000555555555161 <+34>: ret > End of assembler dump. > (gdb) > > And lo, we stopped in the middle of line 11! That is a bug, we should have > stepped back all the way to the beginning of the line. The "reverse-next" > should have fully undone the prior "next" command. > > The test cases gdb.reverse/func-map-to-same-line-no-colum-info.exp and > gdb.reverse/func-map-to-same-line.exp were added to test the fix for scenario > 2 when the binary was compiled with and without line table information. > > bug: https://sourceware.org/bugzilla/show_bug.cgi?id=28426 > > Co-authored-by: Luis Machado <luis.machado@arm.com> > Co-authored-by: Carl Love <cel@us.ibm.com> Hey Carl, Thanks for working on this. I'm wondering which parts will be part of the final commit messages and which is just context for the mailing list, so some clarity would be nice, but that is not a huge deal. I wanted to test this change, but it doesn't apply anymore on master, and `git apply --3way` can't figure out how to do it. Which commit did you use as base (or alternatively, can you rebase it)? -- Cheers, Bruno > --- > gdb/infrun.c | 57 +++++++ > gdb/symtab.c | 49 ++++++ > gdb/symtab.h | 16 ++ > .../func-map-to-same-line-no-column-info.exp | 135 ++++++++++++++++ > .../gdb.reverse/func-map-to-same-line.c | 36 +++++ > .../gdb.reverse/func-map-to-same-line.exp | 123 ++++++++++++++ > gdb/testsuite/gdb.reverse/map-to-same-line.c | 58 +++++++ > .../gdb.reverse/map-to-same-line.exp | 153 ++++++++++++++++++ > 8 files changed, 627 insertions(+) > create mode 100644 gdb/testsuite/gdb.reverse/func-map-to-same-line-no-column-info.exp > create mode 100644 gdb/testsuite/gdb.reverse/func-map-to-same-line.c > create mode 100644 gdb/testsuite/gdb.reverse/func-map-to-same-line.exp > create mode 100644 gdb/testsuite/gdb.reverse/map-to-same-line.c > create mode 100644 gdb/testsuite/gdb.reverse/map-to-same-line.exp > > diff --git a/gdb/infrun.c b/gdb/infrun.c > index 2f1c6cd694b..59374a05471 100644 > --- a/gdb/infrun.c > +++ b/gdb/infrun.c > @@ -113,6 +113,9 @@ static struct async_event_handler *infrun_async_inferior_event_token; > Starts off as -1, indicating "never enabled/disabled". */ > static int infrun_is_async = -1; > > +static CORE_ADDR update_line_range_start (CORE_ADDR pc, > + struct execution_control_state *ecs); > + > /* See infrun.h. */ > > void > @@ -6768,6 +6771,25 @@ handle_signal_stop (struct execution_control_state *ecs) > process_event_stop_test (ecs); > } > > +CORE_ADDR > +update_line_range_start (CORE_ADDR pc, struct execution_control_state *ecs) > +{ > + /* The line table may have multiple entries for the same source code line. > + Given the PC, check the line table and return the PC that corresponds > + to the line table entry for the source line that PC is in. */ > + CORE_ADDR start_line_pc = ecs->event_thread->control.step_range_start; > + gdb::optional<CORE_ADDR> real_range_start; > + > + /* Call find_line_range_start to get smallest address in the > + linetable for multiple Line X entries in the line table. */ > + real_range_start = find_line_range_start (pc); > + > + if (real_range_start.has_value ()) > + start_line_pc = *real_range_start; > + > + return start_line_pc; > +} > + > /* Come here when we've got some debug event / signal we can explain > (IOW, not a random signal), and test whether it should cause a > stop, or whether we should resume the inferior (transparently). > @@ -7569,6 +7591,28 @@ process_event_stop_test (struct execution_control_state *ecs) > > if (stop_pc_sal.is_stmt) > { > + if (execution_direction == EXEC_REVERSE) > + { > + /* We are stepping backwards make sure we have reached the > + beginning of the line. */ > + CORE_ADDR stop_pc = ecs->event_thread->stop_pc (); > + CORE_ADDR start_line_pc > + = update_line_range_start (stop_pc, ecs); > + > + if (stop_pc != start_line_pc) > + { > + /* Have not reached the beginning of the source code line. > + Set a step range. Execution should stop in any function > + calls we execute back into before reaching the beginning > + of the line. */ > + ecs->event_thread->control.step_range_start = start_line_pc; > + ecs->event_thread->control.step_range_end = stop_pc; > + set_step_info (ecs->event_thread, frame, stop_pc_sal); > + keep_going (ecs); > + return; > + } > + } > + > /* We are at the start of a statement. > > So stop. Note that we don't stop if we step into the middle of a > @@ -7631,6 +7675,19 @@ process_event_stop_test (struct execution_control_state *ecs) > set_step_info (ecs->event_thread, frame, stop_pc_sal); > > infrun_debug_printf ("keep going"); > + > + if (execution_direction == EXEC_REVERSE) > + { > + CORE_ADDR stop_pc = ecs->event_thread->stop_pc (); > + > + /* Make sure the stop_pc is set to the beginning of the line. */ > + if (stop_pc != ecs->event_thread->control.step_range_start) > + { > + stop_pc = update_line_range_start (stop_pc, ecs); > + ecs->event_thread->control.step_range_start = stop_pc; > + } > + } > + > keep_going (ecs); > } > > diff --git a/gdb/symtab.c b/gdb/symtab.c > index 27611a34ec4..91d35616eb9 100644 > --- a/gdb/symtab.c > +++ b/gdb/symtab.c > @@ -3282,6 +3282,55 @@ find_pc_line (CORE_ADDR pc, int notcurrent) > return sal; > } > > +/* Compare two symtab_and_line entries. Return true if both have > + the same line number and the same symtab pointer. That means we > + are dealing with two entries from the same line and from the same > + source file. > + > + Return false otherwise. */ > + > +static bool > +sal_line_symtab_matches_p (const symtab_and_line &sal1, > + const symtab_and_line &sal2) > +{ > + return (sal1.line == sal2.line && sal1.symtab == sal2.symtab); > +} > + > +/* See symtah.h. */ > + > +gdb::optional<CORE_ADDR> > +find_line_range_start (CORE_ADDR pc) > +{ > + struct symtab_and_line current_sal = find_pc_line (pc, 0); > + > + if (current_sal.line == 0) > + return {}; > + > + struct symtab_and_line prev_sal = find_pc_line (current_sal.pc - 1, 0); > + > + /* If the previous entry is for a different line, that means we are already > + at the entry with the start PC for this line. */ > + if (!sal_line_symtab_matches_p (prev_sal, current_sal)) > + return current_sal.pc; > + > + /* Otherwise, keep looking for entries for the same line but with > + smaller PC's. */ > + bool done = false; > + CORE_ADDR prev_pc; > + while (!done) > + { > + prev_pc = prev_sal.pc; > + > + prev_sal = find_pc_line (prev_pc - 1, 0); > + > + /* Did we notice a line change? If so, we are done with the search. */ > + if (!sal_line_symtab_matches_p (prev_sal, current_sal)) > + done = true; > + } > + > + return prev_pc; > +} > + > /* See symtab.h. */ > > struct symtab * > diff --git a/gdb/symtab.h b/gdb/symtab.h > index 404d0ab30a8..f54305636da 100644 > --- a/gdb/symtab.h > +++ b/gdb/symtab.h > @@ -2346,6 +2346,22 @@ extern struct symtab_and_line find_pc_line (CORE_ADDR, int); > extern struct symtab_and_line find_pc_sect_line (CORE_ADDR, > struct obj_section *, int); > > +/* Given PC, and assuming it is part of a range of addresses that is part of a > + line, go back through the linetable and find the starting PC of that > + line. > + > + For example, suppose we have 3 PC ranges for line X: > + > + Line X - [0x0 - 0x8] > + Line X - [0x8 - 0x10] > + Line X - [0x10 - 0x18] > + > + If we call the function with PC == 0x14, we want to return 0x0, as that is > + the starting PC of line X, and the ranges are contiguous. > +*/ > + > +extern gdb::optional<CORE_ADDR> find_line_range_start (CORE_ADDR pc); > + > /* Wrapper around find_pc_line to just return the symtab. */ > > extern struct symtab *find_pc_line_symtab (CORE_ADDR); > diff --git a/gdb/testsuite/gdb.reverse/func-map-to-same-line-no-column-info.exp b/gdb/testsuite/gdb.reverse/func-map-to-same-line-no-column-info.exp > new file mode 100644 > index 00000000000..20529c90fc2 > --- /dev/null > +++ b/gdb/testsuite/gdb.reverse/func-map-to-same-line-no-column-info.exp > @@ -0,0 +1,135 @@ > +# Copyright 2008-2023 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 <http://www.gnu.org/licenses/>. */ > + > +# This file is part of the GDB testsuite. It tests reverse stepping. > +# Lots of code borrowed from "step-test.exp". > + > +# This test checks to make sure there is no regression failures for > +# the reverse-next command when stepping back over two functions in > +# the same line. > + > +if ![supports_reverse] { > + return > +} > + > +# This test uses the gcc no-column-info command. > +if ![is_c_compiler_gcc] { > + unsupported "gcc is required for this test" > + return 0 > +} > + > +set srcfile func-map-to-same-line.c > +set executable func-map-to-same-line > + > +set options [list debug additional_flags=-gno-column-info] > + > +if {[build_executable "failed to prepare" $executable $srcfile $options] == -1}\ > + { > + return -1 > +} > + > +clean_restart $executable > + > +runto_main > +set target_remote [gdb_is_target_remote] > + > +if [supports_process_record] { > + # Activate process record/replay. > + gdb_test_no_output "record" "turn on process record for test1" > +} > + > +# This regression test verifies the reverse-step and reverse-next commands > +# work properly when executing backwards thru a source line containing > +# two function calls on the same source line, i.e. func1 (); func2 (); > +# This test is compiled so the dwarf info not contain the line table > +# information. > + > +# Test 1, reverse-next command > +# Set breakpoint at the line after the function calls. > +set bp_start_reverse_test [gdb_get_line_number "START REVERSE TEST" $srcfile] > +gdb_breakpoint $srcfile:$bp_start_reverse_test temporary > + > +# Continue to break point for reverse-next test. > +# Command definition: reverse-next [count] > +# Run backward to the beginning of the previous line executed in the current > +# (innermost) stack frame. If the line contains function calls, they will be > +# “un-executed” without stopping. Starting from the first line of a function, > +# reverse-next will take you back to the caller of that function, before the > +# function was called, just as the normal next command would take you from > +# the last line of a function back to its return to its caller 2 . > + > +gdb_continue_to_breakpoint \ > + "stopped at command reverse-next test start location" \ > + ".*$srcfile:$bp_start_reverse_test\r\n.*" > + > +# The reverse-next should step all the way back to the beginning of the line, > +# i.e. at the beginning of the func1 call. > +gdb_test "reverse-next" ".*func1 \\(\\); func2 \\(\\);.*" \ > + "reverse-next to line with two functions" > + > +# A reverse-step should step back and stop at the beginning > +# of the previous line b = 2, i.e. not in func1 (). > +gdb_test "reverse-step" ".*b = 2;.*" \ > + "reverse-step to previous line b = 2" > + > + > +# Setup for test 2 > +# Go back to the start of the function > +gdb_test "reverse-continue" "a = 1;" "At start of main, setup for test 2" > + > +# Turn off record to clear logs and turn on again > +gdb_test "record stop" "Process record is stopped.*" \ > + "turn off process record for test1" > +gdb_test_no_output "record" "turn on process record for test2" > + > +# Delete all breakpoints and catchpoints. > +delete_breakpoints > + > + > +# Test 2, reverse-step command > +# Set breakpoint at the line after the function calls. > +gdb_breakpoint $srcfile:$bp_start_reverse_test temporary > + > +# Continue to the start of the reverse-step test. > +# Command definition: reverse-step [count] > +# Run the program backward until control reaches the start of a > +# different source line; then stop it, and return control to gdb. > +# Like the step command, reverse-step will only stop at the beginning of a > +# source line. It “un-executes” the previously executed source line. If the > +# previous source line included calls to debuggable functions, reverse-step > +# will step (backward) into the called function, stopping at the beginning > +# of the last statement in the called function (typically a return > +# statement). Also, as with the step command, if non-debuggable functions > +# are called, reverse-step will run thru them backward without stopping. > + > +gdb_continue_to_breakpoint \ > + "stopped at command reverse-step test start location" \ > + ".*$srcfile:$bp_start_reverse_test\r\n.*" > + > +# The first reverse step should take us call of func2 (). > +gdb_test "reverse-step" ".*}.*" \ > + "reverse-step into func2 " > + > +# The second reverse step should take us into func1 (). > +gdb_test "reverse-step" ".*}.*" \ > + "reverse-step into func1 " > + > +# The third reverse step should take us call of func1 (). > +gdb_test "reverse-step" ".*func1 \\(\\); func2 \\(\\);.*" \ > + "reverse-step to line func1(); func2(), at call for func1 " > + > +# The fourth reverse step should take us to b = 2 (). > +gdb_test "reverse-step" ".*b = 2;.*" \ > + "reverse-step to line b = 2 " > diff --git a/gdb/testsuite/gdb.reverse/func-map-to-same-line.c b/gdb/testsuite/gdb.reverse/func-map-to-same-line.c > new file mode 100644 > index 00000000000..e9787ef9ff5 > --- /dev/null > +++ b/gdb/testsuite/gdb.reverse/func-map-to-same-line.c > @@ -0,0 +1,36 @@ > +/* Copyright 2008-2023 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 <http://www.gnu.org/licenses/>. > + > + This test is used to test the reverse-step and reverse-next instruction > + execution for a source line that contains multiple function calls. */ > + > +void > +func1 () > +{ > +} > + > +void > +func2 () > +{ > +} > + > +int main () > +{ > + int a, b; > + a = 1; > + b = 2; > + func1 (); func2 (); > + a = a + b; // START REVERSE TEST > +} > diff --git a/gdb/testsuite/gdb.reverse/func-map-to-same-line.exp b/gdb/testsuite/gdb.reverse/func-map-to-same-line.exp > new file mode 100644 > index 00000000000..b632a236bbe > --- /dev/null > +++ b/gdb/testsuite/gdb.reverse/func-map-to-same-line.exp > @@ -0,0 +1,123 @@ > +# Copyright 2008-2023 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 <http://www.gnu.org/licenses/>. */ > + > +# This file is part of the GDB testsuite. It tests reverse stepping. > +# Lots of code borrowed from "step-test.exp". > + > +# This test checks to make sure there is no regression failures for > +# the reverse-next command when stepping back over two functions in > +# the same line. > + > +if ![supports_reverse] { > + return > +} > + > +standard_testfile > + > +if { [prepare_for_testing "failed to prepare" $testfile $srcfile] } { > + return -1 > +} > + > +runto_main > +set target_remote [gdb_is_target_remote] > + > +if [supports_process_record] { > + # Activate process record/replay. > + gdb_test_no_output "record" "turn on process record for test1" > +} > + > +# This regression test verifies the reverse-step and reverse-next commands > +# work properly when executing backwards thru a source line containing > +# two function calls on the same source line, i.e. func1 (); func2 (); > +# The assumption for this test is the dwarf info contain the column > +# information. > + > +# Test 1, reverse-next command > +# Set breakpoint at the line after the function calls. > +set bp_start_reverse_test [gdb_get_line_number "START REVERSE TEST" $srcfile] > +gdb_breakpoint $srcfile:$bp_start_reverse_test temporary > + > +# Continue to break point for reverse-next test. > +# Command definition: reverse-next [count] > +# Run backward to the beginning of the previous line executed in the current > +# (innermost) stack frame. If the line contains function calls, they will be > +# “un-executed” without stopping. Starting from the first line of a function, > +# reverse-next will take you back to the caller of that function, before the > +# function was called, just as the normal next command would take you from > +# the last line of a function back to its return to its caller 2 . > + > +gdb_continue_to_breakpoint \ > + "stopped at command reverse-next test start location" \ > + ".*$srcfile:$bp_start_reverse_test\r\n.*" > + > +# The reverse-next should step all the way back to the beginning of the line, > +# i.e. at the beginning of the func1 call. > +gdb_test "reverse-next" ".*func1 \\(\\); func2 \\(\\);.*" \ > + "reverse-next to line with two functions" > + > +# A reverse-step should step back and stop at the beginning > +# of the previous line b = 2, i.e. not in func1 (). > +gdb_test "reverse-step" ".*b = 2;.*" \ > + "reverse-step to previous line b = 2" > + > + > +# Setup for test 2 > +# Go back to the start of the function > +gdb_test "reverse-continue" "a = 1;" "At start of main, setup for test 2" > + > +# Turn off record to clear logs and turn on again > +gdb_test "record stop" "Process record is stopped.*" \ > + "turn off process record for test1" > +gdb_test_no_output "record" "turn on process record for test2" > + > +# Delete all breakpoints and catchpoints. > +delete_breakpoints > + > + > +# Test 2, reverse-step command > +# Set breakpoint at the line after the function calls. > +gdb_breakpoint $srcfile:$bp_start_reverse_test temporary > + > +# Continue to the start of the reverse-step test. > +# Command definition: reverse-step [count] > +# Run the program backward until control reaches the start of a > +# different source line; then stop it, and return control to gdb. > +# Like the step command, reverse-step will only stop at the beginning of a > +# source line. It “un-executes” the previously executed source line. If the > +# previous source line included calls to debuggable functions, reverse-step > +# will step (backward) into the called function, stopping at the beginning > +# of the last statement in the called function (typically a return > +# statement). Also, as with the step command, if non-debuggable functions > +# are called, reverse-step will run thru them backward without stopping. > + > +gdb_continue_to_breakpoint \ > + "stopped at command reverse-step test start location" \ > + ".*$srcfile:$bp_start_reverse_test\r\n.*" > + > +# The first reverse step should take us call of func2 (). > +gdb_test "reverse-step" ".*}.*" \ > + "reverse-step into func2 " > + > +# The second reverse step should take us into func1 (). > +gdb_test "reverse-step" ".*}.*" \ > + "reverse-step into func1 " > + > +# The third reverse step should take us call of func1 (). > +gdb_test "reverse-step" ".*func1 \\(\\); func2 \\(\\);.*" \ > + "reverse-step to line func1(); func2(), at call for func1 " > + > +# The fourth reverse step should take us to b = 2 (). > +gdb_test "reverse-step" ".*b = 2;.*" \ > + "reverse-step to line b = 2 " > diff --git a/gdb/testsuite/gdb.reverse/map-to-same-line.c b/gdb/testsuite/gdb.reverse/map-to-same-line.c > new file mode 100644 > index 00000000000..f20d778f40e > --- /dev/null > +++ b/gdb/testsuite/gdb.reverse/map-to-same-line.c > @@ -0,0 +1,58 @@ > +/* Copyright 2008-2023 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 <http://www.gnu.org/licenses/ >. */ > + > +/* The purpose of this test is to create a DWARF line table that contains two > + or more entries for the same line. When stepping (forwards or backwards), > + GDB should step over the entire line and not just a particular entry in the > + line table. */ > + > +int > +main () > +{ /* TAG: main prologue */ > + asm ("main_label: .globl main_label"); > + int i = 1, j = 2, k; > + float f1 = 2.0, f2 = 4.1, f3; > + const char *str_1 = "foo", *str_2 = "bar", *str_3; > + > + asm ("line1: .globl line1"); > + k = i; f3 = f1; str_3 = str_1; /* TAG: line 1 */ > + > + asm ("line2: .globl line2"); > + k = j; f3 = f2; str_3 = str_2; /* TAG: line 2 */ > + > + asm ("line3: .globl line3"); > + k = i; f3 = f1; str_3 = str_1; /* TAG: line 3 */ > + > + asm ("line4: .globl line4"); > + k = j; f3 = f2; str_3 = str_2; /* TAG: line 4 */ > + > + asm ("line5: .globl line5"); > + k = i; f3 = f1; str_3 = str_1; /* TAG: line 5 */ > + > + asm ("line6: .globl line6"); > + k = j; f3 = f2; str_3 = str_2; /* TAG: line 6 */ > + > + asm ("line7: .globl line7"); > + k = i; f3 = f1; str_3 = str_1; /* TAG: line 7 */ > + > + asm ("line8: .globl line8"); > + k = j; f3 = f2; str_3 = str_2; /* TAG: line 8 */ > + > + asm ("main_return: .globl main_return"); > + k = j; f3 = f2; str_3 = str_2; /* TAG: main return */ > + > + asm ("end_of_sequence: .globl end_of_sequence"); > + return 0; /* TAG: main return */ > +} > diff --git a/gdb/testsuite/gdb.reverse/map-to-same-line.exp b/gdb/testsuite/gdb.reverse/map-to-same-line.exp > new file mode 100644 > index 00000000000..a01579c2a8d > --- /dev/null > +++ b/gdb/testsuite/gdb.reverse/map-to-same-line.exp > @@ -0,0 +1,153 @@ > +# Copyright 2008-2023 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 <http://www.gnu.org/licenses/ >. > + > +# When stepping (forwards or backwards), GDB should step over the entire line > +# and not just a particular entry in the line table. This test was added to > +# verify the find_line_range_start function properly sets the step range for a > +# line that consists of multiple statements, i.e. multiple entries in the line > +# table. This test creates a DWARF line table that contains two entries for > +# the same line to do the needed testing. > + > +load_lib dwarf.exp > + > +# This test can only be run on targets which support DWARF-2 and use gas. > +if {![dwarf2_support]} { > + unsupported "dwarf2 support required for this test" > + return 0 > +} > + > +if [get_compiler_info] { > + return -1 > +} > + > +# The DWARF assembler requires the gcc compiler. > +if ![is_c_compiler_gcc] { > + unsupported "gcc is required for this test" > + return 0 > +} > + > +# This test suitable only for process record-replay > +if ![supports_process_record] { > + return > +} > + > +standard_testfile .c .S > + > +if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile}] } { > + return -1 > +} > + > +set asm_file [standard_output_file $srcfile2] > +Dwarf::assemble $asm_file { > + global srcdir subdir srcfile > + declare_labels integer_label L > + > + # Find start address and length of program > + lassign [function_range main [list ${srcdir}/${subdir}/$srcfile]] \ > + main_start main_len > + set main_end "$main_start + $main_len" > + > + cu {} { > + compile_unit { > + {language @DW_LANG_C} > + {name map-to-same-line.c} > + {stmt_list $L DW_FORM_sec_offset} > + {low_pc 0 addr} > + } { > + subprogram { > + {external 1 flag} > + {name main} > + {low_pc $main_start addr} > + {high_pc $main_len DW_FORM_data4} > + } > + } > + } > + > + lines {version 2 default_is_stmt 1} L { > + include_dir "${srcdir}/${subdir}" > + file_name "$srcfile" 1 > + > + # Generate the line table program with distinct source lines being > + # mapped to the same line entry. Line 1, 5 and 8 contain 1 statement > + # each. Line 2 contains 2 statements. Line 3 contains 3 statements. > + program { > + DW_LNE_set_address $main_start > + line [gdb_get_line_number "TAG: main prologue"] > + DW_LNS_copy > + DW_LNE_set_address line1 > + line [gdb_get_line_number "TAG: line 1" ] > + DW_LNS_copy > + DW_LNE_set_address line2 > + line [gdb_get_line_number "TAG: line 2" ] > + DW_LNS_copy > + DW_LNE_set_address line3 > + line [gdb_get_line_number "TAG: line 2" ] > + DW_LNS_copy > + DW_LNE_set_address line4 > + line [gdb_get_line_number "TAG: line 3" ] > + DW_LNS_copy > + DW_LNE_set_address line5 > + line [gdb_get_line_number "TAG: line 3" ] > + DW_LNS_copy > + DW_LNE_set_address line6 > + line [gdb_get_line_number "TAG: line 3" ] > + DW_LNS_copy > + DW_LNE_set_address line7 > + line [gdb_get_line_number "TAG: line 5" ] > + DW_LNS_copy > + DW_LNE_set_address line8 > + line [gdb_get_line_number "TAG: line 8" ] > + DW_LNS_copy > + DW_LNE_set_address main_return > + line [gdb_get_line_number "TAG: main return"] > + DW_LNS_copy > + DW_LNE_set_address end_of_sequence > + DW_LNE_end_sequence > + } > + } > +} > + > +if { [prepare_for_testing "failed to prepare" ${testfile} \ > + [list $srcfile $asm_file] {nodebug} ] } { > + return -1 > +} > + > +if ![runto_main] { > + return -1 > +} > + > +# Print the line table > +gdb_test_multiple "maint info line-table ${testfile}" "" { > + -re "\r\n$decimal\[ \t\]+$decimal\[ \t\]+($hex)\[ \t\]+Y\[^\r\n\]*" { > + lappend is_stmt $expect_out(1,string) > + exp_continue > + } > + -re -wrap "" { > + } > +} > + > +# Activate process record/replay > +gdb_test_no_output "record" "turn on process record" > + > +gdb_test "tbreak main_return" "Temporary breakpoint .*" "breakpoint at return" > +gdb_test "continue" "Temporary breakpoint .*" "run to end of main" > +gdb_test "display \$pc" ".*pc =.*" "display pc" > + > +# At this point, GDB has already recorded the execution up until the return > +# statement. Reverse-step and test if GDB transitions between lines in the > +# expected order. It should reverse-step across lines 8, 5, 3, 2 and 1. > +foreach line {8 5 3 2 1} { > + gdb_test "reverse-step" ".*TAG: line $line.*" "reverse step to line $line" > +} ^ permalink raw reply [flat|nested] 41+ messages in thread
* RE: [PATCH] Fix reverse stepping multiple contiguous PC ranges over the line table. 2023-05-02 14:15 ` Bruno Larsen @ 2023-05-02 15:40 ` Carl Love 2023-05-02 15:42 ` Bruno Larsen 2023-05-11 15:11 ` Simon Marchi 1 sibling, 1 reply; 41+ messages in thread From: Carl Love @ 2023-05-02 15:40 UTC (permalink / raw) To: Bruno Larsen, gdb-patches, Ulrich Weigand, pedro; +Cc: luis.machado Bruno: On Tue, 2023-05-02 at 16:15 +0200, Bruno Larsen wrote: I had intended everything from here down as part of the commit message. It is admittedly a lot. It could be cut down my making the first part could be moved to the mailing list context. The commit message would then start with the two scenarios the patch fixes. > > > --------------------------------------------------------------- > > Fix reverse stepping multiple contiguous PC ranges over the line > > table. > > > > There are a couple of scenarios where the GDB reverse-step and > > reverse-next > > commands do not work correctly. The first scenario consists of > > multiple > > assignment statements on the same line. A patch was proposed to > > address the > > issue by Luis Machado and briefly discussed on the mailing list in > > Feb 2021. > > > > https://sourceware.org/pipermail/gdb-patches/2021-February/175678.html > > > > The discussion was revived by Carl Love with regards to fixing the > > same > > issue on PowerPC. > > > > https://sourceware.org/pipermail/gdb-patches/2022-March/186463.html > > > > The patch was not completed and has been on Carl's to do list for > > some time. > > > > Discussion of a patch to change how the reverse-step and reverse- > > next > > commands submitted by Carl Love was started in thread: > > > > https://sourceware.org/pipermail/gdb-patches/2023-January/195563.html > > > > The patch was withdrawn as it was pointed out the proposed patch > > would > > change the intended behavior of the commands as documented in the > > GDB > > manual. However, it was pointed out by Pedro Alves < > > pedro@palves.net> > > that the reverse-step and reverse-next commands do not work when > > there > > are multiple function calls on the same line. This is a second > > scenario > > where the commands do not work correctly. > > > > The following patch is an extended version of the original patch by > > Luis Machado to fix the issues in scenario 1 to also address the > > issues in > > scenario 2. > > Move the above to the mailing list Start here with the commit message > > -------------------------------------------------------- > > > > Scenario 1 issue description by Luis Machado: > > > > When running GDB's testsuite on aarch64-linux/Ubuntu 20.04 (also > > spotted on > > the ppc backend), I noticed some failures in gdb.reverse/solib- > > precsave.exp > > and gdb.reverse/solib-reverse.exp. > > > > The failure happens around the following code: > > > > 38 b[1] = shr2(17); /* middle part two */ > > 40 b[0] = 6; b[1] = 9; /* generic statement, end part two */ > > 42 shr1 ("message 1\n"); /* shr1 one */ > > > > Normal execution: > > > > - step from line 38 will land on line 40. > > - step from line 40 will land on line 42. > > > > Reverse execution: > > - step from line 42 will land on line 40. > > - step from line 40 will land on line 40. > > - step from line 40 will land on line 38. > > > > The problem here is that line 40 contains two contiguous but > > distinct > > PC ranges in the line table, like so: > > > > Line 40 - [0x7ec ~ 0x7f4] > > Line 40 - [0x7f4 ~ 0x7fc] > > > > The two distinct ranges are generated because GCC started > > outputting source > > column information, which GDB doesn't take into account at the > > moment. > > > > When stepping forward from line 40, we skip both of these ranges > > and land on > > line 42. When stepping backward from line 42, we stop at the start > > PC of the > > second (or first, going backwards) range of line 40. > > > > This happens because we have this check in > > infrun.c:process_event_stop_test: > > > > /* When stepping backward, stop at beginning of line range > > (unless it's the function entry point, in which case > > keep going back to the call point). */ > > CORE_ADDR stop_pc = ecs->event_thread->stop_pc (); > > if (stop_pc == ecs->event_thread->control.step_range_start > > && stop_pc != ecs->stop_func_start > > && execution_direction == EXEC_REVERSE) > > end_stepping_range (ecs); > > else > > keep_going (ecs); > > > > Since we've reached ecs->event_thread->control.step_range_start, we > > stop > > stepping backwards. > > > > The right thing to do is to look for adjacent PC ranges for the > > same line, > > until we notice a line change. Then we take that as the start PC of > > the > > range. > > > > Another solution I thought about is to merge the contiguous ranges > > when > > we are reading the line tables. Though I'm not sure if we really > > want to > > process that data as opposed to keeping it as the compiler created, > > and > > then working around that. > > > > The test case gdb.reverse/map-to-same-line.exp is added to test the > > fix > > for the issues in scenario 1. > > > > --------------------------------------------------------- > > > > Scenario 2 issue described by Pedro Alves: > > > > The following explanation of the issue was taken from the gdb > > mailing list > > discussion of the withdrawn patch to change the behavior of the > > reverse-step > > and reverse-next commands. Specifically, message from Pedro Alves > > <pedro@palves.net> where he demonstrates the issue where you have > > multiple > > function calls on the same source code line: > > > > https://sourceware.org/pipermail/gdb-patches/2023-January/196110.html > > > > The source line looks like: > > > > func1 (); func2 (); > > > > so stepping backwards over that line should always stop at the > > first > > instruction of the line, not in the middle. Let's simplify this. > > > > Here's the full source code of my example: > > > > (gdb) list 1 > > 1 void func1 () > > 2 { > > 3 } > > 4 > > 5 void func2 () > > 6 { > > 7 } > > 8 > > 9 int main () > > 10 { > > 11 func1 (); func2 (); > > 12 } > > > > Compiled with: > > > > $ gcc reverse.c -o reverse -g3 -O0 > > $ gcc -v > > ... > > gcc version 11.3.0 (Ubuntu 11.3.0-1ubuntu1~22.04) > > > > Now let's debug it with target record, using current gdb git master > > (f3d8ae90b236), > > without your patch: > > > > $ gdb ~/reverse > > GNU gdb (GDB) 14.0.50.20230124-git > > ... > > Reading symbols from /home/pedro/reverse... > > (gdb) start > > Temporary breakpoint 1 at 0x1147: file reverse.c, line 11. > > Starting program: /home/pedro/reverse > > [Thread debugging using libthread_db enabled] > > Using host libthread_db library "/lib/x86_64-linux- > > gnu/libthread_db.so.1". > > > > Temporary breakpoint 1, main () at reverse.c:11 > > 11 func1 (); func2 (); > > (gdb) record > > > > (gdb) disassemble /s > > Dump of assembler code for function main: > > reverse.c: > > 10 { > > 0x000055555555513f <+0>: endbr64 > > 0x0000555555555143 <+4>: push %rbp > > 0x0000555555555144 <+5>: mov %rsp,%rbp > > > > 11 func1 (); func2 (); > > => 0x0000555555555147 <+8>: mov $0x0,%eax > > 0x000055555555514c <+13>: call 0x555555555129 <func1> > > 0x0000555555555151 <+18>: mov $0x0,%eax > > 0x0000555555555156 <+23>: call 0x555555555134 <func2> > > 0x000055555555515b <+28>: mov $0x0,%eax > > > > 12 } > > 0x0000555555555160 <+33>: pop %rbp > > 0x0000555555555161 <+34>: ret > > End of assembler dump. > > > > (gdb) n > > 12 } > > > > So far so good, a "next" stepped over the whole of line 11 and > > stopped at line 12. > > > > Let's confirm where we are now: > > > > (gdb) disassemble /s > > Dump of assembler code for function main: > > reverse.c: > > 10 { > > 0x000055555555513f <+0>: endbr64 > > 0x0000555555555143 <+4>: push %rbp > > 0x0000555555555144 <+5>: mov %rsp,%rbp > > > > 11 func1 (); func2 (); > > 0x0000555555555147 <+8>: mov $0x0,%eax > > 0x000055555555514c <+13>: call 0x555555555129 <func1> > > 0x0000555555555151 <+18>: mov $0x0,%eax > > 0x0000555555555156 <+23>: call 0x555555555134 <func2> > > 0x000055555555515b <+28>: mov $0x0,%eax > > > > 12 } > > => 0x0000555555555160 <+33>: pop %rbp > > 0x0000555555555161 <+34>: ret > > End of assembler dump. > > > > Good, we're at the first instruction of line 12. > > > > Now let's undo the "next", with "reverse-next": > > > > (gdb) reverse-next > > 11 func1 (); func2 (); > > > > Seemingly stopped at line 11. Let's see exactly where: > > > > (gdb) disassemble /s > > Dump of assembler code for function main: > > reverse.c: > > 10 { > > 0x000055555555513f <+0>: endbr64 > > 0x0000555555555143 <+4>: push %rbp > > 0x0000555555555144 <+5>: mov %rsp,%rbp > > > > 11 func1 (); func2 (); > > 0x0000555555555147 <+8>: mov $0x0,%eax > > 0x000055555555514c <+13>: call 0x555555555129 <func1> > > => 0x0000555555555151 <+18>: mov $0x0,%eax > > 0x0000555555555156 <+23>: call 0x555555555134 <func2> > > 0x000055555555515b <+28>: mov $0x0,%eax > > > > 12 } > > 0x0000555555555160 <+33>: pop %rbp > > 0x0000555555555161 <+34>: ret > > End of assembler dump. > > (gdb) > > > > And lo, we stopped in the middle of line 11! That is a bug, we > > should have > > stepped back all the way to the beginning of the line. The > > "reverse-next" > > should have fully undone the prior "next" command. > > > > The test cases gdb.reverse/func-map-to-same-line-no-colum-info.exp > > and > > gdb.reverse/func-map-to-same-line.exp were added to test the fix > > for scenario > > 2 when the binary was compiled with and without line table > > information. > > > > bug: > > https://sourceware.org/bugzilla/show_bug.cgi?id=28426 > > > > > > Co-authored-by: Luis Machado <luis.machado@arm.com> > > Co-authored-by: Carl Love <cel@us.ibm.com> End of commit message > > Hey Carl, > > Thanks for working on this. I'm wondering which parts will be part > of > the final commit messages and which is just context for the mailing > list, so some clarity would be nice, but that is not a huge deal. > > I wanted to test this change, but it doesn't apply anymore on > master, > and `git apply --3way` can't figure out how to do it. Which commit > did > you use as base (or alternatively, can you rebase it)? OK, let me rebase it and repost. Let me know if you think the suggested commit message is still too much. Thanks. Carl > ^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH] Fix reverse stepping multiple contiguous PC ranges over the line table. 2023-05-02 15:40 ` Carl Love @ 2023-05-02 15:42 ` Bruno Larsen 0 siblings, 0 replies; 41+ messages in thread From: Bruno Larsen @ 2023-05-02 15:42 UTC (permalink / raw) To: Carl Love, gdb-patches, Ulrich Weigand, pedro; +Cc: luis.machado On 02/05/2023 17:40, Carl Love wrote: > Bruno: > > On Tue, 2023-05-02 at 16:15 +0200, Bruno Larsen wrote: > > I had intended everything from here down as part of the commit message. > It is admittedly a lot. It could be cut down my making the first part > could be moved to the mailing list context. The commit message would > then start with the two scenarios the patch fixes. >>> --------------------------------------------------------------- >>> Fix reverse stepping multiple contiguous PC ranges over the line >>> table. >>> >>> There are a couple of scenarios where the GDB reverse-step and >>> reverse-next >>> commands do not work correctly. The first scenario consists of >>> multiple >>> assignment statements on the same line. A patch was proposed to >>> address the >>> issue by Luis Machado and briefly discussed on the mailing list in >>> Feb 2021. >>> >>> https://sourceware.org/pipermail/gdb-patches/2021-February/175678.html >>> >>> The discussion was revived by Carl Love with regards to fixing the >>> same >>> issue on PowerPC. >>> >>> https://sourceware.org/pipermail/gdb-patches/2022-March/186463.html >>> >>> The patch was not completed and has been on Carl's to do list for >>> some time. >>> >>> Discussion of a patch to change how the reverse-step and reverse- >>> next >>> commands submitted by Carl Love was started in thread: >>> >>> https://sourceware.org/pipermail/gdb-patches/2023-January/195563.html >>> >>> The patch was withdrawn as it was pointed out the proposed patch >>> would >>> change the intended behavior of the commands as documented in the >>> GDB >>> manual. However, it was pointed out by Pedro Alves < >>> pedro@palves.net> >>> that the reverse-step and reverse-next commands do not work when >>> there >>> are multiple function calls on the same line. This is a second >>> scenario >>> where the commands do not work correctly. >>> >>> The following patch is an extended version of the original patch by >>> Luis Machado to fix the issues in scenario 1 to also address the >>> issues in >>> scenario 2. >>> > Move the above to the mailing list > > Start here with the commit message > >>> -------------------------------------------------------- >>> >>> Scenario 1 issue description by Luis Machado: >>> >>> When running GDB's testsuite on aarch64-linux/Ubuntu 20.04 (also >>> spotted on >>> the ppc backend), I noticed some failures in gdb.reverse/solib- >>> precsave.exp >>> and gdb.reverse/solib-reverse.exp. >>> >>> The failure happens around the following code: >>> >>> 38 b[1] = shr2(17); /* middle part two */ >>> 40 b[0] = 6; b[1] = 9; /* generic statement, end part two */ >>> 42 shr1 ("message 1\n"); /* shr1 one */ >>> >>> Normal execution: >>> >>> - step from line 38 will land on line 40. >>> - step from line 40 will land on line 42. >>> >>> Reverse execution: >>> - step from line 42 will land on line 40. >>> - step from line 40 will land on line 40. >>> - step from line 40 will land on line 38. >>> >>> The problem here is that line 40 contains two contiguous but >>> distinct >>> PC ranges in the line table, like so: >>> >>> Line 40 - [0x7ec ~ 0x7f4] >>> Line 40 - [0x7f4 ~ 0x7fc] >>> >>> The two distinct ranges are generated because GCC started >>> outputting source >>> column information, which GDB doesn't take into account at the >>> moment. >>> >>> When stepping forward from line 40, we skip both of these ranges >>> and land on >>> line 42. When stepping backward from line 42, we stop at the start >>> PC of the >>> second (or first, going backwards) range of line 40. >>> >>> This happens because we have this check in >>> infrun.c:process_event_stop_test: >>> >>> /* When stepping backward, stop at beginning of line range >>> (unless it's the function entry point, in which case >>> keep going back to the call point). */ >>> CORE_ADDR stop_pc = ecs->event_thread->stop_pc (); >>> if (stop_pc == ecs->event_thread->control.step_range_start >>> && stop_pc != ecs->stop_func_start >>> && execution_direction == EXEC_REVERSE) >>> end_stepping_range (ecs); >>> else >>> keep_going (ecs); >>> >>> Since we've reached ecs->event_thread->control.step_range_start, we >>> stop >>> stepping backwards. >>> >>> The right thing to do is to look for adjacent PC ranges for the >>> same line, >>> until we notice a line change. Then we take that as the start PC of >>> the >>> range. >>> >>> Another solution I thought about is to merge the contiguous ranges >>> when >>> we are reading the line tables. Though I'm not sure if we really >>> want to >>> process that data as opposed to keeping it as the compiler created, >>> and >>> then working around that. >>> >>> The test case gdb.reverse/map-to-same-line.exp is added to test the >>> fix >>> for the issues in scenario 1. >>> >>> --------------------------------------------------------- >>> >>> Scenario 2 issue described by Pedro Alves: >>> >>> The following explanation of the issue was taken from the gdb >>> mailing list >>> discussion of the withdrawn patch to change the behavior of the >>> reverse-step >>> and reverse-next commands. Specifically, message from Pedro Alves >>> <pedro@palves.net> where he demonstrates the issue where you have >>> multiple >>> function calls on the same source code line: >>> >>> https://sourceware.org/pipermail/gdb-patches/2023-January/196110.html >>> >>> The source line looks like: >>> >>> func1 (); func2 (); >>> >>> so stepping backwards over that line should always stop at the >>> first >>> instruction of the line, not in the middle. Let's simplify this. >>> >>> Here's the full source code of my example: >>> >>> (gdb) list 1 >>> 1 void func1 () >>> 2 { >>> 3 } >>> 4 >>> 5 void func2 () >>> 6 { >>> 7 } >>> 8 >>> 9 int main () >>> 10 { >>> 11 func1 (); func2 (); >>> 12 } >>> >>> Compiled with: >>> >>> $ gcc reverse.c -o reverse -g3 -O0 >>> $ gcc -v >>> ... >>> gcc version 11.3.0 (Ubuntu 11.3.0-1ubuntu1~22.04) >>> >>> Now let's debug it with target record, using current gdb git master >>> (f3d8ae90b236), >>> without your patch: >>> >>> $ gdb ~/reverse >>> GNU gdb (GDB) 14.0.50.20230124-git >>> ... >>> Reading symbols from /home/pedro/reverse... >>> (gdb) start >>> Temporary breakpoint 1 at 0x1147: file reverse.c, line 11. >>> Starting program: /home/pedro/reverse >>> [Thread debugging using libthread_db enabled] >>> Using host libthread_db library "/lib/x86_64-linux- >>> gnu/libthread_db.so.1". >>> >>> Temporary breakpoint 1, main () at reverse.c:11 >>> 11 func1 (); func2 (); >>> (gdb) record >>> >>> (gdb) disassemble /s >>> Dump of assembler code for function main: >>> reverse.c: >>> 10 { >>> 0x000055555555513f <+0>: endbr64 >>> 0x0000555555555143 <+4>: push %rbp >>> 0x0000555555555144 <+5>: mov %rsp,%rbp >>> >>> 11 func1 (); func2 (); >>> => 0x0000555555555147 <+8>: mov $0x0,%eax >>> 0x000055555555514c <+13>: call 0x555555555129 <func1> >>> 0x0000555555555151 <+18>: mov $0x0,%eax >>> 0x0000555555555156 <+23>: call 0x555555555134 <func2> >>> 0x000055555555515b <+28>: mov $0x0,%eax >>> >>> 12 } >>> 0x0000555555555160 <+33>: pop %rbp >>> 0x0000555555555161 <+34>: ret >>> End of assembler dump. >>> >>> (gdb) n >>> 12 } >>> >>> So far so good, a "next" stepped over the whole of line 11 and >>> stopped at line 12. >>> >>> Let's confirm where we are now: >>> >>> (gdb) disassemble /s >>> Dump of assembler code for function main: >>> reverse.c: >>> 10 { >>> 0x000055555555513f <+0>: endbr64 >>> 0x0000555555555143 <+4>: push %rbp >>> 0x0000555555555144 <+5>: mov %rsp,%rbp >>> >>> 11 func1 (); func2 (); >>> 0x0000555555555147 <+8>: mov $0x0,%eax >>> 0x000055555555514c <+13>: call 0x555555555129 <func1> >>> 0x0000555555555151 <+18>: mov $0x0,%eax >>> 0x0000555555555156 <+23>: call 0x555555555134 <func2> >>> 0x000055555555515b <+28>: mov $0x0,%eax >>> >>> 12 } >>> => 0x0000555555555160 <+33>: pop %rbp >>> 0x0000555555555161 <+34>: ret >>> End of assembler dump. >>> >>> Good, we're at the first instruction of line 12. >>> >>> Now let's undo the "next", with "reverse-next": >>> >>> (gdb) reverse-next >>> 11 func1 (); func2 (); >>> >>> Seemingly stopped at line 11. Let's see exactly where: >>> >>> (gdb) disassemble /s >>> Dump of assembler code for function main: >>> reverse.c: >>> 10 { >>> 0x000055555555513f <+0>: endbr64 >>> 0x0000555555555143 <+4>: push %rbp >>> 0x0000555555555144 <+5>: mov %rsp,%rbp >>> >>> 11 func1 (); func2 (); >>> 0x0000555555555147 <+8>: mov $0x0,%eax >>> 0x000055555555514c <+13>: call 0x555555555129 <func1> >>> => 0x0000555555555151 <+18>: mov $0x0,%eax >>> 0x0000555555555156 <+23>: call 0x555555555134 <func2> >>> 0x000055555555515b <+28>: mov $0x0,%eax >>> >>> 12 } >>> 0x0000555555555160 <+33>: pop %rbp >>> 0x0000555555555161 <+34>: ret >>> End of assembler dump. >>> (gdb) >>> >>> And lo, we stopped in the middle of line 11! That is a bug, we >>> should have >>> stepped back all the way to the beginning of the line. The >>> "reverse-next" >>> should have fully undone the prior "next" command. >>> >>> The test cases gdb.reverse/func-map-to-same-line-no-colum-info.exp >>> and >>> gdb.reverse/func-map-to-same-line.exp were added to test the fix >>> for scenario >>> 2 when the binary was compiled with and without line table >>> information. >>> >>> bug: >>> https://sourceware.org/bugzilla/show_bug.cgi?id=28426 >>> >>> >>> Co-authored-by: Luis Machado <luis.machado@arm.com> >>> Co-authored-by: Carl Love <cel@us.ibm.com> > End of commit message >> Hey Carl, >> >> Thanks for working on this. I'm wondering which parts will be part >> of >> the final commit messages and which is just context for the mailing >> list, so some clarity would be nice, but that is not a huge deal. >> >> I wanted to test this change, but it doesn't apply anymore on >> master, >> and `git apply --3way` can't figure out how to do it. Which commit >> did >> you use as base (or alternatively, can you rebase it)? > OK, let me rebase it and repost. Let me know if you think the > suggested commit message is still too much. Thanks. Hey sorry, this part was my bad, no need to rebase. I'll look at the commit message in a bit -- Cheers, Bruno > > Carl ^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH] Fix reverse stepping multiple contiguous PC ranges over the line table. 2023-05-02 14:15 ` Bruno Larsen 2023-05-02 15:40 ` Carl Love @ 2023-05-11 15:11 ` Simon Marchi 1 sibling, 0 replies; 41+ messages in thread From: Simon Marchi @ 2023-05-11 15:11 UTC (permalink / raw) To: Bruno Larsen, Carl Love, gdb-patches, Ulrich Weigand, pedro; +Cc: luis.machado > I wanted to test this change, but it doesn't apply anymore on master, > and `git apply --3way` can't figure out how to do it. Which commit did > you use as base (or alternatively, can you rebase it)? To help with this, you can consider setting the `format.useAutoBase` configuration option in git (documented in the git-format-patch man page). Simon ^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH] Fix reverse stepping multiple contiguous PC ranges over the line table. 2023-04-27 20:59 [PATCH] Fix reverse stepping multiple contiguous PC ranges over the line table Carl Love 2023-05-02 14:15 ` Bruno Larsen @ 2023-05-03 9:53 ` Bruno Larsen 2023-05-04 2:55 ` Carl Love 2023-05-04 2:55 ` [PATCH v2] " Carl Love 1 sibling, 2 replies; 41+ messages in thread From: Bruno Larsen @ 2023-05-03 9:53 UTC (permalink / raw) To: Carl Love, gdb-patches, Ulrich Weigand, pedro; +Cc: luis.machado On 27/04/2023 22:59, Carl Love wrote: > GDB maintainers: > > The following patch fixes issues on PowerPC with the reverse-step and > reverse-next instructions when there are multiple assignment statements > on the same line and when there are multiple function calls on the same > line. The commit log below discusses these issues in further depth. > The discussion included what the correct operation should be for these > commands based on the GDB documentation. The proposed patch at that > time changed how the commands worked on other platforms such as X86 in > a way they no longer matched the documentation. > > The issue is the line table contains multiple entries for the same > source line. The patch adds a function to search the line table to > find the address of the first instruction of a line. When setup up the > reverse stepping range, the function is called to make sure the start > of the range corresponds to the address of the first instruction for > the line. > > The following patch fixes the execution of the reveres-step and > reverse-next commands for both senarios of multiple statements on the > same line for PowerPC and aarch64-linux. Unlike the previous patch, it > does not change the operation of the commands on other platforms, i.e. > X86. The patch adds new test cases for both scenarios to verify they > work correctly. > > The patch has been tested on PowerPC, Intel X86 and aarch64-linux with > no new regression failures. > > Please let me know if the patch is acceptable for mainline. Thanks. > > Carl > > --------------------------------------------------------------- > Fix reverse stepping multiple contiguous PC ranges over the line table. > > There are a couple of scenarios where the GDB reverse-step and reverse-next > commands do not work correctly. The first scenario consists of multiple > assignment statements on the same line. A patch was proposed to address the > issue by Luis Machado and briefly discussed on the mailing list in Feb 2021. > > https://sourceware.org/pipermail/gdb-patches/2021-February/175678.html > > The discussion was revived by Carl Love with regards to fixing the same > issue on PowerPC. > > https://sourceware.org/pipermail/gdb-patches/2022-March/186463.html > > The patch was not completed and has been on Carl's to do list for some time. > > Discussion of a patch to change how the reverse-step and reverse-next > commands submitted by Carl Love was started in thread: > > https://sourceware.org/pipermail/gdb-patches/2023-January/195563.html > > The patch was withdrawn as it was pointed out the proposed patch would > change the intended behavior of the commands as documented in the GDB > manual. However, it was pointed out by Pedro Alves <pedro@palves.net> > that the reverse-step and reverse-next commands do not work when there > are multiple function calls on the same line. This is a second scenario > where the commands do not work correctly. > > The following patch is an extended version of the original patch by > Luis Machado to fix the issues in scenario 1 to also address the issues in > scenario 2. > > -------------------------------------------------------- Hi Carl, thanks for clarifying the intended commit message. I'm reacting to it here because I also have some thoughts on the code, now that I managed to apply it locally. Starting on the commit message, it would be nice to have a 1-line description of the problem before describing the scenarios in depth. Taking the first line of the previous block is enough IMO. > Scenario 1 issue description by Luis Machado: > > When running GDB's testsuite on aarch64-linux/Ubuntu 20.04 (also spotted on > the ppc backend), I noticed some failures in gdb.reverse/solib-precsave.exp > and gdb.reverse/solib-reverse.exp. > > The failure happens around the following code: > > 38 b[1] = shr2(17); /* middle part two */ > 40 b[0] = 6; b[1] = 9; /* generic statement, end part two */ > 42 shr1 ("message 1\n"); /* shr1 one */ > > Normal execution: > > - step from line 38 will land on line 40. > - step from line 40 will land on line 42. > > Reverse execution: > - step from line 42 will land on line 40. > - step from line 40 will land on line 40. > - step from line 40 will land on line 38. > > The problem here is that line 40 contains two contiguous but distinct > PC ranges in the line table, like so: > > Line 40 - [0x7ec ~ 0x7f4] > Line 40 - [0x7f4 ~ 0x7fc] > > The two distinct ranges are generated because GCC started outputting source > column information, which GDB doesn't take into account at the moment. > > When stepping forward from line 40, we skip both of these ranges and land on > line 42. When stepping backward from line 42, we stop at the start PC of the > second (or first, going backwards) range of line 40. > > This happens because we have this check in infrun.c:process_event_stop_test: > > /* When stepping backward, stop at beginning of line range > (unless it's the function entry point, in which case > keep going back to the call point). */ > CORE_ADDR stop_pc = ecs->event_thread->stop_pc (); > if (stop_pc == ecs->event_thread->control.step_range_start > && stop_pc != ecs->stop_func_start > && execution_direction == EXEC_REVERSE) > end_stepping_range (ecs); > else > keep_going (ecs); > > Since we've reached ecs->event_thread->control.step_range_start, we stop > stepping backwards. I think these last 3 paragraphs should be moved. I like to finish commits with a description of the solution, rather than having it in the middle of the text. Also, I think we like to avoid mentioning explicit code in the commit text (though I might be mistaken). > The right thing to do is to look for adjacent PC ranges for the same line, > until we notice a line change. Then we take that as the start PC of the > range. > > Another solution I thought about is to merge the contiguous ranges when > we are reading the line tables. Though I'm not sure if we really want to > process that data as opposed to keeping it as the compiler created, and > then working around that. This paragraph doesn't need to be here in the final commit message IMO. It was nice context for the mailing list but is not necessary for future reference, I don't think. > > The test case gdb.reverse/map-to-same-line.exp is added to test the fix > for the issues in scenario 1. > > --------------------------------------------------------- > > Scenario 2 issue described by Pedro Alves: > > The following explanation of the issue was taken from the gdb mailing list > discussion of the withdrawn patch to change the behavior of the reverse-step > and reverse-next commands. Specifically, message from Pedro Alves > <pedro@palves.net> where he demonstrates the issue where you have multiple > function calls on the same source code line: > > https://sourceware.org/pipermail/gdb-patches/2023-January/196110.html > The source line looks like: > > func1 (); func2 (); > > so stepping backwards over that line should always stop at the first > instruction of the line, not in the middle. Let's simplify this. > > Here's the full source code of my example: > > (gdb) list 1 > 1 void func1 () > 2 { > 3 } > 4 > 5 void func2 () > 6 { > 7 } > 8 > 9 int main () > 10 { > 11 func1 (); func2 (); > 12 } > > Compiled with: > > $ gcc reverse.c -o reverse -g3 -O0 > $ gcc -v > ... > gcc version 11.3.0 (Ubuntu 11.3.0-1ubuntu1~22.04) > > Now let's debug it with target record, using current gdb git master (f3d8ae90b236), > without your patch: > > $ gdb ~/reverse > GNU gdb (GDB) 14.0.50.20230124-git > ... > Reading symbols from /home/pedro/reverse... > (gdb) start > Temporary breakpoint 1 at 0x1147: file reverse.c, line 11. > Starting program: /home/pedro/reverse > [Thread debugging using libthread_db enabled] > Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". > > Temporary breakpoint 1, main () at reverse.c:11 > 11 func1 (); func2 (); > (gdb) record > > (gdb) disassemble /s > Dump of assembler code for function main: > reverse.c: > 10 { > 0x000055555555513f <+0>: endbr64 > 0x0000555555555143 <+4>: push %rbp > 0x0000555555555144 <+5>: mov %rsp,%rbp > > 11 func1 (); func2 (); > => 0x0000555555555147 <+8>: mov $0x0,%eax > 0x000055555555514c <+13>: call 0x555555555129 <func1> > 0x0000555555555151 <+18>: mov $0x0,%eax > 0x0000555555555156 <+23>: call 0x555555555134 <func2> > 0x000055555555515b <+28>: mov $0x0,%eax > > 12 } > 0x0000555555555160 <+33>: pop %rbp > 0x0000555555555161 <+34>: ret > End of assembler dump. > > (gdb) n > 12 } > > So far so good, a "next" stepped over the whole of line 11 and stopped at line 12. > > Let's confirm where we are now: > > (gdb) disassemble /s > Dump of assembler code for function main: > reverse.c: > 10 { > 0x000055555555513f <+0>: endbr64 > 0x0000555555555143 <+4>: push %rbp > 0x0000555555555144 <+5>: mov %rsp,%rbp > > 11 func1 (); func2 (); > 0x0000555555555147 <+8>: mov $0x0,%eax > 0x000055555555514c <+13>: call 0x555555555129 <func1> > 0x0000555555555151 <+18>: mov $0x0,%eax > 0x0000555555555156 <+23>: call 0x555555555134 <func2> > 0x000055555555515b <+28>: mov $0x0,%eax > > 12 } > => 0x0000555555555160 <+33>: pop %rbp > 0x0000555555555161 <+34>: ret > End of assembler dump. > > Good, we're at the first instruction of line 12. > > Now let's undo the "next", with "reverse-next": > > (gdb) reverse-next > 11 func1 (); func2 (); > > Seemingly stopped at line 11. Let's see exactly where: > > (gdb) disassemble /s > Dump of assembler code for function main: > reverse.c: > 10 { > 0x000055555555513f <+0>: endbr64 > 0x0000555555555143 <+4>: push %rbp > 0x0000555555555144 <+5>: mov %rsp,%rbp > > 11 func1 (); func2 (); > 0x0000555555555147 <+8>: mov $0x0,%eax > 0x000055555555514c <+13>: call 0x555555555129 <func1> > => 0x0000555555555151 <+18>: mov $0x0,%eax > 0x0000555555555156 <+23>: call 0x555555555134 <func2> > 0x000055555555515b <+28>: mov $0x0,%eax > > 12 } > 0x0000555555555160 <+33>: pop %rbp > 0x0000555555555161 <+34>: ret > End of assembler dump. > (gdb) > > And lo, we stopped in the middle of line 11! That is a bug, we should have > stepped back all the way to the beginning of the line. The "reverse-next" > should have fully undone the prior "next" command. > > The test cases gdb.reverse/func-map-to-same-line-no-colum-info.exp and > gdb.reverse/func-map-to-same-line.exp were added to test the fix for scenario > 2 when the binary was compiled with and without line table information. > > bug: https://sourceware.org/bugzilla/show_bug.cgi?id=28426 > > Co-authored-by: Luis Machado <luis.machado@arm.com> > Co-authored-by: Carl Love <cel@us.ibm.com> > --- > gdb/infrun.c | 57 +++++++ > gdb/symtab.c | 49 ++++++ > gdb/symtab.h | 16 ++ > .../func-map-to-same-line-no-column-info.exp | 135 ++++++++++++++++ > .../gdb.reverse/func-map-to-same-line.c | 36 +++++ > .../gdb.reverse/func-map-to-same-line.exp | 123 ++++++++++++++ > gdb/testsuite/gdb.reverse/map-to-same-line.c | 58 +++++++ > .../gdb.reverse/map-to-same-line.exp | 153 ++++++++++++++++++ > 8 files changed, 627 insertions(+) > create mode 100644 gdb/testsuite/gdb.reverse/func-map-to-same-line-no-column-info.exp > create mode 100644 gdb/testsuite/gdb.reverse/func-map-to-same-line.c > create mode 100644 gdb/testsuite/gdb.reverse/func-map-to-same-line.exp > create mode 100644 gdb/testsuite/gdb.reverse/map-to-same-line.c > create mode 100644 gdb/testsuite/gdb.reverse/map-to-same-line.exp > > diff --git a/gdb/infrun.c b/gdb/infrun.c > index 2f1c6cd694b..59374a05471 100644 > --- a/gdb/infrun.c > +++ b/gdb/infrun.c > @@ -113,6 +113,9 @@ static struct async_event_handler *infrun_async_inferior_event_token; > Starts off as -1, indicating "never enabled/disabled". */ > static int infrun_is_async = -1; > > +static CORE_ADDR update_line_range_start (CORE_ADDR pc, > + struct execution_control_state *ecs); > + > /* See infrun.h. */ > > void > @@ -6768,6 +6771,25 @@ handle_signal_stop (struct execution_control_state *ecs) > process_event_stop_test (ecs); > } > > +CORE_ADDR > +update_line_range_start (CORE_ADDR pc, struct execution_control_state *ecs) > +{ > + /* The line table may have multiple entries for the same source code line. > + Given the PC, check the line table and return the PC that corresponds > + to the line table entry for the source line that PC is in. */ > + CORE_ADDR start_line_pc = ecs->event_thread->control.step_range_start; > + gdb::optional<CORE_ADDR> real_range_start; > + > + /* Call find_line_range_start to get smallest address in the > + linetable for multiple Line X entries in the line table. */ > + real_range_start = find_line_range_start (pc); > + > + if (real_range_start.has_value ()) > + start_line_pc = *real_range_start; > + > + return start_line_pc; > +} > + > /* Come here when we've got some debug event / signal we can explain > (IOW, not a random signal), and test whether it should cause a > stop, or whether we should resume the inferior (transparently). > @@ -7569,6 +7591,28 @@ process_event_stop_test (struct execution_control_state *ecs) > > if (stop_pc_sal.is_stmt) > { > + if (execution_direction == EXEC_REVERSE) > + { > + /* We are stepping backwards make sure we have reached the > + beginning of the line. */ > + CORE_ADDR stop_pc = ecs->event_thread->stop_pc (); > + CORE_ADDR start_line_pc > + = update_line_range_start (stop_pc, ecs); > + > + if (stop_pc != start_line_pc) > + { > + /* Have not reached the beginning of the source code line. > + Set a step range. Execution should stop in any function > + calls we execute back into before reaching the beginning > + of the line. */ > + ecs->event_thread->control.step_range_start = start_line_pc; > + ecs->event_thread->control.step_range_end = stop_pc; > + set_step_info (ecs->event_thread, frame, stop_pc_sal); > + keep_going (ecs); > + return; > + } > + } > + > /* We are at the start of a statement. > > So stop. Note that we don't stop if we step into the middle of a > @@ -7631,6 +7675,19 @@ process_event_stop_test (struct execution_control_state *ecs) > set_step_info (ecs->event_thread, frame, stop_pc_sal); > > infrun_debug_printf ("keep going"); > + > + if (execution_direction == EXEC_REVERSE) > + { > + CORE_ADDR stop_pc = ecs->event_thread->stop_pc (); > + > + /* Make sure the stop_pc is set to the beginning of the line. */ > + if (stop_pc != ecs->event_thread->control.step_range_start) > + { > + stop_pc = update_line_range_start (stop_pc, ecs); > + ecs->event_thread->control.step_range_start = stop_pc; > + } > + } > + > keep_going (ecs); > } > > diff --git a/gdb/symtab.c b/gdb/symtab.c > index 27611a34ec4..91d35616eb9 100644 > --- a/gdb/symtab.c > +++ b/gdb/symtab.c > @@ -3282,6 +3282,55 @@ find_pc_line (CORE_ADDR pc, int notcurrent) > return sal; > } > > +/* Compare two symtab_and_line entries. Return true if both have > + the same line number and the same symtab pointer. That means we > + are dealing with two entries from the same line and from the same > + source file. > + > + Return false otherwise. */ > + > +static bool > +sal_line_symtab_matches_p (const symtab_and_line &sal1, > + const symtab_and_line &sal2) > +{ > + return (sal1.line == sal2.line && sal1.symtab == sal2.symtab); > +} > + > +/* See symtah.h. */ > + > +gdb::optional<CORE_ADDR> > +find_line_range_start (CORE_ADDR pc) > +{ > + struct symtab_and_line current_sal = find_pc_line (pc, 0); > + > + if (current_sal.line == 0) > + return {}; > + > + struct symtab_and_line prev_sal = find_pc_line (current_sal.pc - 1, 0); > + > + /* If the previous entry is for a different line, that means we are already > + at the entry with the start PC for this line. */ > + if (!sal_line_symtab_matches_p (prev_sal, current_sal)) > + return current_sal.pc; > + > + /* Otherwise, keep looking for entries for the same line but with > + smaller PC's. */ > + bool done = false; > + CORE_ADDR prev_pc; > + while (!done) > + { > + prev_pc = prev_sal.pc; > + > + prev_sal = find_pc_line (prev_pc - 1, 0); > + > + /* Did we notice a line change? If so, we are done with the search. */ > + if (!sal_line_symtab_matches_p (prev_sal, current_sal)) > + done = true; > + } > + > + return prev_pc; > +} > + > /* See symtab.h. */ > > struct symtab * > diff --git a/gdb/symtab.h b/gdb/symtab.h > index 404d0ab30a8..f54305636da 100644 > --- a/gdb/symtab.h > +++ b/gdb/symtab.h > @@ -2346,6 +2346,22 @@ extern struct symtab_and_line find_pc_line (CORE_ADDR, int); > extern struct symtab_and_line find_pc_sect_line (CORE_ADDR, > struct obj_section *, int); > > +/* Given PC, and assuming it is part of a range of addresses that is part of a > + line, go back through the linetable and find the starting PC of that > + line. > + > + For example, suppose we have 3 PC ranges for line X: > + > + Line X - [0x0 - 0x8] > + Line X - [0x8 - 0x10] > + Line X - [0x10 - 0x18] > + > + If we call the function with PC == 0x14, we want to return 0x0, as that is > + the starting PC of line X, and the ranges are contiguous. > +*/ > + > +extern gdb::optional<CORE_ADDR> find_line_range_start (CORE_ADDR pc); > + > /* Wrapper around find_pc_line to just return the symtab. */ > > extern struct symtab *find_pc_line_symtab (CORE_ADDR); > diff --git a/gdb/testsuite/gdb.reverse/func-map-to-same-line-no-column-info.exp b/gdb/testsuite/gdb.reverse/func-map-to-same-line-no-column-info.exp > new file mode 100644 > index 00000000000..20529c90fc2 > --- /dev/null > +++ b/gdb/testsuite/gdb.reverse/func-map-to-same-line-no-column-info.exp > @@ -0,0 +1,135 @@ > +# Copyright 2008-2023 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 <http://www.gnu.org/licenses/>. */ > + > +# This file is part of the GDB testsuite. It tests reverse stepping. > +# Lots of code borrowed from "step-test.exp". > + > +# This test checks to make sure there is no regression failures for > +# the reverse-next command when stepping back over two functions in > +# the same line. > + > +if ![supports_reverse] { > + return > +} Nowadays you should use require instead of the if clause, like in gdb.reverse/break-reverse.exp > + > +# This test uses the gcc no-column-info command. Correct me if I'm wrong, but it seems to me that the other test is a more generic version of this one, so this test could check for a gcc recent enough to support this feature, instead of just generically gcc. That said, gcc added it on version 7(https://gcc.gnu.org/git/gitweb.cgi?p=gcc.git;h=0029b929c9719a), is it old enough that we don't care? > +if ![is_c_compiler_gcc] { > + unsupported "gcc is required for this test" > + return 0 > +} > + > +set srcfile func-map-to-same-line.c > +set executable func-map-to-same-line > + > +set options [list debug additional_flags=-gno-column-info] > + > +if {[build_executable "failed to prepare" $executable $srcfile $options] == -1}\ > + { > + return -1 > +} > + > +clean_restart $executable > + > +runto_main > +set target_remote [gdb_is_target_remote] > + > +if [supports_process_record] { > + # Activate process record/replay. > + gdb_test_no_output "record" "turn on process record for test1" > +} > + > +# This regression test verifies the reverse-step and reverse-next commands > +# work properly when executing backwards thru a source line containing > +# two function calls on the same source line, i.e. func1 (); func2 (); > +# This test is compiled so the dwarf info not contain the line table > +# information. > + > +# Test 1, reverse-next command > +# Set breakpoint at the line after the function calls. > +set bp_start_reverse_test [gdb_get_line_number "START REVERSE TEST" $srcfile] > +gdb_breakpoint $srcfile:$bp_start_reverse_test temporary > + > +# Continue to break point for reverse-next test. > +# Command definition: reverse-next [count] > +# Run backward to the beginning of the previous line executed in the current > +# (innermost) stack frame. If the line contains function calls, they will be > +# “un-executed” without stopping. Starting from the first line of a function, > +# reverse-next will take you back to the caller of that function, before the > +# function was called, just as the normal next command would take you from > +# the last line of a function back to its return to its caller 2 . > + > +gdb_continue_to_breakpoint \ > + "stopped at command reverse-next test start location" \ > + ".*$srcfile:$bp_start_reverse_test\r\n.*" > + > +# The reverse-next should step all the way back to the beginning of the line, > +# i.e. at the beginning of the func1 call. > +gdb_test "reverse-next" ".*func1 \\(\\); func2 \\(\\);.*" \ > + "reverse-next to line with two functions" > + > +# A reverse-step should step back and stop at the beginning > +# of the previous line b = 2, i.e. not in func1 (). > +gdb_test "reverse-step" ".*b = 2;.*" \ > + "reverse-step to previous line b = 2" The point of this test is to confirm that we are at the very first instruction of the line, right? So I would think it is better to do a reverse-stepi here, to make sure that even walking a single instruction we reach a different line. Either that or doing what Pedro did in his email: save the PC before executing the line, then do and undo the line and confirm that PCs match exactly. > + > + > +# Setup for test 2 > +# Go back to the start of the function > +gdb_test "reverse-continue" "a = 1;" "At start of main, setup for test 2" > + > +# Turn off record to clear logs and turn on again > +gdb_test "record stop" "Process record is stopped.*" \ > + "turn off process record for test1" > +gdb_test_no_output "record" "turn on process record for test2" Since you don't require process record for this test, you can't assume these to work. I think its better to clean restart and record if the process supports recording, this way you're sure to reset history no matter the inferior. > + > +# Delete all breakpoints and catchpoints. > +delete_breakpoints > + > + > +# Test 2, reverse-step command > +# Set breakpoint at the line after the function calls. > +gdb_breakpoint $srcfile:$bp_start_reverse_test temporary > + > +# Continue to the start of the reverse-step test. > +# Command definition: reverse-step [count] > +# Run the program backward until control reaches the start of a > +# different source line; then stop it, and return control to gdb. > +# Like the step command, reverse-step will only stop at the beginning of a > +# source line. It “un-executes” the previously executed source line. If the > +# previous source line included calls to debuggable functions, reverse-step > +# will step (backward) into the called function, stopping at the beginning > +# of the last statement in the called function (typically a return > +# statement). Also, as with the step command, if non-debuggable functions > +# are called, reverse-step will run thru them backward without stopping. > + > +gdb_continue_to_breakpoint \ > + "stopped at command reverse-step test start location" \ > + ".*$srcfile:$bp_start_reverse_test\r\n.*" > + > +# The first reverse step should take us call of func2 (). > +gdb_test "reverse-step" ".*}.*" \ > + "reverse-step into func2 " > + > +# The second reverse step should take us into func1 (). > +gdb_test "reverse-step" ".*}.*" \ > + "reverse-step into func1 " > + > +# The third reverse step should take us call of func1 (). > +gdb_test "reverse-step" ".*func1 \\(\\); func2 \\(\\);.*" \ > + "reverse-step to line func1(); func2(), at call for func1 " > + > +# The fourth reverse step should take us to b = 2 (). > +gdb_test "reverse-step" ".*b = 2;.*" \ > + "reverse-step to line b = 2 " Ditto from the other test like this. Also, I feel that, while the test name for the last 2 gdb_test are different, they don't meaningfully communicate which part of the test is failing. I think it would be better if you differentiated them by adding "for step test" or "for test 2" at the end of the name would make it easier to understand where things went wrong when looking at the sum file. > diff --git a/gdb/testsuite/gdb.reverse/func-map-to-same-line.c b/gdb/testsuite/gdb.reverse/func-map-to-same-line.c > new file mode 100644 > index 00000000000..e9787ef9ff5 > --- /dev/null > +++ b/gdb/testsuite/gdb.reverse/func-map-to-same-line.c > @@ -0,0 +1,36 @@ > +/* Copyright 2008-2023 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 <http://www.gnu.org/licenses/>. > + > + This test is used to test the reverse-step and reverse-next instruction > + execution for a source line that contains multiple function calls. */ > + > +void > +func1 () > +{ > +} > + > +void > +func2 () > +{ > +} > + > +int main () > +{ > + int a, b; > + a = 1; > + b = 2; > + func1 (); func2 (); > + a = a + b; // START REVERSE TEST > +} > diff --git a/gdb/testsuite/gdb.reverse/func-map-to-same-line.exp b/gdb/testsuite/gdb.reverse/func-map-to-same-line.exp > new file mode 100644 > index 00000000000..b632a236bbe > --- /dev/null > +++ b/gdb/testsuite/gdb.reverse/func-map-to-same-line.exp > @@ -0,0 +1,123 @@ > +# Copyright 2008-2023 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 <http://www.gnu.org/licenses/>. */ > + > +# This file is part of the GDB testsuite. It tests reverse stepping. > +# Lots of code borrowed from "step-test.exp". > + > +# This test checks to make sure there is no regression failures for > +# the reverse-next command when stepping back over two functions in > +# the same line. > + > +if ![supports_reverse] { > + return > +} I'm not sure it's worth separating these 2 tests into separate files. You could instead just have most of the test defined as a proc, and call it twice, once after compiling the inferior with column info, the other compiling without if gcc is used. This way it's less likely that the tests will diverge over time. > + > +standard_testfile > + > +if { [prepare_for_testing "failed to prepare" $testfile $srcfile] } { > + return -1 > +} > + > +runto_main > +set target_remote [gdb_is_target_remote] > + > +if [supports_process_record] { > + # Activate process record/replay. > + gdb_test_no_output "record" "turn on process record for test1" > +} > + > +# This regression test verifies the reverse-step and reverse-next commands > +# work properly when executing backwards thru a source line containing > +# two function calls on the same source line, i.e. func1 (); func2 (); > +# The assumption for this test is the dwarf info contain the column > +# information. > + > +# Test 1, reverse-next command > +# Set breakpoint at the line after the function calls. > +set bp_start_reverse_test [gdb_get_line_number "START REVERSE TEST" $srcfile] > +gdb_breakpoint $srcfile:$bp_start_reverse_test temporary > + > +# Continue to break point for reverse-next test. > +# Command definition: reverse-next [count] > +# Run backward to the beginning of the previous line executed in the current > +# (innermost) stack frame. If the line contains function calls, they will be > +# “un-executed” without stopping. Starting from the first line of a function, > +# reverse-next will take you back to the caller of that function, before the > +# function was called, just as the normal next command would take you from > +# the last line of a function back to its return to its caller 2 . > + > +gdb_continue_to_breakpoint \ > + "stopped at command reverse-next test start location" \ > + ".*$srcfile:$bp_start_reverse_test\r\n.*" > + > +# The reverse-next should step all the way back to the beginning of the line, > +# i.e. at the beginning of the func1 call. > +gdb_test "reverse-next" ".*func1 \\(\\); func2 \\(\\);.*" \ > + "reverse-next to line with two functions" > + > +# A reverse-step should step back and stop at the beginning > +# of the previous line b = 2, i.e. not in func1 (). > +gdb_test "reverse-step" ".*b = 2;.*" \ > + "reverse-step to previous line b = 2" > + > + > +# Setup for test 2 > +# Go back to the start of the function > +gdb_test "reverse-continue" "a = 1;" "At start of main, setup for test 2" > + > +# Turn off record to clear logs and turn on again > +gdb_test "record stop" "Process record is stopped.*" \ > + "turn off process record for test1" > +gdb_test_no_output "record" "turn on process record for test2" > + > +# Delete all breakpoints and catchpoints. > +delete_breakpoints > + > + > +# Test 2, reverse-step command > +# Set breakpoint at the line after the function calls. > +gdb_breakpoint $srcfile:$bp_start_reverse_test temporary > + > +# Continue to the start of the reverse-step test. > +# Command definition: reverse-step [count] > +# Run the program backward until control reaches the start of a > +# different source line; then stop it, and return control to gdb. > +# Like the step command, reverse-step will only stop at the beginning of a > +# source line. It “un-executes” the previously executed source line. If the > +# previous source line included calls to debuggable functions, reverse-step > +# will step (backward) into the called function, stopping at the beginning > +# of the last statement in the called function (typically a return > +# statement). Also, as with the step command, if non-debuggable functions > +# are called, reverse-step will run thru them backward without stopping. > + > +gdb_continue_to_breakpoint \ > + "stopped at command reverse-step test start location" \ > + ".*$srcfile:$bp_start_reverse_test\r\n.*" > + > +# The first reverse step should take us call of func2 (). > +gdb_test "reverse-step" ".*}.*" \ > + "reverse-step into func2 " > + > +# The second reverse step should take us into func1 (). > +gdb_test "reverse-step" ".*}.*" \ > + "reverse-step into func1 " > + > +# The third reverse step should take us call of func1 (). > +gdb_test "reverse-step" ".*func1 \\(\\); func2 \\(\\);.*" \ > + "reverse-step to line func1(); func2(), at call for func1 " > + > +# The fourth reverse step should take us to b = 2 (). > +gdb_test "reverse-step" ".*b = 2;.*" \ > + "reverse-step to line b = 2 " > diff --git a/gdb/testsuite/gdb.reverse/map-to-same-line.c b/gdb/testsuite/gdb.reverse/map-to-same-line.c > new file mode 100644 > index 00000000000..f20d778f40e > --- /dev/null > +++ b/gdb/testsuite/gdb.reverse/map-to-same-line.c > @@ -0,0 +1,58 @@ > +/* Copyright 2008-2023 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 <http://www.gnu.org/licenses/ >. */ > + > +/* The purpose of this test is to create a DWARF line table that contains two > + or more entries for the same line. When stepping (forwards or backwards), > + GDB should step over the entire line and not just a particular entry in the > + line table. */ > + > +int > +main () > +{ /* TAG: main prologue */ > + asm ("main_label: .globl main_label"); > + int i = 1, j = 2, k; > + float f1 = 2.0, f2 = 4.1, f3; > + const char *str_1 = "foo", *str_2 = "bar", *str_3; > + > + asm ("line1: .globl line1"); > + k = i; f3 = f1; str_3 = str_1; /* TAG: line 1 */ > + > + asm ("line2: .globl line2"); > + k = j; f3 = f2; str_3 = str_2; /* TAG: line 2 */ > + > + asm ("line3: .globl line3"); > + k = i; f3 = f1; str_3 = str_1; /* TAG: line 3 */ > + > + asm ("line4: .globl line4"); > + k = j; f3 = f2; str_3 = str_2; /* TAG: line 4 */ > + > + asm ("line5: .globl line5"); > + k = i; f3 = f1; str_3 = str_1; /* TAG: line 5 */ > + > + asm ("line6: .globl line6"); > + k = j; f3 = f2; str_3 = str_2; /* TAG: line 6 */ > + > + asm ("line7: .globl line7"); > + k = i; f3 = f1; str_3 = str_1; /* TAG: line 7 */ > + > + asm ("line8: .globl line8"); > + k = j; f3 = f2; str_3 = str_2; /* TAG: line 8 */ > + > + asm ("main_return: .globl main_return"); > + k = j; f3 = f2; str_3 = str_2; /* TAG: main return */ > + > + asm ("end_of_sequence: .globl end_of_sequence"); > + return 0; /* TAG: main return */ > +} > diff --git a/gdb/testsuite/gdb.reverse/map-to-same-line.exp b/gdb/testsuite/gdb.reverse/map-to-same-line.exp > new file mode 100644 > index 00000000000..a01579c2a8d > --- /dev/null > +++ b/gdb/testsuite/gdb.reverse/map-to-same-line.exp > @@ -0,0 +1,153 @@ > +# Copyright 2008-2023 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 <http://www.gnu.org/licenses/ >. > + > +# When stepping (forwards or backwards), GDB should step over the entire line > +# and not just a particular entry in the line table. This test was added to > +# verify the find_line_range_start function properly sets the step range for a > +# line that consists of multiple statements, i.e. multiple entries in the line > +# table. This test creates a DWARF line table that contains two entries for > +# the same line to do the needed testing. > + > +load_lib dwarf.exp > + > +# This test can only be run on targets which support DWARF-2 and use gas. > +if {![dwarf2_support]} { > + unsupported "dwarf2 support required for this test" > + return 0 > +} Again, the new way to check for these is "required". And IIUC, you can add multiple requirements into a singe require call. > + > +if [get_compiler_info] { > + return -1 > +} > + > +# The DWARF assembler requires the gcc compiler. > +if ![is_c_compiler_gcc] { > + unsupported "gcc is required for this test" > + return 0 > +} > + > +# This test suitable only for process record-replay > +if ![supports_process_record] { > + return > +} > + > +standard_testfile .c .S > + > +if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile}] } { > + return -1 > +} > + > +set asm_file [standard_output_file $srcfile2] > +Dwarf::assemble $asm_file { > + global srcdir subdir srcfile > + declare_labels integer_label L > + > + # Find start address and length of program > + lassign [function_range main [list ${srcdir}/${subdir}/$srcfile]] \ > + main_start main_len > + set main_end "$main_start + $main_len" > + > + cu {} { > + compile_unit { > + {language @DW_LANG_C} > + {name map-to-same-line.c} > + {stmt_list $L DW_FORM_sec_offset} > + {low_pc 0 addr} > + } { > + subprogram { > + {external 1 flag} > + {name main} > + {low_pc $main_start addr} > + {high_pc $main_len DW_FORM_data4} > + } > + } > + } > + > + lines {version 2 default_is_stmt 1} L { > + include_dir "${srcdir}/${subdir}" > + file_name "$srcfile" 1 > + > + # Generate the line table program with distinct source lines being > + # mapped to the same line entry. Line 1, 5 and 8 contain 1 statement > + # each. Line 2 contains 2 statements. Line 3 contains 3 statements. > + program { > + DW_LNE_set_address $main_start > + line [gdb_get_line_number "TAG: main prologue"] > + DW_LNS_copy > + DW_LNE_set_address line1 > + line [gdb_get_line_number "TAG: line 1" ] > + DW_LNS_copy > + DW_LNE_set_address line2 > + line [gdb_get_line_number "TAG: line 2" ] > + DW_LNS_copy > + DW_LNE_set_address line3 > + line [gdb_get_line_number "TAG: line 2" ] > + DW_LNS_copy > + DW_LNE_set_address line4 > + line [gdb_get_line_number "TAG: line 3" ] > + DW_LNS_copy > + DW_LNE_set_address line5 > + line [gdb_get_line_number "TAG: line 3" ] > + DW_LNS_copy > + DW_LNE_set_address line6 > + line [gdb_get_line_number "TAG: line 3" ] > + DW_LNS_copy > + DW_LNE_set_address line7 > + line [gdb_get_line_number "TAG: line 5" ] > + DW_LNS_copy > + DW_LNE_set_address line8 > + line [gdb_get_line_number "TAG: line 8" ] > + DW_LNS_copy > + DW_LNE_set_address main_return > + line [gdb_get_line_number "TAG: main return"] > + DW_LNS_copy > + DW_LNE_set_address end_of_sequence > + DW_LNE_end_sequence > + } > + } > +} > + > +if { [prepare_for_testing "failed to prepare" ${testfile} \ > + [list $srcfile $asm_file] {nodebug} ] } { > + return -1 > +} > + > +if ![runto_main] { > + return -1 > +} runto_main already errors out and leaves, I think, so no need for the if clause. > + > +# Print the line table > +gdb_test_multiple "maint info line-table ${testfile}" "" { > + -re "\r\n$decimal\[ \t\]+$decimal\[ \t\]+($hex)\[ \t\]+Y\[^\r\n\]*" { > + lappend is_stmt $expect_out(1,string) > + exp_continue > + } > + -re -wrap "" { > + } > +} > + > +# Activate process record/replay > +gdb_test_no_output "record" "turn on process record" > + > +gdb_test "tbreak main_return" "Temporary breakpoint .*" "breakpoint at return" you can set a temporary breakpoint using gdb_breakpoint "..." temporary, no need to manually call tbreak. > +gdb_test "continue" "Temporary breakpoint .*" "run to end of main" gdb_continue_to_breakpoint can handle temporary breakpoints as well. > +gdb_test "display \$pc" ".*pc =.*" "display pc" > + > +# At this point, GDB has already recorded the execution up until the return > +# statement. Reverse-step and test if GDB transitions between lines in the > +# expected order. It should reverse-step across lines 8, 5, 3, 2 and 1. > +foreach line {8 5 3 2 1} { > + gdb_test "reverse-step" ".*TAG: line $line.*" "reverse step to line $line" > +} I'm not sure if it is needed, but I don't think it would hurt to also test reverse-next in a separate foreach right after this one. -- Cheers, Bruno ^ permalink raw reply [flat|nested] 41+ messages in thread
* RE: [PATCH] Fix reverse stepping multiple contiguous PC ranges over the line table. 2023-05-03 9:53 ` Bruno Larsen @ 2023-05-04 2:55 ` Carl Love 2023-05-04 9:24 ` Bruno Larsen 2023-05-04 2:55 ` [PATCH v2] " Carl Love 1 sibling, 1 reply; 41+ messages in thread From: Carl Love @ 2023-05-04 2:55 UTC (permalink / raw) To: Bruno Larsen, gdb-patches, Ulrich Weigand, pedro; +Cc: luis.machado, cel Bruno: On Wed, 2023-05-03 at 11:53 +0200, Bruno Larsen wrote: > On 27/04/2023 22:59, Carl Love wrote: <snip> > > Hi Carl, thanks for clarifying the intended commit message. I'm > reacting > to it here because I also have some thoughts on the code, now that I > managed to apply it locally. > > Starting on the commit message, it would be nice to have a 1-line > description of the problem before describing the scenarios in depth. > Taking the first line of the previous block is enough IMO. Yes, agreed. Kept the first line before the discussion of the different failure scenarios. > > > Scenario 1 issue description by Luis Machado: > > > > When running GDB's testsuite on aarch64-linux/Ubuntu 20.04 (also > > spotted on > > the ppc backend), I noticed some failures in gdb.reverse/solib- > > precsave.exp > > and gdb.reverse/solib-reverse.exp. > > > > The failure happens around the following code: > > > > 38 b[1] = shr2(17); /* middle part two */ > > 40 b[0] = 6; b[1] = 9; /* generic statement, end part two */ > > 42 shr1 ("message 1\n"); /* shr1 one */ > > > > Normal execution: > > > > - step from line 38 will land on line 40. > > - step from line 40 will land on line 42. > > > > Reverse execution: > > - step from line 42 will land on line 40. > > - step from line 40 will land on line 40. > > - step from line 40 will land on line 38. > > > > The problem here is that line 40 contains two contiguous but > > distinct > > PC ranges in the line table, like so: > > > > Line 40 - [0x7ec ~ 0x7f4] > > Line 40 - [0x7f4 ~ 0x7fc] > > > > The two distinct ranges are generated because GCC started > > outputting source > > column information, which GDB doesn't take into account at the > > moment. > > > > When stepping forward from line 40, we skip both of these ranges > > and land on > > line 42. When stepping backward from line 42, we stop at the start > > PC of the > > second (or first, going backwards) range of line 40. > > > > This happens because we have this check in > > infrun.c:process_event_stop_test: > > > > /* When stepping backward, stop at beginning of line range > > (unless it's the function entry point, in which case > > keep going back to the call point). */ > > CORE_ADDR stop_pc = ecs->event_thread->stop_pc (); > > if (stop_pc == ecs->event_thread->control.step_range_start > > && stop_pc != ecs->stop_func_start > > && execution_direction == EXEC_REVERSE) > > end_stepping_range (ecs); > > else > > keep_going (ecs); > > > > Since we've reached ecs->event_thread->control.step_range_start, we > > stop > > stepping backwards. > I think these last 3 paragraphs should be moved. I like to finish > commits with a description of the solution, rather than having it in > the > middle of the text. Also, I think we like to avoid mentioning > explicit > code in the commit text (though I might be mistaken). OK, I moved the fix discussion to the end. I also dropped the explicit reference to infrun.c:process_event_stop_test. > > The right thing to do is to look for adjacent PC ranges for the > > same line, > > until we notice a line change. Then we take that as the start PC of > > the > > range. > > > > Another solution I thought about is to merge the contiguous ranges > > when > > we are reading the line tables. Though I'm not sure if we really > > want to > > process that data as opposed to keeping it as the compiler created, > > and > > then working around that. > This paragraph doesn't need to be here in the final commit message > IMO. > It was nice context for the mailing list but is not necessary for > future OK, removed it from the commit log and need to update the mailing list message with this. > reference, I don't think. > > The test case gdb.reverse/map-to-same-line.exp is added to test the > > fix > > for the issues in scenario 1. > > > > --------------------------------------------------------- > > > > Scenario 2 issue described by Pedro Alves: > > > > The following explanation of the issue was taken from the gdb > > mailing list > > discussion of the withdrawn patch to change the behavior of the > > reverse-step > > and reverse-next commands. Specifically, message from Pedro Alves > > <pedro@palves.net> where he demonstrates the issue where you have > > multiple > > function calls on the same source code line: > > > > https://sourceware.org/pipermail/gdb-patches/2023-January/196110.html > > The source line looks like: > > > > func1 (); func2 (); > > > > so stepping backwards over that line should always stop at the > > first > > instruction of the line, not in the middle. Let's simplify this. > > > > Here's the full source code of my example: > > > > (gdb) list 1 > > 1 void func1 () > > 2 { > > 3 } > > 4 > > 5 void func2 () > > 6 { > > 7 } > > 8 > > 9 int main () > > 10 { > > 11 func1 (); func2 (); > > 12 } > > > > Compiled with: > > > > $ gcc reverse.c -o reverse -g3 -O0 > > $ gcc -v > > ... > > gcc version 11.3.0 (Ubuntu 11.3.0-1ubuntu1~22.04) > > > > Now let's debug it with target record, using current gdb git master > > (f3d8ae90b236), > > without your patch: > > > > $ gdb ~/reverse > > GNU gdb (GDB) 14.0.50.20230124-git > > ... > > Reading symbols from /home/pedro/reverse... > > (gdb) start > > Temporary breakpoint 1 at 0x1147: file reverse.c, line 11. > > Starting program: /home/pedro/reverse > > [Thread debugging using libthread_db enabled] > > Using host libthread_db library "/lib/x86_64-linux- > > gnu/libthread_db.so.1". > > > > Temporary breakpoint 1, main () at reverse.c:11 > > 11 func1 (); func2 (); > > (gdb) record > > > > (gdb) disassemble /s > > Dump of assembler code for function main: > > reverse.c: > > 10 { > > 0x000055555555513f <+0>: endbr64 > > 0x0000555555555143 <+4>: push %rbp > > 0x0000555555555144 <+5>: mov %rsp,%rbp > > > > 11 func1 (); func2 (); > > => 0x0000555555555147 <+8>: mov $0x0,%eax > > 0x000055555555514c <+13>: call 0x555555555129 <func1> > > 0x0000555555555151 <+18>: mov $0x0,%eax > > 0x0000555555555156 <+23>: call 0x555555555134 <func2> > > 0x000055555555515b <+28>: mov $0x0,%eax > > > > 12 } > > 0x0000555555555160 <+33>: pop %rbp > > 0x0000555555555161 <+34>: ret > > End of assembler dump. > > > > (gdb) n > > 12 } > > > > So far so good, a "next" stepped over the whole of line 11 and > > stopped at line 12. > > > > Let's confirm where we are now: > > > > (gdb) disassemble /s > > Dump of assembler code for function main: > > reverse.c: > > 10 { > > 0x000055555555513f <+0>: endbr64 > > 0x0000555555555143 <+4>: push %rbp > > 0x0000555555555144 <+5>: mov %rsp,%rbp > > > > 11 func1 (); func2 (); > > 0x0000555555555147 <+8>: mov $0x0,%eax > > 0x000055555555514c <+13>: call 0x555555555129 <func1> > > 0x0000555555555151 <+18>: mov $0x0,%eax > > 0x0000555555555156 <+23>: call 0x555555555134 <func2> > > 0x000055555555515b <+28>: mov $0x0,%eax > > > > 12 } > > => 0x0000555555555160 <+33>: pop %rbp > > 0x0000555555555161 <+34>: ret > > End of assembler dump. > > > > Good, we're at the first instruction of line 12. > > > > Now let's undo the "next", with "reverse-next": > > > > (gdb) reverse-next > > 11 func1 (); func2 (); > > > > Seemingly stopped at line 11. Let's see exactly where: > > > > (gdb) disassemble /s > > Dump of assembler code for function main: > > reverse.c: > > 10 { > > 0x000055555555513f <+0>: endbr64 > > 0x0000555555555143 <+4>: push %rbp > > 0x0000555555555144 <+5>: mov %rsp,%rbp > > > > 11 func1 (); func2 (); > > 0x0000555555555147 <+8>: mov $0x0,%eax > > 0x000055555555514c <+13>: call 0x555555555129 <func1> > > => 0x0000555555555151 <+18>: mov $0x0,%eax > > 0x0000555555555156 <+23>: call 0x555555555134 <func2> > > 0x000055555555515b <+28>: mov $0x0,%eax > > > > 12 } > > 0x0000555555555160 <+33>: pop %rbp > > 0x0000555555555161 <+34>: ret > > End of assembler dump. > > (gdb) > > > > And lo, we stopped in the middle of line 11! That is a bug, we > > should have > > stepped back all the way to the beginning of the line. The > > "reverse-next" > > should have fully undone the prior "next" command. > > > > The test cases gdb.reverse/func-map-to-same-line-no-colum-info.exp > > and > > gdb.reverse/func-map-to-same-line.exp were added to test the fix > > for scenario > > 2 when the binary was compiled with and without line table > > information. > > > > bug: > > https://sourceware.org/bugzilla/show_bug.cgi?id=28426 > > > > > > Co-authored-by: Luis Machado <luis.machado@arm.com> > > Co-authored-by: Carl Love <cel@us.ibm.com> > > --- > > gdb/infrun.c | 57 +++++++ > > gdb/symtab.c | 49 ++++++ > > gdb/symtab.h | 16 ++ > > .../func-map-to-same-line-no-column-info.exp | 135 > > ++++++++++++++++ > > .../gdb.reverse/func-map-to-same-line.c | 36 +++++ > > .../gdb.reverse/func-map-to-same-line.exp | 123 > > ++++++++++++++ > > gdb/testsuite/gdb.reverse/map-to-same-line.c | 58 +++++++ > > .../gdb.reverse/map-to-same-line.exp | 153 > > ++++++++++++++++++ > > 8 files changed, 627 insertions(+) > > create mode 100644 gdb/testsuite/gdb.reverse/func-map-to-same- > > line-no-column-info.exp > > create mode 100644 gdb/testsuite/gdb.reverse/func-map-to-same- > > line.c > > create mode 100644 gdb/testsuite/gdb.reverse/func-map-to-same- > > line.exp > > create mode 100644 gdb/testsuite/gdb.reverse/map-to-same-line.c > > create mode 100644 gdb/testsuite/gdb.reverse/map-to-same-line.exp > > > > diff --git a/gdb/infrun.c b/gdb/infrun.c > > index 2f1c6cd694b..59374a05471 100644 > > --- a/gdb/infrun.c > > +++ b/gdb/infrun.c > > @@ -113,6 +113,9 @@ static struct async_event_handler > > *infrun_async_inferior_event_token; > > Starts off as -1, indicating "never enabled/disabled". */ > > static int infrun_is_async = -1; > > > > +static CORE_ADDR update_line_range_start (CORE_ADDR pc, > > + struct > > execution_control_state *ecs); > > + > > /* See infrun.h. */ > > > > void > > @@ -6768,6 +6771,25 @@ handle_signal_stop (struct > > execution_control_state *ecs) > > process_event_stop_test (ecs); > > } > > > > +CORE_ADDR > > +update_line_range_start (CORE_ADDR pc, struct > > execution_control_state *ecs) > > +{ > > + /* The line table may have multiple entries for the same source > > code line. > > + Given the PC, check the line table and return the PC that > > corresponds > > + to the line table entry for the source line that PC is > > in. */ > > + CORE_ADDR start_line_pc = ecs->event_thread- > > >control.step_range_start; > > + gdb::optional<CORE_ADDR> real_range_start; > > + > > + /* Call find_line_range_start to get smallest address in the > > + linetable for multiple Line X entries in the line table. */ > > + real_range_start = find_line_range_start (pc); > > + > > + if (real_range_start.has_value ()) > > + start_line_pc = *real_range_start; > > + > > + return start_line_pc; > > +} > > + > > /* Come here when we've got some debug event / signal we can > > explain > > (IOW, not a random signal), and test whether it should cause a > > stop, or whether we should resume the inferior > > (transparently). > > @@ -7569,6 +7591,28 @@ process_event_stop_test (struct > > execution_control_state *ecs) > > > > if (stop_pc_sal.is_stmt) > > { > > + if (execution_direction == EXEC_REVERSE) > > + { > > + /* We are stepping backwards make sure we have reached > > the > > + beginning of the line. */ > > + CORE_ADDR stop_pc = ecs->event_thread->stop_pc (); > > + CORE_ADDR start_line_pc > > + = update_line_range_start (stop_pc, ecs); > > + > > + if (stop_pc != start_line_pc) > > + { > > + /* Have not reached the beginning of the source code > > line. > > + Set a step range. Execution should stop in any > > function > > + calls we execute back into before reaching the > > beginning > > + of the line. */ > > + ecs->event_thread->control.step_range_start = > > start_line_pc; > > + ecs->event_thread->control.step_range_end = stop_pc; > > + set_step_info (ecs->event_thread, frame, > > stop_pc_sal); > > + keep_going (ecs); > > + return; > > + } > > + } > > + > > /* We are at the start of a statement. > > > > So stop. Note that we don't stop if we step into the > > middle of a > > @@ -7631,6 +7675,19 @@ process_event_stop_test (struct > > execution_control_state *ecs) > > set_step_info (ecs->event_thread, frame, stop_pc_sal); > > > > infrun_debug_printf ("keep going"); > > + > > + if (execution_direction == EXEC_REVERSE) > > + { > > + CORE_ADDR stop_pc = ecs->event_thread->stop_pc (); > > + > > + /* Make sure the stop_pc is set to the beginning of the > > line. */ > > + if (stop_pc != ecs->event_thread->control.step_range_start) > > + { > > + stop_pc = update_line_range_start (stop_pc, ecs); > > + ecs->event_thread->control.step_range_start = stop_pc; > > + } > > + } > > + > > keep_going (ecs); > > } > > > > diff --git a/gdb/symtab.c b/gdb/symtab.c > > index 27611a34ec4..91d35616eb9 100644 > > --- a/gdb/symtab.c > > +++ b/gdb/symtab.c > > @@ -3282,6 +3282,55 @@ find_pc_line (CORE_ADDR pc, int notcurrent) > > return sal; > > } > > > > +/* Compare two symtab_and_line entries. Return true if both have > > + the same line number and the same symtab pointer. That means > > we > > + are dealing with two entries from the same line and from the > > same > > + source file. > > + > > + Return false otherwise. */ > > + > > +static bool > > +sal_line_symtab_matches_p (const symtab_and_line &sal1, > > + const symtab_and_line &sal2) > > +{ > > + return (sal1.line == sal2.line && sal1.symtab == sal2.symtab); > > +} > > + > > +/* See symtah.h. */ > > + > > +gdb::optional<CORE_ADDR> > > +find_line_range_start (CORE_ADDR pc) > > +{ > > + struct symtab_and_line current_sal = find_pc_line (pc, 0); > > + > > + if (current_sal.line == 0) > > + return {}; > > + > > + struct symtab_and_line prev_sal = find_pc_line (current_sal.pc - > > 1, 0); > > + > > + /* If the previous entry is for a different line, that means we > > are already > > + at the entry with the start PC for this line. */ > > + if (!sal_line_symtab_matches_p (prev_sal, current_sal)) > > + return current_sal.pc; > > + > > + /* Otherwise, keep looking for entries for the same line but > > with > > + smaller PC's. */ > > + bool done = false; > > + CORE_ADDR prev_pc; > > + while (!done) > > + { > > + prev_pc = prev_sal.pc; > > + > > + prev_sal = find_pc_line (prev_pc - 1, 0); > > + > > + /* Did we notice a line change? If so, we are done with the > > search. */ > > + if (!sal_line_symtab_matches_p (prev_sal, current_sal)) > > + done = true; > > + } > > + > > + return prev_pc; > > +} > > + > > /* See symtab.h. */ > > > > struct symtab * > > diff --git a/gdb/symtab.h b/gdb/symtab.h > > index 404d0ab30a8..f54305636da 100644 > > --- a/gdb/symtab.h > > +++ b/gdb/symtab.h > > @@ -2346,6 +2346,22 @@ extern struct symtab_and_line find_pc_line > > (CORE_ADDR, int); > > extern struct symtab_and_line find_pc_sect_line (CORE_ADDR, > > struct obj_section *, > > int); > > > > +/* Given PC, and assuming it is part of a range of addresses that > > is part of a > > + line, go back through the linetable and find the starting PC of > > that > > + line. > > + > > + For example, suppose we have 3 PC ranges for line X: > > + > > + Line X - [0x0 - 0x8] > > + Line X - [0x8 - 0x10] > > + Line X - [0x10 - 0x18] > > + > > + If we call the function with PC == 0x14, we want to return 0x0, > > as that is > > + the starting PC of line X, and the ranges are contiguous. > > +*/ > > + > > +extern gdb::optional<CORE_ADDR> find_line_range_start (CORE_ADDR > > pc); > > + > > /* Wrapper around find_pc_line to just return the symtab. */ > > > > extern struct symtab *find_pc_line_symtab (CORE_ADDR); > > diff --git a/gdb/testsuite/gdb.reverse/func-map-to-same-line-no- > > column-info.exp b/gdb/testsuite/gdb.reverse/func-map-to-same-line- > > no-column-info.exp > > new file mode 100644 > > index 00000000000..20529c90fc2 > > --- /dev/null > > +++ b/gdb/testsuite/gdb.reverse/func-map-to-same-line-no-column- > > info.exp > > @@ -0,0 +1,135 @@ > > +# Copyright 2008-2023 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 < > > http://www.gnu.org/licenses/ > > >. */ > > + > > +# This file is part of the GDB testsuite. It tests reverse > > stepping. > > +# Lots of code borrowed from "step-test.exp". > > + > > +# This test checks to make sure there is no regression failures > > for > > +# the reverse-next command when stepping back over two functions > > in > > +# the same line. > > + > > +if ![supports_reverse] { > > + return > > +} > Nowadays you should use require instead of the if clause, like in > gdb.reverse/break-reverse.exp OK, changed that > > + > > +# This test uses the gcc no-column-info command. > Correct me if I'm wrong, but it seems to me that the other test is a > more generic version of this one, so this test could check for a gcc > recent enough to support this feature, instead of just generically > gcc. > That said, gcc added it on version > 7( > https://gcc.gnu.org/git/gitweb.cgi?p=gcc.git;h=0029b929c9719a > ), is it > old enough that we don't care? GCC supports line tables, I don't know that clang or other compilers do. So all we really need is to check for gcc. The line table stuff was added a long time ago so not sure that we really need to check for version 7 at this point. So just checked that we are using gcc. The "other test" func-map-to-same-line.exp expects the line table so it should probably also be checking that we are using gcc. > > +if ![is_c_compiler_gcc] { > > + unsupported "gcc is required for this test" > > + return 0 > > +} > > + > > +set srcfile func-map-to-same-line.c > > +set executable func-map-to-same-line > > + > > +set options [list debug additional_flags=-gno-column-info] > > + > > +if {[build_executable "failed to prepare" $executable $srcfile > > $options] == -1}\ > > + { > > + return -1 > > +} > > + > > +clean_restart $executable > > + > > +runto_main > > +set target_remote [gdb_is_target_remote] > > + > > +if [supports_process_record] { > > + # Activate process record/replay. > > + gdb_test_no_output "record" "turn on process record for test1" > > +} > > + > > +# This regression test verifies the reverse-step and reverse-next > > commands > > +# work properly when executing backwards thru a source line > > containing > > +# two function calls on the same source line, i.e. func1 (); func2 > > (); > > +# This test is compiled so the dwarf info not contain the line > > table > > +# information. > > + > > +# Test 1, reverse-next command > > +# Set breakpoint at the line after the function calls. > > +set bp_start_reverse_test [gdb_get_line_number "START REVERSE > > TEST" $srcfile] > > +gdb_breakpoint $srcfile:$bp_start_reverse_test temporary > > + > > +# Continue to break point for reverse-next test. > > +# Command definition: reverse-next [count] > > +# Run backward to the beginning of the previous line executed in > > the current > > +# (innermost) stack frame. If the line contains function calls, > > they will be > > +# “un-executed” without stopping. Starting from the first line > > of a function, > > +# reverse-next will take you back to the caller of that > > function, before the > > +# function was called, just as the normal next command would > > take you from > > +# the last line of a function back to its return to its caller 2 > > . > > + > > +gdb_continue_to_breakpoint \ > > + "stopped at command reverse-next test start location" \ > > + ".*$srcfile:$bp_start_reverse_test\r\n.*" > > + > > +# The reverse-next should step all the way back to the beginning > > of the line, > > +# i.e. at the beginning of the func1 call. > > +gdb_test "reverse-next" ".*func1 \\(\\); func2 \\(\\);.*" \ > > + "reverse-next to line with two functions" > > + > > +# A reverse-step should step back and stop at the beginning > > +# of the previous line b = 2, i.e. not in func1 (). > > +gdb_test "reverse-step" ".*b = 2;.*" \ > > + "reverse-step to previous line b = 2" > The point of this test is to confirm that we are at the very first > instruction of the line, right? So I would think it is better to do > a > reverse-stepi here, to make sure that even walking a single > instruction > we reach a different line. Yes, we should be on the first instruction of the line so stepi would be a better test to prove we are really on the first instruction. Changed the reverse-step to reverse-stepi. > Either that or doing what Pedro did in his > email: save the PC before executing the line, then do and undo the > line > and confirm that PCs match exactly. > > + > > + > > +# Setup for test 2 > > +# Go back to the start of the function > > +gdb_test "reverse-continue" "a = 1;" "At start of main, setup for > > test 2" > > + > > +# Turn off record to clear logs and turn on again > > +gdb_test "record stop" "Process record is stopped.*" \ > > + "turn off process record for test1" > > +gdb_test_no_output "record" "turn on process record for test2" > Since you don't require process record for this test, you can't > assume > these to work. I think its better to clean restart and record if the > process supports recording, this way you're sure to reset history no > matter the inferior. No, the test requires process record. If record is not supported, we can't do reverse execution. That said, doing a "clean restart" and then record would be another way, probably better way, of clearing the history. Put in a clean restart rather than turning off/on the recording to clear the log file. > > + > > +# Delete all breakpoints and catchpoints. > > +delete_breakpoints > > + > > + > > +# Test 2, reverse-step command > > +# Set breakpoint at the line after the function calls. > > +gdb_breakpoint $srcfile:$bp_start_reverse_test temporary > > + > > +# Continue to the start of the reverse-step test. > > +# Command definition: reverse-step [count] > > +# Run the program backward until control reaches the start of a > > +# different source line; then stop it, and return control to > > gdb. > > +# Like the step command, reverse-step will only stop at the > > beginning of a > > +# source line. It “un-executes” the previously executed source > > line. If the > > +# previous source line included calls to debuggable functions, > > reverse-step > > +# will step (backward) into the called function, stopping at > > the beginning > > +# of the last statement in the called function (typically a > > return > > +# statement). Also, as with the step command, if non- > > debuggable functions > > +# are called, reverse-step will run thru them backward without > > stopping. > > + > > +gdb_continue_to_breakpoint \ > > + "stopped at command reverse-step test start location" \ > > + ".*$srcfile:$bp_start_reverse_test\r\n.*" > > + > > +# The first reverse step should take us call of func2 (). > > +gdb_test "reverse-step" ".*}.*" \ > > + "reverse-step into func2 " > > + > > +# The second reverse step should take us into func1 (). > > +gdb_test "reverse-step" ".*}.*" \ > > + "reverse-step into func1 " > > + > > +# The third reverse step should take us call of func1 (). > > +gdb_test "reverse-step" ".*func1 \\(\\); func2 \\(\\);.*" \ > > + "reverse-step to line func1(); func2(), at call for func1 " > > + > > +# The fourth reverse step should take us to b = 2 (). > > +gdb_test "reverse-step" ".*b = 2;.*" \ > > + "reverse-step to line b = 2 " > Ditto from the other test like this. Yes, stepi is a better test to prove you were on the first instruction in the line. Changed. > Also, I feel that, while the test > name for the last 2 gdb_test are different, they don't meaningfully > communicate which part of the test is failing. I think it would be > better if you differentiated them by adding "for step test" or "for > test > 2" at the end of the name would make it easier to understand where > things went wrong when looking at the sum file. OK, agreed. For the above tests I added "test#," where # is either 1 or 2 to the test names. For example: +gdb_test "reverse-step" ".*b = 2;.*" \ + "reverse-step to line b = 2 " was changed to +gdb_test "reverse-stepi" ".*b = 2;.*" \ + "test2, reverse-step to line b = 2 " I also updated the test to identify the closing } for func1 and func2 to make it clearer in the test which function we just steped back into. > > diff --git a/gdb/testsuite/gdb.reverse/func-map-to-same-line.c > > b/gdb/testsuite/gdb.reverse/func-map-to-same-line.c > > new file mode 100644 > > index 00000000000..e9787ef9ff5 > > --- /dev/null > > +++ b/gdb/testsuite/gdb.reverse/func-map-to-same-line.c > > @@ -0,0 +1,36 @@ > > +/* Copyright 2008-2023 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 < > > http://www.gnu.org/licenses/ > > >. > > + > > + This test is used to test the reverse-step and reverse-next > > instruction > > + execution for a source line that contains multiple function > > calls. */ > > + > > +void > > +func1 () > > +{ > > +} > > + > > +void > > +func2 () > > +{ > > +} > > + > > +int main () > > +{ > > + int a, b; > > + a = 1; > > + b = 2; > > + func1 (); func2 (); > > + a = a + b; // START REVERSE TEST > > +} > > diff --git a/gdb/testsuite/gdb.reverse/func-map-to-same-line.exp > > b/gdb/testsuite/gdb.reverse/func-map-to-same-line.exp > > new file mode 100644 > > index 00000000000..b632a236bbe > > --- /dev/null > > +++ b/gdb/testsuite/gdb.reverse/func-map-to-same-line.exp > > @@ -0,0 +1,123 @@ > > +# Copyright 2008-2023 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 < > > http://www.gnu.org/licenses/ > > >. */ > > + > > +# This file is part of the GDB testsuite. It tests reverse > > stepping. > > +# Lots of code borrowed from "step-test.exp". > > + > > +# This test checks to make sure there is no regression failures > > for > > +# the reverse-next command when stepping back over two functions > > in > > +# the same line. > > + > > +if ![supports_reverse] { > > + return > > +} > > I'm not sure it's worth separating these 2 tests into separate > files. > You could instead just have most of the test defined as a proc, and > call > it twice, once after compiling the inferior with column info, the > other > compiling without if gcc is used. This way it's less likely that the > tests will diverge over time. OK, good point. I created a test proc which is then called with the binary compiled with and without line information. The extra exp test file was removed. > > > + > > +standard_testfile > > + > > +if { [prepare_for_testing "failed to prepare" $testfile $srcfile] > > } { > > + return -1 > > +} > > + > > +runto_main > > +set target_remote [gdb_is_target_remote] > > + > > +if [supports_process_record] { > > + # Activate process record/replay. > > + gdb_test_no_output "record" "turn on process record for test1" > > +} > > + > > +# This regression test verifies the reverse-step and reverse-next > > commands > > +# work properly when executing backwards thru a source line > > containing > > +# two function calls on the same source line, i.e. func1 (); func2 > > (); > > +# The assumption for this test is the dwarf info contain the > > column > > +# information. > > + > > +# Test 1, reverse-next command > > +# Set breakpoint at the line after the function calls. > > +set bp_start_reverse_test [gdb_get_line_number "START REVERSE > > TEST" $srcfile] > > +gdb_breakpoint $srcfile:$bp_start_reverse_test temporary > > + > > +# Continue to break point for reverse-next test. > > +# Command definition: reverse-next [count] > > +# Run backward to the beginning of the previous line executed in > > the current > > +# (innermost) stack frame. If the line contains function calls, > > they will be > > +# “un-executed” without stopping. Starting from the first line > > of a function, > > +# reverse-next will take you back to the caller of that > > function, before the > > +# function was called, just as the normal next command would > > take you from > > +# the last line of a function back to its return to its caller 2 > > . > > + > > +gdb_continue_to_breakpoint \ > > + "stopped at command reverse-next test start location" \ > > + ".*$srcfile:$bp_start_reverse_test\r\n.*" > > + > > +# The reverse-next should step all the way back to the beginning > > of the line, > > +# i.e. at the beginning of the func1 call. > > +gdb_test "reverse-next" ".*func1 \\(\\); func2 \\(\\);.*" \ > > + "reverse-next to line with two functions" > > + > > +# A reverse-step should step back and stop at the beginning > > +# of the previous line b = 2, i.e. not in func1 (). > > +gdb_test "reverse-step" ".*b = 2;.*" \ > > + "reverse-step to previous line b = 2" > > + > > + > > +# Setup for test 2 > > +# Go back to the start of the function > > +gdb_test "reverse-continue" "a = 1;" "At start of main, setup for > > test 2" > > + > > +# Turn off record to clear logs and turn on again > > +gdb_test "record stop" "Process record is stopped.*" \ > > + "turn off process record for test1" > > +gdb_test_no_output "record" "turn on process record for test2" > > + > > +# Delete all breakpoints and catchpoints. > > +delete_breakpoints > > + > > + > > +# Test 2, reverse-step command > > +# Set breakpoint at the line after the function calls. > > +gdb_breakpoint $srcfile:$bp_start_reverse_test temporary > > + > > +# Continue to the start of the reverse-step test. > > +# Command definition: reverse-step [count] > > +# Run the program backward until control reaches the start of a > > +# different source line; then stop it, and return control to > > gdb. > > +# Like the step command, reverse-step will only stop at the > > beginning of a > > +# source line. It “un-executes” the previously executed source > > line. If the > > +# previous source line included calls to debuggable functions, > > reverse-step > > +# will step (backward) into the called function, stopping at > > the beginning > > +# of the last statement in the called function (typically a > > return > > +# statement). Also, as with the step command, if non- > > debuggable functions > > +# are called, reverse-step will run thru them backward without > > stopping. > > + > > +gdb_continue_to_breakpoint \ > > + "stopped at command reverse-step test start location" \ > > + ".*$srcfile:$bp_start_reverse_test\r\n.*" > > + > > +# The first reverse step should take us call of func2 (). > > +gdb_test "reverse-step" ".*}.*" \ > > + "reverse-step into func2 " > > + > > +# The second reverse step should take us into func1 (). > > +gdb_test "reverse-step" ".*}.*" \ > > + "reverse-step into func1 " > > + > > +# The third reverse step should take us call of func1 (). > > +gdb_test "reverse-step" ".*func1 \\(\\); func2 \\(\\);.*" \ > > + "reverse-step to line func1(); func2(), at call for func1 " > > + > > +# The fourth reverse step should take us to b = 2 (). > > +gdb_test "reverse-step" ".*b = 2;.*" \ > > + "reverse-step to line b = 2 " > > diff --git a/gdb/testsuite/gdb.reverse/map-to-same-line.c > > b/gdb/testsuite/gdb.reverse/map-to-same-line.c > > new file mode 100644 > > index 00000000000..f20d778f40e > > --- /dev/null > > +++ b/gdb/testsuite/gdb.reverse/map-to-same-line.c > > @@ -0,0 +1,58 @@ > > +/* Copyright 2008-2023 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 < > > http://www.gnu.org/licenses/ > > >. */ > > + > > +/* The purpose of this test is to create a DWARF line table that > > contains two > > + or more entries for the same line. When stepping (forwards or > > backwards), > > + GDB should step over the entire line and not just a particular > > entry in the > > + line table. */ > > + > > +int > > +main () > > +{ /* TAG: main prologue */ > > + asm ("main_label: .globl main_label"); > > + int i = 1, j = 2, k; > > + float f1 = 2.0, f2 = 4.1, f3; > > + const char *str_1 = "foo", *str_2 = "bar", *str_3; > > + > > + asm ("line1: .globl line1"); > > + k = i; f3 = f1; str_3 = str_1; /* TAG: line 1 */ > > + > > + asm ("line2: .globl line2"); > > + k = j; f3 = f2; str_3 = str_2; /* TAG: line 2 */ > > + > > + asm ("line3: .globl line3"); > > + k = i; f3 = f1; str_3 = str_1; /* TAG: line 3 */ > > + > > + asm ("line4: .globl line4"); > > + k = j; f3 = f2; str_3 = str_2; /* TAG: line 4 */ > > + > > + asm ("line5: .globl line5"); > > + k = i; f3 = f1; str_3 = str_1; /* TAG: line 5 */ > > + > > + asm ("line6: .globl line6"); > > + k = j; f3 = f2; str_3 = str_2; /* TAG: line 6 */ > > + > > + asm ("line7: .globl line7"); > > + k = i; f3 = f1; str_3 = str_1; /* TAG: line 7 */ > > + > > + asm ("line8: .globl line8"); > > + k = j; f3 = f2; str_3 = str_2; /* TAG: line 8 */ > > + > > + asm ("main_return: .globl main_return"); > > + k = j; f3 = f2; str_3 = str_2; /* TAG: main return */ > > + > > + asm ("end_of_sequence: .globl end_of_sequence"); > > + return 0; /* TAG: main return */ > > +} > > diff --git a/gdb/testsuite/gdb.reverse/map-to-same-line.exp > > b/gdb/testsuite/gdb.reverse/map-to-same-line.exp > > new file mode 100644 > > index 00000000000..a01579c2a8d > > --- /dev/null > > +++ b/gdb/testsuite/gdb.reverse/map-to-same-line.exp > > @@ -0,0 +1,153 @@ > > +# Copyright 2008-2023 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 < > > http://www.gnu.org/licenses/ > > = >. > > + > > +# When stepping (forwards or backwards), GDB should step over the > > entire line > > +# and not just a particular entry in the line table. This test was > > added to > > +# verify the find_line_range_start function properly sets the step > > range for a > > +# line that consists of multiple statements, i.e. multiple entries > > in the line > > +# table. This test creates a DWARF line table that contains two > > entries for > > +# the same line to do the needed testing. > > + > > +load_lib dwarf.exp > > + > > +# This test can only be run on targets which support DWARF-2 and > > use gas. > > +if {![dwarf2_support]} { > > + unsupported "dwarf2 support required for this test" > > + return 0 > > +} > Again, the new way to check for these is "required". And IIUC, you > can > add multiple requirements into a singe require call. OK, updated. > > + > > +if [get_compiler_info] { > > + return -1 > > +} > > + > > +# The DWARF assembler requires the gcc compiler. > > +if ![is_c_compiler_gcc] { > > + unsupported "gcc is required for this test" > > + return 0 > > +} > > + > > +# This test suitable only for process record-replay > > +if ![supports_process_record] { > > + return > > +} > > + > > +standard_testfile .c .S > > + > > +if { [prepare_for_testing "failed to prepare" ${testfile} > > ${srcfile}] } { > > + return -1 > > +} > > + > > +set asm_file [standard_output_file $srcfile2] > > +Dwarf::assemble $asm_file { > > + global srcdir subdir srcfile > > + declare_labels integer_label L > > + > > + # Find start address and length of program > > + lassign [function_range main [list > > ${srcdir}/${subdir}/$srcfile]] \ > > + main_start main_len > > + set main_end "$main_start + $main_len" > > + > > + cu {} { > > + compile_unit { > > + {language @DW_LANG_C} > > + {name map-to-same-line.c} > > + {stmt_list $L DW_FORM_sec_offset} > > + {low_pc 0 addr} > > + } { > > + subprogram { > > + {external 1 flag} > > + {name main} > > + {low_pc $main_start addr} > > + {high_pc $main_len DW_FORM_data4} > > + } > > + } > > + } > > + > > + lines {version 2 default_is_stmt 1} L { > > + include_dir "${srcdir}/${subdir}" > > + file_name "$srcfile" 1 > > + > > + # Generate the line table program with distinct source lines > > being > > + # mapped to the same line entry. Line 1, 5 and 8 contain 1 > > statement > > + # each. Line 2 contains 2 statements. Line 3 contains 3 > > statements. > > + program { > > + DW_LNE_set_address $main_start > > + line [gdb_get_line_number "TAG: main prologue"] > > + DW_LNS_copy > > + DW_LNE_set_address line1 > > + line [gdb_get_line_number "TAG: line 1" ] > > + DW_LNS_copy > > + DW_LNE_set_address line2 > > + line [gdb_get_line_number "TAG: line 2" ] > > + DW_LNS_copy > > + DW_LNE_set_address line3 > > + line [gdb_get_line_number "TAG: line 2" ] > > + DW_LNS_copy > > + DW_LNE_set_address line4 > > + line [gdb_get_line_number "TAG: line 3" ] > > + DW_LNS_copy > > + DW_LNE_set_address line5 > > + line [gdb_get_line_number "TAG: line 3" ] > > + DW_LNS_copy > > + DW_LNE_set_address line6 > > + line [gdb_get_line_number "TAG: line 3" ] > > + DW_LNS_copy > > + DW_LNE_set_address line7 > > + line [gdb_get_line_number "TAG: line 5" ] > > + DW_LNS_copy > > + DW_LNE_set_address line8 > > + line [gdb_get_line_number "TAG: line 8" ] > > + DW_LNS_copy > > + DW_LNE_set_address main_return > > + line [gdb_get_line_number "TAG: main return"] > > + DW_LNS_copy > > + DW_LNE_set_address end_of_sequence > > + DW_LNE_end_sequence > > + } > > + } > > +} > > + > > +if { [prepare_for_testing "failed to prepare" ${testfile} \ > > + [list $srcfile $asm_file] {nodebug} ] } { > > + return -1 > > +} > > + > > +if ![runto_main] { > > + return -1 > > +} > runto_main already errors out and leaves, I think, so no need for the > if > clause. OK, updated. > > + > > +# Print the line table > > +gdb_test_multiple "maint info line-table ${testfile}" "" { > > + -re "\r\n$decimal\[ \t\]+$decimal\[ \t\]+($hex)\[ > > \t\]+Y\[^\r\n\]*" { > > + lappend is_stmt $expect_out(1,string) > > + exp_continue > > + } > > + -re -wrap "" { > > + } > > +} > > + > > +# Activate process record/replay > > +gdb_test_no_output "record" "turn on process record" > > + > > +gdb_test "tbreak main_return" "Temporary breakpoint .*" > > "breakpoint at return" > you can set a temporary breakpoint using gdb_breakpoint "..." > temporary, > no need to manually call tbreak. > > +gdb_test "continue" "Temporary breakpoint .*" "run to end of main" > gdb_continue_to_breakpoint can handle temporary breakpoints as well. OK, updated the break and continue statements. > > +gdb_test "display \$pc" ".*pc =.*" "display pc" > > + > > +# At this point, GDB has already recorded the execution up until > > the return > > +# statement. Reverse-step and test if GDB transitions between > > lines in the > > +# expected order. It should reverse-step across lines 8, 5, 3, 2 > > and 1. > > +foreach line {8 5 3 2 1} { > > + gdb_test "reverse-step" ".*TAG: line $line.*" "reverse step to > > line $line" > > +} > > I'm not sure if it is needed, but I don't think it would hurt to > also > test reverse-next in a separate foreach right after this one. OK, added a clean restart, run to the end of main then do reverse next using a foreach line. Carl ^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH] Fix reverse stepping multiple contiguous PC ranges over the line table. 2023-05-04 2:55 ` Carl Love @ 2023-05-04 9:24 ` Bruno Larsen 2023-05-04 14:52 ` Carl Love 0 siblings, 1 reply; 41+ messages in thread From: Bruno Larsen @ 2023-05-04 9:24 UTC (permalink / raw) To: Carl Love, gdb-patches, Ulrich Weigand, pedro; +Cc: luis.machado On 04/05/2023 04:55, Carl Love wrote: > Bruno: > > On Wed, 2023-05-03 at 11:53 +0200, Bruno Larsen wrote: >> On 27/04/2023 22:59, Carl Love wrote: > <snip> <snip> >>> + >>> +# This test uses the gcc no-column-info command. >> Correct me if I'm wrong, but it seems to me that the other test is a >> more generic version of this one, so this test could check for a gcc >> recent enough to support this feature, instead of just generically >> gcc. >> That said, gcc added it on version >> 7( >> https://gcc.gnu.org/git/gitweb.cgi?p=gcc.git;h=0029b929c9719a >> ), is it >> old enough that we don't care? > GCC supports line tables, I don't know that clang or other compilers > do. So all we really need is to check for gcc. The line table stuff > was added a long time ago so not sure that we really need to check for > version 7 at this point. So just checked that we are using gcc. The > "other test" func-map-to-same-line.exp expects the line table so it > should probably also be checking that we are using gcc. GCC 7.1 (first gcc 7 release) was on May 2nd 2017, almost exactly 6 years ago, and there was a gcc 6.8 release in october 2018. I don't know if 5 years is long enough to assume that everyone has abandoned the old version (especially seeing as we sometimes test for gcc 4 or 3, but that might just be old cruft). That said, it's not a blocker for me, so /shrug Also, clang will have line tables - otherwise almost nothing on our test suite would work. It doesn't have column info, though, which is why I'm fine with it being ignored in the test that uses -gno-column-info. > >>> +if ![is_c_compiler_gcc] { >>> + unsupported "gcc is required for this test" >>> + return 0 >>> +} >>> + >>> +set srcfile func-map-to-same-line.c >>> +set executable func-map-to-same-line >>> + >>> +set options [list debug additional_flags=-gno-column-info] >>> + >>> +if {[build_executable "failed to prepare" $executable $srcfile >>> $options] == -1}\ >>> + { >>> + return -1 >>> +} >>> + >>> +clean_restart $executable >>> + >>> +runto_main >>> +set target_remote [gdb_is_target_remote] >>> + >>> +if [supports_process_record] { >>> + # Activate process record/replay. >>> + gdb_test_no_output "record" "turn on process record for test1" >>> +} >>> + >>> +# This regression test verifies the reverse-step and reverse-next >>> commands >>> +# work properly when executing backwards thru a source line >>> containing >>> +# two function calls on the same source line, i.e. func1 (); func2 >>> (); >>> +# This test is compiled so the dwarf info not contain the line >>> table >>> +# information. >>> + >>> +# Test 1, reverse-next command >>> +# Set breakpoint at the line after the function calls. >>> +set bp_start_reverse_test [gdb_get_line_number "START REVERSE >>> TEST" $srcfile] >>> +gdb_breakpoint $srcfile:$bp_start_reverse_test temporary >>> + >>> +# Continue to break point for reverse-next test. >>> +# Command definition: reverse-next [count] >>> +# Run backward to the beginning of the previous line executed in >>> the current >>> +# (innermost) stack frame. If the line contains function calls, >>> they will be >>> +# “un-executed” without stopping. Starting from the first line >>> of a function, >>> +# reverse-next will take you back to the caller of that >>> function, before the >>> +# function was called, just as the normal next command would >>> take you from >>> +# the last line of a function back to its return to its caller 2 >>> . >>> + >>> +gdb_continue_to_breakpoint \ >>> + "stopped at command reverse-next test start location" \ >>> + ".*$srcfile:$bp_start_reverse_test\r\n.*" >>> + >>> +# The reverse-next should step all the way back to the beginning >>> of the line, >>> +# i.e. at the beginning of the func1 call. >>> +gdb_test "reverse-next" ".*func1 \\(\\); func2 \\(\\);.*" \ >>> + "reverse-next to line with two functions" >>> + >>> +# A reverse-step should step back and stop at the beginning >>> +# of the previous line b = 2, i.e. not in func1 (). >>> +gdb_test "reverse-step" ".*b = 2;.*" \ >>> + "reverse-step to previous line b = 2" >> The point of this test is to confirm that we are at the very first >> instruction of the line, right? So I would think it is better to do >> a >> reverse-stepi here, to make sure that even walking a single >> instruction >> we reach a different line. > Yes, we should be on the first instruction of the line so stepi would > be a better test to prove we are really on the first instruction. > Changed the reverse-step to reverse-stepi. > >> Either that or doing what Pedro did in his >> email: save the PC before executing the line, then do and undo the >> line >> and confirm that PCs match exactly. >>> + >>> + >>> +# Setup for test 2 >>> +# Go back to the start of the function >>> +gdb_test "reverse-continue" "a = 1;" "At start of main, setup for >>> test 2" >>> + >>> +# Turn off record to clear logs and turn on again >>> +gdb_test "record stop" "Process record is stopped.*" \ >>> + "turn off process record for test1" >>> +gdb_test_no_output "record" "turn on process record for test2" >> Since you don't require process record for this test, you can't >> assume >> these to work. I think its better to clean restart and record if the >> process supports recording, this way you're sure to reset history no >> matter the inferior. > No, the test requires process record. If record is not supported, we > can't do reverse execution. That said, doing a "clean restart" and > then record would be another way, probably better way, of clearing the > history. Put in a clean restart rather than turning off/on the > recording to clear the log file. From my understanding, you could have architectures or inferiors that support reverse execution without needing to record, that's why "supports_reverse" and "supports_process_record" are different. However, if you want to restrict this to record-only, that's fine, I just think it should be a requirement at the top of the test, not in the middle of the execution. -- Cheers, Bruno ^ permalink raw reply [flat|nested] 41+ messages in thread
* RE: [PATCH] Fix reverse stepping multiple contiguous PC ranges over the line table. 2023-05-04 9:24 ` Bruno Larsen @ 2023-05-04 14:52 ` Carl Love 0 siblings, 0 replies; 41+ messages in thread From: Carl Love @ 2023-05-04 14:52 UTC (permalink / raw) To: Bruno Larsen, gdb-patches, Ulrich Weigand, pedro; +Cc: luis.machado, cel Bruno: On Thu, 2023-05-04 at 11:24 +0200, Bruno Larsen wrote: > On 04/05/2023 04:55, Carl Love wrote: > > Bruno: > > > > On Wed, 2023-05-03 at 11:53 +0200, Bruno Larsen wrote: > > > On 27/04/2023 22:59, Carl Love wrote: > > <snip> > <snip> > > > > + > > > > +# This test uses the gcc no-column-info command. > > > Correct me if I'm wrong, but it seems to me that the other test > > > is a > > > more generic version of this one, so this test could check for a > > > gcc > > > recent enough to support this feature, instead of just > > > generically > > > gcc. > > > That said, gcc added it on version > > > 7( > > > https://gcc.gnu.org/git/gitweb.cgi?p=gcc.git;h=0029b929c9719a > > > ), is it > > > old enough that we don't care? > > GCC supports line tables, I don't know that clang or other > > compilers > > do. So all we really need is to check for gcc. The line table > > stuff > > was added a long time ago so not sure that we really need to check > > for > > version 7 at this point. So just checked that we are using > > gcc. The > > "other test" func-map-to-same-line.exp expects the line table so it > > should probably also be checking that we are using gcc. > > GCC 7.1 (first gcc 7 release) was on May 2nd 2017, almost exactly 6 > years ago, and there was a gcc 6.8 release in october 2018. I don't > know > if 5 years is long enough to assume that everyone has abandoned the > old > version (especially seeing as we sometimes test for gcc 4 or 3, but > that > might just be old cruft). That said, it's not a blocker for me, so > /shrug OK, I would think that it has been there long enough. But it does sound like there is the occasional test on an old gcc. So, OK lets restrict it to GCC 7 and later. > > Also, clang will have line tables - otherwise almost nothing on our > test > suite would work. It doesn't have column info, though, which is why > I'm > fine with it being ignored in the test that uses -gno-column-info. > > > > > +if ![is_c_compiler_gcc] { > > > > + unsupported "gcc is required for this test" > > > > + return 0 > > > > +} > > > > + > > > > <snip> > From my understanding, you could have architectures or inferiors > that > support reverse execution without needing to record, that's why > "supports_reverse" and "supports_process_record" are different. > > However, if you want to restrict this to record-only, that's fine, I > just think it should be a requirement at the top of the test, not in > the > middle of the execution. I can't imagine how you can do remote execution without recording but if they can do that OK. The test really should be at the top, missed that part. So, I will change this to "supports_reverse" and get it at the top where it belongs. Thanks for the clarification and help with this. Carl ^ permalink raw reply [flat|nested] 41+ messages in thread
* [PATCH v2] Fix reverse stepping multiple contiguous PC ranges over the line table. 2023-05-03 9:53 ` Bruno Larsen 2023-05-04 2:55 ` Carl Love @ 2023-05-04 2:55 ` Carl Love 2023-05-04 15:59 ` [PATCH v3] " Carl Love 1 sibling, 1 reply; 41+ messages in thread From: Carl Love @ 2023-05-04 2:55 UTC (permalink / raw) To: Bruno Larsen, gdb-patches, Ulrich Weigand, pedro; +Cc: luis.machado, cel Bruno, GDB maintainers: I believe I have addressed all of Bruno's comments on version 1. The following patch fixes issues on PowerPC with the reverse-step and reverse-next instructions when there are multiple assignment statements on the same line and when there are multiple function calls on the same line. The commit log below discusses these issues in further depth. The discussion included what the correct operation should be for these commands based on the GDB documentation. The proposed patch at that time changed how the commands worked on other platforms such as X86 in a way they no longer matched the documentation. The issue is the line table contains multiple entries for the same source line. The patch adds a function to search the line table to find the address of the first instruction of a line. When setup up the reverse stepping range, the function is called to make sure the start of the range corresponds to the address of the first instruction for the line. This approach was used. When Luis initially developed the patch, he considered merging the contiguous ranges in the line table when reading the line tables. He decided it was better to work with the data directly in the line table rather than creating and using a modified version of the line table. The following patch fixes the execution of the reveres-step and reverse-next commands for both senarios of multiple statements on the same line for PowerPC and aarch64-linux. Unlike the previous patch, it does not change the operation of the commands on other platforms, i.e. X86. The patch adds new test cases for both scenarios to verify they work correctly. The patch has been tested on PowerPC, Intel X86 and aarch64-linux with no new regression failures. Please let me know if the patch is acceptable for mainline. Thanks. Carl --------------------------------------------------------------- Fix reverse stepping multiple contiguous PC ranges over the line table. There are a couple of scenarios where the GDB reverse-step and reverse-next commands do not work correctly. Scenario 1 issue description by Luis Machado: When running GDB's testsuite on aarch64-linux/Ubuntu 20.04 (also spotted on the ppc backend), I noticed some failures in gdb.reverse/solib-precsave.exp and gdb.reverse/solib-reverse.exp. The failure happens around the following code: 38 b[1] = shr2(17); /* middle part two */ 40 b[0] = 6; b[1] = 9; /* generic statement, end part two */ 42 shr1 ("message 1\n"); /* shr1 one */ Normal execution: - step from line 38 will land on line 40. - step from line 40 will land on line 42. Reverse execution: - step from line 42 will land on line 40. - step from line 40 will land on line 40. - step from line 40 will land on line 38. The problem here is that line 40 contains two contiguous but distinct PC ranges in the line table, like so: Line 40 - [0x7ec ~ 0x7f4] Line 40 - [0x7f4 ~ 0x7fc] The two distinct ranges are generated because GCC started outputting source column information, which GDB doesn't take into account at the moment. When stepping forward from line 40, we skip both of these ranges and land on line 42. When stepping backward from line 42, we stop at the start PC of the second (or first, going backwards) range of line 40. Since we've reached ecs->event_thread->control.step_range_start, we stop stepping backwards. --------------------------------------------------------- Scenario 2 issue described by Pedro Alves: The following explanation of the issue was taken from the gdb mailing list discussion of the withdrawn patch to change the behavior of the reverse-step and reverse-next commands. Specifically, message from Pedro Alves <pedro@palves.net> where he demonstrates the issue where you have multiple function calls on the same source code line: https://sourceware.org/pipermail/gdb-patches/2023-January/196110.html The source line looks like: func1 (); func2 (); so stepping backwards over that line should always stop at the first instruction of the line, not in the middle. Let's simplify this. Here's the full source code of my example: (gdb) list 1 1 void func1 () 2 { 3 } 4 5 void func2 () 6 { 7 } 8 9 int main () 10 { 11 func1 (); func2 (); 12 } Compiled with: $ gcc reverse.c -o reverse -g3 -O0 $ gcc -v ... gcc version 11.3.0 (Ubuntu 11.3.0-1ubuntu1~22.04) Now let's debug it with target record, using current gdb git master (f3d8ae90b236), without your patch: $ gdb ~/reverse GNU gdb (GDB) 14.0.50.20230124-git ... Reading symbols from /home/pedro/reverse... (gdb) start Temporary breakpoint 1 at 0x1147: file reverse.c, line 11. Starting program: /home/pedro/reverse [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". Temporary breakpoint 1, main () at reverse.c:11 11 func1 (); func2 (); (gdb) record (gdb) disassemble /s Dump of assembler code for function main: reverse.c: 10 { 0x000055555555513f <+0>: endbr64 0x0000555555555143 <+4>: push %rbp 0x0000555555555144 <+5>: mov %rsp,%rbp 11 func1 (); func2 (); => 0x0000555555555147 <+8>: mov $0x0,%eax 0x000055555555514c <+13>: call 0x555555555129 <func1> 0x0000555555555151 <+18>: mov $0x0,%eax 0x0000555555555156 <+23>: call 0x555555555134 <func2> 0x000055555555515b <+28>: mov $0x0,%eax 12 } 0x0000555555555160 <+33>: pop %rbp 0x0000555555555161 <+34>: ret End of assembler dump. (gdb) n 12 } So far so good, a "next" stepped over the whole of line 11 and stopped at line 12. Let's confirm where we are now: (gdb) disassemble /s Dump of assembler code for function main: reverse.c: 10 { 0x000055555555513f <+0>: endbr64 0x0000555555555143 <+4>: push %rbp 0x0000555555555144 <+5>: mov %rsp,%rbp 11 func1 (); func2 (); 0x0000555555555147 <+8>: mov $0x0,%eax 0x000055555555514c <+13>: call 0x555555555129 <func1> 0x0000555555555151 <+18>: mov $0x0,%eax 0x0000555555555156 <+23>: call 0x555555555134 <func2> 0x000055555555515b <+28>: mov $0x0,%eax 12 } => 0x0000555555555160 <+33>: pop %rbp 0x0000555555555161 <+34>: ret End of assembler dump. Good, we're at the first instruction of line 12. Now let's undo the "next", with "reverse-next": (gdb) reverse-next 11 func1 (); func2 (); Seemingly stopped at line 11. Let's see exactly where: (gdb) disassemble /s Dump of assembler code for function main: reverse.c: 10 { 0x000055555555513f <+0>: endbr64 0x0000555555555143 <+4>: push %rbp 0x0000555555555144 <+5>: mov %rsp,%rbp 11 func1 (); func2 (); 0x0000555555555147 <+8>: mov $0x0,%eax 0x000055555555514c <+13>: call 0x555555555129 <func1> => 0x0000555555555151 <+18>: mov $0x0,%eax 0x0000555555555156 <+23>: call 0x555555555134 <func2> 0x000055555555515b <+28>: mov $0x0,%eax 12 } 0x0000555555555160 <+33>: pop %rbp 0x0000555555555161 <+34>: ret End of assembler dump. (gdb) And lo, we stopped in the middle of line 11! That is a bug, we should have stepped back all the way to the beginning of the line. The "reverse-next" should have fully undone the prior "next" command. The above issues were fixed by introducing a new function that looks for adjacent PC ranges for the same line, until we notice a line change. Then we take that as the start PC of the range. The new start PC for the range is used for the control.step_range_start when setting up a step range. The test case gdb.reverse/map-to-same-line.exp is added to test the fix for the issues in scenario 1. The test case gdb.reverse/func-map-to-same-line.exp was added to test the fix for scenario 2 when the binary was compiled with and without line table information. bug: https://sourceware.org/bugzilla/show_bug.cgi?id=28426 Co-authored-by: Luis Machado <luis.machado@arm.com> Co-authored-by: Carl Love <cel@us.ibm.com> --- gdb/infrun.c | 57 ++++++ gdb/symtab.c | 49 ++++++ gdb/symtab.h | 16 ++ .../gdb.reverse/func-map-to-same-line.c | 36 ++++ .../gdb.reverse/func-map-to-same-line.exp | 155 ++++++++++++++++ gdb/testsuite/gdb.reverse/map-to-same-line.c | 58 ++++++ .../gdb.reverse/map-to-same-line.exp | 166 ++++++++++++++++++ 7 files changed, 537 insertions(+) create mode 100644 gdb/testsuite/gdb.reverse/func-map-to-same-line.c create mode 100644 gdb/testsuite/gdb.reverse/func-map-to-same-line.exp create mode 100644 gdb/testsuite/gdb.reverse/map-to-same-line.c create mode 100644 gdb/testsuite/gdb.reverse/map-to-same-line.exp diff --git a/gdb/infrun.c b/gdb/infrun.c index efe2c00c489..8555e3c979f 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -114,6 +114,9 @@ static struct async_event_handler *infrun_async_inferior_event_token; Starts off as -1, indicating "never enabled/disabled". */ static int infrun_is_async = -1; +static CORE_ADDR update_line_range_start (CORE_ADDR pc, + struct execution_control_state *ecs); + /* See infrun.h. */ void @@ -6769,6 +6772,25 @@ handle_signal_stop (struct execution_control_state *ecs) process_event_stop_test (ecs); } +CORE_ADDR +update_line_range_start (CORE_ADDR pc, struct execution_control_state *ecs) +{ + /* The line table may have multiple entries for the same source code line. + Given the PC, check the line table and return the PC that corresponds + to the line table entry for the source line that PC is in. */ + CORE_ADDR start_line_pc = ecs->event_thread->control.step_range_start; + gdb::optional<CORE_ADDR> real_range_start; + + /* Call find_line_range_start to get smallest address in the + linetable for multiple Line X entries in the line table. */ + real_range_start = find_line_range_start (pc); + + if (real_range_start.has_value ()) + start_line_pc = *real_range_start; + + return start_line_pc; +} + /* Come here when we've got some debug event / signal we can explain (IOW, not a random signal), and test whether it should cause a stop, or whether we should resume the inferior (transparently). @@ -7570,6 +7592,28 @@ process_event_stop_test (struct execution_control_state *ecs) if (stop_pc_sal.is_stmt) { + if (execution_direction == EXEC_REVERSE) + { + /* We are stepping backwards make sure we have reached the + beginning of the line. */ + CORE_ADDR stop_pc = ecs->event_thread->stop_pc (); + CORE_ADDR start_line_pc + = update_line_range_start (stop_pc, ecs); + + if (stop_pc != start_line_pc) + { + /* Have not reached the beginning of the source code line. + Set a step range. Execution should stop in any function + calls we execute back into before reaching the beginning + of the line. */ + ecs->event_thread->control.step_range_start = start_line_pc; + ecs->event_thread->control.step_range_end = stop_pc; + set_step_info (ecs->event_thread, frame, stop_pc_sal); + keep_going (ecs); + return; + } + } + /* We are at the start of a statement. So stop. Note that we don't stop if we step into the middle of a @@ -7632,6 +7676,19 @@ process_event_stop_test (struct execution_control_state *ecs) set_step_info (ecs->event_thread, frame, stop_pc_sal); infrun_debug_printf ("keep going"); + + if (execution_direction == EXEC_REVERSE) + { + CORE_ADDR stop_pc = ecs->event_thread->stop_pc (); + + /* Make sure the stop_pc is set to the beginning of the line. */ + if (stop_pc != ecs->event_thread->control.step_range_start) + { + stop_pc = update_line_range_start (stop_pc, ecs); + ecs->event_thread->control.step_range_start = stop_pc; + } + } + keep_going (ecs); } diff --git a/gdb/symtab.c b/gdb/symtab.c index 27611a34ec4..91d35616eb9 100644 --- a/gdb/symtab.c +++ b/gdb/symtab.c @@ -3282,6 +3282,55 @@ find_pc_line (CORE_ADDR pc, int notcurrent) return sal; } +/* Compare two symtab_and_line entries. Return true if both have + the same line number and the same symtab pointer. That means we + are dealing with two entries from the same line and from the same + source file. + + Return false otherwise. */ + +static bool +sal_line_symtab_matches_p (const symtab_and_line &sal1, + const symtab_and_line &sal2) +{ + return (sal1.line == sal2.line && sal1.symtab == sal2.symtab); +} + +/* See symtah.h. */ + +gdb::optional<CORE_ADDR> +find_line_range_start (CORE_ADDR pc) +{ + struct symtab_and_line current_sal = find_pc_line (pc, 0); + + if (current_sal.line == 0) + return {}; + + struct symtab_and_line prev_sal = find_pc_line (current_sal.pc - 1, 0); + + /* If the previous entry is for a different line, that means we are already + at the entry with the start PC for this line. */ + if (!sal_line_symtab_matches_p (prev_sal, current_sal)) + return current_sal.pc; + + /* Otherwise, keep looking for entries for the same line but with + smaller PC's. */ + bool done = false; + CORE_ADDR prev_pc; + while (!done) + { + prev_pc = prev_sal.pc; + + prev_sal = find_pc_line (prev_pc - 1, 0); + + /* Did we notice a line change? If so, we are done with the search. */ + if (!sal_line_symtab_matches_p (prev_sal, current_sal)) + done = true; + } + + return prev_pc; +} + /* See symtab.h. */ struct symtab * diff --git a/gdb/symtab.h b/gdb/symtab.h index 404d0ab30a8..f54305636da 100644 --- a/gdb/symtab.h +++ b/gdb/symtab.h @@ -2346,6 +2346,22 @@ extern struct symtab_and_line find_pc_line (CORE_ADDR, int); extern struct symtab_and_line find_pc_sect_line (CORE_ADDR, struct obj_section *, int); +/* Given PC, and assuming it is part of a range of addresses that is part of a + line, go back through the linetable and find the starting PC of that + line. + + For example, suppose we have 3 PC ranges for line X: + + Line X - [0x0 - 0x8] + Line X - [0x8 - 0x10] + Line X - [0x10 - 0x18] + + If we call the function with PC == 0x14, we want to return 0x0, as that is + the starting PC of line X, and the ranges are contiguous. +*/ + +extern gdb::optional<CORE_ADDR> find_line_range_start (CORE_ADDR pc); + /* Wrapper around find_pc_line to just return the symtab. */ extern struct symtab *find_pc_line_symtab (CORE_ADDR); diff --git a/gdb/testsuite/gdb.reverse/func-map-to-same-line.c b/gdb/testsuite/gdb.reverse/func-map-to-same-line.c new file mode 100644 index 00000000000..412ab180943 --- /dev/null +++ b/gdb/testsuite/gdb.reverse/func-map-to-same-line.c @@ -0,0 +1,36 @@ +/* Copyright 2008-2023 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 <http://www.gnu.org/licenses/>. + + This test is used to test the reverse-step and reverse-next instruction + execution for a source line that contains multiple function calls. */ + +void +func1 () +{ +} // END FUNC1 + +void +func2 () +{ +} // END FUNC2 + +int main () +{ + int a, b; + a = 1; + b = 2; + func1 (); func2 (); + a = a + b; // START REVERSE TEST +} diff --git a/gdb/testsuite/gdb.reverse/func-map-to-same-line.exp b/gdb/testsuite/gdb.reverse/func-map-to-same-line.exp new file mode 100644 index 00000000000..a75673dc99b --- /dev/null +++ b/gdb/testsuite/gdb.reverse/func-map-to-same-line.exp @@ -0,0 +1,155 @@ +# Copyright 2008-2023 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 <http://www.gnu.org/licenses/>. */ + +# This file is part of the GDB testsuite. It tests reverse stepping. +# Lots of code borrowed from "step-test.exp". + +# This test checks to make sure there is no regression failures for +# the reverse-next command when stepping back over two functions in +# the same line. + +require supports_reverse + +# This test uses the gcc no-column-info command. +if ![is_c_compiler_gcc] { + unsupported "gcc is required for this test" + return 0 +} + +proc run_tests {msg} { + global srcfile + global executable + + runto_main + set target_remote [gdb_is_target_remote] + + if [supports_process_record] { + # Activate process record/replay. + gdb_test_no_output "record" "$msg: turn on process record for test1" + } + + # This regression test verifies the reverse-step and reverse-next commands + # work properly when executing backwards thru a source line containing + # two function calls on the same source line, i.e. func1 (); func2 (); + # This test is compiled so the dwarf info not contain the line table + # information. + + # Test 1, reverse-next command + # Set breakpoint at the line after the function calls. + set bp_start_reverse_test [gdb_get_line_number "START REVERSE TEST" \ + $srcfile] + gdb_breakpoint $srcfile:$bp_start_reverse_test temporary + + # Continue to break point for reverse-next test. + # Command definition: reverse-next [count] + # Run backward to the beginning of the previous line executed in the + # current (innermost) stack frame. If the line contains function calls, + # they will be “un-executed” without stopping. Starting from the first + # line of a function, reverse-next will take you back to the caller of + # that function, before the function was called, just as the normal next + # command would take you from the last line of a function back to its + # return to its caller 2 . + gdb_continue_to_breakpoint \ + "$msg: test1: stopped at command reverse-next test start location" \ + ".*$srcfile:$bp_start_reverse_test\r\n.*" + + # The reverse-next should step all the way back to the beginning of the + # line, i.e. at the beginning of the func1 call. + gdb_test "reverse-next" ".*func1 \\(\\); func2 \\(\\);.*" \ + "$msg: test1: reverse-next to line with two functions" + + # We should be stopped at the first instruction of the line. A reverse-step + # should step back and stop at the beginning of the previous line b = 2, + # i.e. not in func1 (). + gdb_test "reverse-stepi" ".*b = 2;.*" \ + "$msg: test1: reverse-stepi to previous line b = 2" + + + # Setup for test 2 + clean_restart $executable + runto_main + + if [supports_process_record] { + # Activate process record/replay. + gdb_test_no_output "record" "$msg: turn on process record for test2" + } + + # Test 2, reverse-step command + # Set breakpoint at the line after the function calls. + gdb_breakpoint $srcfile:$bp_start_reverse_test temporary + + # Continue to the start of the reverse-step test. + # Command definition: reverse-step [count] + # Run the program backward until control reaches the start of a + # different source line; then stop it, and return control to gdb. + # Like the step command, reverse-step will only stop at the beginning + # of a source line. It “un-executes” the previously executed source + # line. If the previous source line included calls to debuggable + # functions, reverse-step will step (backward) into the called function, + # stopping at the beginning of the last statement in the called + # function (typically a return statement). Also, as with the step + # command, if non-debuggable functions are called, reverse-step will + # run thru them backward without stopping. + + gdb_continue_to_breakpoint \ + "$msg: test2: stopped at command reverse-step test start location" \ + ".*$srcfile:$bp_start_reverse_test\r\n.*" + + # The first reverse step should take us call of func2 (). + gdb_test "reverse-step" ".*END FUNC2.*" \ + "$msg: test2: reverse-step into func2 " + + # The second reverse step should take us into func1 (). + gdb_test "reverse-step" ".*END FUNC1.*" \ + "$msg: test2: reverse-step into func1 " + + # The third reverse step should take us call of func1 (). + gdb_test "reverse-step" ".*func1 \\(\\); func2 \\(\\);.*" \ + "$msg: test2: reverse-step to line func1(); func2(), at call for func1 " + + # We should be stopped at the first instruction of the line. A reverse + # stepi should take us to b = 2 (). + gdb_test "reverse-stepi" ".*b = 2;.*" \ + "$msg: test2: reverse-stepi to line b = 2 " +} + +set srcfile func-map-to-same-line.c +set executable func-map-to-same-line + +# test with gcc column info enabled +set options [list debug additional_flags=] + +if {[build_executable "failed to prepare" $executable $srcfile $options] == -1}\ + { + return -1 +} + +clean_restart $executable + +run_tests {"with-column-info"} + + +#test with gcc column info disabled +set options [list debug additional_flags=-gno-column-info] + +if {[build_executable "failed to prepare" $executable $srcfile $options] == -1}\ + { + return -1 +} + +set $executable executable_without_column_info +clean_restart $executable + +run_tests {"no-column-info"} diff --git a/gdb/testsuite/gdb.reverse/map-to-same-line.c b/gdb/testsuite/gdb.reverse/map-to-same-line.c new file mode 100644 index 00000000000..f20d778f40e --- /dev/null +++ b/gdb/testsuite/gdb.reverse/map-to-same-line.c @@ -0,0 +1,58 @@ +/* Copyright 2008-2023 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 <http://www.gnu.org/licenses/ >. */ + +/* The purpose of this test is to create a DWARF line table that contains two + or more entries for the same line. When stepping (forwards or backwards), + GDB should step over the entire line and not just a particular entry in the + line table. */ + +int +main () +{ /* TAG: main prologue */ + asm ("main_label: .globl main_label"); + int i = 1, j = 2, k; + float f1 = 2.0, f2 = 4.1, f3; + const char *str_1 = "foo", *str_2 = "bar", *str_3; + + asm ("line1: .globl line1"); + k = i; f3 = f1; str_3 = str_1; /* TAG: line 1 */ + + asm ("line2: .globl line2"); + k = j; f3 = f2; str_3 = str_2; /* TAG: line 2 */ + + asm ("line3: .globl line3"); + k = i; f3 = f1; str_3 = str_1; /* TAG: line 3 */ + + asm ("line4: .globl line4"); + k = j; f3 = f2; str_3 = str_2; /* TAG: line 4 */ + + asm ("line5: .globl line5"); + k = i; f3 = f1; str_3 = str_1; /* TAG: line 5 */ + + asm ("line6: .globl line6"); + k = j; f3 = f2; str_3 = str_2; /* TAG: line 6 */ + + asm ("line7: .globl line7"); + k = i; f3 = f1; str_3 = str_1; /* TAG: line 7 */ + + asm ("line8: .globl line8"); + k = j; f3 = f2; str_3 = str_2; /* TAG: line 8 */ + + asm ("main_return: .globl main_return"); + k = j; f3 = f2; str_3 = str_2; /* TAG: main return */ + + asm ("end_of_sequence: .globl end_of_sequence"); + return 0; /* TAG: main return */ +} diff --git a/gdb/testsuite/gdb.reverse/map-to-same-line.exp b/gdb/testsuite/gdb.reverse/map-to-same-line.exp new file mode 100644 index 00000000000..97a3ba46fdd --- /dev/null +++ b/gdb/testsuite/gdb.reverse/map-to-same-line.exp @@ -0,0 +1,166 @@ +# Copyright 2008-2023 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 <http://www.gnu.org/licenses/ >. + +# When stepping (forwards or backwards), GDB should step over the entire line +# and not just a particular entry in the line table. This test was added to +# verify the find_line_range_start function properly sets the step range for a +# line that consists of multiple statements, i.e. multiple entries in the line +# table. This test creates a DWARF line table that contains two entries for +# the same line to do the needed testing. + +load_lib dwarf.exp + +# This test can only be run on targets which support DWARF-2 and use gas. +require dwarf2_support + +if [get_compiler_info] { + return -1 +} + +# The DWARF assembler requires the gcc compiler. +if ![is_c_compiler_gcc] { + unsupported "gcc is required for this test" + return 0 +} + +# This test suitable only for process record-replay +if ![supports_process_record] { + return +} + +standard_testfile .c .S + +if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile}] } { + return -1 +} + +set asm_file [standard_output_file $srcfile2] +Dwarf::assemble $asm_file { + global srcdir subdir srcfile + declare_labels integer_label L + + # Find start address and length of program + lassign [function_range main [list ${srcdir}/${subdir}/$srcfile]] \ + main_start main_len + set main_end "$main_start + $main_len" + + cu {} { + compile_unit { + {language @DW_LANG_C} + {name map-to-same-line.c} + {stmt_list $L DW_FORM_sec_offset} + {low_pc 0 addr} + } { + subprogram { + {external 1 flag} + {name main} + {low_pc $main_start addr} + {high_pc $main_len DW_FORM_data4} + } + } + } + + lines {version 2 default_is_stmt 1} L { + include_dir "${srcdir}/${subdir}" + file_name "$srcfile" 1 + + # Generate the line table program with distinct source lines being + # mapped to the same line entry. Line 1, 5 and 8 contain 1 statement + # each. Line 2 contains 2 statements. Line 3 contains 3 statements. + program { + DW_LNE_set_address $main_start + line [gdb_get_line_number "TAG: main prologue"] + DW_LNS_copy + DW_LNE_set_address line1 + line [gdb_get_line_number "TAG: line 1" ] + DW_LNS_copy + DW_LNE_set_address line2 + line [gdb_get_line_number "TAG: line 2" ] + DW_LNS_copy + DW_LNE_set_address line3 + line [gdb_get_line_number "TAG: line 2" ] + DW_LNS_copy + DW_LNE_set_address line4 + line [gdb_get_line_number "TAG: line 3" ] + DW_LNS_copy + DW_LNE_set_address line5 + line [gdb_get_line_number "TAG: line 3" ] + DW_LNS_copy + DW_LNE_set_address line6 + line [gdb_get_line_number "TAG: line 3" ] + DW_LNS_copy + DW_LNE_set_address line7 + line [gdb_get_line_number "TAG: line 5" ] + DW_LNS_copy + DW_LNE_set_address line8 + line [gdb_get_line_number "TAG: line 8" ] + DW_LNS_copy + DW_LNE_set_address main_return + line [gdb_get_line_number "TAG: main return"] + DW_LNS_copy + DW_LNE_set_address end_of_sequence + DW_LNE_end_sequence + } + } +} + +if { [prepare_for_testing "failed to prepare" ${testfile} \ + [list $srcfile $asm_file] {nodebug} ] } { + return -1 +} + +runto_main + +# Print the line table +gdb_test_multiple "maint info line-table ${testfile}" "" { + -re "\r\n$decimal\[ \t\]+$decimal\[ \t\]+($hex)\[ \t\]+Y\[^\r\n\]*" { + lappend is_stmt $expect_out(1,string) + exp_continue + } + -re -wrap "" { + } +} + +# Do the reverse-step test +gdb_test_no_output "record" "turn on process record" + +set bp_main_return [gdb_get_line_number "TAG: main return" $srcfile] +gdb_breakpoint $srcfile:$bp_main_return +gdb_continue_to_breakpoint "run to end of main, reverse-step test" ".*$srcfile:$bp_main_return.*" +gdb_test "display \$pc" ".*pc =.*" "display pc, reverse-step test" + +# At this point, GDB has already recorded the execution up until the return +# statement. Reverse-step and test if GDB transitions between lines in the +# expected order. It should reverse-step across lines 8, 5, 3, 2 and 1. +foreach line {8 5 3 2 1} { + gdb_test "reverse-step" ".*TAG: line $line.*" "reverse step to line $line" +} + +## Clean restart, test reverse-next command +clean_restart ${testfile} +runto_main +gdb_test_no_output "record" "turn on process record, reverst-next test" + +set bp_main_return [gdb_get_line_number "TAG: main return" $srcfile] +gdb_breakpoint $srcfile:$bp_main_return +gdb_continue_to_breakpoint "run to end of main, reverse-next test" ".*$srcfile:$bp_main_return.*" +gdb_test "display \$pc" ".*pc =.*" "display pc, reverse-next test" + +# At this point, GDB has already recorded the execution up until the return +# statement. Reverse-next and test if GDB transitions between lines in the +# expected order. It should reverse-next across lines 8, 5, 3, 2 and 1. +foreach line {8 5 3 2 1} { + gdb_test "reverse-next" ".*TAG: line $line.*" "reverse next to line $line" +} -- 2.37.2 ^ permalink raw reply [flat|nested] 41+ messages in thread
* [PATCH v3] Fix reverse stepping multiple contiguous PC ranges over the line table. 2023-05-04 2:55 ` [PATCH v2] " Carl Love @ 2023-05-04 15:59 ` Carl Love 2023-05-05 14:59 ` Luis Machado 2023-05-10 13:47 ` Bruno Larsen 0 siblings, 2 replies; 41+ messages in thread From: Carl Love @ 2023-05-04 15:59 UTC (permalink / raw) To: Bruno Larsen, gdb-patches, UlrichWeigand, pedro; +Cc: luis.machado, cel Bruno, GDB maintainers: Version 3, added the gcc version check as discussed further from version 2 of the patch. Also updated the tests to check for supporting reverse execution rather than requiring recording. I also noticed there were a couple more instances of a requirement check, i.e. if [] which I changed to "require" per the current style for checking on the test requirements. The following patch fixes issues on PowerPC with the reverse-step and reverse-next instructions when there are multiple assignment statements on the same line and when there are multiple function calls on the same line. The commit log below discusses these issues in further depth. The discussion included what the correct operation should be for these commands based on the GDB documentation. The proposed patch at that time changed how the commands worked on other platforms such as X86 in a way they no longer matched the documentation. The issue is the line table contains multiple entries for the same source line. The patch adds a function to search the line table to find the address of the first instruction of a line. When setup up the reverse stepping range, the function is called to make sure the start of the range corresponds to the address of the first instruction for the line. This approach was used. When Luis initially developed the patch, he considered merging the contiguous ranges in the line table when reading the line tables. He decided it was better to work with the data directly in the line table rather than creating and using a modified version of the line table. The following patch fixes the execution of the reveres-step and reverse-next commands for both senarios of multiple statements on the same line for PowerPC and aarch64-linux. Unlike the previous patch, it does not change the operation of the commands on other platforms, i.e. X86. The patch adds new test cases for both scenarios to verify they work correctly. The patch has been tested on PowerPC, Intel X86 and aarch64-linux with no new regression failures. Please let me know if the patch is acceptable for mainline. Thanks. Carl --------------------------------------------------------------- Fix reverse stepping multiple contiguous PC ranges over the line table. There are a couple of scenarios where the GDB reverse-step and reverse-next commands do not work correctly. Scenario 1 issue description by Luis Machado: When running GDB's testsuite on aarch64-linux/Ubuntu 20.04 (also spotted on the ppc backend), I noticed some failures in gdb.reverse/solib-precsave.exp and gdb.reverse/solib-reverse.exp. The failure happens around the following code: 38 b[1] = shr2(17); /* middle part two */ 40 b[0] = 6; b[1] = 9; /* generic statement, end part two */ 42 shr1 ("message 1\n"); /* shr1 one */ Normal execution: - step from line 38 will land on line 40. - step from line 40 will land on line 42. Reverse execution: - step from line 42 will land on line 40. - step from line 40 will land on line 40. - step from line 40 will land on line 38. The problem here is that line 40 contains two contiguous but distinct PC ranges in the line table, like so: Line 40 - [0x7ec ~ 0x7f4] Line 40 - [0x7f4 ~ 0x7fc] The two distinct ranges are generated because GCC started outputting source column information, which GDB doesn't take into account at the moment. When stepping forward from line 40, we skip both of these ranges and land on line 42. When stepping backward from line 42, we stop at the start PC of the second (or first, going backwards) range of line 40. Since we've reached ecs->event_thread->control.step_range_start, we stop stepping backwards. --------------------------------------------------------- Scenario 2 issue described by Pedro Alves: The following explanation of the issue was taken from the gdb mailing list discussion of the withdrawn patch to change the behavior of the reverse-step and reverse-next commands. Specifically, message from Pedro Alves <pedro@palves.net> where he demonstrates the issue where you have multiple function calls on the same source code line: https://sourceware.org/pipermail/gdb-patches/2023-January/196110.html The source line looks like: func1 (); func2 (); so stepping backwards over that line should always stop at the first instruction of the line, not in the middle. Let's simplify this. Here's the full source code of my example: (gdb) list 1 1 void func1 () 2 { 3 } 4 5 void func2 () 6 { 7 } 8 9 int main () 10 { 11 func1 (); func2 (); 12 } Compiled with: $ gcc reverse.c -o reverse -g3 -O0 $ gcc -v ... gcc version 11.3.0 (Ubuntu 11.3.0-1ubuntu1~22.04) Now let's debug it with target record, using current gdb git master (f3d8ae90b236), without your patch: $ gdb ~/reverse GNU gdb (GDB) 14.0.50.20230124-git ... Reading symbols from /home/pedro/reverse... (gdb) start Temporary breakpoint 1 at 0x1147: file reverse.c, line 11. Starting program: /home/pedro/reverse [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". Temporary breakpoint 1, main () at reverse.c:11 11 func1 (); func2 (); (gdb) record (gdb) disassemble /s Dump of assembler code for function main: reverse.c: 10 { 0x000055555555513f <+0>: endbr64 0x0000555555555143 <+4>: push %rbp 0x0000555555555144 <+5>: mov %rsp,%rbp 11 func1 (); func2 (); => 0x0000555555555147 <+8>: mov $0x0,%eax 0x000055555555514c <+13>: call 0x555555555129 <func1> 0x0000555555555151 <+18>: mov $0x0,%eax 0x0000555555555156 <+23>: call 0x555555555134 <func2> 0x000055555555515b <+28>: mov $0x0,%eax 12 } 0x0000555555555160 <+33>: pop %rbp 0x0000555555555161 <+34>: ret End of assembler dump. (gdb) n 12 } So far so good, a "next" stepped over the whole of line 11 and stopped at line 12. Let's confirm where we are now: (gdb) disassemble /s Dump of assembler code for function main: reverse.c: 10 { 0x000055555555513f <+0>: endbr64 0x0000555555555143 <+4>: push %rbp 0x0000555555555144 <+5>: mov %rsp,%rbp 11 func1 (); func2 (); 0x0000555555555147 <+8>: mov $0x0,%eax 0x000055555555514c <+13>: call 0x555555555129 <func1> 0x0000555555555151 <+18>: mov $0x0,%eax 0x0000555555555156 <+23>: call 0x555555555134 <func2> 0x000055555555515b <+28>: mov $0x0,%eax 12 } => 0x0000555555555160 <+33>: pop %rbp 0x0000555555555161 <+34>: ret End of assembler dump. Good, we're at the first instruction of line 12. Now let's undo the "next", with "reverse-next": (gdb) reverse-next 11 func1 (); func2 (); Seemingly stopped at line 11. Let's see exactly where: (gdb) disassemble /s Dump of assembler code for function main: reverse.c: 10 { 0x000055555555513f <+0>: endbr64 0x0000555555555143 <+4>: push %rbp 0x0000555555555144 <+5>: mov %rsp,%rbp 11 func1 (); func2 (); 0x0000555555555147 <+8>: mov $0x0,%eax 0x000055555555514c <+13>: call 0x555555555129 <func1> => 0x0000555555555151 <+18>: mov $0x0,%eax 0x0000555555555156 <+23>: call 0x555555555134 <func2> 0x000055555555515b <+28>: mov $0x0,%eax 12 } 0x0000555555555160 <+33>: pop %rbp 0x0000555555555161 <+34>: ret End of assembler dump. (gdb) And lo, we stopped in the middle of line 11! That is a bug, we should have stepped back all the way to the beginning of the line. The "reverse-next" should have fully undone the prior "next" command. The above issues were fixed by introducing a new function that looks for adjacent PC ranges for the same line, until we notice a line change. Then we take that as the start PC of the range. The new start PC for the range is used for the control.step_range_start when setting up a step range. The test case gdb.reverse/map-to-same-line.exp is added to test the fix for the issues in scenario 1. The test case gdb.reverse/func-map-to-same-line.exp was added to test the fix for scenario 2 when the binary was compiled with and without line table information. bug: https://sourceware.org/bugzilla/show_bug.cgi?id=28426 Co-authored-by: Luis Machado <luis.machado@arm.com> Co-authored-by: Carl Love <cel@us.ibm.com> --- gdb/infrun.c | 57 +++++++ gdb/symtab.c | 49 ++++++ gdb/symtab.h | 16 ++ .../gdb.reverse/func-map-to-same-line.c | 36 ++++ .../gdb.reverse/func-map-to-same-line.exp | 146 ++++++++++++++++ gdb/testsuite/gdb.reverse/map-to-same-line.c | 58 +++++++ .../gdb.reverse/map-to-same-line.exp | 156 ++++++++++++++++++ 7 files changed, 518 insertions(+) create mode 100644 gdb/testsuite/gdb.reverse/func-map-to-same-line.c create mode 100644 gdb/testsuite/gdb.reverse/func-map-to-same-line.exp create mode 100644 gdb/testsuite/gdb.reverse/map-to-same-line.c create mode 100644 gdb/testsuite/gdb.reverse/map-to-same-line.exp diff --git a/gdb/infrun.c b/gdb/infrun.c index efe2c00c489..8555e3c979f 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -114,6 +114,9 @@ static struct async_event_handler *infrun_async_inferior_event_token; Starts off as -1, indicating "never enabled/disabled". */ static int infrun_is_async = -1; +static CORE_ADDR update_line_range_start (CORE_ADDR pc, + struct execution_control_state *ecs); + /* See infrun.h. */ void @@ -6769,6 +6772,25 @@ handle_signal_stop (struct execution_control_state *ecs) process_event_stop_test (ecs); } +CORE_ADDR +update_line_range_start (CORE_ADDR pc, struct execution_control_state *ecs) +{ + /* The line table may have multiple entries for the same source code line. + Given the PC, check the line table and return the PC that corresponds + to the line table entry for the source line that PC is in. */ + CORE_ADDR start_line_pc = ecs->event_thread->control.step_range_start; + gdb::optional<CORE_ADDR> real_range_start; + + /* Call find_line_range_start to get smallest address in the + linetable for multiple Line X entries in the line table. */ + real_range_start = find_line_range_start (pc); + + if (real_range_start.has_value ()) + start_line_pc = *real_range_start; + + return start_line_pc; +} + /* Come here when we've got some debug event / signal we can explain (IOW, not a random signal), and test whether it should cause a stop, or whether we should resume the inferior (transparently). @@ -7570,6 +7592,28 @@ process_event_stop_test (struct execution_control_state *ecs) if (stop_pc_sal.is_stmt) { + if (execution_direction == EXEC_REVERSE) + { + /* We are stepping backwards make sure we have reached the + beginning of the line. */ + CORE_ADDR stop_pc = ecs->event_thread->stop_pc (); + CORE_ADDR start_line_pc + = update_line_range_start (stop_pc, ecs); + + if (stop_pc != start_line_pc) + { + /* Have not reached the beginning of the source code line. + Set a step range. Execution should stop in any function + calls we execute back into before reaching the beginning + of the line. */ + ecs->event_thread->control.step_range_start = start_line_pc; + ecs->event_thread->control.step_range_end = stop_pc; + set_step_info (ecs->event_thread, frame, stop_pc_sal); + keep_going (ecs); + return; + } + } + /* We are at the start of a statement. So stop. Note that we don't stop if we step into the middle of a @@ -7632,6 +7676,19 @@ process_event_stop_test (struct execution_control_state *ecs) set_step_info (ecs->event_thread, frame, stop_pc_sal); infrun_debug_printf ("keep going"); + + if (execution_direction == EXEC_REVERSE) + { + CORE_ADDR stop_pc = ecs->event_thread->stop_pc (); + + /* Make sure the stop_pc is set to the beginning of the line. */ + if (stop_pc != ecs->event_thread->control.step_range_start) + { + stop_pc = update_line_range_start (stop_pc, ecs); + ecs->event_thread->control.step_range_start = stop_pc; + } + } + keep_going (ecs); } diff --git a/gdb/symtab.c b/gdb/symtab.c index 27611a34ec4..91d35616eb9 100644 --- a/gdb/symtab.c +++ b/gdb/symtab.c @@ -3282,6 +3282,55 @@ find_pc_line (CORE_ADDR pc, int notcurrent) return sal; } +/* Compare two symtab_and_line entries. Return true if both have + the same line number and the same symtab pointer. That means we + are dealing with two entries from the same line and from the same + source file. + + Return false otherwise. */ + +static bool +sal_line_symtab_matches_p (const symtab_and_line &sal1, + const symtab_and_line &sal2) +{ + return (sal1.line == sal2.line && sal1.symtab == sal2.symtab); +} + +/* See symtah.h. */ + +gdb::optional<CORE_ADDR> +find_line_range_start (CORE_ADDR pc) +{ + struct symtab_and_line current_sal = find_pc_line (pc, 0); + + if (current_sal.line == 0) + return {}; + + struct symtab_and_line prev_sal = find_pc_line (current_sal.pc - 1, 0); + + /* If the previous entry is for a different line, that means we are already + at the entry with the start PC for this line. */ + if (!sal_line_symtab_matches_p (prev_sal, current_sal)) + return current_sal.pc; + + /* Otherwise, keep looking for entries for the same line but with + smaller PC's. */ + bool done = false; + CORE_ADDR prev_pc; + while (!done) + { + prev_pc = prev_sal.pc; + + prev_sal = find_pc_line (prev_pc - 1, 0); + + /* Did we notice a line change? If so, we are done with the search. */ + if (!sal_line_symtab_matches_p (prev_sal, current_sal)) + done = true; + } + + return prev_pc; +} + /* See symtab.h. */ struct symtab * diff --git a/gdb/symtab.h b/gdb/symtab.h index 404d0ab30a8..f54305636da 100644 --- a/gdb/symtab.h +++ b/gdb/symtab.h @@ -2346,6 +2346,22 @@ extern struct symtab_and_line find_pc_line (CORE_ADDR, int); extern struct symtab_and_line find_pc_sect_line (CORE_ADDR, struct obj_section *, int); +/* Given PC, and assuming it is part of a range of addresses that is part of a + line, go back through the linetable and find the starting PC of that + line. + + For example, suppose we have 3 PC ranges for line X: + + Line X - [0x0 - 0x8] + Line X - [0x8 - 0x10] + Line X - [0x10 - 0x18] + + If we call the function with PC == 0x14, we want to return 0x0, as that is + the starting PC of line X, and the ranges are contiguous. +*/ + +extern gdb::optional<CORE_ADDR> find_line_range_start (CORE_ADDR pc); + /* Wrapper around find_pc_line to just return the symtab. */ extern struct symtab *find_pc_line_symtab (CORE_ADDR); diff --git a/gdb/testsuite/gdb.reverse/func-map-to-same-line.c b/gdb/testsuite/gdb.reverse/func-map-to-same-line.c new file mode 100644 index 00000000000..412ab180943 --- /dev/null +++ b/gdb/testsuite/gdb.reverse/func-map-to-same-line.c @@ -0,0 +1,36 @@ +/* Copyright 2008-2023 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 <http://www.gnu.org/licenses/>. + + This test is used to test the reverse-step and reverse-next instruction + execution for a source line that contains multiple function calls. */ + +void +func1 () +{ +} // END FUNC1 + +void +func2 () +{ +} // END FUNC2 + +int main () +{ + int a, b; + a = 1; + b = 2; + func1 (); func2 (); + a = a + b; // START REVERSE TEST +} diff --git a/gdb/testsuite/gdb.reverse/func-map-to-same-line.exp b/gdb/testsuite/gdb.reverse/func-map-to-same-line.exp new file mode 100644 index 00000000000..4eae042a6bf --- /dev/null +++ b/gdb/testsuite/gdb.reverse/func-map-to-same-line.exp @@ -0,0 +1,146 @@ +# Copyright 2008-2023 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 <http://www.gnu.org/licenses/>. */ + +# This file is part of the GDB testsuite. It tests reverse stepping. +# Lots of code borrowed from "step-test.exp". + +# This test checks to make sure there is no regression failures for +# the reverse-next command when stepping back over two functions in +# the same line. + +require supports_reverse + +# This test uses the gcc no-column-info command which was added in gcc 7.1. +require get_compiler_info "gcc-7-*" + +proc run_tests {msg} { + global srcfile + global executable + + runto_main + set target_remote [gdb_is_target_remote] + + gdb_test_no_output "record" "turn on process record" + + # This regression test verifies the reverse-step and reverse-next commands + # work properly when executing backwards thru a source line containing + # two function calls on the same source line, i.e. func1 (); func2 (); + # This test is compiled so the dwarf info not contain the line table + # information. + + # Test 1, reverse-next command + # Set breakpoint at the line after the function calls. + set bp_start_reverse_test [gdb_get_line_number "START REVERSE TEST" \ + $srcfile] + gdb_breakpoint $srcfile:$bp_start_reverse_test temporary + + # Continue to break point for reverse-next test. + # Command definition: reverse-next [count] + # Run backward to the beginning of the previous line executed in the + # current (innermost) stack frame. If the line contains function calls, + # they will be “un-executed” without stopping. Starting from the first + # line of a function, reverse-next will take you back to the caller of + # that function, before the function was called, just as the normal next + # command would take you from the last line of a function back to its + # return to its caller 2 . + gdb_continue_to_breakpoint \ + "$msg: test1: stopped at command reverse-next test start location" \ + ".*$srcfile:$bp_start_reverse_test\r\n.*" + + # The reverse-next should step all the way back to the beginning of the + # line, i.e. at the beginning of the func1 call. + gdb_test "reverse-next" ".*func1 \\(\\); func2 \\(\\);.*" \ + "$msg: test1: reverse-next to line with two functions" + + # We should be stopped at the first instruction of the line. A reverse-step + # should step back and stop at the beginning of the previous line b = 2, + # i.e. not in func1 (). + gdb_test "reverse-stepi" ".*b = 2;.*" \ + "$msg: test1: reverse-stepi to previous line b = 2" + + + # Setup for test 2 + clean_restart $executable + runto_main + + gdb_test_no_output "record" "turn on process record" + + # Test 2, reverse-step command + # Set breakpoint at the line after the function calls. + gdb_breakpoint $srcfile:$bp_start_reverse_test temporary + + # Continue to the start of the reverse-step test. + # Command definition: reverse-step [count] + # Run the program backward until control reaches the start of a + # different source line; then stop it, and return control to gdb. + # Like the step command, reverse-step will only stop at the beginning + # of a source line. It “un-executes” the previously executed source + # line. If the previous source line included calls to debuggable + # functions, reverse-step will step (backward) into the called function, + # stopping at the beginning of the last statement in the called + # function (typically a return statement). Also, as with the step + # command, if non-debuggable functions are called, reverse-step will + # run thru them backward without stopping. + + gdb_continue_to_breakpoint \ + "$msg: test2: stopped at command reverse-step test start location" \ + ".*$srcfile:$bp_start_reverse_test\r\n.*" + + # The first reverse step should take us call of func2 (). + gdb_test "reverse-step" ".*END FUNC2.*" \ + "$msg: test2: reverse-step into func2 " + + # The second reverse step should take us into func1 (). + gdb_test "reverse-step" ".*END FUNC1.*" \ + "$msg: test2: reverse-step into func1 " + + # The third reverse step should take us call of func1 (). + gdb_test "reverse-step" ".*func1 \\(\\); func2 \\(\\);.*" \ + "$msg: test2: reverse-step to line func1(); func2(), at call for func1 " + + # We should be stopped at the first instruction of the line. A reverse + # stepi should take us to b = 2 (). + gdb_test "reverse-stepi" ".*b = 2;.*" \ + "$msg: test2: reverse-stepi to line b = 2 " +} + +set srcfile func-map-to-same-line.c +set executable func-map-to-same-line + +# test with gcc column info enabled +set options [list debug additional_flags=] + +if {[build_executable "failed to prepare" $executable $srcfile $options] == -1}\ + { + return -1 +} + +clean_restart $executable + +run_tests {"with-column-info"} + + +#test with gcc column info disabled +set options [list debug additional_flags=-gno-column-info] + +if {[build_executable "failed to prepare" $executable $srcfile $options] == -1}\ + { + return -1 +} + +set $executable executable_without_column_info +clean_restart $executable + +run_tests {"no-column-info"} diff --git a/gdb/testsuite/gdb.reverse/map-to-same-line.c b/gdb/testsuite/gdb.reverse/map-to-same-line.c new file mode 100644 index 00000000000..f20d778f40e --- /dev/null +++ b/gdb/testsuite/gdb.reverse/map-to-same-line.c @@ -0,0 +1,58 @@ +/* Copyright 2008-2023 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 <http://www.gnu.org/licenses/ >. */ + +/* The purpose of this test is to create a DWARF line table that contains two + or more entries for the same line. When stepping (forwards or backwards), + GDB should step over the entire line and not just a particular entry in the + line table. */ + +int +main () +{ /* TAG: main prologue */ + asm ("main_label: .globl main_label"); + int i = 1, j = 2, k; + float f1 = 2.0, f2 = 4.1, f3; + const char *str_1 = "foo", *str_2 = "bar", *str_3; + + asm ("line1: .globl line1"); + k = i; f3 = f1; str_3 = str_1; /* TAG: line 1 */ + + asm ("line2: .globl line2"); + k = j; f3 = f2; str_3 = str_2; /* TAG: line 2 */ + + asm ("line3: .globl line3"); + k = i; f3 = f1; str_3 = str_1; /* TAG: line 3 */ + + asm ("line4: .globl line4"); + k = j; f3 = f2; str_3 = str_2; /* TAG: line 4 */ + + asm ("line5: .globl line5"); + k = i; f3 = f1; str_3 = str_1; /* TAG: line 5 */ + + asm ("line6: .globl line6"); + k = j; f3 = f2; str_3 = str_2; /* TAG: line 6 */ + + asm ("line7: .globl line7"); + k = i; f3 = f1; str_3 = str_1; /* TAG: line 7 */ + + asm ("line8: .globl line8"); + k = j; f3 = f2; str_3 = str_2; /* TAG: line 8 */ + + asm ("main_return: .globl main_return"); + k = j; f3 = f2; str_3 = str_2; /* TAG: main return */ + + asm ("end_of_sequence: .globl end_of_sequence"); + return 0; /* TAG: main return */ +} diff --git a/gdb/testsuite/gdb.reverse/map-to-same-line.exp b/gdb/testsuite/gdb.reverse/map-to-same-line.exp new file mode 100644 index 00000000000..02f3f4d8c9c --- /dev/null +++ b/gdb/testsuite/gdb.reverse/map-to-same-line.exp @@ -0,0 +1,156 @@ +# Copyright 2008-2023 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 <http://www.gnu.org/licenses/ >. + +# When stepping (forwards or backwards), GDB should step over the entire line +# and not just a particular entry in the line table. This test was added to +# verify the find_line_range_start function properly sets the step range for a +# line that consists of multiple statements, i.e. multiple entries in the line +# table. This test creates a DWARF line table that contains two entries for +# the same line to do the needed testing. + +# This test can only be run on targets which support DWARF-2 and use gas. +require dwarf2_support +load_lib dwarf.exp + +# The DWARF assembler requires the gcc compiler. +require is_c_compiler_gcc + +# This test suitable only for process that can do reverse execution +requires supports_reverse + +standard_testfile .c .S + +if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile}] } { + return -1 +} + +set asm_file [standard_output_file $srcfile2] +Dwarf::assemble $asm_file { + global srcdir subdir srcfile + declare_labels integer_label L + + # Find start address and length of program + lassign [function_range main [list ${srcdir}/${subdir}/$srcfile]] \ + main_start main_len + set main_end "$main_start + $main_len" + + cu {} { + compile_unit { + {language @DW_LANG_C} + {name map-to-same-line.c} + {stmt_list $L DW_FORM_sec_offset} + {low_pc 0 addr} + } { + subprogram { + {external 1 flag} + {name main} + {low_pc $main_start addr} + {high_pc $main_len DW_FORM_data4} + } + } + } + + lines {version 2 default_is_stmt 1} L { + include_dir "${srcdir}/${subdir}" + file_name "$srcfile" 1 + + # Generate the line table program with distinct source lines being + # mapped to the same line entry. Line 1, 5 and 8 contain 1 statement + # each. Line 2 contains 2 statements. Line 3 contains 3 statements. + program { + DW_LNE_set_address $main_start + line [gdb_get_line_number "TAG: main prologue"] + DW_LNS_copy + DW_LNE_set_address line1 + line [gdb_get_line_number "TAG: line 1" ] + DW_LNS_copy + DW_LNE_set_address line2 + line [gdb_get_line_number "TAG: line 2" ] + DW_LNS_copy + DW_LNE_set_address line3 + line [gdb_get_line_number "TAG: line 2" ] + DW_LNS_copy + DW_LNE_set_address line4 + line [gdb_get_line_number "TAG: line 3" ] + DW_LNS_copy + DW_LNE_set_address line5 + line [gdb_get_line_number "TAG: line 3" ] + DW_LNS_copy + DW_LNE_set_address line6 + line [gdb_get_line_number "TAG: line 3" ] + DW_LNS_copy + DW_LNE_set_address line7 + line [gdb_get_line_number "TAG: line 5" ] + DW_LNS_copy + DW_LNE_set_address line8 + line [gdb_get_line_number "TAG: line 8" ] + DW_LNS_copy + DW_LNE_set_address main_return + line [gdb_get_line_number "TAG: main return"] + DW_LNS_copy + DW_LNE_set_address end_of_sequence + DW_LNE_end_sequence + } + } +} + +if { [prepare_for_testing "failed to prepare" ${testfile} \ + [list $srcfile $asm_file] {nodebug} ] } { + return -1 +} + +runto_main + +# Print the line table +gdb_test_multiple "maint info line-table ${testfile}" "" { + -re "\r\n$decimal\[ \t\]+$decimal\[ \t\]+($hex)\[ \t\]+Y\[^\r\n\]*" { + lappend is_stmt $expect_out(1,string) + exp_continue + } + -re -wrap "" { + } +} + +# Do the reverse-step test +gdb_test_no_output "record" "turn on process record" + +set bp_main_return [gdb_get_line_number "TAG: main return" $srcfile] +gdb_breakpoint $srcfile:$bp_main_return +gdb_continue_to_breakpoint "run to end of main, reverse-step test" ".*$srcfile:$bp_main_return.*" +gdb_test "display \$pc" ".*pc =.*" "display pc, reverse-step test" + +# At this point, GDB has already recorded the execution up until the return +# statement. Reverse-step and test if GDB transitions between lines in the +# expected order. It should reverse-step across lines 8, 5, 3, 2 and 1. +foreach line {8 5 3 2 1} { + gdb_test "reverse-step" ".*TAG: line $line.*" "reverse step to line $line" +} + +## Clean restart, test reverse-next command +clean_restart ${testfile} +runto_main +gdb_test_no_output "record" "turn on process record, reverst-next test" + +set bp_main_return [gdb_get_line_number "TAG: main return" $srcfile] +gdb_breakpoint $srcfile:$bp_main_return +gdb_continue_to_breakpoint "run to end of main, reverse-next test" ".*$srcfile:$bp_main_return.*" +gdb_test "display \$pc" ".*pc =.*" "display pc, reverse-next test" + +# At this point, GDB has already recorded the execution up until the return +# statement. Reverse-next and test if GDB transitions between lines in the +# expected order. It should reverse-next across lines 8, 5, 3, 2 and 1. +foreach line {8 5 3 2 1} { + gdb_test "reverse-next" ".*TAG: line $line.*" "reverse next to line $line" +} -- 2.37.2 ^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v3] Fix reverse stepping multiple contiguous PC ranges over the line table. 2023-05-04 15:59 ` [PATCH v3] " Carl Love @ 2023-05-05 14:59 ` Luis Machado 2023-05-05 16:10 ` Carl Love 2023-05-10 13:47 ` Bruno Larsen 1 sibling, 1 reply; 41+ messages in thread From: Luis Machado @ 2023-05-05 14:59 UTC (permalink / raw) To: Carl Love, Bruno Larsen, gdb-patches, UlrichWeigand, pedro On 5/4/23 16:59, Carl Love wrote: > > Bruno, GDB maintainers: f> > Version 3, added the gcc version check as discussed further from > version 2 of the patch. Also updated the tests to check for supporting > reverse execution rather than requiring recording. I also noticed > there were a couple more instances of a requirement check, i.e. if [] > which I changed to "require" per the current style for checking on the > test requirements. > > > The following patch fixes issues on PowerPC with the reverse-step and > reverse-next instructions when there are multiple assignment statements > on the same line and when there are multiple function calls on the same > line. The commit log below discusses these issues in further depth. > The discussion included what the correct operation should be for these > commands based on the GDB documentation. The proposed patch at that > time changed how the commands worked on other platforms such as X86 in > a way they no longer matched the documentation. > > The issue is the line table contains multiple entries for the same > source line. The patch adds a function to search the line table to > find the address of the first instruction of a line. When setup up the > reverse stepping range, the function is called to make sure the start > of the range corresponds to the address of the first instruction for > the line. This approach was used. When Luis initially developed the > patch, he considered merging the contiguous ranges in the line table > when reading the line tables. He decided it was better to work with the > data directly in the line table rather than creating and using a > modified version of the line table. > > The following patch fixes the execution of the reveres-step and > reverse-next commands for both senarios of multiple statements on the > same line for PowerPC and aarch64-linux. Unlike the previous patch, it > does not change the operation of the commands on other platforms, i.e. > X86. The patch adds new test cases for both scenarios to verify they > work correctly. > > The patch has been tested on PowerPC, Intel X86 and aarch64-linux with > no new regression failures. > > Please let me know if the patch is acceptable for mainline. Thanks. > > Carl > > --------------------------------------------------------------- > Fix reverse stepping multiple contiguous PC ranges over the line table. > > There are a couple of scenarios where the GDB reverse-step and reverse-next > commands do not work correctly. > > Scenario 1 issue description by Luis Machado: > > When running GDB's testsuite on aarch64-linux/Ubuntu 20.04 (also spotted on > the ppc backend), I noticed some failures in gdb.reverse/solib-precsave.exp > and gdb.reverse/solib-reverse.exp. > > The failure happens around the following code: > > 38 b[1] = shr2(17); /* middle part two */ > 40 b[0] = 6; b[1] = 9; /* generic statement, end part two */ > 42 shr1 ("message 1\n"); /* shr1 one */ > > Normal execution: > > - step from line 38 will land on line 40. > - step from line 40 will land on line 42. > > Reverse execution: > - step from line 42 will land on line 40. > - step from line 40 will land on line 40. > - step from line 40 will land on line 38. > > The problem here is that line 40 contains two contiguous but distinct > PC ranges in the line table, like so: > > Line 40 - [0x7ec ~ 0x7f4] > Line 40 - [0x7f4 ~ 0x7fc] > > The two distinct ranges are generated because GCC started outputting source > column information, which GDB doesn't take into account at the moment. > > When stepping forward from line 40, we skip both of these ranges and land on > line 42. When stepping backward from line 42, we stop at the start PC of the > second (or first, going backwards) range of line 40. > > Since we've reached ecs->event_thread->control.step_range_start, we stop > stepping backwards. > > --------------------------------------------------------- > > Scenario 2 issue described by Pedro Alves: > > The following explanation of the issue was taken from the gdb mailing list > discussion of the withdrawn patch to change the behavior of the reverse-step > and reverse-next commands. Specifically, message from Pedro Alves > <pedro@palves.net> where he demonstrates the issue where you have multiple > function calls on the same source code line: > > https://sourceware.org/pipermail/gdb-patches/2023-January/196110.html > > The source line looks like: > > func1 (); func2 (); > > so stepping backwards over that line should always stop at the first > instruction of the line, not in the middle. Let's simplify this. > > Here's the full source code of my example: > > (gdb) list 1 > 1 void func1 () > 2 { > 3 } > 4 > 5 void func2 () > 6 { > 7 } > 8 > 9 int main () > 10 { > 11 func1 (); func2 (); > 12 } > > Compiled with: > > $ gcc reverse.c -o reverse -g3 -O0 > $ gcc -v > ... > gcc version 11.3.0 (Ubuntu 11.3.0-1ubuntu1~22.04) > > Now let's debug it with target record, using current gdb git master (f3d8ae90b236), > without your patch: > > $ gdb ~/reverse > GNU gdb (GDB) 14.0.50.20230124-git > ... > Reading symbols from /home/pedro/reverse... > (gdb) start > Temporary breakpoint 1 at 0x1147: file reverse.c, line 11. > Starting program: /home/pedro/reverse > [Thread debugging using libthread_db enabled] > Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". > > Temporary breakpoint 1, main () at reverse.c:11 > 11 func1 (); func2 (); > (gdb) record > > (gdb) disassemble /s > Dump of assembler code for function main: > reverse.c: > 10 { > 0x000055555555513f <+0>: endbr64 > 0x0000555555555143 <+4>: push %rbp > 0x0000555555555144 <+5>: mov %rsp,%rbp > > 11 func1 (); func2 (); > => 0x0000555555555147 <+8>: mov $0x0,%eax > 0x000055555555514c <+13>: call 0x555555555129 <func1> > 0x0000555555555151 <+18>: mov $0x0,%eax > 0x0000555555555156 <+23>: call 0x555555555134 <func2> > 0x000055555555515b <+28>: mov $0x0,%eax > > 12 } > 0x0000555555555160 <+33>: pop %rbp > 0x0000555555555161 <+34>: ret > End of assembler dump. > > (gdb) n > 12 } > > So far so good, a "next" stepped over the whole of line 11 and stopped at line 12. > > Let's confirm where we are now: > > (gdb) disassemble /s > Dump of assembler code for function main: > reverse.c: > 10 { > 0x000055555555513f <+0>: endbr64 > 0x0000555555555143 <+4>: push %rbp > 0x0000555555555144 <+5>: mov %rsp,%rbp > > 11 func1 (); func2 (); > 0x0000555555555147 <+8>: mov $0x0,%eax > 0x000055555555514c <+13>: call 0x555555555129 <func1> > 0x0000555555555151 <+18>: mov $0x0,%eax > 0x0000555555555156 <+23>: call 0x555555555134 <func2> > 0x000055555555515b <+28>: mov $0x0,%eax > > 12 } > => 0x0000555555555160 <+33>: pop %rbp > 0x0000555555555161 <+34>: ret > End of assembler dump. > > Good, we're at the first instruction of line 12. > > Now let's undo the "next", with "reverse-next": > > (gdb) reverse-next > 11 func1 (); func2 (); > > Seemingly stopped at line 11. Let's see exactly where: > > (gdb) disassemble /s > Dump of assembler code for function main: > reverse.c: > 10 { > 0x000055555555513f <+0>: endbr64 > 0x0000555555555143 <+4>: push %rbp > 0x0000555555555144 <+5>: mov %rsp,%rbp > > 11 func1 (); func2 (); > 0x0000555555555147 <+8>: mov $0x0,%eax > 0x000055555555514c <+13>: call 0x555555555129 <func1> > => 0x0000555555555151 <+18>: mov $0x0,%eax > 0x0000555555555156 <+23>: call 0x555555555134 <func2> > 0x000055555555515b <+28>: mov $0x0,%eax > > 12 } > 0x0000555555555160 <+33>: pop %rbp > 0x0000555555555161 <+34>: ret > End of assembler dump. > (gdb) > > And lo, we stopped in the middle of line 11! That is a bug, we should have > stepped back all the way to the beginning of the line. The "reverse-next" > should have fully undone the prior "next" command. > > The above issues were fixed by introducing a new function that looks for > adjacent PC ranges for the same line, until we notice a line change. Then > we take that as the start PC of the range. The new start PC for the range > is used for the control.step_range_start when setting up a step range. > > The test case gdb.reverse/map-to-same-line.exp is added to test the fix > for the issues in scenario 1. > > The test case gdb.reverse/func-map-to-same-line.exp was added to test the > fix for scenario 2 when the binary was compiled with and without line > table information. > > bug: https://sourceware.org/bugzilla/show_bug.cgi?id=28426 > > Co-authored-by: Luis Machado <luis.machado@arm.com> > Co-authored-by: Carl Love <cel@us.ibm.com> > --- > gdb/infrun.c | 57 +++++++ > gdb/symtab.c | 49 ++++++ > gdb/symtab.h | 16 ++ > .../gdb.reverse/func-map-to-same-line.c | 36 ++++ > .../gdb.reverse/func-map-to-same-line.exp | 146 ++++++++++++++++ > gdb/testsuite/gdb.reverse/map-to-same-line.c | 58 +++++++ > .../gdb.reverse/map-to-same-line.exp | 156 ++++++++++++++++++ > 7 files changed, 518 insertions(+) > create mode 100644 gdb/testsuite/gdb.reverse/func-map-to-same-line.c > create mode 100644 gdb/testsuite/gdb.reverse/func-map-to-same-line.exp > create mode 100644 gdb/testsuite/gdb.reverse/map-to-same-line.c > create mode 100644 gdb/testsuite/gdb.reverse/map-to-same-line.exp > > diff --git a/gdb/infrun.c b/gdb/infrun.c > index efe2c00c489..8555e3c979f 100644 > --- a/gdb/infrun.c > +++ b/gdb/infrun.c > @@ -114,6 +114,9 @@ static struct async_event_handler *infrun_async_inferior_event_token; > Starts off as -1, indicating "never enabled/disabled". */ > static int infrun_is_async = -1; > > +static CORE_ADDR update_line_range_start (CORE_ADDR pc, > + struct execution_control_state *ecs); > + > /* See infrun.h. */ > > void > @@ -6769,6 +6772,25 @@ handle_signal_stop (struct execution_control_state *ecs) > process_event_stop_test (ecs); > } > > +CORE_ADDR > +update_line_range_start (CORE_ADDR pc, struct execution_control_state *ecs) > +{ > + /* The line table may have multiple entries for the same source code line. > + Given the PC, check the line table and return the PC that corresponds > + to the line table entry for the source line that PC is in. */ > + CORE_ADDR start_line_pc = ecs->event_thread->control.step_range_start; > + gdb::optional<CORE_ADDR> real_range_start; > + > + /* Call find_line_range_start to get smallest address in the s/smallest/the smallest > + linetable for multiple Line X entries in the line table. */ > + real_range_start = find_line_range_start (pc); > + > + if (real_range_start.has_value ()) > + start_line_pc = *real_range_start; > + > + return start_line_pc; > +} > + > /* Come here when we've got some debug event / signal we can explain > (IOW, not a random signal), and test whether it should cause a > stop, or whether we should resume the inferior (transparently). > @@ -7570,6 +7592,28 @@ process_event_stop_test (struct execution_control_state *ecs) > > if (stop_pc_sal.is_stmt) > { > + if (execution_direction == EXEC_REVERSE) > + { > + /* We are stepping backwards make sure we have reached the > + beginning of the line. */ > + CORE_ADDR stop_pc = ecs->event_thread->stop_pc (); > + CORE_ADDR start_line_pc > + = update_line_range_start (stop_pc, ecs); > + > + if (stop_pc != start_line_pc) > + { > + /* Have not reached the beginning of the source code line. > + Set a step range. Execution should stop in any function > + calls we execute back into before reaching the beginning > + of the line. */ > + ecs->event_thread->control.step_range_start = start_line_pc; > + ecs->event_thread->control.step_range_end = stop_pc; > + set_step_info (ecs->event_thread, frame, stop_pc_sal); > + keep_going (ecs); > + return; > + } > + } > + > /* We are at the start of a statement. > > So stop. Note that we don't stop if we step into the middle of a > @@ -7632,6 +7676,19 @@ process_event_stop_test (struct execution_control_state *ecs) > set_step_info (ecs->event_thread, frame, stop_pc_sal); > > infrun_debug_printf ("keep going"); > + > + if (execution_direction == EXEC_REVERSE) > + { > + CORE_ADDR stop_pc = ecs->event_thread->stop_pc (); > + > + /* Make sure the stop_pc is set to the beginning of the line. */ > + if (stop_pc != ecs->event_thread->control.step_range_start) > + { > + stop_pc = update_line_range_start (stop_pc, ecs); > + ecs->event_thread->control.step_range_start = stop_pc; > + } > + } > + > keep_going (ecs); > } > > diff --git a/gdb/symtab.c b/gdb/symtab.c > index 27611a34ec4..91d35616eb9 100644 > --- a/gdb/symtab.c > +++ b/gdb/symtab.c > @@ -3282,6 +3282,55 @@ find_pc_line (CORE_ADDR pc, int notcurrent) > return sal; > } > > +/* Compare two symtab_and_line entries. Return true if both have > + the same line number and the same symtab pointer. That means we > + are dealing with two entries from the same line and from the same > + source file. > + > + Return false otherwise. */ > + > +static bool > +sal_line_symtab_matches_p (const symtab_and_line &sal1, > + const symtab_and_line &sal2) > +{ > + return (sal1.line == sal2.line && sal1.symtab == sal2.symtab); > +} > + > +/* See symtah.h. */ > + > +gdb::optional<CORE_ADDR> > +find_line_range_start (CORE_ADDR pc) > +{ > + struct symtab_and_line current_sal = find_pc_line (pc, 0); > + > + if (current_sal.line == 0) > + return {}; > + > + struct symtab_and_line prev_sal = find_pc_line (current_sal.pc - 1, 0); > + > + /* If the previous entry is for a different line, that means we are already > + at the entry with the start PC for this line. */ > + if (!sal_line_symtab_matches_p (prev_sal, current_sal)) > + return current_sal.pc; > + > + /* Otherwise, keep looking for entries for the same line but with > + smaller PC's. */ > + bool done = false; > + CORE_ADDR prev_pc; > + while (!done) > + { > + prev_pc = prev_sal.pc; > + > + prev_sal = find_pc_line (prev_pc - 1, 0); > + > + /* Did we notice a line change? If so, we are done with the search. */ > + if (!sal_line_symtab_matches_p (prev_sal, current_sal)) > + done = true; > + } > + > + return prev_pc; > +} > + > /* See symtab.h. */ > > struct symtab * > diff --git a/gdb/symtab.h b/gdb/symtab.h > index 404d0ab30a8..f54305636da 100644 > --- a/gdb/symtab.h > +++ b/gdb/symtab.h > @@ -2346,6 +2346,22 @@ extern struct symtab_and_line find_pc_line (CORE_ADDR, int); > extern struct symtab_and_line find_pc_sect_line (CORE_ADDR, > struct obj_section *, int); > > +/* Given PC, and assuming it is part of a range of addresses that is part of a > + line, go back through the linetable and find the starting PC of that > + line. > + > + For example, suppose we have 3 PC ranges for line X: > + > + Line X - [0x0 - 0x8] > + Line X - [0x8 - 0x10] > + Line X - [0x10 - 0x18] > + > + If we call the function with PC == 0x14, we want to return 0x0, as that is > + the starting PC of line X, and the ranges are contiguous. > +*/ > + > +extern gdb::optional<CORE_ADDR> find_line_range_start (CORE_ADDR pc); > + > /* Wrapper around find_pc_line to just return the symtab. */ > > extern struct symtab *find_pc_line_symtab (CORE_ADDR); > diff --git a/gdb/testsuite/gdb.reverse/func-map-to-same-line.c b/gdb/testsuite/gdb.reverse/func-map-to-same-line.c > new file mode 100644 > index 00000000000..412ab180943 > --- /dev/null > +++ b/gdb/testsuite/gdb.reverse/func-map-to-same-line.c > @@ -0,0 +1,36 @@ > +/* Copyright 2008-2023 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 <http://www.gnu.org/licenses/>. > + > + This test is used to test the reverse-step and reverse-next instruction > + execution for a source line that contains multiple function calls. */ > + > +void > +func1 () > +{ > +} // END FUNC1 > + > +void > +func2 () > +{ > +} // END FUNC2 > + > +int main () > +{ > + int a, b; > + a = 1; > + b = 2; > + func1 (); func2 (); > + a = a + b; // START REVERSE TEST > +} > diff --git a/gdb/testsuite/gdb.reverse/func-map-to-same-line.exp b/gdb/testsuite/gdb.reverse/func-map-to-same-line.exp > new file mode 100644 > index 00000000000..4eae042a6bf > --- /dev/null > +++ b/gdb/testsuite/gdb.reverse/func-map-to-same-line.exp > @@ -0,0 +1,146 @@ > +# Copyright 2008-2023 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 <http://www.gnu.org/licenses/>. */ > + > +# This file is part of the GDB testsuite. It tests reverse stepping. > +# Lots of code borrowed from "step-test.exp". > + > +# This test checks to make sure there is no regression failures for > +# the reverse-next command when stepping back over two functions in > +# the same line. > + > +require supports_reverse > + > +# This test uses the gcc no-column-info command which was added in gcc 7.1. > +require get_compiler_info "gcc-7-*" > + > +proc run_tests {msg} { > + global srcfile > + global executable > + > + runto_main > + set target_remote [gdb_is_target_remote] > + > + gdb_test_no_output "record" "turn on process record" > + > + # This regression test verifies the reverse-step and reverse-next commands > + # work properly when executing backwards thru a source line containing > + # two function calls on the same source line, i.e. func1 (); func2 (); > + # This test is compiled so the dwarf info not contain the line table > + # information. > + > + # Test 1, reverse-next command > + # Set breakpoint at the line after the function calls. > + set bp_start_reverse_test [gdb_get_line_number "START REVERSE TEST" \ > + $srcfile] > + gdb_breakpoint $srcfile:$bp_start_reverse_test temporary > + > + # Continue to break point for reverse-next test. > + # Command definition: reverse-next [count] > + # Run backward to the beginning of the previous line executed in the > + # current (innermost) stack frame. If the line contains function calls, > + # they will be “un-executed” without stopping. Starting from the first > + # line of a function, reverse-next will take you back to the caller of > + # that function, before the function was called, just as the normal next > + # command would take you from the last line of a function back to its > + # return to its caller 2 . > + gdb_continue_to_breakpoint \ > + "$msg: test1: stopped at command reverse-next test start location" \ > + ".*$srcfile:$bp_start_reverse_test\r\n.*" > + > + # The reverse-next should step all the way back to the beginning of the > + # line, i.e. at the beginning of the func1 call. > + gdb_test "reverse-next" ".*func1 \\(\\); func2 \\(\\);.*" \ > + "$msg: test1: reverse-next to line with two functions" > + > + # We should be stopped at the first instruction of the line. A reverse-step > + # should step back and stop at the beginning of the previous line b = 2, > + # i.e. not in func1 (). > + gdb_test "reverse-stepi" ".*b = 2;.*" \ > + "$msg: test1: reverse-stepi to previous line b = 2" > + > + > + # Setup for test 2 > + clean_restart $executable > + runto_main > + > + gdb_test_no_output "record" "turn on process record" > + > + # Test 2, reverse-step command > + # Set breakpoint at the line after the function calls. > + gdb_breakpoint $srcfile:$bp_start_reverse_test temporary > + > + # Continue to the start of the reverse-step test. > + # Command definition: reverse-step [count] > + # Run the program backward until control reaches the start of a > + # different source line; then stop it, and return control to gdb. > + # Like the step command, reverse-step will only stop at the beginning > + # of a source line. It “un-executes” the previously executed source > + # line. If the previous source line included calls to debuggable > + # functions, reverse-step will step (backward) into the called function, > + # stopping at the beginning of the last statement in the called > + # function (typically a return statement). Also, as with the step > + # command, if non-debuggable functions are called, reverse-step will > + # run thru them backward without stopping. > + > + gdb_continue_to_breakpoint \ > + "$msg: test2: stopped at command reverse-step test start location" \ > + ".*$srcfile:$bp_start_reverse_test\r\n.*" > + > + # The first reverse step should take us call of func2 (). > + gdb_test "reverse-step" ".*END FUNC2.*" \ > + "$msg: test2: reverse-step into func2 " > + > + # The second reverse step should take us into func1 (). > + gdb_test "reverse-step" ".*END FUNC1.*" \ > + "$msg: test2: reverse-step into func1 " > + > + # The third reverse step should take us call of func1 (). > + gdb_test "reverse-step" ".*func1 \\(\\); func2 \\(\\);.*" \ > + "$msg: test2: reverse-step to line func1(); func2(), at call for func1 " > + > + # We should be stopped at the first instruction of the line. A reverse > + # stepi should take us to b = 2 (). > + gdb_test "reverse-stepi" ".*b = 2;.*" \ > + "$msg: test2: reverse-stepi to line b = 2 " > +} > + > +set srcfile func-map-to-same-line.c > +set executable func-map-to-same-line > + > +# test with gcc column info enabled > +set options [list debug additional_flags=] > + > +if {[build_executable "failed to prepare" $executable $srcfile $options] == -1}\ > + { > + return -1 > +} > + > +clean_restart $executable > + > +run_tests {"with-column-info"} > + > + > +#test with gcc column info disabled > +set options [list debug additional_flags=-gno-column-info] > + > +if {[build_executable "failed to prepare" $executable $srcfile $options] == -1}\ > + { > + return -1 > +} > + > +set $executable executable_without_column_info > +clean_restart $executable > + > +run_tests {"no-column-info"} > diff --git a/gdb/testsuite/gdb.reverse/map-to-same-line.c b/gdb/testsuite/gdb.reverse/map-to-same-line.c > new file mode 100644 > index 00000000000..f20d778f40e > --- /dev/null > +++ b/gdb/testsuite/gdb.reverse/map-to-same-line.c > @@ -0,0 +1,58 @@ > +/* Copyright 2008-2023 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 <http://www.gnu.org/licenses/ >. */ > + > +/* The purpose of this test is to create a DWARF line table that contains two > + or more entries for the same line. When stepping (forwards or backwards), > + GDB should step over the entire line and not just a particular entry in the > + line table. */ > + > +int > +main () > +{ /* TAG: main prologue */ > + asm ("main_label: .globl main_label"); > + int i = 1, j = 2, k; > + float f1 = 2.0, f2 = 4.1, f3; > + const char *str_1 = "foo", *str_2 = "bar", *str_3; > + > + asm ("line1: .globl line1"); > + k = i; f3 = f1; str_3 = str_1; /* TAG: line 1 */ > + > + asm ("line2: .globl line2"); > + k = j; f3 = f2; str_3 = str_2; /* TAG: line 2 */ > + > + asm ("line3: .globl line3"); > + k = i; f3 = f1; str_3 = str_1; /* TAG: line 3 */ > + > + asm ("line4: .globl line4"); > + k = j; f3 = f2; str_3 = str_2; /* TAG: line 4 */ > + > + asm ("line5: .globl line5"); > + k = i; f3 = f1; str_3 = str_1; /* TAG: line 5 */ > + > + asm ("line6: .globl line6"); > + k = j; f3 = f2; str_3 = str_2; /* TAG: line 6 */ > + > + asm ("line7: .globl line7"); > + k = i; f3 = f1; str_3 = str_1; /* TAG: line 7 */ > + > + asm ("line8: .globl line8"); > + k = j; f3 = f2; str_3 = str_2; /* TAG: line 8 */ > + > + asm ("main_return: .globl main_return"); > + k = j; f3 = f2; str_3 = str_2; /* TAG: main return */ > + > + asm ("end_of_sequence: .globl end_of_sequence"); > + return 0; /* TAG: main return */ > +} > diff --git a/gdb/testsuite/gdb.reverse/map-to-same-line.exp b/gdb/testsuite/gdb.reverse/map-to-same-line.exp > new file mode 100644 > index 00000000000..02f3f4d8c9c > --- /dev/null > +++ b/gdb/testsuite/gdb.reverse/map-to-same-line.exp > @@ -0,0 +1,156 @@ > +# Copyright 2008-2023 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 <http://www.gnu.org/licenses/ >. > + > +# When stepping (forwards or backwards), GDB should step over the entire line > +# and not just a particular entry in the line table. This test was added to > +# verify the find_line_range_start function properly sets the step range for a > +# line that consists of multiple statements, i.e. multiple entries in the line > +# table. This test creates a DWARF line table that contains two entries for > +# the same line to do the needed testing. > + > +# This test can only be run on targets which support DWARF-2 and use gas. > +require dwarf2_support > +load_lib dwarf.exp > + > +# The DWARF assembler requires the gcc compiler. > +require is_c_compiler_gcc > + > +# This test suitable only for process that can do reverse execution > +requires supports_reverse > + > +standard_testfile .c .S > + > +if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile}] } { > + return -1 > +} > + > +set asm_file [standard_output_file $srcfile2] > +Dwarf::assemble $asm_file { > + global srcdir subdir srcfile > + declare_labels integer_label L > + > + # Find start address and length of program > + lassign [function_range main [list ${srcdir}/${subdir}/$srcfile]] \ > + main_start main_len > + set main_end "$main_start + $main_len" > + > + cu {} { > + compile_unit { > + {language @DW_LANG_C} > + {name map-to-same-line.c} > + {stmt_list $L DW_FORM_sec_offset} > + {low_pc 0 addr} > + } { > + subprogram { > + {external 1 flag} > + {name main} > + {low_pc $main_start addr} > + {high_pc $main_len DW_FORM_data4} > + } > + } > + } > + > + lines {version 2 default_is_stmt 1} L { > + include_dir "${srcdir}/${subdir}" > + file_name "$srcfile" 1 > + > + # Generate the line table program with distinct source lines being > + # mapped to the same line entry. Line 1, 5 and 8 contain 1 statement > + # each. Line 2 contains 2 statements. Line 3 contains 3 statements. > + program { > + DW_LNE_set_address $main_start > + line [gdb_get_line_number "TAG: main prologue"] > + DW_LNS_copy > + DW_LNE_set_address line1 > + line [gdb_get_line_number "TAG: line 1" ] > + DW_LNS_copy > + DW_LNE_set_address line2 > + line [gdb_get_line_number "TAG: line 2" ] > + DW_LNS_copy > + DW_LNE_set_address line3 > + line [gdb_get_line_number "TAG: line 2" ] > + DW_LNS_copy > + DW_LNE_set_address line4 > + line [gdb_get_line_number "TAG: line 3" ] > + DW_LNS_copy > + DW_LNE_set_address line5 > + line [gdb_get_line_number "TAG: line 3" ] > + DW_LNS_copy > + DW_LNE_set_address line6 > + line [gdb_get_line_number "TAG: line 3" ] > + DW_LNS_copy > + DW_LNE_set_address line7 > + line [gdb_get_line_number "TAG: line 5" ] > + DW_LNS_copy > + DW_LNE_set_address line8 > + line [gdb_get_line_number "TAG: line 8" ] > + DW_LNS_copy > + DW_LNE_set_address main_return > + line [gdb_get_line_number "TAG: main return"] > + DW_LNS_copy > + DW_LNE_set_address end_of_sequence > + DW_LNE_end_sequence > + } > + } > +} > + > +if { [prepare_for_testing "failed to prepare" ${testfile} \ > + [list $srcfile $asm_file] {nodebug} ] } { > + return -1 > +} > + > +runto_main > + > +# Print the line table > +gdb_test_multiple "maint info line-table ${testfile}" "" { > + -re "\r\n$decimal\[ \t\]+$decimal\[ \t\]+($hex)\[ \t\]+Y\[^\r\n\]*" { > + lappend is_stmt $expect_out(1,string) > + exp_continue > + } > + -re -wrap "" { > + } > +} > + > +# Do the reverse-step test > +gdb_test_no_output "record" "turn on process record" > + > +set bp_main_return [gdb_get_line_number "TAG: main return" $srcfile] > +gdb_breakpoint $srcfile:$bp_main_return > +gdb_continue_to_breakpoint "run to end of main, reverse-step test" ".*$srcfile:$bp_main_return.*" > +gdb_test "display \$pc" ".*pc =.*" "display pc, reverse-step test" > + > +# At this point, GDB has already recorded the execution up until the return > +# statement. Reverse-step and test if GDB transitions between lines in the > +# expected order. It should reverse-step across lines 8, 5, 3, 2 and 1. > +foreach line {8 5 3 2 1} { > + gdb_test "reverse-step" ".*TAG: line $line.*" "reverse step to line $line" > +} > + > +## Clean restart, test reverse-next command > +clean_restart ${testfile} > +runto_main > +gdb_test_no_output "record" "turn on process record, reverst-next test" > + > +set bp_main_return [gdb_get_line_number "TAG: main return" $srcfile] > +gdb_breakpoint $srcfile:$bp_main_return > +gdb_continue_to_breakpoint "run to end of main, reverse-next test" ".*$srcfile:$bp_main_return.*" > +gdb_test "display \$pc" ".*pc =.*" "display pc, reverse-next test" > + > +# At this point, GDB has already recorded the execution up until the return > +# statement. Reverse-next and test if GDB transitions between lines in the > +# expected order. It should reverse-next across lines 8, 5, 3, 2 and 1. > +foreach line {8 5 3 2 1} { > + gdb_test "reverse-next" ".*TAG: line $line.*" "reverse next to line $line" > +} Other than the nit, this LGTM. Thanks for picking this one up and improving it to fix other issues. Reviewed-by: Luis Machado <luis.machado@arm.com> ^ permalink raw reply [flat|nested] 41+ messages in thread
* RE: [PATCH v3] Fix reverse stepping multiple contiguous PC ranges over the line table. 2023-05-05 14:59 ` Luis Machado @ 2023-05-05 16:10 ` Carl Love 0 siblings, 0 replies; 41+ messages in thread From: Carl Love @ 2023-05-05 16:10 UTC (permalink / raw) To: Luis Machado, Bruno Larsen, gdb-patches, UlrichWeigand, pedro; +Cc: cel Luis: On Fri, 2023-05-05 at 15:59 +0100, Luis Machado wrote: > > + /* Call find_line_range_start to get smallest address in the > > s/smallest/the smallest > > > + linetable for multiple Line X entries in the line table. */ > > + real_range_start = find_line_range_start (pc); > > + > > + if (real_range_start.has_value ()) <snip> > Other than the nit, this LGTM. Thanks for picking this one up and > improving it to fix other issues. > > Reviewed-by: Luis Machado <luis.machado@arm.com> Thanks for the review. I updated the patch with the fix. Probably not worth resending patch but will make sure it does get included in any future posts or commits. Carl ^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v3] Fix reverse stepping multiple contiguous PC ranges over the line table. 2023-05-04 15:59 ` [PATCH v3] " Carl Love 2023-05-05 14:59 ` Luis Machado @ 2023-05-10 13:47 ` Bruno Larsen 2023-05-10 17:16 ` Carl Love 1 sibling, 1 reply; 41+ messages in thread From: Bruno Larsen @ 2023-05-10 13:47 UTC (permalink / raw) To: Carl Love, gdb-patches, UlrichWeigand, pedro; +Cc: luis.machado On 04/05/2023 17:59, Carl Love wrote: > Bruno, GDB maintainers: > > Version 3, added the gcc version check as discussed further from > version 2 of the patch. Also updated the tests to check for supporting > reverse execution rather than requiring recording. I also noticed > there were a couple more instances of a requirement check, i.e. if [] > which I changed to "require" per the current style for checking on the > test requirements. > > > The following patch fixes issues on PowerPC with the reverse-step and > reverse-next instructions when there are multiple assignment statements > on the same line and when there are multiple function calls on the same > line. The commit log below discusses these issues in further depth. > The discussion included what the correct operation should be for these > commands based on the GDB documentation. The proposed patch at that > time changed how the commands worked on other platforms such as X86 in > a way they no longer matched the documentation. > > The issue is the line table contains multiple entries for the same > source line. The patch adds a function to search the line table to > find the address of the first instruction of a line. When setup up the > reverse stepping range, the function is called to make sure the start > of the range corresponds to the address of the first instruction for > the line. This approach was used. When Luis initially developed the > patch, he considered merging the contiguous ranges in the line table > when reading the line tables. He decided it was better to work with the > data directly in the line table rather than creating and using a > modified version of the line table. > > The following patch fixes the execution of the reveres-step and > reverse-next commands for both senarios of multiple statements on the > same line for PowerPC and aarch64-linux. Unlike the previous patch, it > does not change the operation of the commands on other platforms, i.e. > X86. The patch adds new test cases for both scenarios to verify they > work correctly. > > The patch has been tested on PowerPC, Intel X86 and aarch64-linux with > no new regression failures. > > Please let me know if the patch is acceptable for mainline. Thanks. > > Carl > > --------------------------------------------------------------- > Fix reverse stepping multiple contiguous PC ranges over the line table. > > There are a couple of scenarios where the GDB reverse-step and reverse-next > commands do not work correctly. > > Scenario 1 issue description by Luis Machado: > > When running GDB's testsuite on aarch64-linux/Ubuntu 20.04 (also spotted on > the ppc backend), I noticed some failures in gdb.reverse/solib-precsave.exp > and gdb.reverse/solib-reverse.exp. > > The failure happens around the following code: > > 38 b[1] = shr2(17); /* middle part two */ > 40 b[0] = 6; b[1] = 9; /* generic statement, end part two */ > 42 shr1 ("message 1\n"); /* shr1 one */ > > Normal execution: > > - step from line 38 will land on line 40. > - step from line 40 will land on line 42. > > Reverse execution: > - step from line 42 will land on line 40. > - step from line 40 will land on line 40. > - step from line 40 will land on line 38. > > The problem here is that line 40 contains two contiguous but distinct > PC ranges in the line table, like so: > > Line 40 - [0x7ec ~ 0x7f4] > Line 40 - [0x7f4 ~ 0x7fc] > > The two distinct ranges are generated because GCC started outputting source > column information, which GDB doesn't take into account at the moment. > > When stepping forward from line 40, we skip both of these ranges and land on > line 42. When stepping backward from line 42, we stop at the start PC of the > second (or first, going backwards) range of line 40. > > Since we've reached ecs->event_thread->control.step_range_start, we stop > stepping backwards. > > --------------------------------------------------------- > > Scenario 2 issue described by Pedro Alves: > > The following explanation of the issue was taken from the gdb mailing list > discussion of the withdrawn patch to change the behavior of the reverse-step > and reverse-next commands. Specifically, message from Pedro Alves > <pedro@palves.net> where he demonstrates the issue where you have multiple > function calls on the same source code line: > > https://sourceware.org/pipermail/gdb-patches/2023-January/196110.html > > The source line looks like: > > func1 (); func2 (); > > so stepping backwards over that line should always stop at the first > instruction of the line, not in the middle. Let's simplify this. > > Here's the full source code of my example: > > (gdb) list 1 > 1 void func1 () > 2 { > 3 } > 4 > 5 void func2 () > 6 { > 7 } > 8 > 9 int main () > 10 { > 11 func1 (); func2 (); > 12 } > > Compiled with: > > $ gcc reverse.c -o reverse -g3 -O0 > $ gcc -v > ... > gcc version 11.3.0 (Ubuntu 11.3.0-1ubuntu1~22.04) > > Now let's debug it with target record, using current gdb git master (f3d8ae90b236), > without your patch: > > $ gdb ~/reverse > GNU gdb (GDB) 14.0.50.20230124-git > ... > Reading symbols from /home/pedro/reverse... > (gdb) start > Temporary breakpoint 1 at 0x1147: file reverse.c, line 11. > Starting program: /home/pedro/reverse > [Thread debugging using libthread_db enabled] > Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". > > Temporary breakpoint 1, main () at reverse.c:11 > 11 func1 (); func2 (); > (gdb) record > > (gdb) disassemble /s > Dump of assembler code for function main: > reverse.c: > 10 { > 0x000055555555513f <+0>: endbr64 > 0x0000555555555143 <+4>: push %rbp > 0x0000555555555144 <+5>: mov %rsp,%rbp > > 11 func1 (); func2 (); > => 0x0000555555555147 <+8>: mov $0x0,%eax > 0x000055555555514c <+13>: call 0x555555555129 <func1> > 0x0000555555555151 <+18>: mov $0x0,%eax > 0x0000555555555156 <+23>: call 0x555555555134 <func2> > 0x000055555555515b <+28>: mov $0x0,%eax > > 12 } > 0x0000555555555160 <+33>: pop %rbp > 0x0000555555555161 <+34>: ret > End of assembler dump. > > (gdb) n > 12 } > > So far so good, a "next" stepped over the whole of line 11 and stopped at line 12. > > Let's confirm where we are now: > > (gdb) disassemble /s > Dump of assembler code for function main: > reverse.c: > 10 { > 0x000055555555513f <+0>: endbr64 > 0x0000555555555143 <+4>: push %rbp > 0x0000555555555144 <+5>: mov %rsp,%rbp > > 11 func1 (); func2 (); > 0x0000555555555147 <+8>: mov $0x0,%eax > 0x000055555555514c <+13>: call 0x555555555129 <func1> > 0x0000555555555151 <+18>: mov $0x0,%eax > 0x0000555555555156 <+23>: call 0x555555555134 <func2> > 0x000055555555515b <+28>: mov $0x0,%eax > > 12 } > => 0x0000555555555160 <+33>: pop %rbp > 0x0000555555555161 <+34>: ret > End of assembler dump. > > Good, we're at the first instruction of line 12. > > Now let's undo the "next", with "reverse-next": > > (gdb) reverse-next > 11 func1 (); func2 (); > > Seemingly stopped at line 11. Let's see exactly where: > > (gdb) disassemble /s > Dump of assembler code for function main: > reverse.c: > 10 { > 0x000055555555513f <+0>: endbr64 > 0x0000555555555143 <+4>: push %rbp > 0x0000555555555144 <+5>: mov %rsp,%rbp > > 11 func1 (); func2 (); > 0x0000555555555147 <+8>: mov $0x0,%eax > 0x000055555555514c <+13>: call 0x555555555129 <func1> > => 0x0000555555555151 <+18>: mov $0x0,%eax > 0x0000555555555156 <+23>: call 0x555555555134 <func2> > 0x000055555555515b <+28>: mov $0x0,%eax > > 12 } > 0x0000555555555160 <+33>: pop %rbp > 0x0000555555555161 <+34>: ret > End of assembler dump. > (gdb) > > And lo, we stopped in the middle of line 11! That is a bug, we should have > stepped back all the way to the beginning of the line. The "reverse-next" > should have fully undone the prior "next" command. > > The above issues were fixed by introducing a new function that looks for > adjacent PC ranges for the same line, until we notice a line change. Then > we take that as the start PC of the range. The new start PC for the range > is used for the control.step_range_start when setting up a step range. > > The test case gdb.reverse/map-to-same-line.exp is added to test the fix > for the issues in scenario 1. > > The test case gdb.reverse/func-map-to-same-line.exp was added to test the > fix for scenario 2 when the binary was compiled with and without line > table information. > > bug: https://sourceware.org/bugzilla/show_bug.cgi?id=28426 > > Co-authored-by: Luis Machado <luis.machado@arm.com> > Co-authored-by: Carl Love <cel@us.ibm.com> > --- > gdb/infrun.c | 57 +++++++ > gdb/symtab.c | 49 ++++++ > gdb/symtab.h | 16 ++ > .../gdb.reverse/func-map-to-same-line.c | 36 ++++ > .../gdb.reverse/func-map-to-same-line.exp | 146 ++++++++++++++++ > gdb/testsuite/gdb.reverse/map-to-same-line.c | 58 +++++++ > .../gdb.reverse/map-to-same-line.exp | 156 ++++++++++++++++++ > 7 files changed, 518 insertions(+) > create mode 100644 gdb/testsuite/gdb.reverse/func-map-to-same-line.c > create mode 100644 gdb/testsuite/gdb.reverse/func-map-to-same-line.exp > create mode 100644 gdb/testsuite/gdb.reverse/map-to-same-line.c > create mode 100644 gdb/testsuite/gdb.reverse/map-to-same-line.exp > > diff --git a/gdb/infrun.c b/gdb/infrun.c > index efe2c00c489..8555e3c979f 100644 > --- a/gdb/infrun.c > +++ b/gdb/infrun.c > @@ -114,6 +114,9 @@ static struct async_event_handler *infrun_async_inferior_event_token; > Starts off as -1, indicating "never enabled/disabled". */ > static int infrun_is_async = -1; > > +static CORE_ADDR update_line_range_start (CORE_ADDR pc, > + struct execution_control_state *ecs); > + > /* See infrun.h. */ > > void > @@ -6769,6 +6772,25 @@ handle_signal_stop (struct execution_control_state *ecs) > process_event_stop_test (ecs); > } > > +CORE_ADDR > +update_line_range_start (CORE_ADDR pc, struct execution_control_state *ecs) > +{ > + /* The line table may have multiple entries for the same source code line. > + Given the PC, check the line table and return the PC that corresponds > + to the line table entry for the source line that PC is in. */ > + CORE_ADDR start_line_pc = ecs->event_thread->control.step_range_start; > + gdb::optional<CORE_ADDR> real_range_start; > + > + /* Call find_line_range_start to get smallest address in the > + linetable for multiple Line X entries in the line table. */ > + real_range_start = find_line_range_start (pc); > + > + if (real_range_start.has_value ()) > + start_line_pc = *real_range_start; > + > + return start_line_pc; > +} > + > /* Come here when we've got some debug event / signal we can explain > (IOW, not a random signal), and test whether it should cause a > stop, or whether we should resume the inferior (transparently). > @@ -7570,6 +7592,28 @@ process_event_stop_test (struct execution_control_state *ecs) > > if (stop_pc_sal.is_stmt) > { > + if (execution_direction == EXEC_REVERSE) > + { > + /* We are stepping backwards make sure we have reached the > + beginning of the line. */ > + CORE_ADDR stop_pc = ecs->event_thread->stop_pc (); > + CORE_ADDR start_line_pc > + = update_line_range_start (stop_pc, ecs); > + > + if (stop_pc != start_line_pc) > + { > + /* Have not reached the beginning of the source code line. > + Set a step range. Execution should stop in any function > + calls we execute back into before reaching the beginning > + of the line. */ > + ecs->event_thread->control.step_range_start = start_line_pc; > + ecs->event_thread->control.step_range_end = stop_pc; > + set_step_info (ecs->event_thread, frame, stop_pc_sal); > + keep_going (ecs); > + return; > + } > + } > + > /* We are at the start of a statement. > > So stop. Note that we don't stop if we step into the middle of a > @@ -7632,6 +7676,19 @@ process_event_stop_test (struct execution_control_state *ecs) > set_step_info (ecs->event_thread, frame, stop_pc_sal); > > infrun_debug_printf ("keep going"); > + > + if (execution_direction == EXEC_REVERSE) > + { > + CORE_ADDR stop_pc = ecs->event_thread->stop_pc (); > + > + /* Make sure the stop_pc is set to the beginning of the line. */ > + if (stop_pc != ecs->event_thread->control.step_range_start) > + { > + stop_pc = update_line_range_start (stop_pc, ecs); > + ecs->event_thread->control.step_range_start = stop_pc; > + } > + } > + > keep_going (ecs); > } > > diff --git a/gdb/symtab.c b/gdb/symtab.c > index 27611a34ec4..91d35616eb9 100644 > --- a/gdb/symtab.c > +++ b/gdb/symtab.c > @@ -3282,6 +3282,55 @@ find_pc_line (CORE_ADDR pc, int notcurrent) > return sal; > } > > +/* Compare two symtab_and_line entries. Return true if both have > + the same line number and the same symtab pointer. That means we > + are dealing with two entries from the same line and from the same > + source file. > + > + Return false otherwise. */ > + > +static bool > +sal_line_symtab_matches_p (const symtab_and_line &sal1, > + const symtab_and_line &sal2) > +{ > + return (sal1.line == sal2.line && sal1.symtab == sal2.symtab); > +} > + > +/* See symtah.h. */ > + > +gdb::optional<CORE_ADDR> > +find_line_range_start (CORE_ADDR pc) > +{ > + struct symtab_and_line current_sal = find_pc_line (pc, 0); > + > + if (current_sal.line == 0) > + return {}; > + > + struct symtab_and_line prev_sal = find_pc_line (current_sal.pc - 1, 0); > + > + /* If the previous entry is for a different line, that means we are already > + at the entry with the start PC for this line. */ > + if (!sal_line_symtab_matches_p (prev_sal, current_sal)) > + return current_sal.pc; > + > + /* Otherwise, keep looking for entries for the same line but with > + smaller PC's. */ > + bool done = false; > + CORE_ADDR prev_pc; > + while (!done) > + { > + prev_pc = prev_sal.pc; > + > + prev_sal = find_pc_line (prev_pc - 1, 0); > + > + /* Did we notice a line change? If so, we are done with the search. */ > + if (!sal_line_symtab_matches_p (prev_sal, current_sal)) > + done = true; > + } > + > + return prev_pc; > +} > + > /* See symtab.h. */ > > struct symtab * > diff --git a/gdb/symtab.h b/gdb/symtab.h > index 404d0ab30a8..f54305636da 100644 > --- a/gdb/symtab.h > +++ b/gdb/symtab.h > @@ -2346,6 +2346,22 @@ extern struct symtab_and_line find_pc_line (CORE_ADDR, int); > extern struct symtab_and_line find_pc_sect_line (CORE_ADDR, > struct obj_section *, int); > > +/* Given PC, and assuming it is part of a range of addresses that is part of a > + line, go back through the linetable and find the starting PC of that > + line. > + > + For example, suppose we have 3 PC ranges for line X: > + > + Line X - [0x0 - 0x8] > + Line X - [0x8 - 0x10] > + Line X - [0x10 - 0x18] > + > + If we call the function with PC == 0x14, we want to return 0x0, as that is > + the starting PC of line X, and the ranges are contiguous. > +*/ > + > +extern gdb::optional<CORE_ADDR> find_line_range_start (CORE_ADDR pc); > + > /* Wrapper around find_pc_line to just return the symtab. */ > > extern struct symtab *find_pc_line_symtab (CORE_ADDR); > diff --git a/gdb/testsuite/gdb.reverse/func-map-to-same-line.c b/gdb/testsuite/gdb.reverse/func-map-to-same-line.c > new file mode 100644 > index 00000000000..412ab180943 > --- /dev/null > +++ b/gdb/testsuite/gdb.reverse/func-map-to-same-line.c > @@ -0,0 +1,36 @@ > +/* Copyright 2008-2023 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 <http://www.gnu.org/licenses/>. > + > + This test is used to test the reverse-step and reverse-next instruction > + execution for a source line that contains multiple function calls. */ > + > +void > +func1 () > +{ > +} // END FUNC1 > + > +void > +func2 () > +{ > +} // END FUNC2 > + > +int main () > +{ > + int a, b; > + a = 1; > + b = 2; > + func1 (); func2 (); > + a = a + b; // START REVERSE TEST > +} > diff --git a/gdb/testsuite/gdb.reverse/func-map-to-same-line.exp b/gdb/testsuite/gdb.reverse/func-map-to-same-line.exp > new file mode 100644 > index 00000000000..4eae042a6bf > --- /dev/null > +++ b/gdb/testsuite/gdb.reverse/func-map-to-same-line.exp > @@ -0,0 +1,146 @@ > +# Copyright 2008-2023 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 <http://www.gnu.org/licenses/>. */ > + > +# This file is part of the GDB testsuite. It tests reverse stepping. > +# Lots of code borrowed from "step-test.exp". > + > +# This test checks to make sure there is no regression failures for > +# the reverse-next command when stepping back over two functions in > +# the same line. > + > +require supports_reverse > + > +# This test uses the gcc no-column-info command which was added in gcc 7.1. > +require get_compiler_info "gcc-7-*" By constructing your regex like this, you are only allowing this test to be run on gcc 7. Anything later is also not accepted. I would do something like (Warning, untested) require get_compiler_info "gcc" require !get_compiler_info "gcc-[1-6]-*" Which requires gcc, but does not allow versions 1 to 6. There is probably a way to do it with a single require line, but I'm not the best with regexes. > + > +proc run_tests {msg} { > + global srcfile > + global executable > + > + runto_main > + set target_remote [gdb_is_target_remote] When probing for target remote, GDB will emit pass/fails with hardcoded names, so the current proc setup gives us some duplicated test names. I would suggest that, instead of passing a message as a parameter, you wrapped all function calls in a with_test_prefix scope, like: with_test_prefix "with-column-info" { run_test } > + > + gdb_test_no_output "record" "turn on process record" > + > + # This regression test verifies the reverse-step and reverse-next commands > + # work properly when executing backwards thru a source line containing > + # two function calls on the same source line, i.e. func1 (); func2 (); > + # This test is compiled so the dwarf info not contain the line table > + # information. > + > + # Test 1, reverse-next command > + # Set breakpoint at the line after the function calls. > + set bp_start_reverse_test [gdb_get_line_number "START REVERSE TEST" \ > + $srcfile] > + gdb_breakpoint $srcfile:$bp_start_reverse_test temporary > + > + # Continue to break point for reverse-next test. > + # Command definition: reverse-next [count] > + # Run backward to the beginning of the previous line executed in the > + # current (innermost) stack frame. If the line contains function calls, > + # they will be “un-executed” without stopping. Starting from the first > + # line of a function, reverse-next will take you back to the caller of > + # that function, before the function was called, just as the normal next > + # command would take you from the last line of a function back to its > + # return to its caller 2 . > + gdb_continue_to_breakpoint \ > + "$msg: test1: stopped at command reverse-next test start location" \ > + ".*$srcfile:$bp_start_reverse_test\r\n.*" > + > + # The reverse-next should step all the way back to the beginning of the > + # line, i.e. at the beginning of the func1 call. > + gdb_test "reverse-next" ".*func1 \\(\\); func2 \\(\\);.*" \ > + "$msg: test1: reverse-next to line with two functions" > + > + # We should be stopped at the first instruction of the line. A reverse-step > + # should step back and stop at the beginning of the previous line b = 2, > + # i.e. not in func1 (). > + gdb_test "reverse-stepi" ".*b = 2;.*" \ > + "$msg: test1: reverse-stepi to previous line b = 2" > + > + > + # Setup for test 2 > + clean_restart $executable > + runto_main > + > + gdb_test_no_output "record" "turn on process record" This gives a duplicate test name from setting up for the first test. Adding "test 2:(...)" in here solves it. > + > + # Test 2, reverse-step command > + # Set breakpoint at the line after the function calls. > + gdb_breakpoint $srcfile:$bp_start_reverse_test temporary > + > + # Continue to the start of the reverse-step test. > + # Command definition: reverse-step [count] > + # Run the program backward until control reaches the start of a > + # different source line; then stop it, and return control to gdb. > + # Like the step command, reverse-step will only stop at the beginning > + # of a source line. It “un-executes” the previously executed source > + # line. If the previous source line included calls to debuggable > + # functions, reverse-step will step (backward) into the called function, > + # stopping at the beginning of the last statement in the called > + # function (typically a return statement). Also, as with the step > + # command, if non-debuggable functions are called, reverse-step will > + # run thru them backward without stopping. > + > + gdb_continue_to_breakpoint \ > + "$msg: test2: stopped at command reverse-step test start location" \ > + ".*$srcfile:$bp_start_reverse_test\r\n.*" > + > + # The first reverse step should take us call of func2 (). > + gdb_test "reverse-step" ".*END FUNC2.*" \ > + "$msg: test2: reverse-step into func2 " > + > + # The second reverse step should take us into func1 (). > + gdb_test "reverse-step" ".*END FUNC1.*" \ > + "$msg: test2: reverse-step into func1 " > + > + # The third reverse step should take us call of func1 (). > + gdb_test "reverse-step" ".*func1 \\(\\); func2 \\(\\);.*" \ > + "$msg: test2: reverse-step to line func1(); func2(), at call for func1 " > + > + # We should be stopped at the first instruction of the line. A reverse > + # stepi should take us to b = 2 (). > + gdb_test "reverse-stepi" ".*b = 2;.*" \ > + "$msg: test2: reverse-stepi to line b = 2 " > +} > + > +set srcfile func-map-to-same-line.c > +set executable func-map-to-same-line > + > +# test with gcc column info enabled > +set options [list debug additional_flags=] > + > +if {[build_executable "failed to prepare" $executable $srcfile $options] == -1}\ > + { > + return -1 > +} > + > +clean_restart $executable > + > +run_tests {"with-column-info"} > + > + > +#test with gcc column info disabled > +set options [list debug additional_flags=-gno-column-info] > + > +if {[build_executable "failed to prepare" $executable $srcfile $options] == -1}\ > + { > + return -1 > +} > + > +set $executable executable_without_column_info > +clean_restart $executable > + > +run_tests {"no-column-info"} > diff --git a/gdb/testsuite/gdb.reverse/map-to-same-line.c b/gdb/testsuite/gdb.reverse/map-to-same-line.c > new file mode 100644 > index 00000000000..f20d778f40e > --- /dev/null > +++ b/gdb/testsuite/gdb.reverse/map-to-same-line.c > @@ -0,0 +1,58 @@ > +/* Copyright 2008-2023 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 <http://www.gnu.org/licenses/ >. */ > + > +/* The purpose of this test is to create a DWARF line table that contains two > + or more entries for the same line. When stepping (forwards or backwards), > + GDB should step over the entire line and not just a particular entry in the > + line table. */ > + > +int > +main () > +{ /* TAG: main prologue */ > + asm ("main_label: .globl main_label"); > + int i = 1, j = 2, k; > + float f1 = 2.0, f2 = 4.1, f3; > + const char *str_1 = "foo", *str_2 = "bar", *str_3; > + > + asm ("line1: .globl line1"); > + k = i; f3 = f1; str_3 = str_1; /* TAG: line 1 */ > + > + asm ("line2: .globl line2"); > + k = j; f3 = f2; str_3 = str_2; /* TAG: line 2 */ > + > + asm ("line3: .globl line3"); > + k = i; f3 = f1; str_3 = str_1; /* TAG: line 3 */ > + > + asm ("line4: .globl line4"); > + k = j; f3 = f2; str_3 = str_2; /* TAG: line 4 */ > + > + asm ("line5: .globl line5"); > + k = i; f3 = f1; str_3 = str_1; /* TAG: line 5 */ > + > + asm ("line6: .globl line6"); > + k = j; f3 = f2; str_3 = str_2; /* TAG: line 6 */ > + > + asm ("line7: .globl line7"); > + k = i; f3 = f1; str_3 = str_1; /* TAG: line 7 */ > + > + asm ("line8: .globl line8"); > + k = j; f3 = f2; str_3 = str_2; /* TAG: line 8 */ > + > + asm ("main_return: .globl main_return"); > + k = j; f3 = f2; str_3 = str_2; /* TAG: main return */ > + > + asm ("end_of_sequence: .globl end_of_sequence"); > + return 0; /* TAG: main return */ > +} > diff --git a/gdb/testsuite/gdb.reverse/map-to-same-line.exp b/gdb/testsuite/gdb.reverse/map-to-same-line.exp > new file mode 100644 > index 00000000000..02f3f4d8c9c > --- /dev/null > +++ b/gdb/testsuite/gdb.reverse/map-to-same-line.exp > @@ -0,0 +1,156 @@ > +# Copyright 2008-2023 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 <http://www.gnu.org/licenses/ >. > + > +# When stepping (forwards or backwards), GDB should step over the entire line > +# and not just a particular entry in the line table. This test was added to > +# verify the find_line_range_start function properly sets the step range for a > +# line that consists of multiple statements, i.e. multiple entries in the line > +# table. This test creates a DWARF line table that contains two entries for > +# the same line to do the needed testing. > + > +# This test can only be run on targets which support DWARF-2 and use gas. > +require dwarf2_support > +load_lib dwarf.exp the library has to be imported before the "require" > + > +# The DWARF assembler requires the gcc compiler. > +require is_c_compiler_gcc > + > +# This test suitable only for process that can do reverse execution > +requires supports_reverse s/requires/require With these nits fixed, you can add my tag too! Reviewed-By: Bruno Larsen <blarsen@redhat.com> -- Cheers, Bruno > + > +standard_testfile .c .S > + > +if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile}] } { > + return -1 > +} > + > +set asm_file [standard_output_file $srcfile2] > +Dwarf::assemble $asm_file { > + global srcdir subdir srcfile > + declare_labels integer_label L > + > + # Find start address and length of program > + lassign [function_range main [list ${srcdir}/${subdir}/$srcfile]] \ > + main_start main_len > + set main_end "$main_start + $main_len" > + > + cu {} { > + compile_unit { > + {language @DW_LANG_C} > + {name map-to-same-line.c} > + {stmt_list $L DW_FORM_sec_offset} > + {low_pc 0 addr} > + } { > + subprogram { > + {external 1 flag} > + {name main} > + {low_pc $main_start addr} > + {high_pc $main_len DW_FORM_data4} > + } > + } > + } > + > + lines {version 2 default_is_stmt 1} L { > + include_dir "${srcdir}/${subdir}" > + file_name "$srcfile" 1 > + > + # Generate the line table program with distinct source lines being > + # mapped to the same line entry. Line 1, 5 and 8 contain 1 statement > + # each. Line 2 contains 2 statements. Line 3 contains 3 statements. > + program { > + DW_LNE_set_address $main_start > + line [gdb_get_line_number "TAG: main prologue"] > + DW_LNS_copy > + DW_LNE_set_address line1 > + line [gdb_get_line_number "TAG: line 1" ] > + DW_LNS_copy > + DW_LNE_set_address line2 > + line [gdb_get_line_number "TAG: line 2" ] > + DW_LNS_copy > + DW_LNE_set_address line3 > + line [gdb_get_line_number "TAG: line 2" ] > + DW_LNS_copy > + DW_LNE_set_address line4 > + line [gdb_get_line_number "TAG: line 3" ] > + DW_LNS_copy > + DW_LNE_set_address line5 > + line [gdb_get_line_number "TAG: line 3" ] > + DW_LNS_copy > + DW_LNE_set_address line6 > + line [gdb_get_line_number "TAG: line 3" ] > + DW_LNS_copy > + DW_LNE_set_address line7 > + line [gdb_get_line_number "TAG: line 5" ] > + DW_LNS_copy > + DW_LNE_set_address line8 > + line [gdb_get_line_number "TAG: line 8" ] > + DW_LNS_copy > + DW_LNE_set_address main_return > + line [gdb_get_line_number "TAG: main return"] > + DW_LNS_copy > + DW_LNE_set_address end_of_sequence > + DW_LNE_end_sequence > + } > + } > +} > + > +if { [prepare_for_testing "failed to prepare" ${testfile} \ > + [list $srcfile $asm_file] {nodebug} ] } { > + return -1 > +} > + > +runto_main > + > +# Print the line table > +gdb_test_multiple "maint info line-table ${testfile}" "" { > + -re "\r\n$decimal\[ \t\]+$decimal\[ \t\]+($hex)\[ \t\]+Y\[^\r\n\]*" { > + lappend is_stmt $expect_out(1,string) > + exp_continue > + } > + -re -wrap "" { > + } > +} > + > +# Do the reverse-step test > +gdb_test_no_output "record" "turn on process record" > + > +set bp_main_return [gdb_get_line_number "TAG: main return" $srcfile] > +gdb_breakpoint $srcfile:$bp_main_return > +gdb_continue_to_breakpoint "run to end of main, reverse-step test" ".*$srcfile:$bp_main_return.*" > +gdb_test "display \$pc" ".*pc =.*" "display pc, reverse-step test" > + > +# At this point, GDB has already recorded the execution up until the return > +# statement. Reverse-step and test if GDB transitions between lines in the > +# expected order. It should reverse-step across lines 8, 5, 3, 2 and 1. > +foreach line {8 5 3 2 1} { > + gdb_test "reverse-step" ".*TAG: line $line.*" "reverse step to line $line" > +} > + > +## Clean restart, test reverse-next command > +clean_restart ${testfile} > +runto_main > +gdb_test_no_output "record" "turn on process record, reverst-next test" > + > +set bp_main_return [gdb_get_line_number "TAG: main return" $srcfile] > +gdb_breakpoint $srcfile:$bp_main_return > +gdb_continue_to_breakpoint "run to end of main, reverse-next test" ".*$srcfile:$bp_main_return.*" > +gdb_test "display \$pc" ".*pc =.*" "display pc, reverse-next test" > + > +# At this point, GDB has already recorded the execution up until the return > +# statement. Reverse-next and test if GDB transitions between lines in the > +# expected order. It should reverse-next across lines 8, 5, 3, 2 and 1. > +foreach line {8 5 3 2 1} { > + gdb_test "reverse-next" ".*TAG: line $line.*" "reverse next to line $line" > +} ^ permalink raw reply [flat|nested] 41+ messages in thread
* RE: [PATCH v3] Fix reverse stepping multiple contiguous PC ranges over the line table. 2023-05-10 13:47 ` Bruno Larsen @ 2023-05-10 17:16 ` Carl Love 2023-05-10 17:32 ` [PATCH v4] " Carl Love 2023-05-11 7:52 ` [PATCH v3] " Bruno Larsen 0 siblings, 2 replies; 41+ messages in thread From: Carl Love @ 2023-05-10 17:16 UTC (permalink / raw) To: Bruno Larsen, gdb-patches, UlrichWeigand, pedro; +Cc: luis.machado, cel Bruno: Thanks for the review. I addressed your comments as mentioned below. I will post version 4 with the changes. FYI, I will be out of the office from May 11 thru May 15. So will reply to any additional comments when I return. On Wed, 2023-05-10 at 15:47 +0200, Bruno Larsen wrote: > On 04/05/2023 17:59, Carl Love wrote: > > Bruno, GDB maintainers: > > > > Version 3, added the gcc version check as discussed further from > > version 2 of the patch. Also updated the tests to check for > > supporting > > <snip> > > extern struct symtab *find_pc_line_symtab (CORE_ADDR); > > diff --git a/gdb/testsuite/gdb.reverse/func-map-to-same-line.c > > b/gdb/testsuite/gdb.reverse/func-map-to-same-line.c > > new file mode 100644 > > index 00000000000..412ab180943 > > --- /dev/null > > +++ b/gdb/testsuite/gdb.reverse/func-map-to-same-line.c > > @@ -0,0 +1,36 @@ > > +/* Copyright 2008-2023 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 L <snip> > > + > > +require supports_reverse > > + > > +# This test uses the gcc no-column-info command which was added in > > gcc 7.1. > > +require get_compiler_info "gcc-7-*" I put the compiler check in last. When I ran it, I obviously didn't double check gdb/testsuite/gdb.log to make sure it really worked. I normally try to make a point of double checking the log file. I have been burned before thinking it was OK when there were no errors visible on the command line. The above command fails if you check the log file. > > By constructing your regex like this, you are only allowing this test > to > be run on gcc 7. Anything later is also not accepted. I would do > something like (Warning, untested) > > require get_compiler_info "gcc" > require !get_compiler_info "gcc-[1-6]-*" I couldn't get require to work like that. The get_compiler_info doesn't seem take "gcc" as an argument. I changed the test to: if {![test_compiler_info {gcc-*}] || [test_compiler_info {gcc-[1-6]-*}]} { return } With this, I do see the correct number of passes in gdb/testsuite/gdb.log. > > Which requires gcc, but does not allow versions 1 to 6. There is > probably a way to do it with a single require line, but I'm not the > best > with regexes. > > > + > > +proc run_tests {msg} { > > + global srcfile > > + global executable > > + > > + runto_main > > + set target_remote [gdb_is_target_remote] > > When probing for target remote, GDB will emit pass/fails with > hardcoded > names, so the current proc setup gives us some duplicated test names. > > I would suggest that, instead of passing a message as a parameter, > you > wrapped all function calls in a with_test_prefix scope, like: > > with_test_prefix "with-column-info" { > run_test > } OK, I changed from passing in an argument and did the wrapped calls to run_test instead. Note, this still didn't fix the duplicate test names for turning on record. > > > + > > + gdb_test_no_output "record" "turn on process record" > > + > > + # This regression test verifies the reverse-step and reverse- > > next commands > > + # work properly when executing backwards thru a source line > > containing > > + # two function calls on the same source line, i.e. func1 (); > > func2 (); > > + # This test is compiled so the dwarf info not contain the line > > table > > + # information. > > + > > + # Test 1, reverse-next command > > + # Set breakpoint at the line after the function calls. > > + set bp_start_reverse_test [gdb_get_line_number "START REVERSE > > TEST" \ > > + $srcfile] > > + gdb_breakpoint $srcfile:$bp_start_reverse_test temporary > > + > > + # Continue to break point for reverse-next test. > > + # Command definition: reverse-next [count] > > + # Run backward to the beginning of the previous line > > executed in the > > + # current (innermost) stack frame. If the line contains > > function calls, > > + # they will be “un-executed” without stopping. Starting from > > the first > > + # line of a function, reverse-next will take you back to the > > caller of > > + # that function, before the function was called, just as the > > normal next > > + # command would take you from the last line of a function > > back to its > > + # return to its caller 2 . > > + gdb_continue_to_breakpoint \ > > + "$msg: test1: stopped at command reverse-next test start > > location" \ > > + ".*$srcfile:$bp_start_reverse_test\r\n.*" > > + > > + # The reverse-next should step all the way back to the > > beginning of the > > + # line, i.e. at the beginning of the func1 call. > > + gdb_test "reverse-next" ".*func1 \\(\\); func2 \\(\\);.*" \ > > + "$msg: test1: reverse-next to line with two functions" > > + > > + # We should be stopped at the first instruction of the line. A > > reverse-step > > + # should step back and stop at the beginning of the previous > > line b = 2, > > + # i.e. not in func1 (). > > + gdb_test "reverse-stepi" ".*b = 2;.*" \ > > + "$msg: test1: reverse-stepi to previous line b = 2" > > + > > + > > + # Setup for test 2 > > + clean_restart $executable > > + runto_main > > + > > + gdb_test_no_output "record" "turn on process record" > > This gives a duplicate test name from setting up for the first test. > Adding "test 2:(...)" in here solves it. I couldn't figure out how to get the above syntax to work. So I used the with_test_prefix instead, i.e. with_test_prefix "test2" { gdb_test_no_output "record" "turn on process record" } That fixed the duplicate names. I also wrapped the first record with "test1" for consistency. > > > + > > + # Test 2, reverse-step command > > + # Set breakpoint at the line after the function calls. > > + gdb_breakpoint $srcfile:$bp_start_reverse_test temporary > > + > > <snip> > > +# the same line to do the needed testing. > > + > > +# This test can only be run on targets which support DWARF-2 and > > use gas. > > +require dwarf2_support > > +load_lib dwarf.exp > the library has to be imported before the "require" OK, switched the order of the lines. > > + > > +# The DWARF assembler requires the gcc compiler. > > +require is_c_compiler_gcc > > + > > +# This test suitable only for process that can do reverse > > execution > > +requires supports_reverse > > s/requires/require Fixed. Carl ^ permalink raw reply [flat|nested] 41+ messages in thread
* [PATCH v4] Fix reverse stepping multiple contiguous PC ranges over the line table. 2023-05-10 17:16 ` Carl Love @ 2023-05-10 17:32 ` Carl Love 2023-05-11 16:01 ` Simon Marchi 2023-05-11 7:52 ` [PATCH v3] " Bruno Larsen 1 sibling, 1 reply; 41+ messages in thread From: Carl Love @ 2023-05-10 17:32 UTC (permalink / raw) To: Bruno Larsen, gdb-patches, UlrichWeigand, pedro; +Cc: luis.machado, cel, cel Bruno, GDB maintainers: Version 4, additional fixes for gcc version check, wrap function calls using "with_test_prefix", move load_lib dwarf.exe. Fixed typo noted by Luis. Version 3, added the gcc version check as discussed further from version 2 of the patch. Also updated the tests to check for supporting reverse execution rather than requiring recording. I also noticed there were a couple more instances of a requirement check, i.e. if [] which I changed to "require" per the current style for checking on the test requirements. The following patch fixes issues on PowerPC with the reverse-step and reverse-next instructions when there are multiple assignment statements on the same line and when there are multiple function calls on the same line. The commit log below discusses these issues in further depth. The discussion included what the correct operation should be for these commands based on the GDB documentation. The proposed patch at that time changed how the commands worked on other platforms such as X86 in a way they no longer matched the documentation. The issue is the line table contains multiple entries for the same source line. The patch adds a function to search the line table to find the address of the first instruction of a line. When setup up the reverse stepping range, the function is called to make sure the start of the range corresponds to the address of the first instruction for the line. This approach was used. When Luis initially developed the patch, he considered merging the contiguous ranges in the line table when reading the line tables. He decided it was better to work with the data directly in the line table rather than creating and using a modified version of the line table. The following patch fixes the execution of the reveres-step and reverse-next commands for both senarios of multiple statements on the same line for PowerPC and aarch64-linux. Unlike the previous patch, it does not change the operation of the commands on other platforms, i.e. X86. The patch adds new test cases for both scenarios to verify they work correctly. The patch has been tested on PowerPC, Intel X86 and aarch64-linux with no new regression failures. Please let me know if the patch is acceptable for mainline. Thanks. Carl --------------------------------------------- Fix reverse stepping multiple contiguous PC ranges over the line table. There are a couple of scenarios where the GDB reverse-step and reverse-next commands do not work correctly. Scenario 1 issue description by Luis Machado: When running GDB's testsuite on aarch64-linux/Ubuntu 20.04 (also spotted on the ppc backend), I noticed some failures in gdb.reverse/solib-precsave.exp and gdb.reverse/solib-reverse.exp. The failure happens around the following code: 38 b[1] = shr2(17); /* middle part two */ 40 b[0] = 6; b[1] = 9; /* generic statement, end part two */ 42 shr1 ("message 1\n"); /* shr1 one */ Normal execution: - step from line 38 will land on line 40. - step from line 40 will land on line 42. Reverse execution: - step from line 42 will land on line 40. - step from line 40 will land on line 40. - step from line 40 will land on line 38. The problem here is that line 40 contains two contiguous but distinct PC ranges in the line table, like so: Line 40 - [0x7ec ~ 0x7f4] Line 40 - [0x7f4 ~ 0x7fc] The two distinct ranges are generated because GCC started outputting source column information, which GDB doesn't take into account at the moment. When stepping forward from line 40, we skip both of these ranges and land on line 42. When stepping backward from line 42, we stop at the start PC of the second (or first, going backwards) range of line 40. Since we've reached ecs->event_thread->control.step_range_start, we stop stepping backwards. --------------------------------------------------------- Scenario 2 issue described by Pedro Alves: The following explanation of the issue was taken from the gdb mailing list discussion of the withdrawn patch to change the behavior of the reverse-step and reverse-next commands. Specifically, message from Pedro Alves <pedro@palves.net> where he demonstrates the issue where you have multiple function calls on the same source code line: https://sourceware.org/pipermail/gdb-patches/2023-January/196110.html The source line looks like: func1 (); func2 (); so stepping backwards over that line should always stop at the first instruction of the line, not in the middle. Let's simplify this. Here's the full source code of my example: (gdb) list 1 1 void func1 () 2 { 3 } 4 5 void func2 () 6 { 7 } 8 9 int main () 10 { 11 func1 (); func2 (); 12 } Compiled with: $ gcc reverse.c -o reverse -g3 -O0 $ gcc -v ... gcc version 11.3.0 (Ubuntu 11.3.0-1ubuntu1~22.04) Now let's debug it with target record, using current gdb git master (f3d8ae90b236), without your patch: $ gdb ~/reverse GNU gdb (GDB) 14.0.50.20230124-git ... Reading symbols from /home/pedro/reverse... (gdb) start Temporary breakpoint 1 at 0x1147: file reverse.c, line 11. Starting program: /home/pedro/reverse [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". Temporary breakpoint 1, main () at reverse.c:11 11 func1 (); func2 (); (gdb) record (gdb) disassemble /s Dump of assembler code for function main: reverse.c: 10 { 0x000055555555513f <+0>: endbr64 0x0000555555555143 <+4>: push %rbp 0x0000555555555144 <+5>: mov %rsp,%rbp 11 func1 (); func2 (); => 0x0000555555555147 <+8>: mov $0x0,%eax 0x000055555555514c <+13>: call 0x555555555129 <func1> 0x0000555555555151 <+18>: mov $0x0,%eax 0x0000555555555156 <+23>: call 0x555555555134 <func2> 0x000055555555515b <+28>: mov $0x0,%eax 12 } 0x0000555555555160 <+33>: pop %rbp 0x0000555555555161 <+34>: ret End of assembler dump. (gdb) n 12 } So far so good, a "next" stepped over the whole of line 11 and stopped at line 12. Let's confirm where we are now: (gdb) disassemble /s Dump of assembler code for function main: reverse.c: 10 { 0x000055555555513f <+0>: endbr64 0x0000555555555143 <+4>: push %rbp 0x0000555555555144 <+5>: mov %rsp,%rbp 11 func1 (); func2 (); 0x0000555555555147 <+8>: mov $0x0,%eax 0x000055555555514c <+13>: call 0x555555555129 <func1> 0x0000555555555151 <+18>: mov $0x0,%eax 0x0000555555555156 <+23>: call 0x555555555134 <func2> 0x000055555555515b <+28>: mov $0x0,%eax 12 } => 0x0000555555555160 <+33>: pop %rbp 0x0000555555555161 <+34>: ret End of assembler dump. Good, we're at the first instruction of line 12. Now let's undo the "next", with "reverse-next": (gdb) reverse-next 11 func1 (); func2 (); Seemingly stopped at line 11. Let's see exactly where: (gdb) disassemble /s Dump of assembler code for function main: reverse.c: 10 { 0x000055555555513f <+0>: endbr64 0x0000555555555143 <+4>: push %rbp 0x0000555555555144 <+5>: mov %rsp,%rbp 11 func1 (); func2 (); 0x0000555555555147 <+8>: mov $0x0,%eax 0x000055555555514c <+13>: call 0x555555555129 <func1> => 0x0000555555555151 <+18>: mov $0x0,%eax 0x0000555555555156 <+23>: call 0x555555555134 <func2> 0x000055555555515b <+28>: mov $0x0,%eax 12 } 0x0000555555555160 <+33>: pop %rbp 0x0000555555555161 <+34>: ret End of assembler dump. (gdb) And lo, we stopped in the middle of line 11! That is a bug, we should have stepped back all the way to the beginning of the line. The "reverse-next" should have fully undone the prior "next" command. The above issues were fixed by introducing a new function that looks for adjacent PC ranges for the same line, until we notice a line change. Then we take that as the start PC of the range. The new start PC for the range is used for the control.step_range_start when setting up a step range. The test case gdb.reverse/map-to-same-line.exp is added to test the fix for the issues in scenario 1. The test case gdb.reverse/func-map-to-same-line.exp was added to test the fix for scenario 2 when the binary was compiled with and without line table information. bug: https://sourceware.org/bugzilla/show_bug.cgi?id=28426 Co-authored-by: Luis Machado <luis.machado@arm.com> Co-authored-by: Carl Love <cel@us.ibm.com> Reviewed-By: Bruno Larsen <blarsen@redhat.com> --- gdb/infrun.c | 57 +++++++ gdb/symtab.c | 49 ++++++ gdb/symtab.h | 16 ++ .../gdb.reverse/func-map-to-same-line.c | 36 ++++ .../gdb.reverse/func-map-to-same-line.exp | 156 ++++++++++++++++++ gdb/testsuite/gdb.reverse/map-to-same-line.c | 58 +++++++ .../gdb.reverse/map-to-same-line.exp | 156 ++++++++++++++++++ 7 files changed, 528 insertions(+) create mode 100644 gdb/testsuite/gdb.reverse/func-map-to-same-line.c create mode 100644 gdb/testsuite/gdb.reverse/func-map-to-same-line.exp create mode 100644 gdb/testsuite/gdb.reverse/map-to-same-line.c create mode 100644 gdb/testsuite/gdb.reverse/map-to-same-line.exp diff --git a/gdb/infrun.c b/gdb/infrun.c index efe2c00c489..31cd817c733 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -114,6 +114,9 @@ static struct async_event_handler *infrun_async_inferior_event_token; Starts off as -1, indicating "never enabled/disabled". */ static int infrun_is_async = -1; +static CORE_ADDR update_line_range_start (CORE_ADDR pc, + struct execution_control_state *ecs); + /* See infrun.h. */ void @@ -6769,6 +6772,25 @@ handle_signal_stop (struct execution_control_state *ecs) process_event_stop_test (ecs); } +CORE_ADDR +update_line_range_start (CORE_ADDR pc, struct execution_control_state *ecs) +{ + /* The line table may have multiple entries for the same source code line. + Given the PC, check the line table and return the PC that corresponds + to the line table entry for the source line that PC is in. */ + CORE_ADDR start_line_pc = ecs->event_thread->control.step_range_start; + gdb::optional<CORE_ADDR> real_range_start; + + /* Call find_line_range_start to get the smallest address in the + linetable for multiple Line X entries in the line table. */ + real_range_start = find_line_range_start (pc); + + if (real_range_start.has_value ()) + start_line_pc = *real_range_start; + + return start_line_pc; +} + /* Come here when we've got some debug event / signal we can explain (IOW, not a random signal), and test whether it should cause a stop, or whether we should resume the inferior (transparently). @@ -7570,6 +7592,28 @@ process_event_stop_test (struct execution_control_state *ecs) if (stop_pc_sal.is_stmt) { + if (execution_direction == EXEC_REVERSE) + { + /* We are stepping backwards make sure we have reached the + beginning of the line. */ + CORE_ADDR stop_pc = ecs->event_thread->stop_pc (); + CORE_ADDR start_line_pc + = update_line_range_start (stop_pc, ecs); + + if (stop_pc != start_line_pc) + { + /* Have not reached the beginning of the source code line. + Set a step range. Execution should stop in any function + calls we execute back into before reaching the beginning + of the line. */ + ecs->event_thread->control.step_range_start = start_line_pc; + ecs->event_thread->control.step_range_end = stop_pc; + set_step_info (ecs->event_thread, frame, stop_pc_sal); + keep_going (ecs); + return; + } + } + /* We are at the start of a statement. So stop. Note that we don't stop if we step into the middle of a @@ -7632,6 +7676,19 @@ process_event_stop_test (struct execution_control_state *ecs) set_step_info (ecs->event_thread, frame, stop_pc_sal); infrun_debug_printf ("keep going"); + + if (execution_direction == EXEC_REVERSE) + { + CORE_ADDR stop_pc = ecs->event_thread->stop_pc (); + + /* Make sure the stop_pc is set to the beginning of the line. */ + if (stop_pc != ecs->event_thread->control.step_range_start) + { + stop_pc = update_line_range_start (stop_pc, ecs); + ecs->event_thread->control.step_range_start = stop_pc; + } + } + keep_going (ecs); } diff --git a/gdb/symtab.c b/gdb/symtab.c index 27611a34ec4..91d35616eb9 100644 --- a/gdb/symtab.c +++ b/gdb/symtab.c @@ -3282,6 +3282,55 @@ find_pc_line (CORE_ADDR pc, int notcurrent) return sal; } +/* Compare two symtab_and_line entries. Return true if both have + the same line number and the same symtab pointer. That means we + are dealing with two entries from the same line and from the same + source file. + + Return false otherwise. */ + +static bool +sal_line_symtab_matches_p (const symtab_and_line &sal1, + const symtab_and_line &sal2) +{ + return (sal1.line == sal2.line && sal1.symtab == sal2.symtab); +} + +/* See symtah.h. */ + +gdb::optional<CORE_ADDR> +find_line_range_start (CORE_ADDR pc) +{ + struct symtab_and_line current_sal = find_pc_line (pc, 0); + + if (current_sal.line == 0) + return {}; + + struct symtab_and_line prev_sal = find_pc_line (current_sal.pc - 1, 0); + + /* If the previous entry is for a different line, that means we are already + at the entry with the start PC for this line. */ + if (!sal_line_symtab_matches_p (prev_sal, current_sal)) + return current_sal.pc; + + /* Otherwise, keep looking for entries for the same line but with + smaller PC's. */ + bool done = false; + CORE_ADDR prev_pc; + while (!done) + { + prev_pc = prev_sal.pc; + + prev_sal = find_pc_line (prev_pc - 1, 0); + + /* Did we notice a line change? If so, we are done with the search. */ + if (!sal_line_symtab_matches_p (prev_sal, current_sal)) + done = true; + } + + return prev_pc; +} + /* See symtab.h. */ struct symtab * diff --git a/gdb/symtab.h b/gdb/symtab.h index 404d0ab30a8..f54305636da 100644 --- a/gdb/symtab.h +++ b/gdb/symtab.h @@ -2346,6 +2346,22 @@ extern struct symtab_and_line find_pc_line (CORE_ADDR, int); extern struct symtab_and_line find_pc_sect_line (CORE_ADDR, struct obj_section *, int); +/* Given PC, and assuming it is part of a range of addresses that is part of a + line, go back through the linetable and find the starting PC of that + line. + + For example, suppose we have 3 PC ranges for line X: + + Line X - [0x0 - 0x8] + Line X - [0x8 - 0x10] + Line X - [0x10 - 0x18] + + If we call the function with PC == 0x14, we want to return 0x0, as that is + the starting PC of line X, and the ranges are contiguous. +*/ + +extern gdb::optional<CORE_ADDR> find_line_range_start (CORE_ADDR pc); + /* Wrapper around find_pc_line to just return the symtab. */ extern struct symtab *find_pc_line_symtab (CORE_ADDR); diff --git a/gdb/testsuite/gdb.reverse/func-map-to-same-line.c b/gdb/testsuite/gdb.reverse/func-map-to-same-line.c new file mode 100644 index 00000000000..412ab180943 --- /dev/null +++ b/gdb/testsuite/gdb.reverse/func-map-to-same-line.c @@ -0,0 +1,36 @@ +/* Copyright 2008-2023 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 <http://www.gnu.org/licenses/>. + + This test is used to test the reverse-step and reverse-next instruction + execution for a source line that contains multiple function calls. */ + +void +func1 () +{ +} // END FUNC1 + +void +func2 () +{ +} // END FUNC2 + +int main () +{ + int a, b; + a = 1; + b = 2; + func1 (); func2 (); + a = a + b; // START REVERSE TEST +} diff --git a/gdb/testsuite/gdb.reverse/func-map-to-same-line.exp b/gdb/testsuite/gdb.reverse/func-map-to-same-line.exp new file mode 100644 index 00000000000..da5ee282053 --- /dev/null +++ b/gdb/testsuite/gdb.reverse/func-map-to-same-line.exp @@ -0,0 +1,156 @@ +# Copyright 2008-2023 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 <http://www.gnu.org/licenses/>. */ + +# This file is part of the GDB testsuite. It tests reverse stepping. +# Lots of code borrowed from "step-test.exp". + +# This test checks to make sure there is no regression failures for +# the reverse-next command when stepping back over two functions in +# the same line. + +require supports_reverse + +# This test uses the gcc no-column-info command which was added in gcc 7.1. +if {![test_compiler_info {gcc-*}] + || [test_compiler_info {gcc-[1-6]-*}]} { + return +} + +proc run_tests {} { + global srcfile + global executable + + runto_main + set target_remote [gdb_is_target_remote] + + with_test_prefix "test1" { + gdb_test_no_output "record" "turn on process record" + } + + # This regression test verifies the reverse-step and reverse-next commands + # work properly when executing backwards thru a source line containing + # two function calls on the same source line, i.e. func1 (); func2 (); + # This test is compiled so the dwarf info not contain the line table + # information. + + # Test 1, reverse-next command + # Set breakpoint at the line after the function calls. + set bp_start_reverse_test [gdb_get_line_number "START REVERSE TEST" \ + $srcfile] + gdb_breakpoint $srcfile:$bp_start_reverse_test temporary + + # Continue to break point for reverse-next test. + # Command definition: reverse-next [count] + # Run backward to the beginning of the previous line executed in the + # current (innermost) stack frame. If the line contains function calls, + # they will be “un-executed” without stopping. Starting from the first + # line of a function, reverse-next will take you back to the caller of + # that function, before the function was called, just as the normal next + # command would take you from the last line of a function back to its + # return to its caller 2 . + gdb_continue_to_breakpoint \ + "test1: stopped at command reverse-next test start location" \ + ".*$srcfile:$bp_start_reverse_test\r\n.*" + + # The reverse-next should step all the way back to the beginning of the + # line, i.e. at the beginning of the func1 call. + gdb_test "reverse-next" ".*func1 \\(\\); func2 \\(\\);.*" \ + "test1: reverse-next to line with two functions" + + # We should be stopped at the first instruction of the line. A reverse-step + # should step back and stop at the beginning of the previous line b = 2, + # i.e. not in func1 (). + gdb_test "reverse-stepi" ".*b = 2;.*" \ + "test1: reverse-stepi to previous line b = 2" + + + # Setup for test 2 + clean_restart $executable + runto_main + + with_test_prefix "test2" { + gdb_test_no_output "record" "turn on process record" + } + + # Test 2, reverse-step command + # Set breakpoint at the line after the function calls. + gdb_breakpoint $srcfile:$bp_start_reverse_test temporary + + # Continue to the start of the reverse-step test. + # Command definition: reverse-step [count] + # Run the program backward until control reaches the start of a + # different source line; then stop it, and return control to gdb. + # Like the step command, reverse-step will only stop at the beginning + # of a source line. It “un-executes” the previously executed source + # line. If the previous source line included calls to debuggable + # functions, reverse-step will step (backward) into the called function, + # stopping at the beginning of the last statement in the called + # function (typically a return statement). Also, as with the step + # command, if non-debuggable functions are called, reverse-step will + # run thru them backward without stopping. + + gdb_continue_to_breakpoint \ + "test2: stopped at command reverse-step test start location" \ + ".*$srcfile:$bp_start_reverse_test\r\n.*" + + # The first reverse step should take us call of func2 (). + gdb_test "reverse-step" ".*END FUNC2.*" \ + "test2: reverse-step into func2 " + + # The second reverse step should take us into func1 (). + gdb_test "reverse-step" ".*END FUNC1.*" \ + "test2: reverse-step into func1 " + + # The third reverse step should take us call of func1 (). + gdb_test "reverse-step" ".*func1 \\(\\); func2 \\(\\);.*" \ + "test2: reverse-step to line func1(); func2(), at call for func1 " + + # We should be stopped at the first instruction of the line. A reverse + # stepi should take us to b = 2 (). + gdb_test "reverse-stepi" ".*b = 2;.*" \ + "test2: reverse-stepi to line b = 2 " +} + +set srcfile func-map-to-same-line.c +set executable func-map-to-same-line + +# test with gcc column info enabled +set options [list debug additional_flags=] + +if {[build_executable "failed to prepare" $executable $srcfile $options] == -1}\ + { + return -1 +} + +clean_restart $executable + +with_test_prefix "with-column-info" { + run_tests +} + +#test with gcc column info disabled +set options [list debug additional_flags=-gno-column-info] + +if {[build_executable "failed to prepare" $executable $srcfile $options] == -1}\ + { + return -1 +} + +set $executable executable_without_column_info +clean_restart $executable + +with_test_prefix "no-column-info" { + run_tests +} diff --git a/gdb/testsuite/gdb.reverse/map-to-same-line.c b/gdb/testsuite/gdb.reverse/map-to-same-line.c new file mode 100644 index 00000000000..f20d778f40e --- /dev/null +++ b/gdb/testsuite/gdb.reverse/map-to-same-line.c @@ -0,0 +1,58 @@ +/* Copyright 2008-2023 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 <http://www.gnu.org/licenses/ >. */ + +/* The purpose of this test is to create a DWARF line table that contains two + or more entries for the same line. When stepping (forwards or backwards), + GDB should step over the entire line and not just a particular entry in the + line table. */ + +int +main () +{ /* TAG: main prologue */ + asm ("main_label: .globl main_label"); + int i = 1, j = 2, k; + float f1 = 2.0, f2 = 4.1, f3; + const char *str_1 = "foo", *str_2 = "bar", *str_3; + + asm ("line1: .globl line1"); + k = i; f3 = f1; str_3 = str_1; /* TAG: line 1 */ + + asm ("line2: .globl line2"); + k = j; f3 = f2; str_3 = str_2; /* TAG: line 2 */ + + asm ("line3: .globl line3"); + k = i; f3 = f1; str_3 = str_1; /* TAG: line 3 */ + + asm ("line4: .globl line4"); + k = j; f3 = f2; str_3 = str_2; /* TAG: line 4 */ + + asm ("line5: .globl line5"); + k = i; f3 = f1; str_3 = str_1; /* TAG: line 5 */ + + asm ("line6: .globl line6"); + k = j; f3 = f2; str_3 = str_2; /* TAG: line 6 */ + + asm ("line7: .globl line7"); + k = i; f3 = f1; str_3 = str_1; /* TAG: line 7 */ + + asm ("line8: .globl line8"); + k = j; f3 = f2; str_3 = str_2; /* TAG: line 8 */ + + asm ("main_return: .globl main_return"); + k = j; f3 = f2; str_3 = str_2; /* TAG: main return */ + + asm ("end_of_sequence: .globl end_of_sequence"); + return 0; /* TAG: main return */ +} diff --git a/gdb/testsuite/gdb.reverse/map-to-same-line.exp b/gdb/testsuite/gdb.reverse/map-to-same-line.exp new file mode 100644 index 00000000000..16a359d90ec --- /dev/null +++ b/gdb/testsuite/gdb.reverse/map-to-same-line.exp @@ -0,0 +1,156 @@ +# Copyright 2008-2023 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 <http://www.gnu.org/licenses/ >. + +# When stepping (forwards or backwards), GDB should step over the entire line +# and not just a particular entry in the line table. This test was added to +# verify the find_line_range_start function properly sets the step range for a +# line that consists of multiple statements, i.e. multiple entries in the line +# table. This test creates a DWARF line table that contains two entries for +# the same line to do the needed testing. + +# This test can only be run on targets which support DWARF-2 and use gas. +load_lib dwarf.exp +require dwarf2_support + +# The DWARF assembler requires the gcc compiler. +require is_c_compiler_gcc + +# This test suitable only for process that can do reverse execution +require supports_reverse + +standard_testfile .c .S + +if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile}] } { + return -1 +} + +set asm_file [standard_output_file $srcfile2] +Dwarf::assemble $asm_file { + global srcdir subdir srcfile + declare_labels integer_label L + + # Find start address and length of program + lassign [function_range main [list ${srcdir}/${subdir}/$srcfile]] \ + main_start main_len + set main_end "$main_start + $main_len" + + cu {} { + compile_unit { + {language @DW_LANG_C} + {name map-to-same-line.c} + {stmt_list $L DW_FORM_sec_offset} + {low_pc 0 addr} + } { + subprogram { + {external 1 flag} + {name main} + {low_pc $main_start addr} + {high_pc $main_len DW_FORM_data4} + } + } + } + + lines {version 2 default_is_stmt 1} L { + include_dir "${srcdir}/${subdir}" + file_name "$srcfile" 1 + + # Generate the line table program with distinct source lines being + # mapped to the same line entry. Line 1, 5 and 8 contain 1 statement + # each. Line 2 contains 2 statements. Line 3 contains 3 statements. + program { + DW_LNE_set_address $main_start + line [gdb_get_line_number "TAG: main prologue"] + DW_LNS_copy + DW_LNE_set_address line1 + line [gdb_get_line_number "TAG: line 1" ] + DW_LNS_copy + DW_LNE_set_address line2 + line [gdb_get_line_number "TAG: line 2" ] + DW_LNS_copy + DW_LNE_set_address line3 + line [gdb_get_line_number "TAG: line 2" ] + DW_LNS_copy + DW_LNE_set_address line4 + line [gdb_get_line_number "TAG: line 3" ] + DW_LNS_copy + DW_LNE_set_address line5 + line [gdb_get_line_number "TAG: line 3" ] + DW_LNS_copy + DW_LNE_set_address line6 + line [gdb_get_line_number "TAG: line 3" ] + DW_LNS_copy + DW_LNE_set_address line7 + line [gdb_get_line_number "TAG: line 5" ] + DW_LNS_copy + DW_LNE_set_address line8 + line [gdb_get_line_number "TAG: line 8" ] + DW_LNS_copy + DW_LNE_set_address main_return + line [gdb_get_line_number "TAG: main return"] + DW_LNS_copy + DW_LNE_set_address end_of_sequence + DW_LNE_end_sequence + } + } +} + +if { [prepare_for_testing "failed to prepare" ${testfile} \ + [list $srcfile $asm_file] {nodebug} ] } { + return -1 +} + +runto_main + +# Print the line table +gdb_test_multiple "maint info line-table ${testfile}" "" { + -re "\r\n$decimal\[ \t\]+$decimal\[ \t\]+($hex)\[ \t\]+Y\[^\r\n\]*" { + lappend is_stmt $expect_out(1,string) + exp_continue + } + -re -wrap "" { + } +} + +# Do the reverse-step test +gdb_test_no_output "record" "turn on process record" + +set bp_main_return [gdb_get_line_number "TAG: main return" $srcfile] +gdb_breakpoint $srcfile:$bp_main_return +gdb_continue_to_breakpoint "run to end of main, reverse-step test" ".*$srcfile:$bp_main_return.*" +gdb_test "display \$pc" ".*pc =.*" "display pc, reverse-step test" + +# At this point, GDB has already recorded the execution up until the return +# statement. Reverse-step and test if GDB transitions between lines in the +# expected order. It should reverse-step across lines 8, 5, 3, 2 and 1. +foreach line {8 5 3 2 1} { + gdb_test "reverse-step" ".*TAG: line $line.*" "reverse step to line $line" +} + +## Clean restart, test reverse-next command +clean_restart ${testfile} +runto_main +gdb_test_no_output "record" "turn on process record, reverst-next test" + +set bp_main_return [gdb_get_line_number "TAG: main return" $srcfile] +gdb_breakpoint $srcfile:$bp_main_return +gdb_continue_to_breakpoint "run to end of main, reverse-next test" ".*$srcfile:$bp_main_return.*" +gdb_test "display \$pc" ".*pc =.*" "display pc, reverse-next test" + +# At this point, GDB has already recorded the execution up until the return +# statement. Reverse-next and test if GDB transitions between lines in the +# expected order. It should reverse-next across lines 8, 5, 3, 2 and 1. +foreach line {8 5 3 2 1} { + gdb_test "reverse-next" ".*TAG: line $line.*" "reverse next to line $line" +} -- 2.37.2 ^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v4] Fix reverse stepping multiple contiguous PC ranges over the line table. 2023-05-10 17:32 ` [PATCH v4] " Carl Love @ 2023-05-11 16:01 ` Simon Marchi 2023-05-11 16:23 ` Bruno Larsen 2023-05-16 22:54 ` [PATCH v4] " Carl Love 0 siblings, 2 replies; 41+ messages in thread From: Simon Marchi @ 2023-05-11 16:01 UTC (permalink / raw) To: Carl Love, Bruno Larsen, gdb-patches, UlrichWeigand, pedro; +Cc: luis.machado I'd like to help reviewing this, but I don't have much time at the moment, so just a few comments on one test to start with. > diff --git a/gdb/testsuite/gdb.reverse/func-map-to-same-line.c b/gdb/testsuite/gdb.reverse/func-map-to-same-line.c > new file mode 100644 > index 00000000000..412ab180943 > --- /dev/null > +++ b/gdb/testsuite/gdb.reverse/func-map-to-same-line.c > @@ -0,0 +1,36 @@ > +/* Copyright 2008-2023 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 <http://www.gnu.org/licenses/>. > + > + This test is used to test the reverse-step and reverse-next instruction > + execution for a source line that contains multiple function calls. */ > + > +void > +func1 () > +{ > +} // END FUNC1 Use /* */ for comments, for consistency with the rest of the code base. > + > +void > +func2 () > +{ > +} // END FUNC2 > + > +int main () > +{ > + int a, b; > + a = 1; > + b = 2; > + func1 (); func2 (); > + a = a + b; // START REVERSE TEST > +} > diff --git a/gdb/testsuite/gdb.reverse/func-map-to-same-line.exp b/gdb/testsuite/gdb.reverse/func-map-to-same-line.exp > new file mode 100644 > index 00000000000..da5ee282053 > --- /dev/null > +++ b/gdb/testsuite/gdb.reverse/func-map-to-same-line.exp > @@ -0,0 +1,156 @@ > +# Copyright 2008-2023 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 <http://www.gnu.org/licenses/>. */ > + > +# This file is part of the GDB testsuite. It tests reverse stepping. > +# Lots of code borrowed from "step-test.exp". > + > +# This test checks to make sure there is no regression failures for > +# the reverse-next command when stepping back over two functions in > +# the same line. > + > +require supports_reverse > + > +# This test uses the gcc no-column-info command which was added in gcc 7.1. > +if {![test_compiler_info {gcc-*}] > + || [test_compiler_info {gcc-[1-6]-*}]} { > + return > +} I would prefer not to filter out by compiler explicitly like that. It would be useful for the test to run with other compilers too. > + > +proc run_tests {} { > + global srcfile > + global executable > + > + runto_main > + set target_remote [gdb_is_target_remote] > + > + with_test_prefix "test1" { > + gdb_test_no_output "record" "turn on process record" > + } > + > + # This regression test verifies the reverse-step and reverse-next commands > + # work properly when executing backwards thru a source line containing > + # two function calls on the same source line, i.e. func1 (); func2 (); > + # This test is compiled so the dwarf info not contain the line table > + # information. > + > + # Test 1, reverse-next command > + # Set breakpoint at the line after the function calls. > + set bp_start_reverse_test [gdb_get_line_number "START REVERSE TEST" \ > + $srcfile] > + gdb_breakpoint $srcfile:$bp_start_reverse_test temporary > + > + # Continue to break point for reverse-next test. > + # Command definition: reverse-next [count] > + # Run backward to the beginning of the previous line executed in the > + # current (innermost) stack frame. If the line contains function calls, > + # they will be “un-executed” without stopping. Starting from the first > + # line of a function, reverse-next will take you back to the caller of > + # that function, before the function was called, just as the normal next > + # command would take you from the last line of a function back to its > + # return to its caller 2 . > + gdb_continue_to_breakpoint \ > + "test1: stopped at command reverse-next test start location" \ > + ".*$srcfile:$bp_start_reverse_test\r\n.*" > + > + # The reverse-next should step all the way back to the beginning of the > + # line, i.e. at the beginning of the func1 call. > + gdb_test "reverse-next" ".*func1 \\(\\); func2 \\(\\);.*" \ > + "test1: reverse-next to line with two functions" > + > + # We should be stopped at the first instruction of the line. A reverse-step > + # should step back and stop at the beginning of the previous line b = 2, > + # i.e. not in func1 (). > + gdb_test "reverse-stepi" ".*b = 2;.*" \ > + "test1: reverse-stepi to previous line b = 2" > + > + > + # Setup for test 2 > + clean_restart $executable > + runto_main > + > + with_test_prefix "test2" { > + gdb_test_no_output "record" "turn on process record" > + } > + > + # Test 2, reverse-step command > + # Set breakpoint at the line after the function calls. > + gdb_breakpoint $srcfile:$bp_start_reverse_test temporary > + > + # Continue to the start of the reverse-step test. > + # Command definition: reverse-step [count] > + # Run the program backward until control reaches the start of a > + # different source line; then stop it, and return control to gdb. > + # Like the step command, reverse-step will only stop at the beginning > + # of a source line. It “un-executes” the previously executed source > + # line. If the previous source line included calls to debuggable > + # functions, reverse-step will step (backward) into the called function, > + # stopping at the beginning of the last statement in the called > + # function (typically a return statement). Also, as with the step > + # command, if non-debuggable functions are called, reverse-step will > + # run thru them backward without stopping. > + > + gdb_continue_to_breakpoint \ > + "test2: stopped at command reverse-step test start location" \ > + ".*$srcfile:$bp_start_reverse_test\r\n.*" > + > + # The first reverse step should take us call of func2 (). > + gdb_test "reverse-step" ".*END FUNC2.*" \ > + "test2: reverse-step into func2 " > + > + # The second reverse step should take us into func1 (). > + gdb_test "reverse-step" ".*END FUNC1.*" \ > + "test2: reverse-step into func1 " > + > + # The third reverse step should take us call of func1 (). > + gdb_test "reverse-step" ".*func1 \\(\\); func2 \\(\\);.*" \ > + "test2: reverse-step to line func1(); func2(), at call for func1 " > + > + # We should be stopped at the first instruction of the line. A reverse > + # stepi should take us to b = 2 (). > + gdb_test "reverse-stepi" ".*b = 2;.*" \ > + "test2: reverse-stepi to line b = 2 " > +} > + > +set srcfile func-map-to-same-line.c > +set executable func-map-to-same-line > + > +# test with gcc column info enabled > +set options [list debug additional_flags=] > + > +if {[build_executable "failed to prepare" $executable $srcfile $options] == -1}\ > + { > + return -1 > +} > + > +clean_restart $executable > + > +with_test_prefix "with-column-info" { > + run_tests > +} So, the above assumes that the compiler generates column-info by default, which has not historically been the case for GCC (it started to emit columns by default with version 8, according to my tests). Other compilers may choose to not emit them by default. I think it would make sense to make gdb_compile recognize the new "column-info" and "no-column-info" options, which would translate to the right flags for the given compiler. gdb_compile already handles the nitty gritty details of choosing compiler flags for specific compiler versions. This way, individual tests don't contain compiler flags that are possibly compiler-specific. > + > +#test with gcc column info disabled > +set options [list debug additional_flags=-gno-column-info] > + > +if {[build_executable "failed to prepare" $executable $srcfile $options] == -1}\ > + { > + return -1 > +} > + > +set $executable executable_without_column_info > +clean_restart $executable > + > +with_test_prefix "no-column-info" { > + run_tests > +} This would probably be a good use for foreach_with_prefix (if you can make it work), to make things more compact: foreach_with_prefix with_column_info {yes no} { } ... or something like that. Simon ^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v4] Fix reverse stepping multiple contiguous PC ranges over the line table. 2023-05-11 16:01 ` Simon Marchi @ 2023-05-11 16:23 ` Bruno Larsen 2023-05-11 17:28 ` Simon Marchi 2023-05-16 22:54 ` [PATCH v4] " Carl Love 1 sibling, 1 reply; 41+ messages in thread From: Bruno Larsen @ 2023-05-11 16:23 UTC (permalink / raw) To: Simon Marchi, Carl Love, gdb-patches, UlrichWeigand, pedro; +Cc: luis.machado On 11/05/2023 18:01, Simon Marchi wrote: >> +with_test_prefix "with-column-info" { >> + run_tests >> +} > So, the above assumes that the compiler generates column-info by > default, which has not historically been the case for GCC (it started to > emit columns by default with version 8, according to my tests). Other > compilers may choose to not emit them by default. Yes, column info started being generated in gcc7 and was made default in gcc 8 > > I think it would make sense to make gdb_compile recognize the new > "column-info" and "no-column-info" options, which would translate to the > right flags for the given compiler. gdb_compile already handles the > nitty gritty details of choosing compiler flags for specific compiler > versions. This way, individual tests don't contain compiler flags that > are possibly compiler-specific. > I was going to suggest something similar in an earlier revision, but when I tried to look for how to control it in clang, I couldn't see it at all, that's why I thought it was OK to restrict it to gcc only. Can clang (or other compilers for that matter) emit this information? Also, how would gdb_compile handle if the current compiler doesn't support a given option, but the others do? Should it loudly fail, or silently ignore the "broken" option? If the second, I guess there is no harm in allowing clang to run these tests and testing the same scenario twice -- Cheers, Bruno ^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v4] Fix reverse stepping multiple contiguous PC ranges over the line table. 2023-05-11 16:23 ` Bruno Larsen @ 2023-05-11 17:28 ` Simon Marchi 2023-05-16 22:54 ` [PATCH 1/2] " Carl Love 2023-05-16 22:54 ` [PATCH 2/2 v5] " Carl Love 0 siblings, 2 replies; 41+ messages in thread From: Simon Marchi @ 2023-05-11 17:28 UTC (permalink / raw) To: Bruno Larsen, Carl Love, gdb-patches, UlrichWeigand, pedro; +Cc: luis.machado > I was going to suggest something similar in an earlier revision, but > when I tried to look for how to control it in clang, I couldn't see it > at all, that's why I thought it was OK to restrict it to gcc only. Can > clang (or other compilers for that matter) emit this information?clang does, yes, with the same flags as gcc: https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-gcolumn-info > Also, how would gdb_compile handle if the current compiler doesn't > support a given option, but the others do? Should it loudly fail, or > silently ignore the "broken" option? If the second, I guess there is > no harm in allowing clang to run these tests and testing the same > scenario twice I'm not sure, you'd have to look at how other options are handled. But intuitively, if gdb_compile isn't able to fulfill your request, then it should fail. For instance, if you used no-column-info with gcc 6 (which doesn't support column info at all), gdb_compile should succeed, even if there isn't an option to disable column info with that compiler. If you used column-info with gcc 6, gdb_compile would fail. If there exists some compiler that always emits column info, with no option to turn it off, then column-info would work with that compiler, but no-column-info wouldn't. To start with, I think it's fine to make column-info and no-column-info map to -gcolumn-info and -gno-column-info. And then, you can probably teach gdb_compile about older gccs and older clangs that don't support column info. If people test with other compilers that don't support -gcolumn-info or -gno-column-info, the test won't compile. They can then contribute support for the column-info / no-column-info options for that compiler. Simon ^ permalink raw reply [flat|nested] 41+ messages in thread
* [PATCH 1/2] Fix reverse stepping multiple contiguous PC ranges over the line table. 2023-05-11 17:28 ` Simon Marchi @ 2023-05-16 22:54 ` Carl Love 2023-06-19 17:11 ` Simon Marchi 2023-05-16 22:54 ` [PATCH 2/2 v5] " Carl Love 1 sibling, 1 reply; 41+ messages in thread From: Carl Love @ 2023-05-16 22:54 UTC (permalink / raw) To: Simon Marchi, Bruno Larsen, gdb-patches, UlrichWeigand, pedro Cc: luis.machado, cel Simon, GDB maintainers: Per the comments on version 4 for the gdb.reverse/func-map-to-same- line.exp, I have added support to proc gdb_compile to enable or disable generating line information as part of the debug information. The two new options are column-info and no-column-info. This patch implements the new options for gdb_compile. These options have been tested with patch 2 of 2 on PowerPC with the GCC and clang compilers. Please let me know if the patch is acceptable for mainline. Thanks. Carl ------------------------------------------------------------- Add gdb_compile options column-info and no-column-info This patch adds two new options to gdb_compile to specify if the compile should or should not generate the line table information. The options are supported on clang and gcc version 7 and newer. Patch has been tested on PowerPC with both gcc and clang. --- gdb/testsuite/lib/gdb.exp | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp index aed7e2d043c..e993fddf4c7 100644 --- a/gdb/testsuite/lib/gdb.exp +++ b/gdb/testsuite/lib/gdb.exp @@ -4794,6 +4794,8 @@ proc quote_for_host { args } { # debug information # - text_segment=addr: Tell the linker to place the text segment at ADDR. # - build-id: Ensure the final binary includes a build-id. +# - no-column-info: Disable generation of column table information. +# - column-info: Enable generation of column table information. # # And here are some of the not too obscure options understood by DejaGnu that # influence the compilation: @@ -5003,6 +5005,34 @@ proc gdb_compile {source dest type options} { } else { error "Don't know how to handle text_segment option." } + } elseif { $opt == "column-info" } { + if {[test_compiler_info {gcc-*}]} { + if {[test_compiler_info {gcc-[1-6]-*}]} { + error "gdb_compile option no-column-info not supported." + } + lappend new_options "additional_flags=-gcolumn-info" + + } elseif {[test_compiler_info {clang-*}]} { + lappend new_options "additional_flags=-gcolumn-info" + + } else { + error "Don't know how to handle gcolumn-info option." + } + + } elseif { $opt == "no-column-info" } { + if {[test_compiler_info {gcc-*}]} { + if {[test_compiler_info {gcc-[1-6]-*}]} { + error "gdb_compile option no-column-info not supported." + } + lappend new_options "additional_flags=-gno-column-info" + + } elseif {[test_compiler_info {clang-*}]} { + lappend new_options "additional_flags=-gno-column-info" + + } else { + error "Don't know how to handle gno-column-info option." + } + } else { lappend new_options $opt } -- 2.37.2 ^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH 1/2] Fix reverse stepping multiple contiguous PC ranges over the line table. 2023-05-16 22:54 ` [PATCH 1/2] " Carl Love @ 2023-06-19 17:11 ` Simon Marchi 2023-06-22 16:52 ` Carl Love 0 siblings, 1 reply; 41+ messages in thread From: Simon Marchi @ 2023-06-19 17:11 UTC (permalink / raw) To: Carl Love, Bruno Larsen, gdb-patches, UlrichWeigand, pedro; +Cc: luis.machado On 5/16/23 18:54, Carl Love wrote: > Simon, GDB maintainers: > > Per the comments on version 4 for the gdb.reverse/func-map-to-same- > line.exp, I have added support to proc gdb_compile to enable or disable > generating line information as part of the debug information. The two > new options are column-info and no-column-info. > > This patch implements the new options for gdb_compile. > > These options have been tested with patch 2 of 2 on PowerPC with the > GCC and clang compilers. > > Please let me know if the patch is acceptable for mainline. Thanks. > > Carl > > ------------------------------------------------------------- > > Add gdb_compile options column-info and no-column-info > > This patch adds two new options to gdb_compile to specify if the compile > should or should not generate the line table information. The > options are supported on clang and gcc version 7 and newer. > > Patch has been tested on PowerPC with both gcc and clang. > --- > gdb/testsuite/lib/gdb.exp | 30 ++++++++++++++++++++++++++++++ > 1 file changed, 30 insertions(+) > > diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp > index aed7e2d043c..e993fddf4c7 100644 > --- a/gdb/testsuite/lib/gdb.exp > +++ b/gdb/testsuite/lib/gdb.exp > @@ -4794,6 +4794,8 @@ proc quote_for_host { args } { > # debug information > # - text_segment=addr: Tell the linker to place the text segment at ADDR. > # - build-id: Ensure the final binary includes a build-id. > +# - no-column-info: Disable generation of column table information. > +# - column-info: Enable generation of column table information. > # > # And here are some of the not too obscure options understood by DejaGnu that > # influence the compilation: > @@ -5003,6 +5005,34 @@ proc gdb_compile {source dest type options} { > } else { > error "Don't know how to handle text_segment option." > } > + } elseif { $opt == "column-info" } { > + if {[test_compiler_info {gcc-*}]} { > + if {[test_compiler_info {gcc-[1-6]-*}]} { > + error "gdb_compile option no-column-info not supported." I think this path should return the equivalent of "failed to compile", instead of throwing an error. Control will go back to the test, which will generally skip the portion of the test that requires that binary. > + } > + lappend new_options "additional_flags=-gcolumn-info" > + > + } elseif {[test_compiler_info {clang-*}]} { > + lappend new_options "additional_flags=-gcolumn-info" > + > + } else { > + error "Don't know how to handle gcolumn-info option." I think it's ok to throw an error in this path. If you are testing against a compiler that we don't know about, it will produce errors that are easy to spot, and you'll be able to add support for your compiler here. Simon ^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH 1/2] Fix reverse stepping multiple contiguous PC ranges over the line table. 2023-06-19 17:11 ` Simon Marchi @ 2023-06-22 16:52 ` Carl Love 2023-06-23 17:44 ` Simon Marchi 0 siblings, 1 reply; 41+ messages in thread From: Carl Love @ 2023-06-22 16:52 UTC (permalink / raw) To: Simon Marchi, Bruno Larsen, gdb-patches, UlrichWeigand, pedro Cc: luis.machado, cel Simon: On Mon, 2023-06-19 at 13:11 -0400, Simon Marchi wrote: > > --- a/gdb/testsuite/lib/gdb.exp > > +++ b/gdb/testsuite/lib/gdb.exp > > @@ -4794,6 +4794,8 @@ proc quote_for_host { args } { > > # debug information > > # - text_segment=addr: Tell the linker to place the text > > segment at ADDR. > > # - build-id: Ensure the final binary includes a build-id. > > +# - no-column-info: Disable generation of column table > > information. > > +# - column-info: Enable generation of column table information. > > # > > # And here are some of the not too obscure options understood by > > DejaGnu that > > # influence the compilation: > > @@ -5003,6 +5005,34 @@ proc gdb_compile {source dest type options} > > { > > } else { > > error "Don't know how to handle text_segment > > option." > > } > > + } elseif { $opt == "column-info" } { > > + if {[test_compiler_info {gcc-*}]} { > > + if {[test_compiler_info {gcc-[1-6]-*}]} { > > + error "gdb_compile option no-column-info not > > supported." > > I think this path should return the equivalent of "failed to > compile", > instead of throwing an error. Control will go back to the test, > which > will generally skip the portion of the test that requires that > binary. Not entirely sure how to accomplish what you are looking for. I change: error "gdb_compile option no-column-info not supported." to set result "option no-column-info not supported." clone_output "gdb compile failed, $result" return 1 When I force the if {[test_compiler_info...]} tp be true to test this, I get: get_compiler_info: gcc-12-2-1 gdb compile failed, option no-column-info not supported. UNTESTED: gdb.reverse/func-map-to-same-line.exp: with_column_info=yes: failed t\ o prepare testcase /home/carll/GDB/build-reverse-multiple- contiguous/gdb/testsuite/../../\ ../binutils-gdb-reverse-multiple- contiguous/gdb/testsuite/gdb.reverse/func-map-\ to-same-line.exp completed in 0 seconds === gdb Summary === # of untested testcases 1 The test case doesn't have any part of the test that doesn't require compiling so it is not clear if that would work with this fix. Anyway, wanted to run that by you to see if this is an appropriate fix? I am really not sure about it. Thanks. Carl ^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH 1/2] Fix reverse stepping multiple contiguous PC ranges over the line table. 2023-06-22 16:52 ` Carl Love @ 2023-06-23 17:44 ` Simon Marchi 2023-06-23 19:41 ` Carl Love 2023-06-23 20:04 ` [PATCH 1/2 ver 2] " Carl Love 0 siblings, 2 replies; 41+ messages in thread From: Simon Marchi @ 2023-06-23 17:44 UTC (permalink / raw) To: Carl Love, Bruno Larsen, gdb-patches, UlrichWeigand, pedro; +Cc: luis.machado On 6/22/23 12:52, Carl Love wrote: > > Simon: > > On Mon, 2023-06-19 at 13:11 -0400, Simon Marchi wrote: >>> --- a/gdb/testsuite/lib/gdb.exp >>> +++ b/gdb/testsuite/lib/gdb.exp >>> @@ -4794,6 +4794,8 @@ proc quote_for_host { args } { >>> # debug information >>> # - text_segment=addr: Tell the linker to place the text >>> segment at ADDR. >>> # - build-id: Ensure the final binary includes a build-id. >>> +# - no-column-info: Disable generation of column table >>> information. >>> +# - column-info: Enable generation of column table information. >>> # >>> # And here are some of the not too obscure options understood by >>> DejaGnu that >>> # influence the compilation: >>> @@ -5003,6 +5005,34 @@ proc gdb_compile {source dest type options} >>> { >>> } else { >>> error "Don't know how to handle text_segment >>> option." >>> } >>> + } elseif { $opt == "column-info" } { >>> + if {[test_compiler_info {gcc-*}]} { >>> + if {[test_compiler_info {gcc-[1-6]-*}]} { >>> + error "gdb_compile option no-column-info not >>> supported." >> >> I think this path should return the equivalent of "failed to >> compile", >> instead of throwing an error. Control will go back to the test, >> which >> will generally skip the portion of the test that requires that >> binary. > > Not entirely sure how to accomplish what you are looking for. > > I change: > error "gdb_compile option no-column-info not supported." > to > set result "option no-column-info not supported." > clone_output "gdb compile failed, $result" > return 1 > > When I force the if {[test_compiler_info...]} tp be true to test this, > I get: > > get_compiler_info: gcc-12-2-1 > gdb compile failed, option no-column-info not supported. > UNTESTED: gdb.reverse/func-map-to-same-line.exp: > with_column_info=yes: failed t\ > o prepare > testcase /home/carll/GDB/build-reverse-multiple- > contiguous/gdb/testsuite/../../\ > ../binutils-gdb-reverse-multiple- > contiguous/gdb/testsuite/gdb.reverse/func-map-\ > to-same-line.exp completed in 0 seconds > > === gdb Summary === > > # of untested testcases 1 > > The test case doesn't have any part of the test that doesn't require > compiling so it is not clear if that would work with this fix. Anyway, > wanted to run that by you to see if this is an appropriate fix? I am > really not sure about it. Thanks. I think that's the expected behavior. The UNTESTED is emitted by build_executable_from_specs, I think. If the test used gdb_compile, I think we wouldn't see an UNTESTED. But as far as your addition is concerned, I think it's fine. I just thought of a simpler alternative though. Just remove the version check. If we build with an older gcc, there will simply be a message that says that the flag is not recognized, and the result should be just the same. I just hacked it locally and changed the flag name to be wrong (I don't have a gcc <= 6 on hand to test). It looks like: Executing on host: gcc -fno-stack-protector -fdiagnostics-color=never -gcolumn-info-foo -c -g -o /home/simark/build/binutils-gdb/gdb/testsuite/outputs/gdb.reverse/func-map-to-same-line/func-map-to-same-line0.o /home/simark/src/binutils-gdb/gdb/testsuite/gdb.reverse/func-map-to-same-line.c (timeout = 300) builtin_spawn -ignore SIGHUP gcc -fno-stack-protector -fdiagnostics-color=never -gcolumn-info-foo -c -g -o /home/simark/build/binutils-gdb/gdb/testsuite/outputs/gdb.reverse/func-map-to-same-line/func-map-to-same-line0.o /home/simark/src/binutils-gdb/gdb/testsuite/gdb.reverse/func-map-to-same-line.c gcc: error: unrecognized debug output level 'column-info-foo' compiler exited with status 1 output is: gcc: error: unrecognized debug output level 'column-info-foo' gdb compile failed, gcc: error: unrecognized debug output level 'column-info-foo' UNTESTED: gdb.reverse/func-map-to-same-line.exp: with_column_info=yes: failed to prepare I then thought about the "no-column-info" case. Currently, you error out for gccs <= 6. However, shouldn't we just compile without any special flag in that case, since there just wasn't any support for column-info back then? Simon ^ permalink raw reply [flat|nested] 41+ messages in thread
* RE: [PATCH 1/2] Fix reverse stepping multiple contiguous PC ranges over the line table. 2023-06-23 17:44 ` Simon Marchi @ 2023-06-23 19:41 ` Carl Love 2023-06-23 20:04 ` [PATCH 1/2 ver 2] " Carl Love 1 sibling, 0 replies; 41+ messages in thread From: Carl Love @ 2023-06-23 19:41 UTC (permalink / raw) To: Simon Marchi, Bruno Larsen, gdb-patches, UlrichWeigand, pedro Cc: luis.machado, cel On Fri, 2023-06-23 at 13:44 -0400, Simon Marchi wrote: > > On 6/22/23 12:52, Carl Love wrote: > > Simon: > > > > On Mon, 2023-06-19 at 13:11 -0400, Simon Marchi wrote: > > > > --- a/gdb/testsuite/lib/gdb.exp > > > > +++ b/gdb/testsuite/lib/gdb.exp > > > > @@ -4794,6 +4794,8 @@ proc quote_for_host { args } { > > > > # debug information > > > > # - text_segment=addr: Tell the linker to place the text > > > > segment at ADDR. > > > > # - build-id: Ensure the final binary includes a build-id. > > > > +# - no-column-info: Disable generation of column table > > > > information. > > > > +# - column-info: Enable generation of column table > > > > information. > > > > # > > > > # And here are some of the not too obscure options understood > > > > by > > > > DejaGnu that > > > > # influence the compilation: > > > > @@ -5003,6 +5005,34 @@ proc gdb_compile {source dest type > > > > options} > > > > { > > > > } else { > > > > error "Don't know how to handle text_segment > > > > option." > > > > } > > > > + } elseif { $opt == "column-info" } { > > > > + if {[test_compiler_info {gcc-*}]} { > > > > + if {[test_compiler_info {gcc-[1-6]-*}]} { > > > > + error "gdb_compile option no-column-info not > > > > supported." > > > > > > I think this path should return the equivalent of "failed to > > > compile", > > > instead of throwing an error. Control will go back to the test, > > > which > > > will generally skip the portion of the test that requires that > > > binary. > > > > Not entirely sure how to accomplish what you are looking for. > > > > I change: > > error "gdb_compile option no-column-info not supported." > > to > > set result "option no-column-info not supported." > > clone_output "gdb compile failed, $result" > > return 1 > > > > When I force the if {[test_compiler_info...]} tp be true to test > > this, > > I get: > > > > get_compiler_info: gcc-12-2-1 > > gdb compile failed, option no-column-info not supported. > > UNTESTED: gdb.reverse/func-map-to-same-line.exp: > > with_column_info=yes: failed t\ > > o prepare > > testcase /home/carll/GDB/build-reverse-multiple- > > contiguous/gdb/testsuite/../../\ > > ../binutils-gdb-reverse-multiple- > > contiguous/gdb/testsuite/gdb.reverse/func-map-\ > > to-same-line.exp completed in 0 seconds > > > > === gdb Summary === > > > > # of untested testcases 1 > > > > The test case doesn't have any part of the test that doesn't > > require > > compiling so it is not clear if that would work with this > > fix. Anyway, > > wanted to run that by you to see if this is an appropriate fix? I > > am > > really not sure about it. Thanks. > > I think that's the expected behavior. The UNTESTED is emitted by > build_executable_from_specs, I think. If the test used gdb_compile, > I > think we wouldn't see an UNTESTED. But as far as your addition is > concerned, I think it's fine. > > I just thought of a simpler alternative though. Just remove the > version > check. If we build with an older gcc, there will simply be a message > that says that the flag is not recognized, and the result should be > just > the same. I just hacked it locally and changed the flag name to be > wrong (I don't have a gcc <= 6 on hand to test). It looks like: Yea, hacking the if {[test_compiler_info {gcc-[...]} is how I have been testing it as well. :-) > > Executing on host: gcc -fno-stack-protector -fdiagnostics- > color=never -gcolumn-info-foo -c -g -o /home/simark/build/binutils- > gdb/gdb/testsuite/outputs/gdb.reverse/func-map-to-same-line/func-map- > to-same-line0.o /home/simark/src/binutils- > gdb/gdb/testsuite/gdb.reverse/func-map-to-same-line.c (timeout = > 300) > builtin_spawn -ignore SIGHUP gcc -fno-stack-protector > -fdiagnostics-color=never -gcolumn-info-foo -c -g -o > /home/simark/build/binutils- > gdb/gdb/testsuite/outputs/gdb.reverse/func-map-to-same-line/func-map- > to-same-line0.o /home/simark/src/binutils- > gdb/gdb/testsuite/gdb.reverse/func-map-to-same-line.c > > gcc: error: unrecognized debug output level 'column-info-foo' > > compiler exited with status 1 > output is: > gcc: error: unrecognized debug output level 'column-info-foo' > > > gdb compile failed, gcc: error: unrecognized debug output level > 'column-info-foo' > UNTESTED: gdb.reverse/func-map-to-same-line.exp: > with_column_info=yes: failed to prepare Yes, that seems to give us the desired result. > > I then thought about the "no-column-info" case. Currently, you error > out for gccs <= 6. However, shouldn't we just compile without any > special flag in that case, since there just wasn't any support for > column-info back then? OK, but seems like we should also warn the user that the option is not supported and we are ignoring it. I put # In this case, don't add the compile line option and # the result will be the same as using no-column-info # on a version that supports the option. warning "gdb_compile option no-column-info not supported, ignoring." in for this case. Carl ^ permalink raw reply [flat|nested] 41+ messages in thread
* [PATCH 1/2 ver 2] Fix reverse stepping multiple contiguous PC ranges over the line table. 2023-06-23 17:44 ` Simon Marchi 2023-06-23 19:41 ` Carl Love @ 2023-06-23 20:04 ` Carl Love 2023-07-06 15:07 ` Carl Love 1 sibling, 1 reply; 41+ messages in thread From: Carl Love @ 2023-06-23 20:04 UTC (permalink / raw) To: Simon Marchi, Bruno Larsen, gdb-patches, UlrichWeigand, pedro Cc: luis.machado, cel Simon, GDB maintainers: Version 2, updated the compiler check and handling for gcc version 6 and earlier. Retested on Power 10. Per the comments on version 4 for the gdb.reverse/func-map-to-same- line.exp, I have added support to proc gdb_compile to enable or disable generating line information as part of the debug information. The two new options are column-info and no-column-info. This patch implements the new options for gdb_compile. These options have been tested with patch 2 of 2 on PowerPC with the GCC and clang compilers. Please let me know if the patch is acceptable for mainline. Thanks. Carl ----------------------------- Add gdb_compile options column-info and no-column-info This patch adds two new options to gdb_compile to specify if the compile should or should not generate the line table information. The options are supported on clang and gcc version 7 and newer. Patch has been tested on PowerPC with both gcc and clang. --- gdb/testsuite/lib/gdb.exp | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp index aed7e2d043c..5857c59a47a 100644 --- a/gdb/testsuite/lib/gdb.exp +++ b/gdb/testsuite/lib/gdb.exp @@ -4794,6 +4794,8 @@ proc quote_for_host { args } { # debug information # - text_segment=addr: Tell the linker to place the text segment at ADDR. # - build-id: Ensure the final binary includes a build-id. +# - no-column-info: Disable generation of column table information. +# - column-info: Enable generation of column table information. # # And here are some of the not too obscure options understood by DejaGnu that # influence the compilation: @@ -5003,6 +5005,38 @@ proc gdb_compile {source dest type options} { } else { error "Don't know how to handle text_segment option." } + } elseif { $opt == "column-info" } { + # If GCC or clang does not support column-info, compilation + # will fail and the usupported column-info option will be + # reported as such. + if {[test_compiler_info {gcc-*}]} { + lappend new_options "additional_flags=-gcolumn-info" + + } elseif {[test_compiler_info {clang-*}]} { + lappend new_options "additional_flags=-gcolumn-info" + + } else { + error "Don't know how to handle gcolumn-info option." + } + + } elseif { $opt == "no-column-info" } { + if {[test_compiler_info {gcc-*}]} { + if {[test_compiler_info {gcc-[1-6]-*}]} { + # In this case, don't add the compile line option and + # the result will be the same as using no-column-info + # on a version that supports the option. + warning "gdb_compile option no-column-info not supported, ignoring." + } else { + lappend new_options "additional_flags=-gno-column-info" + } + + } elseif {[test_compiler_info {clang-*}]} { + lappend new_options "additional_flags=-gno-column-info" + + } else { + error "Don't know how to handle gno-column-info option." + } + } else { lappend new_options $opt } -- 2.37.2 ^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH 1/2 ver 2] Fix reverse stepping multiple contiguous PC ranges over the line table. 2023-06-23 20:04 ` [PATCH 1/2 ver 2] " Carl Love @ 2023-07-06 15:07 ` Carl Love 0 siblings, 0 replies; 41+ messages in thread From: Carl Love @ 2023-07-06 15:07 UTC (permalink / raw) To: Simon Marchi, Bruno Larsen, gdb-patches, UlrichWeigand, pedro Cc: luis.machado, cel GDB maintainers: I believe Simon is on parental leave now. He mentioned in a private email before he sent the last set of comments that he was expecting to go on parental leave shortly. Just wondering if anyone else would be willing to take a look at my feedback on his questions and changes per his comments to see if we can move these two patches forward. Thanks Carl ------------------------------------------- On Fri, 2023-06-23 at 13:04 -0700, Carl Love wrote: > Simon, GDB maintainers: > > Version 2, updated the compiler check and handling for gcc version 6 > and earlier. Retested on Power 10. > > Per the comments on version 4 for the gdb.reverse/func-map-to-same- > line.exp, I have added support to proc gdb_compile to enable or > disable > generating line information as part of the debug information. The > two > new options are column-info and no-column-info. > > This patch implements the new options for gdb_compile. > > These options have been tested with patch 2 of 2 on PowerPC with the > GCC and clang compilers. > > Please let me know if the patch is acceptable for mainline. Thanks. > > Carl > > > > > ----------------------------- > Add gdb_compile options column-info and no-column-info > > This patch adds two new options to gdb_compile to specify if the > compile > should or should not generate the line table information. The > options are supported on clang and gcc version 7 and newer. > > Patch has been tested on PowerPC with both gcc and clang. > --- > gdb/testsuite/lib/gdb.exp | 34 ++++++++++++++++++++++++++++++++++ > 1 file changed, 34 insertions(+) > > diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp > index aed7e2d043c..5857c59a47a 100644 > --- a/gdb/testsuite/lib/gdb.exp > +++ b/gdb/testsuite/lib/gdb.exp > @@ -4794,6 +4794,8 @@ proc quote_for_host { args } { > # debug information > # - text_segment=addr: Tell the linker to place the text segment > at ADDR. > # - build-id: Ensure the final binary includes a build-id. > +# - no-column-info: Disable generation of column table > information. > +# - column-info: Enable generation of column table information. > # > # And here are some of the not too obscure options understood by > DejaGnu that > # influence the compilation: > @@ -5003,6 +5005,38 @@ proc gdb_compile {source dest type options} { > } else { > error "Don't know how to handle text_segment > option." > } > + } elseif { $opt == "column-info" } { > + # If GCC or clang does not support column-info, compilation > + # will fail and the usupported column-info option will be > + # reported as such. > + if {[test_compiler_info {gcc-*}]} { > + lappend new_options "additional_flags=-gcolumn-info" > + > + } elseif {[test_compiler_info {clang-*}]} { > + lappend new_options "additional_flags=-gcolumn-info" > + > + } else { > + error "Don't know how to handle gcolumn-info option." > + } > + > + } elseif { $opt == "no-column-info" } { > + if {[test_compiler_info {gcc-*}]} { > + if {[test_compiler_info {gcc-[1-6]-*}]} { > + # In this case, don't add the compile line option > and > + # the result will be the same as using no-column- > info > + # on a version that supports the option. > + warning "gdb_compile option no-column-info not > supported, ignoring." > + } else { > + lappend new_options "additional_flags=-gno-column- > info" > + } > + > + } elseif {[test_compiler_info {clang-*}]} { > + lappend new_options "additional_flags=-gno-column-info" > + > + } else { > + error "Don't know how to handle gno-column-info > option." > + } > + > } else { > lappend new_options $opt > } ^ permalink raw reply [flat|nested] 41+ messages in thread
* [PATCH 2/2 v5] Fix reverse stepping multiple contiguous PC ranges over the line table. 2023-05-11 17:28 ` Simon Marchi 2023-05-16 22:54 ` [PATCH 1/2] " Carl Love @ 2023-05-16 22:54 ` Carl Love 2023-05-25 15:08 ` Carl Love 2023-06-19 17:58 ` Simon Marchi 1 sibling, 2 replies; 41+ messages in thread From: Carl Love @ 2023-05-16 22:54 UTC (permalink / raw) To: Simon Marchi, Bruno Larsen, gdb-patches, UlrichWeigand, pedro Cc: luis.machado, cel Bruno, Simon, GDB maintainers: Version 5, changed comments in test case func-map-to-same-line.c. Patch 1/2 implemented the new options for gdb_compile. Updated the call to proc run_tests to use the new gdb_compile options in a foreach_with_prefix loop. Version 4, additional fixes for gcc version check, wrap function calls using "with_test_prefix", move load_lib dwarf.exe. Fixed typo noted by Luis. Version 3, added the gcc version check as discussed further from version 2 of the patch. Also updated the tests to check for supporting reverse execution rather than requiring recording. I also noticed there were a couple more instances of a requirement check, i.e. if [] which I changed to "require" per the current style for checking on the test requirements. The following patch fixes issues on PowerPC with the reverse-step and reverse-next instructions when there are multiple assignment statements on the same line and when there are multiple function calls on the same line. The commit log below discusses these issues in further depth. The discussion included what the correct operation should be for these commands based on the GDB documentation. The proposed patch at that time changed how the commands worked on other platforms such as X86 in a way they no longer matched the documentation. The issue is the line table contains multiple entries for the same source line. The patch adds a function to search the line table to find the address of the first instruction of a line. When setup up the reverse stepping range, the function is called to make sure the start of the range corresponds to the address of the first instruction for the line. This approach was used. When Luis initially developed the patch, he considered merging the contiguous ranges in the line table when reading the line tables. He decided it was better to work with the data directly in the line table rather than creating and using a modified version of the line table. The following patch fixes the execution of the reveres-step and reverse-next commands for both senarios of multiple statements on the same line for PowerPC and aarch64-linux. Unlike the previous patch, it does not change the operation of the commands on other platforms, i.e. X86. The patch adds new test cases for both scenarios to verify they work correctly. The patch has been tested on PowerPC, Intel X86 and aarch64-linux with no new regression failures. Please let me know if the patch is acceptable for mainline. Thanks. Carl --------------------------------------------- Fix reverse stepping multiple contiguous PC ranges over the line table. There are a couple of scenarios where the GDB reverse-step and reverse-next commands do not work correctly. Scenario 1 issue description by Luis Machado: When running GDB's testsuite on aarch64-linux/Ubuntu 20.04 (also spotted on the ppc backend), I noticed some failures in gdb.reverse/solib-precsave.exp and gdb.reverse/solib-reverse.exp. The failure happens around the following code: 38 b[1] = shr2(17); /* middle part two */ 40 b[0] = 6; b[1] = 9; /* generic statement, end part two */ 42 shr1 ("message 1\n"); /* shr1 one */ Normal execution: - step from line 38 will land on line 40. - step from line 40 will land on line 42. Reverse execution: - step from line 42 will land on line 40. - step from line 40 will land on line 40. - step from line 40 will land on line 38. The problem here is that line 40 contains two contiguous but distinct PC ranges in the line table, like so: Line 40 - [0x7ec ~ 0x7f4] Line 40 - [0x7f4 ~ 0x7fc] The two distinct ranges are generated because GCC started outputting source column information, which GDB doesn't take into account at the moment. When stepping forward from line 40, we skip both of these ranges and land on line 42. When stepping backward from line 42, we stop at the start PC of the second (or first, going backwards) range of line 40. Since we've reached ecs->event_thread->control.step_range_start, we stop stepping backwards. --------------------------------------------------------- Scenario 2 issue described by Pedro Alves: The following explanation of the issue was taken from the gdb mailing list discussion of the withdrawn patch to change the behavior of the reverse-step and reverse-next commands. Specifically, message from Pedro Alves <pedro@palves.net> where he demonstrates the issue where you have multiple function calls on the same source code line: https://sourceware.org/pipermail/gdb-patches/2023-January/196110.html The source line looks like: func1 (); func2 (); so stepping backwards over that line should always stop at the first instruction of the line, not in the middle. Let's simplify this. Here's the full source code of my example: (gdb) list 1 1 void func1 () 2 { 3 } 4 5 void func2 () 6 { 7 } 8 9 int main () 10 { 11 func1 (); func2 (); 12 } Compiled with: $ gcc reverse.c -o reverse -g3 -O0 $ gcc -v ... gcc version 11.3.0 (Ubuntu 11.3.0-1ubuntu1~22.04) Now let's debug it with target record, using current gdb git master (f3d8ae90b236), without your patch: $ gdb ~/reverse GNU gdb (GDB) 14.0.50.20230124-git ... Reading symbols from /home/pedro/reverse... (gdb) start Temporary breakpoint 1 at 0x1147: file reverse.c, line 11. Starting program: /home/pedro/reverse [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". Temporary breakpoint 1, main () at reverse.c:11 11 func1 (); func2 (); (gdb) record (gdb) disassemble /s Dump of assembler code for function main: reverse.c: 10 { 0x000055555555513f <+0>: endbr64 0x0000555555555143 <+4>: push %rbp 0x0000555555555144 <+5>: mov %rsp,%rbp 11 func1 (); func2 (); => 0x0000555555555147 <+8>: mov $0x0,%eax 0x000055555555514c <+13>: call 0x555555555129 <func1> 0x0000555555555151 <+18>: mov $0x0,%eax 0x0000555555555156 <+23>: call 0x555555555134 <func2> 0x000055555555515b <+28>: mov $0x0,%eax 12 } 0x0000555555555160 <+33>: pop %rbp 0x0000555555555161 <+34>: ret End of assembler dump. (gdb) n 12 } So far so good, a "next" stepped over the whole of line 11 and stopped at line 12. Let's confirm where we are now: (gdb) disassemble /s Dump of assembler code for function main: reverse.c: 10 { 0x000055555555513f <+0>: endbr64 0x0000555555555143 <+4>: push %rbp 0x0000555555555144 <+5>: mov %rsp,%rbp 11 func1 (); func2 (); 0x0000555555555147 <+8>: mov $0x0,%eax 0x000055555555514c <+13>: call 0x555555555129 <func1> 0x0000555555555151 <+18>: mov $0x0,%eax 0x0000555555555156 <+23>: call 0x555555555134 <func2> 0x000055555555515b <+28>: mov $0x0,%eax 12 } => 0x0000555555555160 <+33>: pop %rbp 0x0000555555555161 <+34>: ret End of assembler dump. Good, we're at the first instruction of line 12. Now let's undo the "next", with "reverse-next": (gdb) reverse-next 11 func1 (); func2 (); Seemingly stopped at line 11. Let's see exactly where: (gdb) disassemble /s Dump of assembler code for function main: reverse.c: 10 { 0x000055555555513f <+0>: endbr64 0x0000555555555143 <+4>: push %rbp 0x0000555555555144 <+5>: mov %rsp,%rbp 11 func1 (); func2 (); 0x0000555555555147 <+8>: mov $0x0,%eax 0x000055555555514c <+13>: call 0x555555555129 <func1> => 0x0000555555555151 <+18>: mov $0x0,%eax 0x0000555555555156 <+23>: call 0x555555555134 <func2> 0x000055555555515b <+28>: mov $0x0,%eax 12 } 0x0000555555555160 <+33>: pop %rbp 0x0000555555555161 <+34>: ret End of assembler dump. (gdb) And lo, we stopped in the middle of line 11! That is a bug, we should have stepped back all the way to the beginning of the line. The "reverse-next" should have fully undone the prior "next" command. The above issues were fixed by introducing a new function that looks for adjacent PC ranges for the same line, until we notice a line change. Then we take that as the start PC of the range. The new start PC for the range is used for the control.step_range_start when setting up a step range. The test case gdb.reverse/map-to-same-line.exp is added to test the fix for the issues in scenario 1. The test case gdb.reverse/func-map-to-same-line.exp was added to test the fix for scenario 2 when the binary was compiled with and without line table information. bug: https://sourceware.org/bugzilla/show_bug.cgi?id=28426 Co-authored-by: Luis Machado <luis.machado@arm.com> Co-authored-by: Carl Love <cel@us.ibm.com> Reviewed-By: Bruno Larsen <blarsen@redhat.com> --- gdb/infrun.c | 57 +++++++ gdb/symtab.c | 49 ++++++ gdb/symtab.h | 16 ++ .../gdb.reverse/func-map-to-same-line.c | 36 ++++ .../gdb.reverse/func-map-to-same-line.exp | 140 ++++++++++++++++ gdb/testsuite/gdb.reverse/map-to-same-line.c | 58 +++++++ .../gdb.reverse/map-to-same-line.exp | 156 ++++++++++++++++++ 7 files changed, 512 insertions(+) create mode 100644 gdb/testsuite/gdb.reverse/func-map-to-same-line.c create mode 100644 gdb/testsuite/gdb.reverse/func-map-to-same-line.exp create mode 100644 gdb/testsuite/gdb.reverse/map-to-same-line.c create mode 100644 gdb/testsuite/gdb.reverse/map-to-same-line.exp diff --git a/gdb/infrun.c b/gdb/infrun.c index efe2c00c489..31cd817c733 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -114,6 +114,9 @@ static struct async_event_handler *infrun_async_inferior_event_token; Starts off as -1, indicating "never enabled/disabled". */ static int infrun_is_async = -1; +static CORE_ADDR update_line_range_start (CORE_ADDR pc, + struct execution_control_state *ecs); + /* See infrun.h. */ void @@ -6769,6 +6772,25 @@ handle_signal_stop (struct execution_control_state *ecs) process_event_stop_test (ecs); } +CORE_ADDR +update_line_range_start (CORE_ADDR pc, struct execution_control_state *ecs) +{ + /* The line table may have multiple entries for the same source code line. + Given the PC, check the line table and return the PC that corresponds + to the line table entry for the source line that PC is in. */ + CORE_ADDR start_line_pc = ecs->event_thread->control.step_range_start; + gdb::optional<CORE_ADDR> real_range_start; + + /* Call find_line_range_start to get the smallest address in the + linetable for multiple Line X entries in the line table. */ + real_range_start = find_line_range_start (pc); + + if (real_range_start.has_value ()) + start_line_pc = *real_range_start; + + return start_line_pc; +} + /* Come here when we've got some debug event / signal we can explain (IOW, not a random signal), and test whether it should cause a stop, or whether we should resume the inferior (transparently). @@ -7570,6 +7592,28 @@ process_event_stop_test (struct execution_control_state *ecs) if (stop_pc_sal.is_stmt) { + if (execution_direction == EXEC_REVERSE) + { + /* We are stepping backwards make sure we have reached the + beginning of the line. */ + CORE_ADDR stop_pc = ecs->event_thread->stop_pc (); + CORE_ADDR start_line_pc + = update_line_range_start (stop_pc, ecs); + + if (stop_pc != start_line_pc) + { + /* Have not reached the beginning of the source code line. + Set a step range. Execution should stop in any function + calls we execute back into before reaching the beginning + of the line. */ + ecs->event_thread->control.step_range_start = start_line_pc; + ecs->event_thread->control.step_range_end = stop_pc; + set_step_info (ecs->event_thread, frame, stop_pc_sal); + keep_going (ecs); + return; + } + } + /* We are at the start of a statement. So stop. Note that we don't stop if we step into the middle of a @@ -7632,6 +7676,19 @@ process_event_stop_test (struct execution_control_state *ecs) set_step_info (ecs->event_thread, frame, stop_pc_sal); infrun_debug_printf ("keep going"); + + if (execution_direction == EXEC_REVERSE) + { + CORE_ADDR stop_pc = ecs->event_thread->stop_pc (); + + /* Make sure the stop_pc is set to the beginning of the line. */ + if (stop_pc != ecs->event_thread->control.step_range_start) + { + stop_pc = update_line_range_start (stop_pc, ecs); + ecs->event_thread->control.step_range_start = stop_pc; + } + } + keep_going (ecs); } diff --git a/gdb/symtab.c b/gdb/symtab.c index 27611a34ec4..91d35616eb9 100644 --- a/gdb/symtab.c +++ b/gdb/symtab.c @@ -3282,6 +3282,55 @@ find_pc_line (CORE_ADDR pc, int notcurrent) return sal; } +/* Compare two symtab_and_line entries. Return true if both have + the same line number and the same symtab pointer. That means we + are dealing with two entries from the same line and from the same + source file. + + Return false otherwise. */ + +static bool +sal_line_symtab_matches_p (const symtab_and_line &sal1, + const symtab_and_line &sal2) +{ + return (sal1.line == sal2.line && sal1.symtab == sal2.symtab); +} + +/* See symtah.h. */ + +gdb::optional<CORE_ADDR> +find_line_range_start (CORE_ADDR pc) +{ + struct symtab_and_line current_sal = find_pc_line (pc, 0); + + if (current_sal.line == 0) + return {}; + + struct symtab_and_line prev_sal = find_pc_line (current_sal.pc - 1, 0); + + /* If the previous entry is for a different line, that means we are already + at the entry with the start PC for this line. */ + if (!sal_line_symtab_matches_p (prev_sal, current_sal)) + return current_sal.pc; + + /* Otherwise, keep looking for entries for the same line but with + smaller PC's. */ + bool done = false; + CORE_ADDR prev_pc; + while (!done) + { + prev_pc = prev_sal.pc; + + prev_sal = find_pc_line (prev_pc - 1, 0); + + /* Did we notice a line change? If so, we are done with the search. */ + if (!sal_line_symtab_matches_p (prev_sal, current_sal)) + done = true; + } + + return prev_pc; +} + /* See symtab.h. */ struct symtab * diff --git a/gdb/symtab.h b/gdb/symtab.h index 404d0ab30a8..f54305636da 100644 --- a/gdb/symtab.h +++ b/gdb/symtab.h @@ -2346,6 +2346,22 @@ extern struct symtab_and_line find_pc_line (CORE_ADDR, int); extern struct symtab_and_line find_pc_sect_line (CORE_ADDR, struct obj_section *, int); +/* Given PC, and assuming it is part of a range of addresses that is part of a + line, go back through the linetable and find the starting PC of that + line. + + For example, suppose we have 3 PC ranges for line X: + + Line X - [0x0 - 0x8] + Line X - [0x8 - 0x10] + Line X - [0x10 - 0x18] + + If we call the function with PC == 0x14, we want to return 0x0, as that is + the starting PC of line X, and the ranges are contiguous. +*/ + +extern gdb::optional<CORE_ADDR> find_line_range_start (CORE_ADDR pc); + /* Wrapper around find_pc_line to just return the symtab. */ extern struct symtab *find_pc_line_symtab (CORE_ADDR); diff --git a/gdb/testsuite/gdb.reverse/func-map-to-same-line.c b/gdb/testsuite/gdb.reverse/func-map-to-same-line.c new file mode 100644 index 00000000000..da944874e86 --- /dev/null +++ b/gdb/testsuite/gdb.reverse/func-map-to-same-line.c @@ -0,0 +1,36 @@ +/* Copyright 2008-2023 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 <http://www.gnu.org/licenses/>. + + This test is used to test the reverse-step and reverse-next instruction + execution for a source line that contains multiple function calls. */ + +void +func1 () +{ +} /* END FUNC1 */ + +void +func2 () +{ +} /* END FUNC2 */ + +int main () +{ + int a, b; + a = 1; + b = 2; + func1 (); func2 (); + a = a + b; /* START REVERSE TEST */ +} diff --git a/gdb/testsuite/gdb.reverse/func-map-to-same-line.exp b/gdb/testsuite/gdb.reverse/func-map-to-same-line.exp new file mode 100644 index 00000000000..89e226b0f84 --- /dev/null +++ b/gdb/testsuite/gdb.reverse/func-map-to-same-line.exp @@ -0,0 +1,140 @@ +# Copyright 2008-2023 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 <http://www.gnu.org/licenses/>. */ + +# This file is part of the GDB testsuite. It tests reverse stepping. +# Lots of code borrowed from "step-test.exp". + +# This test checks to make sure there is no regression failures for +# the reverse-next command when stepping back over two functions in +# the same line. + +require supports_reverse + +# This test uses the gcc no-column-info command which was added in gcc 7.1. + +proc run_tests {} { + global srcfile + global executable + + runto_main + set target_remote [gdb_is_target_remote] + + with_test_prefix "test1" { + gdb_test_no_output "record" "turn on process record" + } + + # This regression test verifies the reverse-step and reverse-next commands + # work properly when executing backwards thru a source line containing + # two function calls on the same source line, i.e. func1 (); func2 (); + # This test is compiled so the dwarf info not contain the line table + # information. + + # Test 1, reverse-next command + # Set breakpoint at the line after the function calls. + set bp_start_reverse_test [gdb_get_line_number "START REVERSE TEST" \ + $srcfile] + gdb_breakpoint $srcfile:$bp_start_reverse_test temporary + + # Continue to break point for reverse-next test. + # Command definition: reverse-next [count] + # Run backward to the beginning of the previous line executed in the + # current (innermost) stack frame. If the line contains function calls, + # they will be “un-executed” without stopping. Starting from the first + # line of a function, reverse-next will take you back to the caller of + # that function, before the function was called, just as the normal next + # command would take you from the last line of a function back to its + # return to its caller 2 . + gdb_continue_to_breakpoint \ + "test1: stopped at command reverse-next test start location" \ + ".*$srcfile:$bp_start_reverse_test\r\n.*" + + # The reverse-next should step all the way back to the beginning of the + # line, i.e. at the beginning of the func1 call. + gdb_test "reverse-next" ".*func1 \\(\\); func2 \\(\\);.*" \ + "test1: reverse-next to line with two functions" + + # We should be stopped at the first instruction of the line. A reverse-step + # should step back and stop at the beginning of the previous line b = 2, + # i.e. not in func1 (). + gdb_test "reverse-stepi" ".*b = 2;.*" \ + "test1: reverse-stepi to previous line b = 2" + + + # Setup for test 2 + clean_restart $executable + runto_main + + with_test_prefix "test2" { + gdb_test_no_output "record" "turn on process record" + } + + # Test 2, reverse-step command + # Set breakpoint at the line after the function calls. + gdb_breakpoint $srcfile:$bp_start_reverse_test temporary + + # Continue to the start of the reverse-step test. + # Command definition: reverse-step [count] + # Run the program backward until control reaches the start of a + # different source line; then stop it, and return control to gdb. + # Like the step command, reverse-step will only stop at the beginning + # of a source line. It “un-executes” the previously executed source + # line. If the previous source line included calls to debuggable + # functions, reverse-step will step (backward) into the called function, + # stopping at the beginning of the last statement in the called + # function (typically a return statement). Also, as with the step + # command, if non-debuggable functions are called, reverse-step will + # run thru them backward without stopping. + + gdb_continue_to_breakpoint \ + "test2: stopped at command reverse-step test start location" \ + ".*$srcfile:$bp_start_reverse_test\r\n.*" + + # The first reverse step should take us call of func2 (). + gdb_test "reverse-step" ".*END FUNC2.*" \ + "test2: reverse-step into func2 " + + # The second reverse step should take us into func1 (). + gdb_test "reverse-step" ".*END FUNC1.*" \ + "test2: reverse-step into func1 " + + # The third reverse step should take us call of func1 (). + gdb_test "reverse-step" ".*func1 \\(\\); func2 \\(\\);.*" \ + "test2: reverse-step to line func1(); func2(), at call for func1 " + + # We should be stopped at the first instruction of the line. A reverse + # stepi should take us to b = 2 (). + gdb_test "reverse-stepi" ".*b = 2;.*" \ + "test2: reverse-stepi to line b = 2 " +} + +set srcfile func-map-to-same-line.c +set executable func-map-to-same-line + +# test with and without gcc column info enabled +foreach_with_prefix with_column_info {yes no} { + if {$with_column_info == "yes"} { + set options [list debug column-info] + } else { + set options [list debug no-column-info] + } + + if {[build_executable "failed to prepare" $executable $srcfile \ + $options] == -1} { + return -1 + } + + clean_restart $executable + run_tests +} diff --git a/gdb/testsuite/gdb.reverse/map-to-same-line.c b/gdb/testsuite/gdb.reverse/map-to-same-line.c new file mode 100644 index 00000000000..f20d778f40e --- /dev/null +++ b/gdb/testsuite/gdb.reverse/map-to-same-line.c @@ -0,0 +1,58 @@ +/* Copyright 2008-2023 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 <http://www.gnu.org/licenses/ >. */ + +/* The purpose of this test is to create a DWARF line table that contains two + or more entries for the same line. When stepping (forwards or backwards), + GDB should step over the entire line and not just a particular entry in the + line table. */ + +int +main () +{ /* TAG: main prologue */ + asm ("main_label: .globl main_label"); + int i = 1, j = 2, k; + float f1 = 2.0, f2 = 4.1, f3; + const char *str_1 = "foo", *str_2 = "bar", *str_3; + + asm ("line1: .globl line1"); + k = i; f3 = f1; str_3 = str_1; /* TAG: line 1 */ + + asm ("line2: .globl line2"); + k = j; f3 = f2; str_3 = str_2; /* TAG: line 2 */ + + asm ("line3: .globl line3"); + k = i; f3 = f1; str_3 = str_1; /* TAG: line 3 */ + + asm ("line4: .globl line4"); + k = j; f3 = f2; str_3 = str_2; /* TAG: line 4 */ + + asm ("line5: .globl line5"); + k = i; f3 = f1; str_3 = str_1; /* TAG: line 5 */ + + asm ("line6: .globl line6"); + k = j; f3 = f2; str_3 = str_2; /* TAG: line 6 */ + + asm ("line7: .globl line7"); + k = i; f3 = f1; str_3 = str_1; /* TAG: line 7 */ + + asm ("line8: .globl line8"); + k = j; f3 = f2; str_3 = str_2; /* TAG: line 8 */ + + asm ("main_return: .globl main_return"); + k = j; f3 = f2; str_3 = str_2; /* TAG: main return */ + + asm ("end_of_sequence: .globl end_of_sequence"); + return 0; /* TAG: main return */ +} diff --git a/gdb/testsuite/gdb.reverse/map-to-same-line.exp b/gdb/testsuite/gdb.reverse/map-to-same-line.exp new file mode 100644 index 00000000000..16a359d90ec --- /dev/null +++ b/gdb/testsuite/gdb.reverse/map-to-same-line.exp @@ -0,0 +1,156 @@ +# Copyright 2008-2023 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 <http://www.gnu.org/licenses/ >. + +# When stepping (forwards or backwards), GDB should step over the entire line +# and not just a particular entry in the line table. This test was added to +# verify the find_line_range_start function properly sets the step range for a +# line that consists of multiple statements, i.e. multiple entries in the line +# table. This test creates a DWARF line table that contains two entries for +# the same line to do the needed testing. + +# This test can only be run on targets which support DWARF-2 and use gas. +load_lib dwarf.exp +require dwarf2_support + +# The DWARF assembler requires the gcc compiler. +require is_c_compiler_gcc + +# This test suitable only for process that can do reverse execution +require supports_reverse + +standard_testfile .c .S + +if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile}] } { + return -1 +} + +set asm_file [standard_output_file $srcfile2] +Dwarf::assemble $asm_file { + global srcdir subdir srcfile + declare_labels integer_label L + + # Find start address and length of program + lassign [function_range main [list ${srcdir}/${subdir}/$srcfile]] \ + main_start main_len + set main_end "$main_start + $main_len" + + cu {} { + compile_unit { + {language @DW_LANG_C} + {name map-to-same-line.c} + {stmt_list $L DW_FORM_sec_offset} + {low_pc 0 addr} + } { + subprogram { + {external 1 flag} + {name main} + {low_pc $main_start addr} + {high_pc $main_len DW_FORM_data4} + } + } + } + + lines {version 2 default_is_stmt 1} L { + include_dir "${srcdir}/${subdir}" + file_name "$srcfile" 1 + + # Generate the line table program with distinct source lines being + # mapped to the same line entry. Line 1, 5 and 8 contain 1 statement + # each. Line 2 contains 2 statements. Line 3 contains 3 statements. + program { + DW_LNE_set_address $main_start + line [gdb_get_line_number "TAG: main prologue"] + DW_LNS_copy + DW_LNE_set_address line1 + line [gdb_get_line_number "TAG: line 1" ] + DW_LNS_copy + DW_LNE_set_address line2 + line [gdb_get_line_number "TAG: line 2" ] + DW_LNS_copy + DW_LNE_set_address line3 + line [gdb_get_line_number "TAG: line 2" ] + DW_LNS_copy + DW_LNE_set_address line4 + line [gdb_get_line_number "TAG: line 3" ] + DW_LNS_copy + DW_LNE_set_address line5 + line [gdb_get_line_number "TAG: line 3" ] + DW_LNS_copy + DW_LNE_set_address line6 + line [gdb_get_line_number "TAG: line 3" ] + DW_LNS_copy + DW_LNE_set_address line7 + line [gdb_get_line_number "TAG: line 5" ] + DW_LNS_copy + DW_LNE_set_address line8 + line [gdb_get_line_number "TAG: line 8" ] + DW_LNS_copy + DW_LNE_set_address main_return + line [gdb_get_line_number "TAG: main return"] + DW_LNS_copy + DW_LNE_set_address end_of_sequence + DW_LNE_end_sequence + } + } +} + +if { [prepare_for_testing "failed to prepare" ${testfile} \ + [list $srcfile $asm_file] {nodebug} ] } { + return -1 +} + +runto_main + +# Print the line table +gdb_test_multiple "maint info line-table ${testfile}" "" { + -re "\r\n$decimal\[ \t\]+$decimal\[ \t\]+($hex)\[ \t\]+Y\[^\r\n\]*" { + lappend is_stmt $expect_out(1,string) + exp_continue + } + -re -wrap "" { + } +} + +# Do the reverse-step test +gdb_test_no_output "record" "turn on process record" + +set bp_main_return [gdb_get_line_number "TAG: main return" $srcfile] +gdb_breakpoint $srcfile:$bp_main_return +gdb_continue_to_breakpoint "run to end of main, reverse-step test" ".*$srcfile:$bp_main_return.*" +gdb_test "display \$pc" ".*pc =.*" "display pc, reverse-step test" + +# At this point, GDB has already recorded the execution up until the return +# statement. Reverse-step and test if GDB transitions between lines in the +# expected order. It should reverse-step across lines 8, 5, 3, 2 and 1. +foreach line {8 5 3 2 1} { + gdb_test "reverse-step" ".*TAG: line $line.*" "reverse step to line $line" +} + +## Clean restart, test reverse-next command +clean_restart ${testfile} +runto_main +gdb_test_no_output "record" "turn on process record, reverst-next test" + +set bp_main_return [gdb_get_line_number "TAG: main return" $srcfile] +gdb_breakpoint $srcfile:$bp_main_return +gdb_continue_to_breakpoint "run to end of main, reverse-next test" ".*$srcfile:$bp_main_return.*" +gdb_test "display \$pc" ".*pc =.*" "display pc, reverse-next test" + +# At this point, GDB has already recorded the execution up until the return +# statement. Reverse-next and test if GDB transitions between lines in the +# expected order. It should reverse-next across lines 8, 5, 3, 2 and 1. +foreach line {8 5 3 2 1} { + gdb_test "reverse-next" ".*TAG: line $line.*" "reverse next to line $line" +} -- 2.37.2 ^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH 2/2 v5] Fix reverse stepping multiple contiguous PC ranges over the line table. 2023-05-16 22:54 ` [PATCH 2/2 v5] " Carl Love @ 2023-05-25 15:08 ` Carl Love 2023-06-08 16:36 ` Carl Love 2023-06-19 17:58 ` Simon Marchi 1 sibling, 1 reply; 41+ messages in thread From: Carl Love @ 2023-05-25 15:08 UTC (permalink / raw) To: Simon Marchi, Bruno Larsen, gdb-patches, UlrichWeigand, pedro Cc: luis.machado, cel Ping. Simon: I know Simon said he was busy but wanted to look more at this patch. Just wondering if you have found any time to review the patch further. Thanks Carl On Tue, 2023-05-16 at 15:54 -0700, Carl Love wrote: > Bruno, Simon, GDB maintainers: > > Version 5, changed comments in test case func-map-to-same-line.c. > Patch 1/2 implemented the new options for gdb_compile. Updated the > call to proc run_tests to use the new gdb_compile options in a > foreach_with_prefix loop. > > Version 4, additional fixes for gcc version check, wrap function > calls > using "with_test_prefix", move load_lib dwarf.exe. Fixed typo noted > by > Luis. > > Version 3, added the gcc version check as discussed further from > version 2 of the patch. Also updated the tests to check for > supporting > reverse execution rather than requiring recording. I also noticed > there were a couple more instances of a requirement check, i.e. if [] > which I changed to "require" per the current style for checking on > the > test requirements. > > > The following patch fixes issues on PowerPC with the reverse-step and > reverse-next instructions when there are multiple assignment > statements > on the same line and when there are multiple function calls on the > same > line. The commit log below discusses these issues in further depth. > The discussion included what the correct operation should be for > these > commands based on the GDB documentation. The proposed patch at that > time changed how the commands worked on other platforms such as X86 > in > a way they no longer matched the documentation. > > The issue is the line table contains multiple entries for the same > source line. The patch adds a function to search the line table to > find the address of the first instruction of a line. When setup up > the > reverse stepping range, the function is called to make sure the start > of the range corresponds to the address of the first instruction for > the line. This approach was used. When Luis initially developed the > patch, he considered merging the contiguous ranges in the line table > when reading the line tables. He decided it was better to work with > the > data directly in the line table rather than creating and using a > modified version of the line table. > > The following patch fixes the execution of the reveres-step and > reverse-next commands for both senarios of multiple statements on the > same line for PowerPC and aarch64-linux. Unlike the previous patch, > it > does not change the operation of the commands on other platforms, > i.e. > X86. The patch adds new test cases for both scenarios to verify they > work correctly. > > The patch has been tested on PowerPC, Intel X86 and aarch64-linux > with > no new regression failures. > > Please let me know if the patch is acceptable for mainline. Thanks. > > Carl > > --------------------------------------------- > Fix reverse stepping multiple contiguous PC ranges over the line > table. > > There are a couple of scenarios where the GDB reverse-step and > reverse-next > commands do not work correctly. > > Scenario 1 issue description by Luis Machado: > > When running GDB's testsuite on aarch64-linux/Ubuntu 20.04 (also > spotted on > the ppc backend), I noticed some failures in gdb.reverse/solib- > precsave.exp > and gdb.reverse/solib-reverse.exp. > > The failure happens around the following code: > > 38 b[1] = shr2(17); /* middle part two */ > 40 b[0] = 6; b[1] = 9; /* generic statement, end part two */ > 42 shr1 ("message 1\n"); /* shr1 one */ > > Normal execution: > > - step from line 38 will land on line 40. > - step from line 40 will land on line 42. > > Reverse execution: > - step from line 42 will land on line 40. > - step from line 40 will land on line 40. > - step from line 40 will land on line 38. > > The problem here is that line 40 contains two contiguous but distinct > PC ranges in the line table, like so: > > Line 40 - [0x7ec ~ 0x7f4] > Line 40 - [0x7f4 ~ 0x7fc] > > The two distinct ranges are generated because GCC started outputting > source > column information, which GDB doesn't take into account at the > moment. > > When stepping forward from line 40, we skip both of these ranges and > land on > line 42. When stepping backward from line 42, we stop at the start PC > of the > second (or first, going backwards) range of line 40. > > Since we've reached ecs->event_thread->control.step_range_start, we > stop > stepping backwards. > > --------------------------------------------------------- > > Scenario 2 issue described by Pedro Alves: > > The following explanation of the issue was taken from the gdb mailing > list > discussion of the withdrawn patch to change the behavior of the > reverse-step > and reverse-next commands. Specifically, message from Pedro Alves > <pedro@palves.net> where he demonstrates the issue where you have > multiple > function calls on the same source code line: > > https://sourceware.org/pipermail/gdb-patches/2023-January/196110.html > > The source line looks like: > > func1 (); func2 (); > > so stepping backwards over that line should always stop at the first > instruction of the line, not in the middle. Let's simplify this. > > Here's the full source code of my example: > > (gdb) list 1 > 1 void func1 () > 2 { > 3 } > 4 > 5 void func2 () > 6 { > 7 } > 8 > 9 int main () > 10 { > 11 func1 (); func2 (); > 12 } > > Compiled with: > > $ gcc reverse.c -o reverse -g3 -O0 > $ gcc -v > ... > gcc version 11.3.0 (Ubuntu 11.3.0-1ubuntu1~22.04) > > Now let's debug it with target record, using current gdb git master > (f3d8ae90b236), > without your patch: > > $ gdb ~/reverse > GNU gdb (GDB) 14.0.50.20230124-git > ... > Reading symbols from /home/pedro/reverse... > (gdb) start > Temporary breakpoint 1 at 0x1147: file reverse.c, line 11. > Starting program: /home/pedro/reverse > [Thread debugging using libthread_db enabled] > Using host libthread_db library "/lib/x86_64-linux- > gnu/libthread_db.so.1". > > Temporary breakpoint 1, main () at reverse.c:11 > 11 func1 (); func2 (); > (gdb) record > > (gdb) disassemble /s > Dump of assembler code for function main: > reverse.c: > 10 { > 0x000055555555513f <+0>: endbr64 > 0x0000555555555143 <+4>: push %rbp > 0x0000555555555144 <+5>: mov %rsp,%rbp > > 11 func1 (); func2 (); > => 0x0000555555555147 <+8>: mov $0x0,%eax > 0x000055555555514c <+13>: call 0x555555555129 <func1> > 0x0000555555555151 <+18>: mov $0x0,%eax > 0x0000555555555156 <+23>: call 0x555555555134 <func2> > 0x000055555555515b <+28>: mov $0x0,%eax > > 12 } > 0x0000555555555160 <+33>: pop %rbp > 0x0000555555555161 <+34>: ret > End of assembler dump. > > (gdb) n > 12 } > > So far so good, a "next" stepped over the whole of line 11 and > stopped at line 12. > > Let's confirm where we are now: > > (gdb) disassemble /s > Dump of assembler code for function main: > reverse.c: > 10 { > 0x000055555555513f <+0>: endbr64 > 0x0000555555555143 <+4>: push %rbp > 0x0000555555555144 <+5>: mov %rsp,%rbp > > 11 func1 (); func2 (); > 0x0000555555555147 <+8>: mov $0x0,%eax > 0x000055555555514c <+13>: call 0x555555555129 <func1> > 0x0000555555555151 <+18>: mov $0x0,%eax > 0x0000555555555156 <+23>: call 0x555555555134 <func2> > 0x000055555555515b <+28>: mov $0x0,%eax > > 12 } > => 0x0000555555555160 <+33>: pop %rbp > 0x0000555555555161 <+34>: ret > End of assembler dump. > > Good, we're at the first instruction of line 12. > > Now let's undo the "next", with "reverse-next": > > (gdb) reverse-next > 11 func1 (); func2 (); > > Seemingly stopped at line 11. Let's see exactly where: > > (gdb) disassemble /s > Dump of assembler code for function main: > reverse.c: > 10 { > 0x000055555555513f <+0>: endbr64 > 0x0000555555555143 <+4>: push %rbp > 0x0000555555555144 <+5>: mov %rsp,%rbp > > 11 func1 (); func2 (); > 0x0000555555555147 <+8>: mov $0x0,%eax > 0x000055555555514c <+13>: call 0x555555555129 <func1> > => 0x0000555555555151 <+18>: mov $0x0,%eax > 0x0000555555555156 <+23>: call 0x555555555134 <func2> > 0x000055555555515b <+28>: mov $0x0,%eax > > 12 } > 0x0000555555555160 <+33>: pop %rbp > 0x0000555555555161 <+34>: ret > End of assembler dump. > (gdb) > > And lo, we stopped in the middle of line 11! That is a bug, we > should have > stepped back all the way to the beginning of the line. The "reverse- > next" > should have fully undone the prior "next" command. > > The above issues were fixed by introducing a new function that looks > for > adjacent PC ranges for the same line, until we notice a line change. > Then > we take that as the start PC of the range. The new start PC for the > range > is used for the control.step_range_start when setting up a step > range. > > The test case gdb.reverse/map-to-same-line.exp is added to test the > fix > for the issues in scenario 1. > > The test case gdb.reverse/func-map-to-same-line.exp was added to test > the > fix for scenario 2 when the binary was compiled with and without line > table information. > > bug: https://sourceware.org/bugzilla/show_bug.cgi?id=28426 > > Co-authored-by: Luis Machado <luis.machado@arm.com> > Co-authored-by: Carl Love <cel@us.ibm.com> > Reviewed-By: Bruno Larsen <blarsen@redhat.com> > --- > gdb/infrun.c | 57 +++++++ > gdb/symtab.c | 49 ++++++ > gdb/symtab.h | 16 ++ > .../gdb.reverse/func-map-to-same-line.c | 36 ++++ > .../gdb.reverse/func-map-to-same-line.exp | 140 ++++++++++++++++ > gdb/testsuite/gdb.reverse/map-to-same-line.c | 58 +++++++ > .../gdb.reverse/map-to-same-line.exp | 156 > ++++++++++++++++++ > 7 files changed, 512 insertions(+) > create mode 100644 gdb/testsuite/gdb.reverse/func-map-to-same-line.c > create mode 100644 gdb/testsuite/gdb.reverse/func-map-to-same- > line.exp > create mode 100644 gdb/testsuite/gdb.reverse/map-to-same-line.c > create mode 100644 gdb/testsuite/gdb.reverse/map-to-same-line.exp > > diff --git a/gdb/infrun.c b/gdb/infrun.c > index efe2c00c489..31cd817c733 100644 > --- a/gdb/infrun.c > +++ b/gdb/infrun.c > @@ -114,6 +114,9 @@ static struct async_event_handler > *infrun_async_inferior_event_token; > Starts off as -1, indicating "never enabled/disabled". */ > static int infrun_is_async = -1; > > +static CORE_ADDR update_line_range_start (CORE_ADDR pc, > + struct > execution_control_state *ecs); > + > /* See infrun.h. */ > > void > @@ -6769,6 +6772,25 @@ handle_signal_stop (struct > execution_control_state *ecs) > process_event_stop_test (ecs); > } > > +CORE_ADDR > +update_line_range_start (CORE_ADDR pc, struct > execution_control_state *ecs) > +{ > + /* The line table may have multiple entries for the same source > code line. > + Given the PC, check the line table and return the PC that > corresponds > + to the line table entry for the source line that PC is in. */ > + CORE_ADDR start_line_pc = ecs->event_thread- > >control.step_range_start; > + gdb::optional<CORE_ADDR> real_range_start; > + > + /* Call find_line_range_start to get the smallest address in the > + linetable for multiple Line X entries in the line table. */ > + real_range_start = find_line_range_start (pc); > + > + if (real_range_start.has_value ()) > + start_line_pc = *real_range_start; > + > + return start_line_pc; > +} > + > /* Come here when we've got some debug event / signal we can explain > (IOW, not a random signal), and test whether it should cause a > stop, or whether we should resume the inferior (transparently). > @@ -7570,6 +7592,28 @@ process_event_stop_test (struct > execution_control_state *ecs) > > if (stop_pc_sal.is_stmt) > { > + if (execution_direction == EXEC_REVERSE) > + { > + /* We are stepping backwards make sure we have reached > the > + beginning of the line. */ > + CORE_ADDR stop_pc = ecs->event_thread->stop_pc (); > + CORE_ADDR start_line_pc > + = update_line_range_start (stop_pc, ecs); > + > + if (stop_pc != start_line_pc) > + { > + /* Have not reached the beginning of the source code > line. > + Set a step range. Execution should stop in any > function > + calls we execute back into before reaching the > beginning > + of the line. */ > + ecs->event_thread->control.step_range_start = > start_line_pc; > + ecs->event_thread->control.step_range_end = stop_pc; > + set_step_info (ecs->event_thread, frame, > stop_pc_sal); > + keep_going (ecs); > + return; > + } > + } > + > /* We are at the start of a statement. > > So stop. Note that we don't stop if we step into the > middle of a > @@ -7632,6 +7676,19 @@ process_event_stop_test (struct > execution_control_state *ecs) > set_step_info (ecs->event_thread, frame, stop_pc_sal); > > infrun_debug_printf ("keep going"); > + > + if (execution_direction == EXEC_REVERSE) > + { > + CORE_ADDR stop_pc = ecs->event_thread->stop_pc (); > + > + /* Make sure the stop_pc is set to the beginning of the > line. */ > + if (stop_pc != ecs->event_thread->control.step_range_start) > + { > + stop_pc = update_line_range_start (stop_pc, ecs); > + ecs->event_thread->control.step_range_start = stop_pc; > + } > + } > + > keep_going (ecs); > } > > diff --git a/gdb/symtab.c b/gdb/symtab.c > index 27611a34ec4..91d35616eb9 100644 > --- a/gdb/symtab.c > +++ b/gdb/symtab.c > @@ -3282,6 +3282,55 @@ find_pc_line (CORE_ADDR pc, int notcurrent) > return sal; > } > > +/* Compare two symtab_and_line entries. Return true if both have > + the same line number and the same symtab pointer. That means we > + are dealing with two entries from the same line and from the same > + source file. > + > + Return false otherwise. */ > + > +static bool > +sal_line_symtab_matches_p (const symtab_and_line &sal1, > + const symtab_and_line &sal2) > +{ > + return (sal1.line == sal2.line && sal1.symtab == sal2.symtab); > +} > + > +/* See symtah.h. */ > + > +gdb::optional<CORE_ADDR> > +find_line_range_start (CORE_ADDR pc) > +{ > + struct symtab_and_line current_sal = find_pc_line (pc, 0); > + > + if (current_sal.line == 0) > + return {}; > + > + struct symtab_and_line prev_sal = find_pc_line (current_sal.pc - > 1, 0); > + > + /* If the previous entry is for a different line, that means we > are already > + at the entry with the start PC for this line. */ > + if (!sal_line_symtab_matches_p (prev_sal, current_sal)) > + return current_sal.pc; > + > + /* Otherwise, keep looking for entries for the same line but with > + smaller PC's. */ > + bool done = false; > + CORE_ADDR prev_pc; > + while (!done) > + { > + prev_pc = prev_sal.pc; > + > + prev_sal = find_pc_line (prev_pc - 1, 0); > + > + /* Did we notice a line change? If so, we are done with the > search. */ > + if (!sal_line_symtab_matches_p (prev_sal, current_sal)) > + done = true; > + } > + > + return prev_pc; > +} > + > /* See symtab.h. */ > > struct symtab * > diff --git a/gdb/symtab.h b/gdb/symtab.h > index 404d0ab30a8..f54305636da 100644 > --- a/gdb/symtab.h > +++ b/gdb/symtab.h > @@ -2346,6 +2346,22 @@ extern struct symtab_and_line find_pc_line > (CORE_ADDR, int); > extern struct symtab_and_line find_pc_sect_line (CORE_ADDR, > struct obj_section *, > int); > > +/* Given PC, and assuming it is part of a range of addresses that is > part of a > + line, go back through the linetable and find the starting PC of > that > + line. > + > + For example, suppose we have 3 PC ranges for line X: > + > + Line X - [0x0 - 0x8] > + Line X - [0x8 - 0x10] > + Line X - [0x10 - 0x18] > + > + If we call the function with PC == 0x14, we want to return 0x0, > as that is > + the starting PC of line X, and the ranges are contiguous. > +*/ > + > +extern gdb::optional<CORE_ADDR> find_line_range_start (CORE_ADDR > pc); > + > /* Wrapper around find_pc_line to just return the symtab. */ > > extern struct symtab *find_pc_line_symtab (CORE_ADDR); > diff --git a/gdb/testsuite/gdb.reverse/func-map-to-same-line.c > b/gdb/testsuite/gdb.reverse/func-map-to-same-line.c > new file mode 100644 > index 00000000000..da944874e86 > --- /dev/null > +++ b/gdb/testsuite/gdb.reverse/func-map-to-same-line.c > @@ -0,0 +1,36 @@ > +/* Copyright 2008-2023 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 < > http://www.gnu.org/licenses/>. > + > + This test is used to test the reverse-step and reverse-next > instruction > + execution for a source line that contains multiple function > calls. */ > + > +void > +func1 () > +{ > +} /* END FUNC1 */ > + > +void > +func2 () > +{ > +} /* END FUNC2 */ > + > +int main () > +{ > + int a, b; > + a = 1; > + b = 2; > + func1 (); func2 (); > + a = a + b; /* START REVERSE TEST */ > +} > diff --git a/gdb/testsuite/gdb.reverse/func-map-to-same-line.exp > b/gdb/testsuite/gdb.reverse/func-map-to-same-line.exp > new file mode 100644 > index 00000000000..89e226b0f84 > --- /dev/null > +++ b/gdb/testsuite/gdb.reverse/func-map-to-same-line.exp > @@ -0,0 +1,140 @@ > +# Copyright 2008-2023 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 < > http://www.gnu.org/licenses/>. */ > + > +# This file is part of the GDB testsuite. It tests reverse > stepping. > +# Lots of code borrowed from "step-test.exp". > + > +# This test checks to make sure there is no regression failures for > +# the reverse-next command when stepping back over two functions in > +# the same line. > + > +require supports_reverse > + > +# This test uses the gcc no-column-info command which was added in > gcc 7.1. > + > +proc run_tests {} { > + global srcfile > + global executable > + > + runto_main > + set target_remote [gdb_is_target_remote] > + > + with_test_prefix "test1" { > + gdb_test_no_output "record" "turn on process record" > + } > + > + # This regression test verifies the reverse-step and reverse- > next commands > + # work properly when executing backwards thru a source line > containing > + # two function calls on the same source line, i.e. func1 (); > func2 (); > + # This test is compiled so the dwarf info not contain the line > table > + # information. > + > + # Test 1, reverse-next command > + # Set breakpoint at the line after the function calls. > + set bp_start_reverse_test [gdb_get_line_number "START REVERSE > TEST" \ > + $srcfile] > + gdb_breakpoint $srcfile:$bp_start_reverse_test temporary > + > + # Continue to break point for reverse-next test. > + # Command definition: reverse-next [count] > + # Run backward to the beginning of the previous line executed > in the > + # current (innermost) stack frame. If the line contains > function calls, > + # they will be “un-executed” without stopping. Starting from > the first > + # line of a function, reverse-next will take you back to the > caller of > + # that function, before the function was called, just as the > normal next > + # command would take you from the last line of a function back > to its > + # return to its caller 2 . > + gdb_continue_to_breakpoint \ > + "test1: stopped at command reverse-next test start location" \ > + ".*$srcfile:$bp_start_reverse_test\r\n.*" > + > + # The reverse-next should step all the way back to the beginning > of the > + # line, i.e. at the beginning of the func1 call. > + gdb_test "reverse-next" ".*func1 \\(\\); func2 \\(\\);.*" \ > + "test1: reverse-next to line with two functions" > + > + # We should be stopped at the first instruction of the line. A > reverse-step > + # should step back and stop at the beginning of the previous > line b = 2, > + # i.e. not in func1 (). > + gdb_test "reverse-stepi" ".*b = 2;.*" \ > + "test1: reverse-stepi to previous line b = 2" > + > + > + # Setup for test 2 > + clean_restart $executable > + runto_main > + > + with_test_prefix "test2" { > + gdb_test_no_output "record" "turn on process record" > + } > + > + # Test 2, reverse-step command > + # Set breakpoint at the line after the function calls. > + gdb_breakpoint $srcfile:$bp_start_reverse_test temporary > + > + # Continue to the start of the reverse-step test. > + # Command definition: reverse-step [count] > + # Run the program backward until control reaches the start of > a > + # different source line; then stop it, and return control to > gdb. > + # Like the step command, reverse-step will only stop at the > beginning > + # of a source line. It “un-executes” the previously executed > source > + # line. If the previous source line included calls to > debuggable > + # functions, reverse-step will step (backward) into the > called function, > + # stopping at the beginning of the last statement in the > called > + # function (typically a return statement). Also, as with the > step > + # command, if non-debuggable functions are called, reverse- > step will > + # run thru them backward without stopping. > + > + gdb_continue_to_breakpoint \ > + "test2: stopped at command reverse-step test start location" \ > + ".*$srcfile:$bp_start_reverse_test\r\n.*" > + > + # The first reverse step should take us call of func2 (). > + gdb_test "reverse-step" ".*END FUNC2.*" \ > + "test2: reverse-step into func2 " > + > + # The second reverse step should take us into func1 (). > + gdb_test "reverse-step" ".*END FUNC1.*" \ > + "test2: reverse-step into func1 " > + > + # The third reverse step should take us call of func1 (). > + gdb_test "reverse-step" ".*func1 \\(\\); func2 \\(\\);.*" \ > + "test2: reverse-step to line func1(); func2(), at call for > func1 " > + > + # We should be stopped at the first instruction of the line. A > reverse > + # stepi should take us to b = 2 (). > + gdb_test "reverse-stepi" ".*b = 2;.*" \ > + "test2: reverse-stepi to line b = 2 " > +} > + > +set srcfile func-map-to-same-line.c > +set executable func-map-to-same-line > + > +# test with and without gcc column info enabled > +foreach_with_prefix with_column_info {yes no} { > + if {$with_column_info == "yes"} { > + set options [list debug column-info] > + } else { > + set options [list debug no-column-info] > + } > + > + if {[build_executable "failed to prepare" $executable $srcfile \ > + $options] == -1} { > + return -1 > + } > + > + clean_restart $executable > + run_tests > +} > diff --git a/gdb/testsuite/gdb.reverse/map-to-same-line.c > b/gdb/testsuite/gdb.reverse/map-to-same-line.c > new file mode 100644 > index 00000000000..f20d778f40e > --- /dev/null > +++ b/gdb/testsuite/gdb.reverse/map-to-same-line.c > @@ -0,0 +1,58 @@ > +/* Copyright 2008-2023 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 < > http://www.gnu.org/licenses/ > >. */ > + > +/* The purpose of this test is to create a DWARF line table that > contains two > + or more entries for the same line. When stepping (forwards or > backwards), > + GDB should step over the entire line and not just a particular > entry in the > + line table. */ > + > +int > +main () > +{ /* TAG: main prologue */ > + asm ("main_label: .globl main_label"); > + int i = 1, j = 2, k; > + float f1 = 2.0, f2 = 4.1, f3; > + const char *str_1 = "foo", *str_2 = "bar", *str_3; > + > + asm ("line1: .globl line1"); > + k = i; f3 = f1; str_3 = str_1; /* TAG: line 1 */ > + > + asm ("line2: .globl line2"); > + k = j; f3 = f2; str_3 = str_2; /* TAG: line 2 */ > + > + asm ("line3: .globl line3"); > + k = i; f3 = f1; str_3 = str_1; /* TAG: line 3 */ > + > + asm ("line4: .globl line4"); > + k = j; f3 = f2; str_3 = str_2; /* TAG: line 4 */ > + > + asm ("line5: .globl line5"); > + k = i; f3 = f1; str_3 = str_1; /* TAG: line 5 */ > + > + asm ("line6: .globl line6"); > + k = j; f3 = f2; str_3 = str_2; /* TAG: line 6 */ > + > + asm ("line7: .globl line7"); > + k = i; f3 = f1; str_3 = str_1; /* TAG: line 7 */ > + > + asm ("line8: .globl line8"); > + k = j; f3 = f2; str_3 = str_2; /* TAG: line 8 */ > + > + asm ("main_return: .globl main_return"); > + k = j; f3 = f2; str_3 = str_2; /* TAG: main return */ > + > + asm ("end_of_sequence: .globl end_of_sequence"); > + return 0; /* TAG: main return */ > +} > diff --git a/gdb/testsuite/gdb.reverse/map-to-same-line.exp > b/gdb/testsuite/gdb.reverse/map-to-same-line.exp > new file mode 100644 > index 00000000000..16a359d90ec > --- /dev/null > +++ b/gdb/testsuite/gdb.reverse/map-to-same-line.exp > @@ -0,0 +1,156 @@ > +# Copyright 2008-2023 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 < > http://www.gnu.org/licenses/ > >. > + > +# When stepping (forwards or backwards), GDB should step over the > entire line > +# and not just a particular entry in the line table. This test was > added to > +# verify the find_line_range_start function properly sets the step > range for a > +# line that consists of multiple statements, i.e. multiple entries > in the line > +# table. This test creates a DWARF line table that contains two > entries for > +# the same line to do the needed testing. > + > +# This test can only be run on targets which support DWARF-2 and use > gas. > +load_lib dwarf.exp > +require dwarf2_support > + > +# The DWARF assembler requires the gcc compiler. > +require is_c_compiler_gcc > + > +# This test suitable only for process that can do reverse execution > +require supports_reverse > + > +standard_testfile .c .S > + > +if { [prepare_for_testing "failed to prepare" ${testfile} > ${srcfile}] } { > + return -1 > +} > + > +set asm_file [standard_output_file $srcfile2] > +Dwarf::assemble $asm_file { > + global srcdir subdir srcfile > + declare_labels integer_label L > + > + # Find start address and length of program > + lassign [function_range main [list > ${srcdir}/${subdir}/$srcfile]] \ > + main_start main_len > + set main_end "$main_start + $main_len" > + > + cu {} { > + compile_unit { > + {language @DW_LANG_C} > + {name map-to-same-line.c} > + {stmt_list $L DW_FORM_sec_offset} > + {low_pc 0 addr} > + } { > + subprogram { > + {external 1 flag} > + {name main} > + {low_pc $main_start addr} > + {high_pc $main_len DW_FORM_data4} > + } > + } > + } > + > + lines {version 2 default_is_stmt 1} L { > + include_dir "${srcdir}/${subdir}" > + file_name "$srcfile" 1 > + > + # Generate the line table program with distinct source lines > being > + # mapped to the same line entry. Line 1, 5 and 8 contain 1 > statement > + # each. Line 2 contains 2 statements. Line 3 contains 3 > statements. > + program { > + DW_LNE_set_address $main_start > + line [gdb_get_line_number "TAG: main prologue"] > + DW_LNS_copy > + DW_LNE_set_address line1 > + line [gdb_get_line_number "TAG: line 1" ] > + DW_LNS_copy > + DW_LNE_set_address line2 > + line [gdb_get_line_number "TAG: line 2" ] > + DW_LNS_copy > + DW_LNE_set_address line3 > + line [gdb_get_line_number "TAG: line 2" ] > + DW_LNS_copy > + DW_LNE_set_address line4 > + line [gdb_get_line_number "TAG: line 3" ] > + DW_LNS_copy > + DW_LNE_set_address line5 > + line [gdb_get_line_number "TAG: line 3" ] > + DW_LNS_copy > + DW_LNE_set_address line6 > + line [gdb_get_line_number "TAG: line 3" ] > + DW_LNS_copy > + DW_LNE_set_address line7 > + line [gdb_get_line_number "TAG: line 5" ] > + DW_LNS_copy > + DW_LNE_set_address line8 > + line [gdb_get_line_number "TAG: line 8" ] > + DW_LNS_copy > + DW_LNE_set_address main_return > + line [gdb_get_line_number "TAG: main return"] > + DW_LNS_copy > + DW_LNE_set_address end_of_sequence > + DW_LNE_end_sequence > + } > + } > +} > + > +if { [prepare_for_testing "failed to prepare" ${testfile} \ > + [list $srcfile $asm_file] {nodebug} ] } { > + return -1 > +} > + > +runto_main > + > +# Print the line table > +gdb_test_multiple "maint info line-table ${testfile}" "" { > + -re "\r\n$decimal\[ \t\]+$decimal\[ \t\]+($hex)\[ > \t\]+Y\[^\r\n\]*" { > + lappend is_stmt $expect_out(1,string) > + exp_continue > + } > + -re -wrap "" { > + } > +} > + > +# Do the reverse-step test > +gdb_test_no_output "record" "turn on process record" > + > +set bp_main_return [gdb_get_line_number "TAG: main return" $srcfile] > +gdb_breakpoint $srcfile:$bp_main_return > +gdb_continue_to_breakpoint "run to end of main, reverse-step test" > ".*$srcfile:$bp_main_return.*" > +gdb_test "display \$pc" ".*pc =.*" "display pc, reverse-step test" > + > +# At this point, GDB has already recorded the execution up until the > return > +# statement. Reverse-step and test if GDB transitions between lines > in the > +# expected order. It should reverse-step across lines 8, 5, 3, 2 > and 1. > +foreach line {8 5 3 2 1} { > + gdb_test "reverse-step" ".*TAG: line $line.*" "reverse step to > line $line" > +} > + > +## Clean restart, test reverse-next command > +clean_restart ${testfile} > +runto_main > +gdb_test_no_output "record" "turn on process record, reverst-next > test" > + > +set bp_main_return [gdb_get_line_number "TAG: main return" $srcfile] > +gdb_breakpoint $srcfile:$bp_main_return > +gdb_continue_to_breakpoint "run to end of main, reverse-next test" > ".*$srcfile:$bp_main_return.*" > +gdb_test "display \$pc" ".*pc =.*" "display pc, reverse-next test" > + > +# At this point, GDB has already recorded the execution up until the > return > +# statement. Reverse-next and test if GDB transitions between lines > in the > +# expected order. It should reverse-next across lines 8, 5, 3, 2 > and 1. > +foreach line {8 5 3 2 1} { > + gdb_test "reverse-next" ".*TAG: line $line.*" "reverse next to > line $line" > +} ^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH 2/2 v5] Fix reverse stepping multiple contiguous PC ranges over the line table. 2023-05-25 15:08 ` Carl Love @ 2023-06-08 16:36 ` Carl Love 0 siblings, 0 replies; 41+ messages in thread From: Carl Love @ 2023-06-08 16:36 UTC (permalink / raw) To: Simon Marchi, Bruno Larsen, gdb-patches, UlrichWeigand, pedro Cc: luis.machado, cel Ping. Hoping Simon can take a look at the patch. Thanks. Carl On Thu, 2023-05-25 at 08:08 -0700, Carl Love wrote: > Ping. > > Simon: > > I know Simon said he was busy but wanted to look more at this patch. > Just wondering if you have found any time to review the patch > further. > > Thanks > > Carl > > On Tue, 2023-05-16 at 15:54 -0700, Carl Love wrote: > > Bruno, Simon, GDB maintainers: > > > > Version 5, changed comments in test case func-map-to-same-line.c. > > Patch 1/2 implemented the new options for gdb_compile. Updated the > > call to proc run_tests to use the new gdb_compile options in a > > foreach_with_prefix loop. > > > > Version 4, additional fixes for gcc version check, wrap function > > calls > > using "with_test_prefix", move load_lib dwarf.exe. Fixed typo noted > > by > > Luis. > > > > Version 3, added the gcc version check as discussed further from > > version 2 of the patch. Also updated the tests to check for > > supporting > > reverse execution rather than requiring recording. I also noticed > > there were a couple more instances of a requirement check, i.e. if > > [] > > which I changed to "require" per the current style for checking on > > the > > test requirements. > > > > > > The following patch fixes issues on PowerPC with the reverse-step > > and > > reverse-next instructions when there are multiple assignment > > statements > > on the same line and when there are multiple function calls on the > > same > > line. The commit log below discusses these issues in further > > depth. > > The discussion included what the correct operation should be for > > these > > commands based on the GDB documentation. The proposed patch at > > that > > time changed how the commands worked on other platforms such as X86 > > in > > a way they no longer matched the documentation. > > > > The issue is the line table contains multiple entries for the same > > source line. The patch adds a function to search the line table to > > find the address of the first instruction of a line. When setup up > > the > > reverse stepping range, the function is called to make sure the > > start > > of the range corresponds to the address of the first instruction > > for > > the line. This approach was used. When Luis initially developed > > the > > patch, he considered merging the contiguous ranges in the line > > table > > when reading the line tables. He decided it was better to work with > > the > > data directly in the line table rather than creating and using a > > modified version of the line table. > > > > The following patch fixes the execution of the reveres-step and > > reverse-next commands for both senarios of multiple statements on > > the > > same line for PowerPC and aarch64-linux. Unlike the previous > > patch, > > it > > does not change the operation of the commands on other platforms, > > i.e. > > X86. The patch adds new test cases for both scenarios to verify > > they > > work correctly. > > > > The patch has been tested on PowerPC, Intel X86 and aarch64-linux > > with > > no new regression failures. > > > > Please let me know if the patch is acceptable for > > mainline. Thanks. > > > > Carl > > > > --------------------------------------------- > > Fix reverse stepping multiple contiguous PC ranges over the line > > table. > > > > There are a couple of scenarios where the GDB reverse-step and > > reverse-next > > commands do not work correctly. > > > > Scenario 1 issue description by Luis Machado: > > > > When running GDB's testsuite on aarch64-linux/Ubuntu 20.04 (also > > spotted on > > the ppc backend), I noticed some failures in gdb.reverse/solib- > > precsave.exp > > and gdb.reverse/solib-reverse.exp. > > > > The failure happens around the following code: > > > > 38 b[1] = shr2(17); /* middle part two */ > > 40 b[0] = 6; b[1] = 9; /* generic statement, end part two */ > > 42 shr1 ("message 1\n"); /* shr1 one */ > > > > Normal execution: > > > > - step from line 38 will land on line 40. > > - step from line 40 will land on line 42. > > > > Reverse execution: > > - step from line 42 will land on line 40. > > - step from line 40 will land on line 40. > > - step from line 40 will land on line 38. > > > > The problem here is that line 40 contains two contiguous but > > distinct > > PC ranges in the line table, like so: > > > > Line 40 - [0x7ec ~ 0x7f4] > > Line 40 - [0x7f4 ~ 0x7fc] > > > > The two distinct ranges are generated because GCC started > > outputting > > source > > column information, which GDB doesn't take into account at the > > moment. > > > > When stepping forward from line 40, we skip both of these ranges > > and > > land on > > line 42. When stepping backward from line 42, we stop at the start > > PC > > of the > > second (or first, going backwards) range of line 40. > > > > Since we've reached ecs->event_thread->control.step_range_start, we > > stop > > stepping backwards. > > > > --------------------------------------------------------- > > > > Scenario 2 issue described by Pedro Alves: > > > > The following explanation of the issue was taken from the gdb > > mailing > > list > > discussion of the withdrawn patch to change the behavior of the > > reverse-step > > and reverse-next commands. Specifically, message from Pedro Alves > > <pedro@palves.net> where he demonstrates the issue where you have > > multiple > > function calls on the same source code line: > > > > https://sourceware.org/pipermail/gdb-patches/2023-January/196110.html > > > > The source line looks like: > > > > func1 (); func2 (); > > > > so stepping backwards over that line should always stop at the > > first > > instruction of the line, not in the middle. Let's simplify this. > > > > Here's the full source code of my example: > > > > (gdb) list 1 > > 1 void func1 () > > 2 { > > 3 } > > 4 > > 5 void func2 () > > 6 { > > 7 } > > 8 > > 9 int main () > > 10 { > > 11 func1 (); func2 (); > > 12 } > > > > Compiled with: > > > > $ gcc reverse.c -o reverse -g3 -O0 > > $ gcc -v > > ... > > gcc version 11.3.0 (Ubuntu 11.3.0-1ubuntu1~22.04) > > > > Now let's debug it with target record, using current gdb git master > > (f3d8ae90b236), > > without your patch: > > > > $ gdb ~/reverse > > GNU gdb (GDB) 14.0.50.20230124-git > > ... > > Reading symbols from /home/pedro/reverse... > > (gdb) start > > Temporary breakpoint 1 at 0x1147: file reverse.c, line 11. > > Starting program: /home/pedro/reverse > > [Thread debugging using libthread_db enabled] > > Using host libthread_db library "/lib/x86_64-linux- > > gnu/libthread_db.so.1". > > > > Temporary breakpoint 1, main () at reverse.c:11 > > 11 func1 (); func2 (); > > (gdb) record > > > > (gdb) disassemble /s > > Dump of assembler code for function main: > > reverse.c: > > 10 { > > 0x000055555555513f <+0>: endbr64 > > 0x0000555555555143 <+4>: push %rbp > > 0x0000555555555144 <+5>: mov %rsp,%rbp > > > > 11 func1 (); func2 (); > > => 0x0000555555555147 <+8>: mov $0x0,%eax > > 0x000055555555514c <+13>: call 0x555555555129 <func1> > > 0x0000555555555151 <+18>: mov $0x0,%eax > > 0x0000555555555156 <+23>: call 0x555555555134 <func2> > > 0x000055555555515b <+28>: mov $0x0,%eax > > > > 12 } > > 0x0000555555555160 <+33>: pop %rbp > > 0x0000555555555161 <+34>: ret > > End of assembler dump. > > > > (gdb) n > > 12 } > > > > So far so good, a "next" stepped over the whole of line 11 and > > stopped at line 12. > > > > Let's confirm where we are now: > > > > (gdb) disassemble /s > > Dump of assembler code for function main: > > reverse.c: > > 10 { > > 0x000055555555513f <+0>: endbr64 > > 0x0000555555555143 <+4>: push %rbp > > 0x0000555555555144 <+5>: mov %rsp,%rbp > > > > 11 func1 (); func2 (); > > 0x0000555555555147 <+8>: mov $0x0,%eax > > 0x000055555555514c <+13>: call 0x555555555129 <func1> > > 0x0000555555555151 <+18>: mov $0x0,%eax > > 0x0000555555555156 <+23>: call 0x555555555134 <func2> > > 0x000055555555515b <+28>: mov $0x0,%eax > > > > 12 } > > => 0x0000555555555160 <+33>: pop %rbp > > 0x0000555555555161 <+34>: ret > > End of assembler dump. > > > > Good, we're at the first instruction of line 12. > > > > Now let's undo the "next", with "reverse-next": > > > > (gdb) reverse-next > > 11 func1 (); func2 (); > > > > Seemingly stopped at line 11. Let's see exactly where: > > > > (gdb) disassemble /s > > Dump of assembler code for function main: > > reverse.c: > > 10 { > > 0x000055555555513f <+0>: endbr64 > > 0x0000555555555143 <+4>: push %rbp > > 0x0000555555555144 <+5>: mov %rsp,%rbp > > > > 11 func1 (); func2 (); > > 0x0000555555555147 <+8>: mov $0x0,%eax > > 0x000055555555514c <+13>: call 0x555555555129 <func1> > > => 0x0000555555555151 <+18>: mov $0x0,%eax > > 0x0000555555555156 <+23>: call 0x555555555134 <func2> > > 0x000055555555515b <+28>: mov $0x0,%eax > > > > 12 } > > 0x0000555555555160 <+33>: pop %rbp > > 0x0000555555555161 <+34>: ret > > End of assembler dump. > > (gdb) > > > > And lo, we stopped in the middle of line 11! That is a bug, we > > should have > > stepped back all the way to the beginning of the line. The > > "reverse- > > next" > > should have fully undone the prior "next" command. > > > > The above issues were fixed by introducing a new function that > > looks > > for > > adjacent PC ranges for the same line, until we notice a line > > change. > > Then > > we take that as the start PC of the range. The new start PC for > > the > > range > > is used for the control.step_range_start when setting up a step > > range. > > > > The test case gdb.reverse/map-to-same-line.exp is added to test the > > fix > > for the issues in scenario 1. > > > > The test case gdb.reverse/func-map-to-same-line.exp was added to > > test > > the > > fix for scenario 2 when the binary was compiled with and without > > line > > table information. > > > > bug: https://sourceware.org/bugzilla/show_bug.cgi?id=28426 > > > > Co-authored-by: Luis Machado <luis.machado@arm.com> > > Co-authored-by: Carl Love <cel@us.ibm.com> > > Reviewed-By: Bruno Larsen <blarsen@redhat.com> > > --- > > gdb/infrun.c | 57 +++++++ > > gdb/symtab.c | 49 ++++++ > > gdb/symtab.h | 16 ++ > > .../gdb.reverse/func-map-to-same-line.c | 36 ++++ > > .../gdb.reverse/func-map-to-same-line.exp | 140 > > ++++++++++++++++ > > gdb/testsuite/gdb.reverse/map-to-same-line.c | 58 +++++++ > > .../gdb.reverse/map-to-same-line.exp | 156 > > ++++++++++++++++++ > > 7 files changed, 512 insertions(+) > > create mode 100644 gdb/testsuite/gdb.reverse/func-map-to-same- > > line.c > > create mode 100644 gdb/testsuite/gdb.reverse/func-map-to-same- > > line.exp > > create mode 100644 gdb/testsuite/gdb.reverse/map-to-same-line.c > > create mode 100644 gdb/testsuite/gdb.reverse/map-to-same-line.exp > > > > diff --git a/gdb/infrun.c b/gdb/infrun.c > > index efe2c00c489..31cd817c733 100644 > > --- a/gdb/infrun.c > > +++ b/gdb/infrun.c > > @@ -114,6 +114,9 @@ static struct async_event_handler > > *infrun_async_inferior_event_token; > > Starts off as -1, indicating "never enabled/disabled". */ > > static int infrun_is_async = -1; > > > > +static CORE_ADDR update_line_range_start (CORE_ADDR pc, > > + struct > > execution_control_state *ecs); > > + > > /* See infrun.h. */ > > > > void > > @@ -6769,6 +6772,25 @@ handle_signal_stop (struct > > execution_control_state *ecs) > > process_event_stop_test (ecs); > > } > > > > +CORE_ADDR > > +update_line_range_start (CORE_ADDR pc, struct > > execution_control_state *ecs) > > +{ > > + /* The line table may have multiple entries for the same source > > code line. > > + Given the PC, check the line table and return the PC that > > corresponds > > + to the line table entry for the source line that PC is > > in. */ > > + CORE_ADDR start_line_pc = ecs->event_thread- > > > control.step_range_start; > > + gdb::optional<CORE_ADDR> real_range_start; > > + > > + /* Call find_line_range_start to get the smallest address in the > > + linetable for multiple Line X entries in the line table. */ > > + real_range_start = find_line_range_start (pc); > > + > > + if (real_range_start.has_value ()) > > + start_line_pc = *real_range_start; > > + > > + return start_line_pc; > > +} > > + > > /* Come here when we've got some debug event / signal we can > > explain > > (IOW, not a random signal), and test whether it should cause a > > stop, or whether we should resume the inferior (transparently). > > @@ -7570,6 +7592,28 @@ process_event_stop_test (struct > > execution_control_state *ecs) > > > > if (stop_pc_sal.is_stmt) > > { > > + if (execution_direction == EXEC_REVERSE) > > + { > > + /* We are stepping backwards make sure we have reached > > the > > + beginning of the line. */ > > + CORE_ADDR stop_pc = ecs->event_thread->stop_pc (); > > + CORE_ADDR start_line_pc > > + = update_line_range_start (stop_pc, ecs); > > + > > + if (stop_pc != start_line_pc) > > + { > > + /* Have not reached the beginning of the source code > > line. > > + Set a step range. Execution should stop in any > > function > > + calls we execute back into before reaching the > > beginning > > + of the line. */ > > + ecs->event_thread->control.step_range_start = > > start_line_pc; > > + ecs->event_thread->control.step_range_end = stop_pc; > > + set_step_info (ecs->event_thread, frame, > > stop_pc_sal); > > + keep_going (ecs); > > + return; > > + } > > + } > > + > > /* We are at the start of a statement. > > > > So stop. Note that we don't stop if we step into the > > middle of a > > @@ -7632,6 +7676,19 @@ process_event_stop_test (struct > > execution_control_state *ecs) > > set_step_info (ecs->event_thread, frame, stop_pc_sal); > > > > infrun_debug_printf ("keep going"); > > + > > + if (execution_direction == EXEC_REVERSE) > > + { > > + CORE_ADDR stop_pc = ecs->event_thread->stop_pc (); > > + > > + /* Make sure the stop_pc is set to the beginning of the > > line. */ > > + if (stop_pc != ecs->event_thread->control.step_range_start) > > + { > > + stop_pc = update_line_range_start (stop_pc, ecs); > > + ecs->event_thread->control.step_range_start = stop_pc; > > + } > > + } > > + > > keep_going (ecs); > > } > > > > diff --git a/gdb/symtab.c b/gdb/symtab.c > > index 27611a34ec4..91d35616eb9 100644 > > --- a/gdb/symtab.c > > +++ b/gdb/symtab.c > > @@ -3282,6 +3282,55 @@ find_pc_line (CORE_ADDR pc, int notcurrent) > > return sal; > > } > > > > +/* Compare two symtab_and_line entries. Return true if both have > > + the same line number and the same symtab pointer. That means > > we > > + are dealing with two entries from the same line and from the > > same > > + source file. > > + > > + Return false otherwise. */ > > + > > +static bool > > +sal_line_symtab_matches_p (const symtab_and_line &sal1, > > + const symtab_and_line &sal2) > > +{ > > + return (sal1.line == sal2.line && sal1.symtab == sal2.symtab); > > +} > > + > > +/* See symtah.h. */ > > + > > +gdb::optional<CORE_ADDR> > > +find_line_range_start (CORE_ADDR pc) > > +{ > > + struct symtab_and_line current_sal = find_pc_line (pc, 0); > > + > > + if (current_sal.line == 0) > > + return {}; > > + > > + struct symtab_and_line prev_sal = find_pc_line (current_sal.pc - > > 1, 0); > > + > > + /* If the previous entry is for a different line, that means we > > are already > > + at the entry with the start PC for this line. */ > > + if (!sal_line_symtab_matches_p (prev_sal, current_sal)) > > + return current_sal.pc; > > + > > + /* Otherwise, keep looking for entries for the same line but > > with > > + smaller PC's. */ > > + bool done = false; > > + CORE_ADDR prev_pc; > > + while (!done) > > + { > > + prev_pc = prev_sal.pc; > > + > > + prev_sal = find_pc_line (prev_pc - 1, 0); > > + > > + /* Did we notice a line change? If so, we are done with the > > search. */ > > + if (!sal_line_symtab_matches_p (prev_sal, current_sal)) > > + done = true; > > + } > > + > > + return prev_pc; > > +} > > + > > /* See symtab.h. */ > > > > struct symtab * > > diff --git a/gdb/symtab.h b/gdb/symtab.h > > index 404d0ab30a8..f54305636da 100644 > > --- a/gdb/symtab.h > > +++ b/gdb/symtab.h > > @@ -2346,6 +2346,22 @@ extern struct symtab_and_line find_pc_line > > (CORE_ADDR, int); > > extern struct symtab_and_line find_pc_sect_line (CORE_ADDR, > > struct obj_section *, > > int); > > > > +/* Given PC, and assuming it is part of a range of addresses that > > is > > part of a > > + line, go back through the linetable and find the starting PC of > > that > > + line. > > + > > + For example, suppose we have 3 PC ranges for line X: > > + > > + Line X - [0x0 - 0x8] > > + Line X - [0x8 - 0x10] > > + Line X - [0x10 - 0x18] > > + > > + If we call the function with PC == 0x14, we want to return 0x0, > > as that is > > + the starting PC of line X, and the ranges are contiguous. > > +*/ > > + > > +extern gdb::optional<CORE_ADDR> find_line_range_start (CORE_ADDR > > pc); > > + > > /* Wrapper around find_pc_line to just return the symtab. */ > > > > extern struct symtab *find_pc_line_symtab (CORE_ADDR); > > diff --git a/gdb/testsuite/gdb.reverse/func-map-to-same-line.c > > b/gdb/testsuite/gdb.reverse/func-map-to-same-line.c > > new file mode 100644 > > index 00000000000..da944874e86 > > --- /dev/null > > +++ b/gdb/testsuite/gdb.reverse/func-map-to-same-line.c > > @@ -0,0 +1,36 @@ > > +/* Copyright 2008-2023 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 < > > http://www.gnu.org/licenses/>;. > > + > > + This test is used to test the reverse-step and reverse-next > > instruction > > + execution for a source line that contains multiple function > > calls. */ > > + > > +void > > +func1 () > > +{ > > +} /* END FUNC1 */ > > + > > +void > > +func2 () > > +{ > > +} /* END FUNC2 */ > > + > > +int main () > > +{ > > + int a, b; > > + a = 1; > > + b = 2; > > + func1 (); func2 (); > > + a = a + b; /* START REVERSE TEST */ > > +} > > diff --git a/gdb/testsuite/gdb.reverse/func-map-to-same-line.exp > > b/gdb/testsuite/gdb.reverse/func-map-to-same-line.exp > > new file mode 100644 > > index 00000000000..89e226b0f84 > > --- /dev/null > > +++ b/gdb/testsuite/gdb.reverse/func-map-to-same-line.exp > > @@ -0,0 +1,140 @@ > > +# Copyright 2008-2023 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 < > > http://www.gnu.org/licenses/>. */ > > + > > +# This file is part of the GDB testsuite. It tests reverse > > stepping. > > +# Lots of code borrowed from "step-test.exp". > > + > > +# This test checks to make sure there is no regression failures > > for > > +# the reverse-next command when stepping back over two functions > > in > > +# the same line. > > + > > +require supports_reverse > > + > > +# This test uses the gcc no-column-info command which was added in > > gcc 7.1. > > + > > +proc run_tests {} { > > + global srcfile > > + global executable > > + > > + runto_main > > + set target_remote [gdb_is_target_remote] > > + > > + with_test_prefix "test1" { > > + gdb_test_no_output "record" "turn on process record" > > + } > > + > > + # This regression test verifies the reverse-step and reverse- > > next commands > > + # work properly when executing backwards thru a source line > > containing > > + # two function calls on the same source line, i.e. func1 (); > > func2 (); > > + # This test is compiled so the dwarf info not contain the line > > table > > + # information. > > + > > + # Test 1, reverse-next command > > + # Set breakpoint at the line after the function calls. > > + set bp_start_reverse_test [gdb_get_line_number "START REVERSE > > TEST" \ > > + $srcfile] > > + gdb_breakpoint $srcfile:$bp_start_reverse_test temporary > > + > > + # Continue to break point for reverse-next test. > > + # Command definition: reverse-next [count] > > + # Run backward to the beginning of the previous line > > executed > > in the > > + # current (innermost) stack frame. If the line contains > > function calls, > > + # they will be “un-executed” without stopping. Starting from > > the first > > + # line of a function, reverse-next will take you back to the > > caller of > > + # that function, before the function was called, just as the > > normal next > > + # command would take you from the last line of a function > > back > > to its > > + # return to its caller 2 . > > + gdb_continue_to_breakpoint \ > > + "test1: stopped at command reverse-next test start location" \ > > + ".*$srcfile:$bp_start_reverse_test\r\n.*" > > + > > + # The reverse-next should step all the way back to the > > beginning > > of the > > + # line, i.e. at the beginning of the func1 call. > > + gdb_test "reverse-next" ".*func1 \\(\\); func2 \\(\\);.*" \ > > + "test1: reverse-next to line with two functions" > > + > > + # We should be stopped at the first instruction of the line. A > > reverse-step > > + # should step back and stop at the beginning of the previous > > line b = 2, > > + # i.e. not in func1 (). > > + gdb_test "reverse-stepi" ".*b = 2;.*" \ > > + "test1: reverse-stepi to previous line b = 2" > > + > > + > > + # Setup for test 2 > > + clean_restart $executable > > + runto_main > > + > > + with_test_prefix "test2" { > > + gdb_test_no_output "record" "turn on process record" > > + } > > + > > + # Test 2, reverse-step command > > + # Set breakpoint at the line after the function calls. > > + gdb_breakpoint $srcfile:$bp_start_reverse_test temporary > > + > > + # Continue to the start of the reverse-step test. > > + # Command definition: reverse-step [count] > > + # Run the program backward until control reaches the start > > of > > a > > + # different source line; then stop it, and return control > > to > > gdb. > > + # Like the step command, reverse-step will only stop at the > > beginning > > + # of a source line. It “un-executes” the previously > > executed > > source > > + # line. If the previous source line included calls to > > debuggable > > + # functions, reverse-step will step (backward) into the > > called function, > > + # stopping at the beginning of the last statement in the > > called > > + # function (typically a return statement). Also, as with > > the > > step > > + # command, if non-debuggable functions are called, reverse- > > step will > > + # run thru them backward without stopping. > > + > > + gdb_continue_to_breakpoint \ > > + "test2: stopped at command reverse-step test start location" \ > > + ".*$srcfile:$bp_start_reverse_test\r\n.*" > > + > > + # The first reverse step should take us call of func2 (). > > + gdb_test "reverse-step" ".*END FUNC2.*" \ > > + "test2: reverse-step into func2 " > > + > > + # The second reverse step should take us into func1 (). > > + gdb_test "reverse-step" ".*END FUNC1.*" \ > > + "test2: reverse-step into func1 " > > + > > + # The third reverse step should take us call of func1 (). > > + gdb_test "reverse-step" ".*func1 \\(\\); func2 \\(\\);.*" \ > > + "test2: reverse-step to line func1(); func2(), at call for > > func1 " > > + > > + # We should be stopped at the first instruction of the line. A > > reverse > > + # stepi should take us to b = 2 (). > > + gdb_test "reverse-stepi" ".*b = 2;.*" \ > > + "test2: reverse-stepi to line b = 2 " > > +} > > + > > +set srcfile func-map-to-same-line.c > > +set executable func-map-to-same-line > > + > > +# test with and without gcc column info enabled > > +foreach_with_prefix with_column_info {yes no} { > > + if {$with_column_info == "yes"} { > > + set options [list debug column-info] > > + } else { > > + set options [list debug no-column-info] > > + } > > + > > + if {[build_executable "failed to prepare" $executable $srcfile > > \ > > + $options] == -1} { > > + return -1 > > + } > > + > > + clean_restart $executable > > + run_tests > > +} > > diff --git a/gdb/testsuite/gdb.reverse/map-to-same-line.c > > b/gdb/testsuite/gdb.reverse/map-to-same-line.c > > new file mode 100644 > > index 00000000000..f20d778f40e > > --- /dev/null > > +++ b/gdb/testsuite/gdb.reverse/map-to-same-line.c > > @@ -0,0 +1,58 @@ > > +/* Copyright 2008-2023 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 < > > http://www.gnu.org/licenses/ > > >. */ > > + > > +/* The purpose of this test is to create a DWARF line table that > > contains two > > + or more entries for the same line. When stepping (forwards or > > backwards), > > + GDB should step over the entire line and not just a particular > > entry in the > > + line table. */ > > + > > +int > > +main () > > +{ /* TAG: main prologue */ > > + asm ("main_label: .globl main_label"); > > + int i = 1, j = 2, k; > > + float f1 = 2.0, f2 = 4.1, f3; > > + const char *str_1 = "foo", *str_2 = "bar", *str_3; > > + > > + asm ("line1: .globl line1"); > > + k = i; f3 = f1; str_3 = str_1; /* TAG: line 1 */ > > + > > + asm ("line2: .globl line2"); > > + k = j; f3 = f2; str_3 = str_2; /* TAG: line 2 */ > > + > > + asm ("line3: .globl line3"); > > + k = i; f3 = f1; str_3 = str_1; /* TAG: line 3 */ > > + > > + asm ("line4: .globl line4"); > > + k = j; f3 = f2; str_3 = str_2; /* TAG: line 4 */ > > + > > + asm ("line5: .globl line5"); > > + k = i; f3 = f1; str_3 = str_1; /* TAG: line 5 */ > > + > > + asm ("line6: .globl line6"); > > + k = j; f3 = f2; str_3 = str_2; /* TAG: line 6 */ > > + > > + asm ("line7: .globl line7"); > > + k = i; f3 = f1; str_3 = str_1; /* TAG: line 7 */ > > + > > + asm ("line8: .globl line8"); > > + k = j; f3 = f2; str_3 = str_2; /* TAG: line 8 */ > > + > > + asm ("main_return: .globl main_return"); > > + k = j; f3 = f2; str_3 = str_2; /* TAG: main return */ > > + > > + asm ("end_of_sequence: .globl end_of_sequence"); > > + return 0; /* TAG: main return */ > > +} > > diff --git a/gdb/testsuite/gdb.reverse/map-to-same-line.exp > > b/gdb/testsuite/gdb.reverse/map-to-same-line.exp > > new file mode 100644 > > index 00000000000..16a359d90ec > > --- /dev/null > > +++ b/gdb/testsuite/gdb.reverse/map-to-same-line.exp > > @@ -0,0 +1,156 @@ > > +# Copyright 2008-2023 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 < > > http://www.gnu.org/licenses/ > > >. > > + > > +# When stepping (forwards or backwards), GDB should step over the > > entire line > > +# and not just a particular entry in the line table. This test was > > added to > > +# verify the find_line_range_start function properly sets the step > > range for a > > +# line that consists of multiple statements, i.e. multiple entries > > in the line > > +# table. This test creates a DWARF line table that contains two > > entries for > > +# the same line to do the needed testing. > > + > > +# This test can only be run on targets which support DWARF-2 and > > use > > gas. > > +load_lib dwarf.exp > > +require dwarf2_support > > + > > +# The DWARF assembler requires the gcc compiler. > > +require is_c_compiler_gcc > > + > > +# This test suitable only for process that can do reverse > > execution > > +require supports_reverse > > + > > +standard_testfile .c .S > > + > > +if { [prepare_for_testing "failed to prepare" ${testfile} > > ${srcfile}] } { > > + return -1 > > +} > > + > > +set asm_file [standard_output_file $srcfile2] > > +Dwarf::assemble $asm_file { > > + global srcdir subdir srcfile > > + declare_labels integer_label L > > + > > + # Find start address and length of program > > + lassign [function_range main [list > > ${srcdir}/${subdir}/$srcfile]] \ > > + main_start main_len > > + set main_end "$main_start + $main_len" > > + > > + cu {} { > > + compile_unit { > > + {language @DW_LANG_C} > > + {name map-to-same-line.c} > > + {stmt_list $L DW_FORM_sec_offset} > > + {low_pc 0 addr} > > + } { > > + subprogram { > > + {external 1 flag} > > + {name main} > > + {low_pc $main_start addr} > > + {high_pc $main_len DW_FORM_data4} > > + } > > + } > > + } > > + > > + lines {version 2 default_is_stmt 1} L { > > + include_dir "${srcdir}/${subdir}" > > + file_name "$srcfile" 1 > > + > > + # Generate the line table program with distinct source lines > > being > > + # mapped to the same line entry. Line 1, 5 and 8 contain 1 > > statement > > + # each. Line 2 contains 2 statements. Line 3 contains 3 > > statements. > > + program { > > + DW_LNE_set_address $main_start > > + line [gdb_get_line_number "TAG: main prologue"] > > + DW_LNS_copy > > + DW_LNE_set_address line1 > > + line [gdb_get_line_number "TAG: line 1" ] > > + DW_LNS_copy > > + DW_LNE_set_address line2 > > + line [gdb_get_line_number "TAG: line 2" ] > > + DW_LNS_copy > > + DW_LNE_set_address line3 > > + line [gdb_get_line_number "TAG: line 2" ] > > + DW_LNS_copy > > + DW_LNE_set_address line4 > > + line [gdb_get_line_number "TAG: line 3" ] > > + DW_LNS_copy > > + DW_LNE_set_address line5 > > + line [gdb_get_line_number "TAG: line 3" ] > > + DW_LNS_copy > > + DW_LNE_set_address line6 > > + line [gdb_get_line_number "TAG: line 3" ] > > + DW_LNS_copy > > + DW_LNE_set_address line7 > > + line [gdb_get_line_number "TAG: line 5" ] > > + DW_LNS_copy > > + DW_LNE_set_address line8 > > + line [gdb_get_line_number "TAG: line 8" ] > > + DW_LNS_copy > > + DW_LNE_set_address main_return > > + line [gdb_get_line_number "TAG: main return"] > > + DW_LNS_copy > > + DW_LNE_set_address end_of_sequence > > + DW_LNE_end_sequence > > + } > > + } > > +} > > + > > +if { [prepare_for_testing "failed to prepare" ${testfile} \ > > + [list $srcfile $asm_file] {nodebug} ] } { > > + return -1 > > +} > > + > > +runto_main > > + > > +# Print the line table > > +gdb_test_multiple "maint info line-table ${testfile}" "" { > > + -re "\r\n$decimal\[ \t\]+$decimal\[ \t\]+($hex)\[ > > \t\]+Y\[^\r\n\]*" { > > + lappend is_stmt $expect_out(1,string) > > + exp_continue > > + } > > + -re -wrap "" { > > + } > > +} > > + > > +# Do the reverse-step test > > +gdb_test_no_output "record" "turn on process record" > > + > > +set bp_main_return [gdb_get_line_number "TAG: main return" > > $srcfile] > > +gdb_breakpoint $srcfile:$bp_main_return > > +gdb_continue_to_breakpoint "run to end of main, reverse-step > > test" > > ".*$srcfile:$bp_main_return.*" > > +gdb_test "display \$pc" ".*pc =.*" "display pc, reverse-step test" > > + > > +# At this point, GDB has already recorded the execution up until > > the > > return > > +# statement. Reverse-step and test if GDB transitions between > > lines > > in the > > +# expected order. It should reverse-step across lines 8, 5, 3, 2 > > and 1. > > +foreach line {8 5 3 2 1} { > > + gdb_test "reverse-step" ".*TAG: line $line.*" "reverse step to > > line $line" > > +} > > + > > +## Clean restart, test reverse-next command > > +clean_restart ${testfile} > > +runto_main > > +gdb_test_no_output "record" "turn on process record, reverst-next > > test" > > + > > +set bp_main_return [gdb_get_line_number "TAG: main return" > > $srcfile] > > +gdb_breakpoint $srcfile:$bp_main_return > > +gdb_continue_to_breakpoint "run to end of main, reverse-next > > test" > > ".*$srcfile:$bp_main_return.*" > > +gdb_test "display \$pc" ".*pc =.*" "display pc, reverse-next test" > > + > > +# At this point, GDB has already recorded the execution up until > > the > > return > > +# statement. Reverse-next and test if GDB transitions between > > lines > > in the > > +# expected order. It should reverse-next across lines 8, 5, 3, 2 > > and 1. > > +foreach line {8 5 3 2 1} { > > + gdb_test "reverse-next" ".*TAG: line $line.*" "reverse next to > > line $line" > > +} > > ^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH 2/2 v5] Fix reverse stepping multiple contiguous PC ranges over the line table. 2023-05-16 22:54 ` [PATCH 2/2 v5] " Carl Love 2023-05-25 15:08 ` Carl Love @ 2023-06-19 17:58 ` Simon Marchi 2023-06-22 20:38 ` Carl Love ` (2 more replies) 1 sibling, 3 replies; 41+ messages in thread From: Simon Marchi @ 2023-06-19 17:58 UTC (permalink / raw) To: Carl Love, Bruno Larsen, gdb-patches, UlrichWeigand, pedro; +Cc: luis.machado On 5/16/23 18:54, Carl Love wrote: > Bruno, Simon, GDB maintainers: > > Version 5, changed comments in test case func-map-to-same-line.c. > Patch 1/2 implemented the new options for gdb_compile. Updated the > call to proc run_tests to use the new gdb_compile options in a > foreach_with_prefix loop. > > Version 4, additional fixes for gcc version check, wrap function calls > using "with_test_prefix", move load_lib dwarf.exe. Fixed typo noted by > Luis. > > Version 3, added the gcc version check as discussed further from > version 2 of the patch. Also updated the tests to check for supporting > reverse execution rather than requiring recording. I also noticed > there were a couple more instances of a requirement check, i.e. if [] > which I changed to "require" per the current style for checking on the > test requirements. > > > The following patch fixes issues on PowerPC with the reverse-step and > reverse-next instructions when there are multiple assignment statements > on the same line and when there are multiple function calls on the same > line. The commit log below discusses these issues in further depth. > The discussion included what the correct operation should be for these > commands based on the GDB documentation. The proposed patch at that > time changed how the commands worked on other platforms such as X86 in > a way they no longer matched the documentation. > > The issue is the line table contains multiple entries for the same > source line. The patch adds a function to search the line table to > find the address of the first instruction of a line. When setup up the > reverse stepping range, the function is called to make sure the start > of the range corresponds to the address of the first instruction for > the line. This approach was used. When Luis initially developed the > patch, he considered merging the contiguous ranges in the line table > when reading the line tables. He decided it was better to work with the > data directly in the line table rather than creating and using a > modified version of the line table. > > The following patch fixes the execution of the reveres-step and > reverse-next commands for both senarios of multiple statements on the > same line for PowerPC and aarch64-linux. Unlike the previous patch, it > does not change the operation of the commands on other platforms, i.e. > X86. The patch adds new test cases for both scenarios to verify they > work correctly. > > The patch has been tested on PowerPC, Intel X86 and aarch64-linux with > no new regression failures. > > Please let me know if the patch is acceptable for mainline. Thanks. > > Carl > > --------------------------------------------- > Fix reverse stepping multiple contiguous PC ranges over the line table. > > There are a couple of scenarios where the GDB reverse-step and reverse-next > commands do not work correctly. > > Scenario 1 issue description by Luis Machado: > > When running GDB's testsuite on aarch64-linux/Ubuntu 20.04 (also spotted on > the ppc backend), I noticed some failures in gdb.reverse/solib-precsave.exp > and gdb.reverse/solib-reverse.exp. > > The failure happens around the following code: > > 38 b[1] = shr2(17); /* middle part two */ > 40 b[0] = 6; b[1] = 9; /* generic statement, end part two */ > 42 shr1 ("message 1\n"); /* shr1 one */ > > Normal execution: > > - step from line 38 will land on line 40. > - step from line 40 will land on line 42. > > Reverse execution: > - step from line 42 will land on line 40. > - step from line 40 will land on line 40. > - step from line 40 will land on line 38. > > The problem here is that line 40 contains two contiguous but distinct > PC ranges in the line table, like so: > > Line 40 - [0x7ec ~ 0x7f4] > Line 40 - [0x7f4 ~ 0x7fc] > > The two distinct ranges are generated because GCC started outputting source > column information, which GDB doesn't take into account at the moment. > > When stepping forward from line 40, we skip both of these ranges and land on > line 42. When stepping backward from line 42, we stop at the start PC of the > second (or first, going backwards) range of line 40. > > Since we've reached ecs->event_thread->control.step_range_start, we stop > stepping backwards. > > --------------------------------------------------------- > > Scenario 2 issue described by Pedro Alves: > > The following explanation of the issue was taken from the gdb mailing list > discussion of the withdrawn patch to change the behavior of the reverse-step > and reverse-next commands. Specifically, message from Pedro Alves > <pedro@palves.net> where he demonstrates the issue where you have multiple > function calls on the same source code line: > > https://sourceware.org/pipermail/gdb-patches/2023-January/196110.html > > The source line looks like: > > func1 (); func2 (); > > so stepping backwards over that line should always stop at the first > instruction of the line, not in the middle. Let's simplify this. > > Here's the full source code of my example: > > (gdb) list 1 > 1 void func1 () > 2 { > 3 } > 4 > 5 void func2 () > 6 { > 7 } > 8 > 9 int main () > 10 { > 11 func1 (); func2 (); > 12 } > > Compiled with: > > $ gcc reverse.c -o reverse -g3 -O0 > $ gcc -v > ... > gcc version 11.3.0 (Ubuntu 11.3.0-1ubuntu1~22.04) > > Now let's debug it with target record, using current gdb git master (f3d8ae90b236), > without your patch: > > $ gdb ~/reverse > GNU gdb (GDB) 14.0.50.20230124-git > ... > Reading symbols from /home/pedro/reverse... > (gdb) start > Temporary breakpoint 1 at 0x1147: file reverse.c, line 11. > Starting program: /home/pedro/reverse > [Thread debugging using libthread_db enabled] > Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". > > Temporary breakpoint 1, main () at reverse.c:11 > 11 func1 (); func2 (); > (gdb) record > > (gdb) disassemble /s > Dump of assembler code for function main: > reverse.c: > 10 { > 0x000055555555513f <+0>: endbr64 > 0x0000555555555143 <+4>: push %rbp > 0x0000555555555144 <+5>: mov %rsp,%rbp > > 11 func1 (); func2 (); > => 0x0000555555555147 <+8>: mov $0x0,%eax > 0x000055555555514c <+13>: call 0x555555555129 <func1> > 0x0000555555555151 <+18>: mov $0x0,%eax > 0x0000555555555156 <+23>: call 0x555555555134 <func2> > 0x000055555555515b <+28>: mov $0x0,%eax > > 12 } > 0x0000555555555160 <+33>: pop %rbp > 0x0000555555555161 <+34>: ret > End of assembler dump. > > (gdb) n > 12 } > > So far so good, a "next" stepped over the whole of line 11 and stopped at line 12. > > Let's confirm where we are now: > > (gdb) disassemble /s > Dump of assembler code for function main: > reverse.c: > 10 { > 0x000055555555513f <+0>: endbr64 > 0x0000555555555143 <+4>: push %rbp > 0x0000555555555144 <+5>: mov %rsp,%rbp > > 11 func1 (); func2 (); > 0x0000555555555147 <+8>: mov $0x0,%eax > 0x000055555555514c <+13>: call 0x555555555129 <func1> > 0x0000555555555151 <+18>: mov $0x0,%eax > 0x0000555555555156 <+23>: call 0x555555555134 <func2> > 0x000055555555515b <+28>: mov $0x0,%eax > > 12 } > => 0x0000555555555160 <+33>: pop %rbp > 0x0000555555555161 <+34>: ret > End of assembler dump. > > Good, we're at the first instruction of line 12. > > Now let's undo the "next", with "reverse-next": > > (gdb) reverse-next > 11 func1 (); func2 (); > > Seemingly stopped at line 11. Let's see exactly where: > > (gdb) disassemble /s > Dump of assembler code for function main: > reverse.c: > 10 { > 0x000055555555513f <+0>: endbr64 > 0x0000555555555143 <+4>: push %rbp > 0x0000555555555144 <+5>: mov %rsp,%rbp > > 11 func1 (); func2 (); > 0x0000555555555147 <+8>: mov $0x0,%eax > 0x000055555555514c <+13>: call 0x555555555129 <func1> > => 0x0000555555555151 <+18>: mov $0x0,%eax > 0x0000555555555156 <+23>: call 0x555555555134 <func2> > 0x000055555555515b <+28>: mov $0x0,%eax > > 12 } > 0x0000555555555160 <+33>: pop %rbp > 0x0000555555555161 <+34>: ret > End of assembler dump. > (gdb) > > And lo, we stopped in the middle of line 11! That is a bug, we should have > stepped back all the way to the beginning of the line. The "reverse-next" > should have fully undone the prior "next" command. > > The above issues were fixed by introducing a new function that looks for > adjacent PC ranges for the same line, until we notice a line change. Then > we take that as the start PC of the range. The new start PC for the range > is used for the control.step_range_start when setting up a step range. > > The test case gdb.reverse/map-to-same-line.exp is added to test the fix > for the issues in scenario 1. > > The test case gdb.reverse/func-map-to-same-line.exp was added to test the > fix for scenario 2 when the binary was compiled with and without line > table information. > > bug: https://sourceware.org/bugzilla/show_bug.cgi?id=28426 > > Co-authored-by: Luis Machado <luis.machado@arm.com> > Co-authored-by: Carl Love <cel@us.ibm.com> > Reviewed-By: Bruno Larsen <blarsen@redhat.com> > --- > gdb/infrun.c | 57 +++++++ > gdb/symtab.c | 49 ++++++ > gdb/symtab.h | 16 ++ > .../gdb.reverse/func-map-to-same-line.c | 36 ++++ > .../gdb.reverse/func-map-to-same-line.exp | 140 ++++++++++++++++ > gdb/testsuite/gdb.reverse/map-to-same-line.c | 58 +++++++ > .../gdb.reverse/map-to-same-line.exp | 156 ++++++++++++++++++ > 7 files changed, 512 insertions(+) > create mode 100644 gdb/testsuite/gdb.reverse/func-map-to-same-line.c > create mode 100644 gdb/testsuite/gdb.reverse/func-map-to-same-line.exp > create mode 100644 gdb/testsuite/gdb.reverse/map-to-same-line.c > create mode 100644 gdb/testsuite/gdb.reverse/map-to-same-line.exp > > diff --git a/gdb/infrun.c b/gdb/infrun.c > index efe2c00c489..31cd817c733 100644 > --- a/gdb/infrun.c > +++ b/gdb/infrun.c > @@ -114,6 +114,9 @@ static struct async_event_handler *infrun_async_inferior_event_token; > Starts off as -1, indicating "never enabled/disabled". */ > static int infrun_is_async = -1; > > +static CORE_ADDR update_line_range_start (CORE_ADDR pc, > + struct execution_control_state *ecs); > + This forward-declaration is not needed. > /* See infrun.h. */ > > void > @@ -6769,6 +6772,25 @@ handle_signal_stop (struct execution_control_state *ecs) > process_event_stop_test (ecs); > } > > +CORE_ADDR > +update_line_range_start (CORE_ADDR pc, struct execution_control_state *ecs) Please add a comment for the function. > +{ > + /* The line table may have multiple entries for the same source code line. > + Given the PC, check the line table and return the PC that corresponds > + to the line table entry for the source line that PC is in. */ > + CORE_ADDR start_line_pc = ecs->event_thread->control.step_range_start; > + gdb::optional<CORE_ADDR> real_range_start; > + > + /* Call find_line_range_start to get the smallest address in the > + linetable for multiple Line X entries in the line table. */ > + real_range_start = find_line_range_start (pc); > + > + if (real_range_start.has_value ()) > + start_line_pc = *real_range_start; > + > + return start_line_pc; When I read this, I wonder: why was control.step_range_start not set to the "real" range start in the first place (not only in the context of reverse execution, every time it is set)? It would seem more robust than patching it afterwards in some very specific spots. I could see some benefits for range-stepping uses cases too (relevant when debugging remotely). Using your example here: Line X - [0x0 - 0x8] Line X - [0x8 - 0x10] Line X - [0x10 - 0x18] Imagine we are stopped at 0x14, and we type "next", and 0x14 is a conditional jump to 0x5. It seems like current GDB would send a "range step" request to GDBserver, to step in the [0x10, 0x18[ range. When reaching 0x5, execution would stop, and GDB would resume it again with the [0x0,0x8[ range. When reaching 0x8, it would stop again, GDB would resume it with [0x8,0x10[, and so on. If GDB could send a "range step" request with the [0x0,0x18[ range, it would avoid those unnecessary intermediary stop. > +} > + > /* Come here when we've got some debug event / signal we can explain > (IOW, not a random signal), and test whether it should cause a > stop, or whether we should resume the inferior (transparently). > @@ -7570,6 +7592,28 @@ process_event_stop_test (struct execution_control_state *ecs) > > if (stop_pc_sal.is_stmt) > { > + if (execution_direction == EXEC_REVERSE) > + { > + /* We are stepping backwards make sure we have reached the > + beginning of the line. */ > + CORE_ADDR stop_pc = ecs->event_thread->stop_pc (); > + CORE_ADDR start_line_pc > + = update_line_range_start (stop_pc, ecs); > + > + if (stop_pc != start_line_pc) > + { > + /* Have not reached the beginning of the source code line. > + Set a step range. Execution should stop in any function > + calls we execute back into before reaching the beginning > + of the line. */ > + ecs->event_thread->control.step_range_start = start_line_pc; > + ecs->event_thread->control.step_range_end = stop_pc; > + set_step_info (ecs->event_thread, frame, stop_pc_sal); > + keep_going (ecs); > + return; > + } > + } > + > /* We are at the start of a statement. > > So stop. Note that we don't stop if we step into the middle of a > @@ -7632,6 +7676,19 @@ process_event_stop_test (struct execution_control_state *ecs) > set_step_info (ecs->event_thread, frame, stop_pc_sal); > > infrun_debug_printf ("keep going"); > + > + if (execution_direction == EXEC_REVERSE) > + { > + CORE_ADDR stop_pc = ecs->event_thread->stop_pc (); > + > + /* Make sure the stop_pc is set to the beginning of the line. */ > + if (stop_pc != ecs->event_thread->control.step_range_start) > + { > + stop_pc = update_line_range_start (stop_pc, ecs); > + ecs->event_thread->control.step_range_start = stop_pc; > + } > + } > + > keep_going (ecs); > } > > diff --git a/gdb/symtab.c b/gdb/symtab.c > index 27611a34ec4..91d35616eb9 100644 > --- a/gdb/symtab.c > +++ b/gdb/symtab.c > @@ -3282,6 +3282,55 @@ find_pc_line (CORE_ADDR pc, int notcurrent) > return sal; > } > > +/* Compare two symtab_and_line entries. Return true if both have > + the same line number and the same symtab pointer. That means we > + are dealing with two entries from the same line and from the same > + source file. > + > + Return false otherwise. */ > + > +static bool > +sal_line_symtab_matches_p (const symtab_and_line &sal1, > + const symtab_and_line &sal2) > +{ > + return (sal1.line == sal2.line && sal1.symtab == sal2.symtab); Unnecessary parenthesis. > +} > + > +/* See symtah.h. */ > + > +gdb::optional<CORE_ADDR> > +find_line_range_start (CORE_ADDR pc) > +{ > + struct symtab_and_line current_sal = find_pc_line (pc, 0); > + > + if (current_sal.line == 0) > + return {}; > + > + struct symtab_and_line prev_sal = find_pc_line (current_sal.pc - 1, 0); > + > + /* If the previous entry is for a different line, that means we are already > + at the entry with the start PC for this line. */ > + if (!sal_line_symtab_matches_p (prev_sal, current_sal)) > + return current_sal.pc; > + > + /* Otherwise, keep looking for entries for the same line but with > + smaller PC's. */ > + bool done = false; > + CORE_ADDR prev_pc; > + while (!done) > + { > + prev_pc = prev_sal.pc; > + > + prev_sal = find_pc_line (prev_pc - 1, 0); > + > + /* Did we notice a line change? If so, we are done with the search. */ > + if (!sal_line_symtab_matches_p (prev_sal, current_sal)) > + done = true; > + } > + > + return prev_pc; Algorithmic complexity question: given that line tables are sorted by address, would it work to start at the current line table item, and go look at the previous ones until we find one that is no longer contiguous and same line? find_pc_line is somewhat heavy, so if we don't need to do it repeatedly... > +} > + > /* See symtab.h. */ > > struct symtab * > diff --git a/gdb/symtab.h b/gdb/symtab.h > index 404d0ab30a8..f54305636da 100644 > --- a/gdb/symtab.h > +++ b/gdb/symtab.h > @@ -2346,6 +2346,22 @@ extern struct symtab_and_line find_pc_line (CORE_ADDR, int); > extern struct symtab_and_line find_pc_sect_line (CORE_ADDR, > struct obj_section *, int); > > +/* Given PC, and assuming it is part of a range of addresses that is part of a > + line, go back through the linetable and find the starting PC of that > + line. > + > + For example, suppose we have 3 PC ranges for line X: > + > + Line X - [0x0 - 0x8] > + Line X - [0x8 - 0x10] > + Line X - [0x10 - 0x18] > + > + If we call the function with PC == 0x14, we want to return 0x0, as that is > + the starting PC of line X, and the ranges are contiguous. I think that putting this example in the comment is great. It makes it much more obvious what the function specifically does. > +*/ > + > +extern gdb::optional<CORE_ADDR> find_line_range_start (CORE_ADDR pc); > + > /* Wrapper around find_pc_line to just return the symtab. */ > > extern struct symtab *find_pc_line_symtab (CORE_ADDR); > diff --git a/gdb/testsuite/gdb.reverse/func-map-to-same-line.c b/gdb/testsuite/gdb.reverse/func-map-to-same-line.c > new file mode 100644 > index 00000000000..da944874e86 > --- /dev/null > +++ b/gdb/testsuite/gdb.reverse/func-map-to-same-line.c > @@ -0,0 +1,36 @@ > +/* Copyright 2008-2023 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 <http://www.gnu.org/licenses/>. > + > + This test is used to test the reverse-step and reverse-next instruction > + execution for a source line that contains multiple function calls. */ > + > +void > +func1 () > +{ > +} /* END FUNC1 */ > + > +void > +func2 () > +{ > +} /* END FUNC2 */ > + > +int main () int main (void) > +{ > + int a, b; > + a = 1; > + b = 2; > + func1 (); func2 (); > + a = a + b; /* START REVERSE TEST */ > +} > diff --git a/gdb/testsuite/gdb.reverse/func-map-to-same-line.exp b/gdb/testsuite/gdb.reverse/func-map-to-same-line.exp > new file mode 100644 > index 00000000000..89e226b0f84 > --- /dev/null > +++ b/gdb/testsuite/gdb.reverse/func-map-to-same-line.exp > @@ -0,0 +1,140 @@ > +# Copyright 2008-2023 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 <http://www.gnu.org/licenses/>. */ > + > +# This file is part of the GDB testsuite. It tests reverse stepping. > +# Lots of code borrowed from "step-test.exp". > + > +# This test checks to make sure there is no regression failures for > +# the reverse-next command when stepping back over two functions in > +# the same line. > + > +require supports_reverse > + > +# This test uses the gcc no-column-info command which was added in gcc 7.1. > + > +proc run_tests {} { > + global srcfile > + global executable > + > + runto_main We typically check for runto_main's success: if { ![runto_main] } { return } runto_main logs a FAIL on failure. There are a few runto_mains in the patch. > + set target_remote [gdb_is_target_remote] target_remote seems unused > + > + with_test_prefix "test1" { > + gdb_test_no_output "record" "turn on process record" > + } with_test_prefix with a single test in it is really just the same as: gdb_test_no_output "record" "test1: turn on process record" In fact, you have some other tests with the "test1:" or "test2:" prefix, I think they should be moved to the with_test_prefix. And maybe use "next" and "step" instead of "test1" and "test2". > + > + # This regression test verifies the reverse-step and reverse-next commands > + # work properly when executing backwards thru a source line containing > + # two function calls on the same source line, i.e. func1 (); func2 (); > + # This test is compiled so the dwarf info not contain the line table > + # information. > + > + # Test 1, reverse-next command > + # Set breakpoint at the line after the function calls. > + set bp_start_reverse_test [gdb_get_line_number "START REVERSE TEST" \ > + $srcfile] > + gdb_breakpoint $srcfile:$bp_start_reverse_test temporary > + > + # Continue to break point for reverse-next test. > + # Command definition: reverse-next [count] > + # Run backward to the beginning of the previous line executed in the > + # current (innermost) stack frame. If the line contains function calls, > + # they will be “un-executed” without stopping. Starting from the first > + # line of a function, reverse-next will take you back to the caller of > + # that function, before the function was called, just as the normal next > + # command would take you from the last line of a function back to its > + # return to its caller 2 . > + gdb_continue_to_breakpoint \ > + "test1: stopped at command reverse-next test start location" \ > + ".*$srcfile:$bp_start_reverse_test\r\n.*" > + > + # The reverse-next should step all the way back to the beginning of the > + # line, i.e. at the beginning of the func1 call. > + gdb_test "reverse-next" ".*func1 \\(\\); func2 \\(\\);.*" \ > + "test1: reverse-next to line with two functions" > + > + # We should be stopped at the first instruction of the line. A reverse-step > + # should step back and stop at the beginning of the previous line b = 2, > + # i.e. not in func1 (). > + gdb_test "reverse-stepi" ".*b = 2;.*" \ > + "test1: reverse-stepi to previous line b = 2" > + > + > + # Setup for test 2 > + clean_restart $executable > + runto_main > + > + with_test_prefix "test2" { > + gdb_test_no_output "record" "turn on process record" > + } > + > + # Test 2, reverse-step command > + # Set breakpoint at the line after the function calls. > + gdb_breakpoint $srcfile:$bp_start_reverse_test temporary > + > + # Continue to the start of the reverse-step test. > + # Command definition: reverse-step [count] > + # Run the program backward until control reaches the start of a > + # different source line; then stop it, and return control to gdb. > + # Like the step command, reverse-step will only stop at the beginning > + # of a source line. It “un-executes” the previously executed source > + # line. If the previous source line included calls to debuggable > + # functions, reverse-step will step (backward) into the called function, > + # stopping at the beginning of the last statement in the called > + # function (typically a return statement). Also, as with the step > + # command, if non-debuggable functions are called, reverse-step will > + # run thru them backward without stopping. > + > + gdb_continue_to_breakpoint \ > + "test2: stopped at command reverse-step test start location" \ > + ".*$srcfile:$bp_start_reverse_test\r\n.*" > + > + # The first reverse step should take us call of func2 (). > + gdb_test "reverse-step" ".*END FUNC2.*" \ > + "test2: reverse-step into func2 " > + > + # The second reverse step should take us into func1 (). > + gdb_test "reverse-step" ".*END FUNC1.*" \ > + "test2: reverse-step into func1 " > + > + # The third reverse step should take us call of func1 (). > + gdb_test "reverse-step" ".*func1 \\(\\); func2 \\(\\);.*" \ > + "test2: reverse-step to line func1(); func2(), at call for func1 " > + > + # We should be stopped at the first instruction of the line. A reverse > + # stepi should take us to b = 2 (). > + gdb_test "reverse-stepi" ".*b = 2;.*" \ > + "test2: reverse-stepi to line b = 2 " > +} > + > +set srcfile func-map-to-same-line.c > +set executable func-map-to-same-line Wondering if this test should use standard_testfile (like almost every other tests) to set these. > + > +# test with and without gcc column info enabled > +foreach_with_prefix with_column_info {yes no} { > + if {$with_column_info == "yes"} { > + set options [list debug column-info] > + } else { > + set options [list debug no-column-info] > + } I didn't think of this when proposing the foreach_with_prefix, but you could perhaps use: foreach_with_prefix column_info_flag {column-info no-column-info} ... to avoid this boilerplate. You can then use $column_info_flag directly when setting options. > + > + if {[build_executable "failed to prepare" $executable $srcfile \ > + $options] == -1} { > + return -1 > + } > + > + clean_restart $executable clean_restart can go in run_tests. > + run_tests > +} > diff --git a/gdb/testsuite/gdb.reverse/map-to-same-line.c b/gdb/testsuite/gdb.reverse/map-to-same-line.c > new file mode 100644 > index 00000000000..f20d778f40e > --- /dev/null > +++ b/gdb/testsuite/gdb.reverse/map-to-same-line.c > @@ -0,0 +1,58 @@ > +/* Copyright 2008-2023 Free Software Foundation, Inc. Just wondering if the copyright years are right. > + > + 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 <http://www.gnu.org/licenses/ >. */ > + > +/* The purpose of this test is to create a DWARF line table that contains two > + or more entries for the same line. When stepping (forwards or backwards), > + GDB should step over the entire line and not just a particular entry in the > + line table. */ > + > +int > +main () void in the parenthesis > +{ /* TAG: main prologue */ > + asm ("main_label: .globl main_label"); > + int i = 1, j = 2, k; > + float f1 = 2.0, f2 = 4.1, f3; > + const char *str_1 = "foo", *str_2 = "bar", *str_3; > + > + asm ("line1: .globl line1"); > + k = i; f3 = f1; str_3 = str_1; /* TAG: line 1 */ > + > + asm ("line2: .globl line2"); > + k = j; f3 = f2; str_3 = str_2; /* TAG: line 2 */ > + > + asm ("line3: .globl line3"); > + k = i; f3 = f1; str_3 = str_1; /* TAG: line 3 */ > + > + asm ("line4: .globl line4"); > + k = j; f3 = f2; str_3 = str_2; /* TAG: line 4 */ > + > + asm ("line5: .globl line5"); > + k = i; f3 = f1; str_3 = str_1; /* TAG: line 5 */ > + > + asm ("line6: .globl line6"); > + k = j; f3 = f2; str_3 = str_2; /* TAG: line 6 */ > + > + asm ("line7: .globl line7"); > + k = i; f3 = f1; str_3 = str_1; /* TAG: line 7 */ > + > + asm ("line8: .globl line8"); > + k = j; f3 = f2; str_3 = str_2; /* TAG: line 8 */ > + > + asm ("main_return: .globl main_return"); > + k = j; f3 = f2; str_3 = str_2; /* TAG: main return */ > + > + asm ("end_of_sequence: .globl end_of_sequence"); > + return 0; /* TAG: main return */ > +} > diff --git a/gdb/testsuite/gdb.reverse/map-to-same-line.exp b/gdb/testsuite/gdb.reverse/map-to-same-line.exp > new file mode 100644 > index 00000000000..16a359d90ec > --- /dev/null > +++ b/gdb/testsuite/gdb.reverse/map-to-same-line.exp > @@ -0,0 +1,156 @@ > +# Copyright 2008-2023 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 <http://www.gnu.org/licenses/ >. > + > +# When stepping (forwards or backwards), GDB should step over the entire line > +# and not just a particular entry in the line table. This test was added to > +# verify the find_line_range_start function properly sets the step range for a > +# line that consists of multiple statements, i.e. multiple entries in the line > +# table. This test creates a DWARF line table that contains two entries for > +# the same line to do the needed testing. > + > +# This test can only be run on targets which support DWARF-2 and use gas. > +load_lib dwarf.exp > +require dwarf2_support > + > +# The DWARF assembler requires the gcc compiler. > +require is_c_compiler_gcc > + > +# This test suitable only for process that can do reverse execution > +require supports_reverse > + > +standard_testfile .c .S > + > +if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile}] } { > + return -1 > +} > + > +set asm_file [standard_output_file $srcfile2] > +Dwarf::assemble $asm_file { > + global srcdir subdir srcfile > + declare_labels integer_label L > + > + # Find start address and length of program > + lassign [function_range main [list ${srcdir}/${subdir}/$srcfile]] \ > + main_start main_len > + set main_end "$main_start + $main_len" > + > + cu {} { > + compile_unit { > + {language @DW_LANG_C} > + {name map-to-same-line.c} > + {stmt_list $L DW_FORM_sec_offset} > + {low_pc 0 addr} > + } { > + subprogram { > + {external 1 flag} > + {name main} > + {low_pc $main_start addr} > + {high_pc $main_len DW_FORM_data4} > + } > + } > + } > + > + lines {version 2 default_is_stmt 1} L { > + include_dir "${srcdir}/${subdir}" > + file_name "$srcfile" 1 > + > + # Generate the line table program with distinct source lines being > + # mapped to the same line entry. Line 1, 5 and 8 contain 1 statement > + # each. Line 2 contains 2 statements. Line 3 contains 3 statements. > + program { > + DW_LNE_set_address $main_start > + line [gdb_get_line_number "TAG: main prologue"] > + DW_LNS_copy > + DW_LNE_set_address line1 > + line [gdb_get_line_number "TAG: line 1" ] > + DW_LNS_copy > + DW_LNE_set_address line2 > + line [gdb_get_line_number "TAG: line 2" ] > + DW_LNS_copy > + DW_LNE_set_address line3 > + line [gdb_get_line_number "TAG: line 2" ] > + DW_LNS_copy > + DW_LNE_set_address line4 > + line [gdb_get_line_number "TAG: line 3" ] > + DW_LNS_copy > + DW_LNE_set_address line5 > + line [gdb_get_line_number "TAG: line 3" ] > + DW_LNS_copy > + DW_LNE_set_address line6 > + line [gdb_get_line_number "TAG: line 3" ] > + DW_LNS_copy > + DW_LNE_set_address line7 > + line [gdb_get_line_number "TAG: line 5" ] > + DW_LNS_copy > + DW_LNE_set_address line8 > + line [gdb_get_line_number "TAG: line 8" ] > + DW_LNS_copy > + DW_LNE_set_address main_return > + line [gdb_get_line_number "TAG: main return"] > + DW_LNS_copy > + DW_LNE_set_address end_of_sequence > + DW_LNE_end_sequence > + } > + } > +} > + > +if { [prepare_for_testing "failed to prepare" ${testfile} \ > + [list $srcfile $asm_file] {nodebug} ] } { > + return -1 > +} > + > +runto_main > + > +# Print the line table > +gdb_test_multiple "maint info line-table ${testfile}" "" { > + -re "\r\n$decimal\[ \t\]+$decimal\[ \t\]+($hex)\[ \t\]+Y\[^\r\n\]*" { > + lappend is_stmt $expect_out(1,string) > + exp_continue > + } > + -re -wrap "" { > + } > +} > + > +# Do the reverse-step test > +gdb_test_no_output "record" "turn on process record" > + > +set bp_main_return [gdb_get_line_number "TAG: main return" $srcfile] > +gdb_breakpoint $srcfile:$bp_main_return > +gdb_continue_to_breakpoint "run to end of main, reverse-step test" ".*$srcfile:$bp_main_return.*" > +gdb_test "display \$pc" ".*pc =.*" "display pc, reverse-step test" > + > +# At this point, GDB has already recorded the execution up until the return > +# statement. Reverse-step and test if GDB transitions between lines in the > +# expected order. It should reverse-step across lines 8, 5, 3, 2 and 1. > +foreach line {8 5 3 2 1} { > + gdb_test "reverse-step" ".*TAG: line $line.*" "reverse step to line $line" > +} > + > +## Clean restart, test reverse-next command > +clean_restart ${testfile} > +runto_main > +gdb_test_no_output "record" "turn on process record, reverst-next test" > + > +set bp_main_return [gdb_get_line_number "TAG: main return" $srcfile] > +gdb_breakpoint $srcfile:$bp_main_return > +gdb_continue_to_breakpoint "run to end of main, reverse-next test" ".*$srcfile:$bp_main_return.*" > +gdb_test "display \$pc" ".*pc =.*" "display pc, reverse-next test" > + > +# At this point, GDB has already recorded the execution up until the return > +# statement. Reverse-next and test if GDB transitions between lines in the > +# expected order. It should reverse-next across lines 8, 5, 3, 2 and 1. > +foreach line {8 5 3 2 1} { > + gdb_test "reverse-next" ".*TAG: line $line.*" "reverse next to line $line" > +} It seems like the step and next tests are identical, so I guess it could be factored out using: foreach_with_prefix method {step next} { ... } ? Simon ^ permalink raw reply [flat|nested] 41+ messages in thread
* RE: [PATCH 2/2 v5] Fix reverse stepping multiple contiguous PC ranges over the line table. 2023-06-19 17:58 ` Simon Marchi @ 2023-06-22 20:38 ` Carl Love 2023-06-22 20:39 ` Carl Love 2023-06-23 17:49 ` Simon Marchi 2023-06-23 20:04 ` Carl Love 2023-06-23 20:04 ` [PATCH 2/2 v6] " Carl Love 2 siblings, 2 replies; 41+ messages in thread From: Carl Love @ 2023-06-22 20:38 UTC (permalink / raw) To: Simon Marchi, Bruno Larsen, gdb-patches, UlrichWeigand, pedro Cc: luis.machado Simon: On Mon, 2023-06-19 at 13:58 -0400, Simon Marchi wrote: <snip> > 100644 > > --- a/gdb/infrun.c > > +++ b/gdb/infrun.c > > @@ -114,6 +114,9 @@ static struct async_event_handler > > *infrun_async_inferior_event_token; > > Starts off as -1, indicating "never enabled/disabled". */ > > static int infrun_is_async = -1; > > > > +static CORE_ADDR update_line_range_start (CORE_ADDR pc, > > + struct > > execution_control_state *ecs); > > + > > This forward-declaration is not needed. I tried removing the forward-declaration and the compile fails with the message: ../../binutils-gdb-reverse-multiple-contiguous/gdb/infrun.c:6773:1: error: no previous declaration for ‘CORE_ADDR update_line_range_start(CORE_ADDR, execution_control_state*)’ [- Werror=missing-declarations] 6773 | update_line_range_start (CORE_ADDR pc, struct execution_control_state *ecs) | ^~~~~~~~~~~~~~~~~~~~~~~ cc1plus: all warnings being treated as errors make[2]: *** [Makefile:1922: infrun.o] Error 1 make[2]: Leaving directory '/home/carll/GDB/build-reverse-multiple- contiguous/gdb' make[1]: *** [Makefile:13569: all-gdb] Error 2 make[1]: Leaving directory '/home/carll/GDB/build-reverse-multiple- contiguous' make: *** [Makefile:1005: all] Error 2 Leaving the forward declaration in the code. > > > /* See infrun.h. */ > > > > void > > @@ -6769,6 +6772,25 @@ handle_signal_stop (struct > > execution_control_state *ecs) > > process_event_stop_test (ecs); > > } > > > > +CORE_ADDR > > +update_line_range_start (CORE_ADDR pc, struct > > execution_control_state *ecs) > > Please add a comment for the function. Done. > > > +{ > > + /* The line table may have multiple entries for the same source > > code line. > > + Given the PC, check the line table and return the PC that > > corresponds > > + to the line table entry for the source line that PC is > > in. */ > > + CORE_ADDR start_line_pc = ecs->event_thread- > > >control.step_range_start; > > + gdb::optional<CORE_ADDR> real_range_start; > > + > > + /* Call find_line_range_start to get the smallest address in the > > + linetable for multiple Line X entries in the line table. */ > > + real_range_start = find_line_range_start (pc); > > + > > + if (real_range_start.has_value ()) > > + start_line_pc = *real_range_start; > > + > > + return start_line_pc; > > When I read this, I wonder: why was control.step_range_start not set > to > the "real" range start in the first place (not only in the context of > reverse execution, every time it is set)? It would seem more robust > than patching it afterwards in some very specific spots. > > I could see some benefits for range-stepping uses cases too (relevant > when debugging remotely). Using your example here: > > Line X - [0x0 - 0x8] > Line X - [0x8 - 0x10] > Line X - [0x10 - 0x18] > > Imagine we are stopped at 0x14, and we type "next", and 0x14 is a > conditional jump to 0x5. It seems like current GDB would send a > "range > step" request to GDBserver, to step in the [0x10, 0x18[ range. When > reaching 0x5, execution would stop, and GDB would resume it again > with > the [0x0,0x8[ range. When reaching 0x8, it would stop again, GDB > would > resume it with [0x8,0x10[, and so on. If GDB could send a "range > step" > request with the [0x0,0x18[ range, it would avoid those unnecessary > intermediary stop. > > > +} > > + > > /* Come here when we've got some debug event / signal we can > > explain > > (IOW, not a random signal), and test whether it should cause a > > stop, or whether we should resume the inferior (transparently). > > @@ -7570,6 +7592,28 @@ process_event_stop_test (struct > > execution_control_state *ecs) > > > > if (stop_pc_sal.is_stmt) > > { > > + if (execution_direction == EXEC_REVERSE) > > + { > > + /* We are stepping backwards make sure we have reached > > the > > + beginning of the line. */ > > + CORE_ADDR stop_pc = ecs->event_thread->stop_pc (); > > + CORE_ADDR start_line_pc > > + = update_line_range_start (stop_pc, ecs); > > + > > + if (stop_pc != start_line_pc) > > + { > > + /* Have not reached the beginning of the source code > > line. > > + Set a step range. Execution should stop in any > > function > > + calls we execute back into before reaching the > > beginning > > + of the line. */ > > + ecs->event_thread->control.step_range_start = > > start_line_pc; > > + ecs->event_thread->control.step_range_end = stop_pc; > > + set_step_info (ecs->event_thread, frame, > > stop_pc_sal); > > + keep_going (ecs); > > + return; > > + } > > + } > > + > > /* We are at the start of a statement. > > > > So stop. Note that we don't stop if we step into the > > middle of a > > @@ -7632,6 +7676,19 @@ process_event_stop_test (struct > > execution_control_state *ecs) > > set_step_info (ecs->event_thread, frame, stop_pc_sal); > > > > infrun_debug_printf ("keep going"); > > + > > + if (execution_direction == EXEC_REVERSE) > > + { > > + CORE_ADDR stop_pc = ecs->event_thread->stop_pc (); > > + > > + /* Make sure the stop_pc is set to the beginning of the > > line. */ > > + if (stop_pc != ecs->event_thread->control.step_range_start) > > + { > > + stop_pc = update_line_range_start (stop_pc, ecs); > > + ecs->event_thread->control.step_range_start = stop_pc; > > + } > > + } > > + > > keep_going (ecs); > > } > > > > diff --git a/gdb/symtab.c b/gdb/symtab.c > > index 27611a34ec4..91d35616eb9 100644 > > --- a/gdb/symtab.c > > +++ b/gdb/symtab.c > > @@ -3282,6 +3282,55 @@ find_pc_line (CORE_ADDR pc, int notcurrent) > > return sal; > > } > > > > +/* Compare two symtab_and_line entries. Return true if both have > > + the same line number and the same symtab pointer. That means > > we > > + are dealing with two entries from the same line and from the > > same > > + source file. > > + > > + Return false otherwise. */ > > + > > +static bool > > +sal_line_symtab_matches_p (const symtab_and_line &sal1, > > + const symtab_and_line &sal2) > > +{ > > + return (sal1.line == sal2.line && sal1.symtab == sal2.symtab); > > Unnecessary parenthesis. Removed unnecessary parenthesis. > > > +} > > + > > +/* See symtah.h. */ > > + > > +gdb::optional<CORE_ADDR> > > +find_line_range_start (CORE_ADDR pc) > > +{ > > + struct symtab_and_line current_sal = find_pc_line (pc, 0); > > + > > + if (current_sal.line == 0) > > + return {}; > > + > > + struct symtab_and_line prev_sal = find_pc_line (current_sal.pc - > > 1, 0); > > + > > + /* If the previous entry is for a different line, that means we > > are already > > + at the entry with the start PC for this line. */ > > + if (!sal_line_symtab_matches_p (prev_sal, current_sal)) > > + return current_sal.pc; > > + > > + /* Otherwise, keep looking for entries for the same line but > > with > > + smaller PC's. */ > > + bool done = false; > > + CORE_ADDR prev_pc; > > + while (!done) > > + { > > + prev_pc = prev_sal.pc; > > + > > + prev_sal = find_pc_line (prev_pc - 1, 0); > > + > > + /* Did we notice a line change? If so, we are done with the > > search. */ > > + if (!sal_line_symtab_matches_p (prev_sal, current_sal)) > > + done = true; > > + } > > + > > + return prev_pc; > > Algorithmic complexity question: given that line tables are sorted by > address, would it work to start at the current line table item, and > go > look at the previous ones until we find one that is no longer > contiguous and same line? find_pc_line is somewhat heavy, so if we > don't need to do it repeatedly... > > > +} > > + > > /* See symtab.h. */ > > > > struct symtab * > > diff --git a/gdb/symtab.h b/gdb/symtab.h > > index 404d0ab30a8..f54305636da 100644 > > --- a/gdb/symtab.h > > +++ b/gdb/symtab.h > > @@ -2346,6 +2346,22 @@ extern struct symtab_and_line find_pc_line > > (CORE_ADDR, int); > > extern struct symtab_and_line find_pc_sect_line (CORE_ADDR, > > struct obj_section *, > > int); > > > > +/* Given PC, and assuming it is part of a range of addresses that > > is part of a > > + line, go back through the linetable and find the starting PC of > > that > > + line. > > + > > + For example, suppose we have 3 PC ranges for line X: > > + > > + Line X - [0x0 - 0x8] > > + Line X - [0x8 - 0x10] > > + Line X - [0x10 - 0x18] > > + > > + If we call the function with PC == 0x14, we want to return 0x0, > > as that is > > + the starting PC of line X, and the ranges are contiguous. > > I think that putting this example in the comment is great. It makes > it > much more obvious what the function specifically does. > > > +*/ > > + > > +extern gdb::optional<CORE_ADDR> find_line_range_start (CORE_ADDR > > pc); > > + > > /* Wrapper around find_pc_line to just return the symtab. */ > > > > extern struct symtab *find_pc_line_symtab (CORE_ADDR); > > diff --git a/gdb/testsuite/gdb.reverse/func-map-to-same-line.c > > b/gdb/testsuite/gdb.reverse/func-map-to-same-line.c > > new file mode 100644 > > index 00000000000..da944874e86 > > --- /dev/null > > +++ b/gdb/testsuite/gdb.reverse/func-map-to-same-line.c > > @@ -0,0 +1,36 @@ > > +/* Copyright 2008-2023 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 < > > http://www.gnu.org/licenses/ > > >. > > + > > + This test is used to test the reverse-step and reverse-next > > instruction > > + execution for a source line that contains multiple function > > calls. */ > > + > > +void > > +func1 () > > +{ > > +} /* END FUNC1 */ > > + > > +void > > +func2 () > > +{ > > +} /* END FUNC2 */ > > + > > +int main () > > int > main (void) > Fixed. > > +{ > > + int a, b; > > + a = 1; > > + b = 2; > > + func1 (); func2 (); > > + a = a + b; /* START REVERSE TEST */ > > +} > > diff --git a/gdb/testsuite/gdb.reverse/func-map-to-same-line.exp > > b/gdb/testsuite/gdb.reverse/func-map-to-same-line.exp > > new file mode 100644 > > index 00000000000..89e226b0f84 > > --- /dev/null > > +++ b/gdb/testsuite/gdb.reverse/func-map-to-same-line.exp > > @@ -0,0 +1,140 @@ > > +# Copyright 2008-2023 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 < > > http://www.gnu.org/licenses/ > > >. */ > > + > > +# This file is part of the GDB testsuite. It tests reverse > > stepping. > > +# Lots of code borrowed from "step-test.exp". > > + > > +# This test checks to make sure there is no regression failures > > for > > +# the reverse-next command when stepping back over two functions > > in > > +# the same line. > > + > > +require supports_reverse > > + > > +# This test uses the gcc no-column-info command which was added in > > gcc 7.1. > > + > > +proc run_tests {} { > > + global srcfile > > + global executable > > + > > + runto_main > > We typically check for runto_main's success: Fixed two instances of runto_main in this test case and two in the other test case. > > if { ![runto_main] } { > return > } > > runto_main logs a FAIL on failure. There are a few runto_mains in > the > patch. > > > + set target_remote [gdb_is_target_remote] > > target_remote seems unused Removed. > > > + > > + with_test_prefix "test1" { > > + gdb_test_no_output "record" "turn on process record" > > + } > > with_test_prefix with a single test in it is really just the same as: > > gdb_test_no_output "record" "test1: turn on process record" > > In fact, you have some other tests with the "test1:" or "test2:" > prefix, > I think they should be moved to the with_test_prefix. And maybe use > "next" and "step" instead of "test1" and "test2". Yup, cleaner to have the with_test_prefix cover the whole test. Changed test1 to next-test and test2 to step-next. > > > + > > + # This regression test verifies the reverse-step and reverse- > > next commands > > + # work properly when executing backwards thru a source line > > containing > > + # two function calls on the same source line, i.e. func1 (); > > func2 (); > > + # This test is compiled so the dwarf info not contain the line > > table > > + # information. > > + > > + # Test 1, reverse-next command > > + # Set breakpoint at the line after the function calls. > > + set bp_start_reverse_test [gdb_get_line_number "START REVERSE > > TEST" \ > > + $srcfile] > > + gdb_breakpoint $srcfile:$bp_start_reverse_test temporary > > + > > + # Continue to break point for reverse-next test. > > + # Command definition: reverse-next [count] > > + # Run backward to the beginning of the previous line > > executed in the > > + # current (innermost) stack frame. If the line contains > > function calls, > > + # they will be “un-executed” without stopping. Starting from > > the first > > + # line of a function, reverse-next will take you back to the > > caller of > > + # that function, before the function was called, just as the > > normal next > > + # command would take you from the last line of a function > > back to its > > + # return to its caller 2 . > > + gdb_continue_to_breakpoint \ > > + "test1: stopped at command reverse-next test start location" \ > > + ".*$srcfile:$bp_start_reverse_test\r\n.*" > > + > > + # The reverse-next should step all the way back to the > > beginning of the > > + # line, i.e. at the beginning of the func1 call. > > + gdb_test "reverse-next" ".*func1 \\(\\); func2 \\(\\);.*" \ > > + "test1: reverse-next to line with two functions" > > + > > + # We should be stopped at the first instruction of the line. A > > reverse-step > > + # should step back and stop at the beginning of the previous > > line b = 2, > > + # i.e. not in func1 (). > > + gdb_test "reverse-stepi" ".*b = 2;.*" \ > > + "test1: reverse-stepi to previous line b = 2" > > + > > + > > + # Setup for test 2 > > + clean_restart $executable > > + runto_main > > + > > + with_test_prefix "test2" { > > + gdb_test_no_output "record" "turn on process record" > > + } > > + > > + # Test 2, reverse-step command > > + # Set breakpoint at the line after the function calls. > > + gdb_breakpoint $srcfile:$bp_start_reverse_test temporary > > + > > + # Continue to the start of the reverse-step test. > > + # Command definition: reverse-step [count] > > + # Run the program backward until control reaches the start > > of a > > + # different source line; then stop it, and return control > > to gdb. > > + # Like the step command, reverse-step will only stop at the > > beginning > > + # of a source line. It “un-executes” the previously > > executed source > > + # line. If the previous source line included calls to > > debuggable > > + # functions, reverse-step will step (backward) into the > > called function, > > + # stopping at the beginning of the last statement in the > > called > > + # function (typically a return statement). Also, as with > > the step > > + # command, if non-debuggable functions are called, reverse- > > step will > > + # run thru them backward without stopping. > > + > > + gdb_continue_to_breakpoint \ > > + "test2: stopped at command reverse-step test start location" \ > > + ".*$srcfile:$bp_start_reverse_test\r\n.*" > > + > > + # The first reverse step should take us call of func2 (). > > + gdb_test "reverse-step" ".*END FUNC2.*" \ > > + "test2: reverse-step into func2 " > > + > > + # The second reverse step should take us into func1 (). > > + gdb_test "reverse-step" ".*END FUNC1.*" \ > > + "test2: reverse-step into func1 " > > + > > + # The third reverse step should take us call of func1 (). > > + gdb_test "reverse-step" ".*func1 \\(\\); func2 \\(\\);.*" \ > > + "test2: reverse-step to line func1(); func2(), at call for > > func1 " > > + > > + # We should be stopped at the first instruction of the line. A > > reverse > > + # stepi should take us to b = 2 (). > > + gdb_test "reverse-stepi" ".*b = 2;.*" \ > > + "test2: reverse-stepi to line b = 2 " > > +} > > + > > +set srcfile func-map-to-same-line.c > > +set executable func-map-to-same-line > > Wondering if this test should use standard_testfile (like almost > every > other tests) to set these. OK, changed to use the standard_testfile. > > > + > > +# test with and without gcc column info enabled > > +foreach_with_prefix with_column_info {yes no} { > > + if {$with_column_info == "yes"} { > > + set options [list debug column-info] > > + } else { > > + set options [list debug no-column-info] > > + } > > I didn't think of this when proposing the foreach_with_prefix, but > you > could perhaps use: > > foreach_with_prefix column_info_flag {column-info no-column-info} > > ... to avoid this boilerplate. You can then use $column_info_flag > directly when setting options. > OK, that cleans things up a bit. Changed. + + if {[build_executable "failed to prepare" $executable $srcfile \ + $options] == -1} { + return -1 + } + + clean_restart $executable clean_restart can go in run_tests. + run_tests +} diff --git a/gdb/testsuite/gdb.reverse/map-to-same-line.c b/gdb/testsuite/gdb.reverse/map-to-same-line.c new file mode 100644 index 00000000000..f20d778f40e --- /dev/null +++ b/gdb/testsuite/gdb.reverse/map-to-same-line.c @@ -0,0 +1,58 @@ +/* Copyright 2008-2023 Free Software Foundation, Inc. Just wondering if the copyright years are right. New files so yea, should just start with 2023. > + + 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 < http://www.gnu.org/licenses/ >. */ + +/* The purpose of this test is to create a DWARF line table that contains two + or more entries for the same line. When stepping (forwards or backwards), + GDB should step over the entire line and not just a particular entry in the + line table. */ + +int +main () void in the parenthesis Fixed in both test files. > +{ /* TAG: main prologue */ + asm ("main_label: .globl main_label"); + int i = 1, j = 2, k; + float f1 = 2.0, f2 = 4.1, f3; + const char *str_1 = "foo", *str_2 = "bar", *str_3; + + asm ("line1: .globl line1"); + k = i; f3 = f1; str_3 = str_1; /* TAG: line 1 */ + + asm ("line2: .globl line2"); + k = j; f3 = f2; str_3 = str_2; /* TAG: line 2 */ + + asm ("line3: .globl line3"); + k = i; f3 = f1; str_3 = str_1; /* TAG: line 3 */ + + asm ("line4: .globl line4"); + k = j; f3 = f2; str_3 = str_2; /* TAG: line 4 */ + + asm ("line5: .globl line5"); + k = i; f3 = f1; str_3 = str_1; /* TAG: line 5 */ + + asm ("line6: .globl line6"); + k = j; f3 = f2; str_3 = str_2; /* TAG: line 6 */ + + asm ("line7: .globl line7"); + k = i; f3 = f1; str_3 = str_1; /* TAG: line 7 */ + + asm ("line8: .globl line8"); + k = j; f3 = f2; str_3 = str_2; /* TAG: line 8 */ + + asm ("main_return: .globl main_return"); + k = j; f3 = f2; str_3 = str_2; /* TAG: main return */ + + asm ("end_of_sequence: .globl end_of_sequence"); + return 0; /* TAG: main return */ +} diff --git a/gdb/testsuite/gdb.reverse/map-to-same-line.exp b/gdb/testsuite/gdb.reverse/map-to-same-line.exp new file mode 100644 index 00000000000..16a359d90ec --- /dev/null +++ b/gdb/testsuite/gdb.reverse/map-to-same-line.exp @@ -0,0 +1,156 @@ +# Copyright 2008-2023 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 < http://www.gnu.org/licenses/ = >. + +# When stepping (forwards or backwards), GDB should step over the entire line +# and not just a particular entry in the line table. This test was added to +# verify the find_line_range_start function properly sets the step range for a +# line that consists of multiple statements, i.e. multiple entries in the line +# table. This test creates a DWARF line table that contains two entries for +# the same line to do the needed testing. + +# This test can only be run on targets which support DWARF-2 and use gas. +load_lib dwarf.exp +require dwarf2_support + +# The DWARF assembler requires the gcc compiler. +require is_c_compiler_gcc + +# This test suitable only for process that can do reverse execution +require supports_reverse + +standard_testfile .c .S + +if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile}] } { + return -1 +} + +set asm_file [standard_output_file $srcfile2] +Dwarf::assemble $asm_file { + global srcdir subdir srcfile + declare_labels integer_label L + + # Find start address and length of program + lassign [function_range main [list ${srcdir}/${subdir}/$srcfile]] \ + main_start main_len + set main_end "$main_start + $main_len" + + cu {} { + compile_unit { + {language @DW_LANG_C} + {name map-to-same-line.c} + {stmt_list $L DW_FORM_sec_offset} + {low_pc 0 addr} + } { + subprogram { + {external 1 flag} + {name main} + {low_pc $main_start addr} + {high_pc $main_len DW_FORM_data4} + } + } + } + + lines {version 2 default_is_stmt 1} L { + include_dir "${srcdir}/${subdir}" + file_name "$srcfile" 1 + + # Generate the line table program with distinct source lines being + # mapped to the same line entry. Line 1, 5 and 8 contain 1 statement + # each. Line 2 contains 2 statements. Line 3 contains 3 statements. + program { + DW_LNE_set_address $main_start + line [gdb_get_line_number "TAG: main prologue"] + DW_LNS_copy + DW_LNE_set_address line1 + line [gdb_get_line_number "TAG: line 1" ] + DW_LNS_copy + DW_LNE_set_address line2 + line [gdb_get_line_number "TAG: line 2" ] + DW_LNS_copy + DW_LNE_set_address line3 + line [gdb_get_line_number "TAG: line 2" ] + DW_LNS_copy + DW_LNE_set_address line4 + line [gdb_get_line_number "TAG: line 3" ] + DW_LNS_copy + DW_LNE_set_address line5 + line [gdb_get_line_number "TAG: line 3" ] + DW_LNS_copy + DW_LNE_set_address line6 + line [gdb_get_line_number "TAG: line 3" ] + DW_LNS_copy + DW_LNE_set_address line7 + line [gdb_get_line_number "TAG: line 5" ] + DW_LNS_copy + DW_LNE_set_address line8 + line [gdb_get_line_number "TAG: line 8" ] + DW_LNS_copy + DW_LNE_set_address main_return + line [gdb_get_line_number "TAG: main return"] + DW_LNS_copy + DW_LNE_set_address end_of_sequence + DW_LNE_end_sequence + } + } +} + +if { [prepare_for_testing "failed to prepare" ${testfile} \ + [list $srcfile $asm_file] {nodebug} ] } { + return -1 +} + +runto_main + +# Print the line table +gdb_test_multiple "maint info line-table ${testfile}" "" { + -re "\r\n$decimal\[ \t\]+$decimal\[ \t\]+($hex)\[ \t\]+Y\[^\r\n\]*" { + lappend is_stmt $expect_out(1,string) + exp_continue + } + -re -wrap "" { + } +} + +# Do the reverse-step test +gdb_test_no_output "record" "turn on process record" + +set bp_main_return [gdb_get_line_number "TAG: main return" $srcfile] +gdb_breakpoint $srcfile:$bp_main_return +gdb_continue_to_breakpoint "run to end of main, reverse-step test" ".*$srcfile:$bp_main_return.*" +gdb_test "display \$pc" ".*pc =.*" "display pc, reverse-step test" + +# At this point, GDB has already recorded the execution up until the return +# statement. Reverse-step and test if GDB transitions between lines in the +# expected order. It should reverse-step across lines 8, 5, 3, 2 and 1. +foreach line {8 5 3 2 1} { + gdb_test "reverse-step" ".*TAG: line $line.*" "reverse step to line $line" +} + +## Clean restart, test reverse-next command +clean_restart ${testfile} +runto_main +gdb_test_no_output "record" "turn on process record, reverst-next test" + +set bp_main_return [gdb_get_line_number "TAG: main return" $srcfile] +gdb_breakpoint $srcfile:$bp_main_return +gdb_continue_to_breakpoint "run to end of main, reverse-next test" ".*$srcfile:$bp_main_return.*" +gdb_test "display \$pc" ".*pc =.*" "display pc, reverse-next test" + +# At this point, GDB has already recorded the execution up until the return +# statement. Reverse-next and test if GDB transitions between lines in the +# expected order. It should reverse-next across lines 8, 5, 3, 2 and 1. +foreach line {8 5 3 2 1} { + gdb_test "reverse-next" ".*TAG: line $line.*" "reverse next to line $line" +} It seems like the step and next tests are identical, so I guess it could be factored out using: foreach_with_prefix method {step next} { ... } ? Yup, redid the code using the foreach_with_prefix. > Simon > yR ^ permalink raw reply [flat|nested] 41+ messages in thread
* RE: [PATCH 2/2 v5] Fix reverse stepping multiple contiguous PC ranges over the line table. 2023-06-22 20:38 ` Carl Love @ 2023-06-22 20:39 ` Carl Love 2023-06-23 17:49 ` Simon Marchi 1 sibling, 0 replies; 41+ messages in thread From: Carl Love @ 2023-06-22 20:39 UTC (permalink / raw) To: Simon Marchi, Bruno Larsen, gdb-patches, UlrichWeigand, pedro Cc: luis.machado Sorry, ignore this message. Accidentally hit send before I was done with the message. Carl -------------------------------------------------- On Thu, 2023-06-22 at 13:38 -0700, Carl Love wrote: > Simon: > > On Mon, 2023-06-19 at 13:58 -0400, Simon Marchi wrote: > <snip> > > 100644 > > > --- a/gdb/infrun.c > > > +++ b/gdb/infrun.c > > > @@ -114,6 +114,9 @@ static struct async_event_handler > > > *infrun_async_inferior_event_token; > > > Starts off as -1, indicating "never enabled/disabled". */ > > > static int infrun_is_async = -1; > > > > > > +static CORE_ADDR update_line_range_start (CORE_ADDR pc, > > > + struct > > > execution_control_state *ecs); > > > + > > > > This forward-declaration is not needed. > > I tried removing the forward-declaration and the compile fails with > the > message: > > ../../binutils-gdb-reverse-multiple- > contiguous/gdb/infrun.c:6773:1: > error: no previous declaration for ‘CORE_ADDR > update_line_range_start(CORE_ADDR, execution_control_state*)’ [- > Werror=missing-declarations] > 6773 | update_line_range_start (CORE_ADDR pc, struct > execution_control_state *ecs) > | ^~~~~~~~~~~~~~~~~~~~~~~ > cc1plus: all warnings being treated as errors > make[2]: *** [Makefile:1922: infrun.o] Error 1 > make[2]: Leaving directory '/home/carll/GDB/build-reverse- > multiple- > contiguous/gdb' > make[1]: *** [Makefile:13569: all-gdb] Error 2 > make[1]: Leaving directory '/home/carll/GDB/build-reverse- > multiple- > contiguous' > make: *** [Makefile:1005: all] Error 2 > > Leaving the forward declaration in the code. > > > > > > /* See infrun.h. */ > > > > > > void > > > @@ -6769,6 +6772,25 @@ handle_signal_stop (struct > > > execution_control_state *ecs) > > > process_event_stop_test (ecs); > > > } > > > > > > +CORE_ADDR > > > +update_line_range_start (CORE_ADDR pc, struct > > > execution_control_state *ecs) > > > > Please add a comment for the function. > > Done. > > > > +{ > > > + /* The line table may have multiple entries for the same > > > source > > > code line. > > > + Given the PC, check the line table and return the PC that > > > corresponds > > > + to the line table entry for the source line that PC is > > > in. */ > > > + CORE_ADDR start_line_pc = ecs->event_thread- > > > > control.step_range_start; > > > + gdb::optional<CORE_ADDR> real_range_start; > > > + > > > + /* Call find_line_range_start to get the smallest address in > > > the > > > + linetable for multiple Line X entries in the line > > > table. */ > > > + real_range_start = find_line_range_start (pc); > > > + > > > + if (real_range_start.has_value ()) > > > + start_line_pc = *real_range_start; > > > + > > > + return start_line_pc; > > > > When I read this, I wonder: why was control.step_range_start not > > set > > to > > the "real" range start in the first place (not only in the context > > of > > reverse execution, every time it is set)? It would seem more > > robust > > than patching it afterwards in some very specific spots. > > > > I could see some benefits for range-stepping uses cases too > > (relevant > > when debugging remotely). Using your example here: > > > > Line X - [0x0 - 0x8] > > Line X - [0x8 - 0x10] > > Line X - [0x10 - 0x18] > > > > Imagine we are stopped at 0x14, and we type "next", and 0x14 is a > > conditional jump to 0x5. It seems like current GDB would send a > > "range > > step" request to GDBserver, to step in the [0x10, 0x18[ > > range. When > > reaching 0x5, execution would stop, and GDB would resume it again > > with > > the [0x0,0x8[ range. When reaching 0x8, it would stop again, GDB > > would > > resume it with [0x8,0x10[, and so on. If GDB could send a "range > > step" > > request with the [0x0,0x18[ range, it would avoid those unnecessary > > intermediary stop. > > > > > +} > > > + > > > /* Come here when we've got some debug event / signal we can > > > explain > > > (IOW, not a random signal), and test whether it should cause > > > a > > > stop, or whether we should resume the inferior > > > (transparently). > > > @@ -7570,6 +7592,28 @@ process_event_stop_test (struct > > > execution_control_state *ecs) > > > > > > if (stop_pc_sal.is_stmt) > > > { > > > + if (execution_direction == EXEC_REVERSE) > > > + { > > > + /* We are stepping backwards make sure we have reached > > > the > > > + beginning of the line. */ > > > + CORE_ADDR stop_pc = ecs->event_thread->stop_pc (); > > > + CORE_ADDR start_line_pc > > > + = update_line_range_start (stop_pc, ecs); > > > + > > > + if (stop_pc != start_line_pc) > > > + { > > > + /* Have not reached the beginning of the source code > > > line. > > > + Set a step range. Execution should stop in any > > > function > > > + calls we execute back into before reaching the > > > beginning > > > + of the line. */ > > > + ecs->event_thread->control.step_range_start = > > > start_line_pc; > > > + ecs->event_thread->control.step_range_end = stop_pc; > > > + set_step_info (ecs->event_thread, frame, > > > stop_pc_sal); > > > + keep_going (ecs); > > > + return; > > > + } > > > + } > > > + > > > /* We are at the start of a statement. > > > > > > So stop. Note that we don't stop if we step into the > > > middle of a > > > @@ -7632,6 +7676,19 @@ process_event_stop_test (struct > > > execution_control_state *ecs) > > > set_step_info (ecs->event_thread, frame, stop_pc_sal); > > > > > > infrun_debug_printf ("keep going"); > > > + > > > + if (execution_direction == EXEC_REVERSE) > > > + { > > > + CORE_ADDR stop_pc = ecs->event_thread->stop_pc (); > > > + > > > + /* Make sure the stop_pc is set to the beginning of the > > > line. */ > > > + if (stop_pc != ecs->event_thread- > > > >control.step_range_start) > > > + { > > > + stop_pc = update_line_range_start (stop_pc, ecs); > > > + ecs->event_thread->control.step_range_start = stop_pc; > > > + } > > > + } > > > + > > > keep_going (ecs); > > > } > > > > > > diff --git a/gdb/symtab.c b/gdb/symtab.c > > > index 27611a34ec4..91d35616eb9 100644 > > > --- a/gdb/symtab.c > > > +++ b/gdb/symtab.c > > > @@ -3282,6 +3282,55 @@ find_pc_line (CORE_ADDR pc, int > > > notcurrent) > > > return sal; > > > } > > > > > > +/* Compare two symtab_and_line entries. Return true if both > > > have > > > + the same line number and the same symtab pointer. That means > > > we > > > + are dealing with two entries from the same line and from the > > > same > > > + source file. > > > + > > > + Return false otherwise. */ > > > + > > > +static bool > > > +sal_line_symtab_matches_p (const symtab_and_line &sal1, > > > + const symtab_and_line &sal2) > > > +{ > > > + return (sal1.line == sal2.line && sal1.symtab == sal2.symtab); > > > > Unnecessary parenthesis. > > Removed unnecessary parenthesis. > > > > +} > > > + > > > +/* See symtah.h. */ > > > + > > > +gdb::optional<CORE_ADDR> > > > +find_line_range_start (CORE_ADDR pc) > > > +{ > > > + struct symtab_and_line current_sal = find_pc_line (pc, 0); > > > + > > > + if (current_sal.line == 0) > > > + return {}; > > > + > > > + struct symtab_and_line prev_sal = find_pc_line (current_sal.pc > > > - > > > 1, 0); > > > + > > > + /* If the previous entry is for a different line, that means > > > we > > > are already > > > + at the entry with the start PC for this line. */ > > > + if (!sal_line_symtab_matches_p (prev_sal, current_sal)) > > > + return current_sal.pc; > > > + > > > + /* Otherwise, keep looking for entries for the same line but > > > with > > > + smaller PC's. */ > > > + bool done = false; > > > + CORE_ADDR prev_pc; > > > + while (!done) > > > + { > > > + prev_pc = prev_sal.pc; > > > + > > > + prev_sal = find_pc_line (prev_pc - 1, 0); > > > + > > > + /* Did we notice a line change? If so, we are done with > > > the > > > search. */ > > > + if (!sal_line_symtab_matches_p (prev_sal, current_sal)) > > > + done = true; > > > + } > > > + > > > + return prev_pc; > > > > Algorithmic complexity question: given that line tables are sorted > > by > > address, would it work to start at the current line table item, and > > go > > look at the previous ones until we find one that is no longer > > contiguous and same line? find_pc_line is somewhat heavy, so if we > > don't need to do it repeatedly... > > > > > +} > > > + > > > /* See symtab.h. */ > > > > > > struct symtab * > > > diff --git a/gdb/symtab.h b/gdb/symtab.h > > > index 404d0ab30a8..f54305636da 100644 > > > --- a/gdb/symtab.h > > > +++ b/gdb/symtab.h > > > @@ -2346,6 +2346,22 @@ extern struct symtab_and_line find_pc_line > > > (CORE_ADDR, int); > > > extern struct symtab_and_line find_pc_sect_line (CORE_ADDR, > > > struct obj_section *, > > > int); > > > > > > +/* Given PC, and assuming it is part of a range of addresses > > > that > > > is part of a > > > + line, go back through the linetable and find the starting PC > > > of > > > that > > > + line. > > > + > > > + For example, suppose we have 3 PC ranges for line X: > > > + > > > + Line X - [0x0 - 0x8] > > > + Line X - [0x8 - 0x10] > > > + Line X - [0x10 - 0x18] > > > + > > > + If we call the function with PC == 0x14, we want to return > > > 0x0, > > > as that is > > > + the starting PC of line X, and the ranges are contiguous. > > > > I think that putting this example in the comment is great. It > > makes > > it > > much more obvious what the function specifically does. > > > > > +*/ > > > + > > > +extern gdb::optional<CORE_ADDR> find_line_range_start (CORE_ADDR > > > pc); > > > + > > > /* Wrapper around find_pc_line to just return the symtab. */ > > > > > > extern struct symtab *find_pc_line_symtab (CORE_ADDR); > > > diff --git a/gdb/testsuite/gdb.reverse/func-map-to-same-line.c > > > b/gdb/testsuite/gdb.reverse/func-map-to-same-line.c > > > new file mode 100644 > > > index 00000000000..da944874e86 > > > --- /dev/null > > > +++ b/gdb/testsuite/gdb.reverse/func-map-to-same-line.c > > > @@ -0,0 +1,36 @@ > > > +/* Copyright 2008-2023 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 < > > > http://www.gnu.org/licenses/ > > > >. > > > + > > > + This test is used to test the reverse-step and reverse-next > > > instruction > > > + execution for a source line that contains multiple function > > > calls. */ > > > + > > > +void > > > +func1 () > > > +{ > > > +} /* END FUNC1 */ > > > + > > > +void > > > +func2 () > > > +{ > > > +} /* END FUNC2 */ > > > + > > > +int main () > > > > int > > main (void) > > > > Fixed. > > > > +{ > > > + int a, b; > > > + a = 1; > > > + b = 2; > > > + func1 (); func2 (); > > > + a = a + b; /* START REVERSE TEST */ > > > +} > > > diff --git a/gdb/testsuite/gdb.reverse/func-map-to-same-line.exp > > > b/gdb/testsuite/gdb.reverse/func-map-to-same-line.exp > > > new file mode 100644 > > > index 00000000000..89e226b0f84 > > > --- /dev/null > > > +++ b/gdb/testsuite/gdb.reverse/func-map-to-same-line.exp > > > @@ -0,0 +1,140 @@ > > > +# Copyright 2008-2023 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 < > > > http://www.gnu.org/licenses/ > > > >. */ > > > + > > > +# This file is part of the GDB testsuite. It tests reverse > > > stepping. > > > +# Lots of code borrowed from "step-test.exp". > > > + > > > +# This test checks to make sure there is no regression failures > > > for > > > +# the reverse-next command when stepping back over two functions > > > in > > > +# the same line. > > > + > > > +require supports_reverse > > > + > > > +# This test uses the gcc no-column-info command which was added > > > in > > > gcc 7.1. > > > + > > > +proc run_tests {} { > > > + global srcfile > > > + global executable > > > + > > > + runto_main > > > > We typically check for runto_main's success: > > Fixed two instances of runto_main in this test case and two in the > other test case. > > > if { ![runto_main] } { > > return > > } > > > > runto_main logs a FAIL on failure. There are a few runto_mains in > > the > > patch. > > > > > + set target_remote [gdb_is_target_remote] > > > > target_remote seems unused > > Removed. > > > > + > > > + with_test_prefix "test1" { > > > + gdb_test_no_output "record" "turn on process record" > > > + } > > > > with_test_prefix with a single test in it is really just the same > > as: > > > > gdb_test_no_output "record" "test1: turn on process record" > > > > In fact, you have some other tests with the "test1:" or "test2:" > > prefix, > > I think they should be moved to the with_test_prefix. And maybe > > use > > "next" and "step" instead of "test1" and "test2". > > Yup, cleaner to have the with_test_prefix cover the whole test. > Changed test1 to next-test and test2 to step-next. > > > > + > > > + # This regression test verifies the reverse-step and > > > reverse- > > > next commands > > > + # work properly when executing backwards thru a source line > > > containing > > > + # two function calls on the same source line, i.e. func1 (); > > > func2 (); > > > + # This test is compiled so the dwarf info not contain the > > > line > > > table > > > + # information. > > > + > > > + # Test 1, reverse-next command > > > + # Set breakpoint at the line after the function calls. > > > + set bp_start_reverse_test [gdb_get_line_number "START > > > REVERSE > > > TEST" \ > > > + $srcfile] > > > + gdb_breakpoint $srcfile:$bp_start_reverse_test temporary > > > + > > > + # Continue to break point for reverse-next test. > > > + # Command definition: reverse-next [count] > > > + # Run backward to the beginning of the previous line > > > executed in the > > > + # current (innermost) stack frame. If the line contains > > > function calls, > > > + # they will be “un-executed” without stopping. Starting > > > from > > > the first > > > + # line of a function, reverse-next will take you back to > > > the > > > caller of > > > + # that function, before the function was called, just as > > > the > > > normal next > > > + # command would take you from the last line of a function > > > back to its > > > + # return to its caller 2 . > > > + gdb_continue_to_breakpoint \ > > > + "test1: stopped at command reverse-next test start location" \ > > > + ".*$srcfile:$bp_start_reverse_test\r\n.*" > > > + > > > + # The reverse-next should step all the way back to the > > > beginning of the > > > + # line, i.e. at the beginning of the func1 call. > > > + gdb_test "reverse-next" ".*func1 \\(\\); func2 \\(\\);.*" \ > > > + "test1: reverse-next to line with two functions" > > > + > > > + # We should be stopped at the first instruction of the line. > > > A > > > reverse-step > > > + # should step back and stop at the beginning of the previous > > > line b = 2, > > > + # i.e. not in func1 (). > > > + gdb_test "reverse-stepi" ".*b = 2;.*" \ > > > + "test1: reverse-stepi to previous line b = 2" > > > + > > > + > > > + # Setup for test 2 > > > + clean_restart $executable > > > + runto_main > > > + > > > + with_test_prefix "test2" { > > > + gdb_test_no_output "record" "turn on process record" > > > + } > > > + > > > + # Test 2, reverse-step command > > > + # Set breakpoint at the line after the function calls. > > > + gdb_breakpoint $srcfile:$bp_start_reverse_test temporary > > > + > > > + # Continue to the start of the reverse-step test. > > > + # Command definition: reverse-step [count] > > > + # Run the program backward until control reaches the > > > start > > > of a > > > + # different source line; then stop it, and return control > > > to gdb. > > > + # Like the step command, reverse-step will only stop at > > > the > > > beginning > > > + # of a source line. It “un-executes” the previously > > > executed source > > > + # line. If the previous source line included calls to > > > debuggable > > > + # functions, reverse-step will step (backward) into the > > > called function, > > > + # stopping at the beginning of the last statement in the > > > called > > > + # function (typically a return statement). Also, as with > > > the step > > > + # command, if non-debuggable functions are called, > > > reverse- > > > step will > > > + # run thru them backward without stopping. > > > + > > > + gdb_continue_to_breakpoint \ > > > + "test2: stopped at command reverse-step test start location" \ > > > + ".*$srcfile:$bp_start_reverse_test\r\n.*" > > > + > > > + # The first reverse step should take us call of func2 (). > > > + gdb_test "reverse-step" ".*END FUNC2.*" \ > > > + "test2: reverse-step into func2 " > > > + > > > + # The second reverse step should take us into func1 (). > > > + gdb_test "reverse-step" ".*END FUNC1.*" \ > > > + "test2: reverse-step into func1 " > > > + > > > + # The third reverse step should take us call of func1 (). > > > + gdb_test "reverse-step" ".*func1 \\(\\); func2 \\(\\);.*" \ > > > + "test2: reverse-step to line func1(); func2(), at call for > > > func1 " > > > + > > > + # We should be stopped at the first instruction of the line. > > > A > > > reverse > > > + # stepi should take us to b = 2 (). > > > + gdb_test "reverse-stepi" ".*b = 2;.*" \ > > > + "test2: reverse-stepi to line b = 2 " > > > +} > > > + > > > +set srcfile func-map-to-same-line.c > > > +set executable func-map-to-same-line > > > > Wondering if this test should use standard_testfile (like almost > > every > > other tests) to set these. > > OK, changed to use the standard_testfile. > > > > + > > > +# test with and without gcc column info enabled > > > +foreach_with_prefix with_column_info {yes no} { > > > + if {$with_column_info == "yes"} { > > > + set options [list debug column-info] > > > + } else { > > > + set options [list debug no-column-info] > > > + } > > > > I didn't think of this when proposing the foreach_with_prefix, but > > you > > could perhaps use: > > > > foreach_with_prefix column_info_flag {column-info no-column-info} > > > > ... to avoid this boilerplate. You can then use $column_info_flag > > directly when setting options. > > > OK, that cleans things up a bit. Changed. > + > + if {[build_executable "failed to prepare" $executable $srcfile > \ > + $options] == -1} { > + return -1 > + } > + > + clean_restart $executable > > clean_restart can go in run_tests. > > > + run_tests > +} > diff --git a/gdb/testsuite/gdb.reverse/map-to-same-line.c > b/gdb/testsuite/gdb.reverse/map-to-same-line.c > new file mode 100644 > index 00000000000..f20d778f40e > --- /dev/null > +++ b/gdb/testsuite/gdb.reverse/map-to-same-line.c > @@ -0,0 +1,58 @@ > +/* Copyright 2008-2023 Free Software Foundation, Inc. > > Just wondering if the copyright years are right. > > New files so yea, should just start with 2023. > + > + 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 < > http://www.gnu.org/licenses/ > >. */ > + > +/* The purpose of this test is to create a DWARF line table that > contains two > + or more entries for the same line. When stepping (forwards or > backwards), > + GDB should step over the entire line and not just a particular > entry in the > + line table. */ > + > +int > +main () > > void in the parenthesis > > Fixed in both test files. > > +{ /* TAG: main prologue */ > + asm ("main_label: .globl main_label"); > + int i = 1, j = 2, k; > + float f1 = 2.0, f2 = 4.1, f3; > + const char *str_1 = "foo", *str_2 = "bar", *str_3; > + > + asm ("line1: .globl line1"); > + k = i; f3 = f1; str_3 = str_1; /* TAG: line 1 */ > + > + asm ("line2: .globl line2"); > + k = j; f3 = f2; str_3 = str_2; /* TAG: line 2 */ > + > + asm ("line3: .globl line3"); > + k = i; f3 = f1; str_3 = str_1; /* TAG: line 3 */ > + > + asm ("line4: .globl line4"); > + k = j; f3 = f2; str_3 = str_2; /* TAG: line 4 */ > + > + asm ("line5: .globl line5"); > + k = i; f3 = f1; str_3 = str_1; /* TAG: line 5 */ > + > + asm ("line6: .globl line6"); > + k = j; f3 = f2; str_3 = str_2; /* TAG: line 6 */ > + > + asm ("line7: .globl line7"); > + k = i; f3 = f1; str_3 = str_1; /* TAG: line 7 */ > + > + asm ("line8: .globl line8"); > + k = j; f3 = f2; str_3 = str_2; /* TAG: line 8 */ > + > + asm ("main_return: .globl main_return"); > + k = j; f3 = f2; str_3 = str_2; /* TAG: main return */ > + > + asm ("end_of_sequence: .globl end_of_sequence"); > + return 0; /* TAG: main return */ > +} > diff --git a/gdb/testsuite/gdb.reverse/map-to-same-line.exp > b/gdb/testsuite/gdb.reverse/map-to-same-line.exp > new file mode 100644 > index 00000000000..16a359d90ec > --- /dev/null > +++ b/gdb/testsuite/gdb.reverse/map-to-same-line.exp > @@ -0,0 +1,156 @@ > +# Copyright 2008-2023 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 < > http://www.gnu.org/licenses/ > = >. > + > +# When stepping (forwards or backwards), GDB should step over the > entire line > +# and not just a particular entry in the line table. This test was > added to > +# verify the find_line_range_start function properly sets the step > range for a > +# line that consists of multiple statements, i.e. multiple entries > in the line > +# table. This test creates a DWARF line table that contains two > entries for > +# the same line to do the needed testing. > + > +# This test can only be run on targets which support DWARF-2 and > use gas. > +load_lib dwarf.exp > +require dwarf2_support > + > +# The DWARF assembler requires the gcc compiler. > +require is_c_compiler_gcc > + > +# This test suitable only for process that can do reverse > execution > +require supports_reverse > + > +standard_testfile .c .S > + > +if { [prepare_for_testing "failed to prepare" ${testfile} > ${srcfile}] } { > + return -1 > +} > + > +set asm_file [standard_output_file $srcfile2] > +Dwarf::assemble $asm_file { > + global srcdir subdir srcfile > + declare_labels integer_label L > + > + # Find start address and length of program > + lassign [function_range main [list > ${srcdir}/${subdir}/$srcfile]] \ > + main_start main_len > + set main_end "$main_start + $main_len" > + > + cu {} { > + compile_unit { > + {language @DW_LANG_C} > + {name map-to-same-line.c} > + {stmt_list $L DW_FORM_sec_offset} > + {low_pc 0 addr} > + } { > + subprogram { > + {external 1 flag} > + {name main} > + {low_pc $main_start addr} > + {high_pc $main_len DW_FORM_data4} > + } > + } > + } > + > + lines {version 2 default_is_stmt 1} L { > + include_dir "${srcdir}/${subdir}" > + file_name "$srcfile" 1 > + > + # Generate the line table program with distinct source lines > being > + # mapped to the same line entry. Line 1, 5 and 8 contain 1 > statement > + # each. Line 2 contains 2 statements. Line 3 contains 3 > statements. > + program { > + DW_LNE_set_address $main_start > + line [gdb_get_line_number "TAG: main prologue"] > + DW_LNS_copy > + DW_LNE_set_address line1 > + line [gdb_get_line_number "TAG: line 1" ] > + DW_LNS_copy > + DW_LNE_set_address line2 > + line [gdb_get_line_number "TAG: line 2" ] > + DW_LNS_copy > + DW_LNE_set_address line3 > + line [gdb_get_line_number "TAG: line 2" ] > + DW_LNS_copy > + DW_LNE_set_address line4 > + line [gdb_get_line_number "TAG: line 3" ] > + DW_LNS_copy > + DW_LNE_set_address line5 > + line [gdb_get_line_number "TAG: line 3" ] > + DW_LNS_copy > + DW_LNE_set_address line6 > + line [gdb_get_line_number "TAG: line 3" ] > + DW_LNS_copy > + DW_LNE_set_address line7 > + line [gdb_get_line_number "TAG: line 5" ] > + DW_LNS_copy > + DW_LNE_set_address line8 > + line [gdb_get_line_number "TAG: line 8" ] > + DW_LNS_copy > + DW_LNE_set_address main_return > + line [gdb_get_line_number "TAG: main return"] > + DW_LNS_copy > + DW_LNE_set_address end_of_sequence > + DW_LNE_end_sequence > + } > + } > +} > + > +if { [prepare_for_testing "failed to prepare" ${testfile} \ > + [list $srcfile $asm_file] {nodebug} ] } { > + return -1 > +} > + > +runto_main > + > +# Print the line table > +gdb_test_multiple "maint info line-table ${testfile}" "" { > + -re "\r\n$decimal\[ \t\]+$decimal\[ \t\]+($hex)\[ > \t\]+Y\[^\r\n\]*" { > + lappend is_stmt $expect_out(1,string) > + exp_continue > + } > + -re -wrap "" { > + } > +} > + > +# Do the reverse-step test > +gdb_test_no_output "record" "turn on process record" > + > +set bp_main_return [gdb_get_line_number "TAG: main return" > $srcfile] > +gdb_breakpoint $srcfile:$bp_main_return > +gdb_continue_to_breakpoint "run to end of main, reverse-step > test" ".*$srcfile:$bp_main_return.*" > +gdb_test "display \$pc" ".*pc =.*" "display pc, reverse-step test" > + > +# At this point, GDB has already recorded the execution up until > the return > +# statement. Reverse-step and test if GDB transitions between > lines in the > +# expected order. It should reverse-step across lines 8, 5, 3, 2 > and 1. > +foreach line {8 5 3 2 1} { > + gdb_test "reverse-step" ".*TAG: line $line.*" "reverse step to > line $line" > +} > + > +## Clean restart, test reverse-next command > +clean_restart ${testfile} > +runto_main > +gdb_test_no_output "record" "turn on process record, reverst-next > test" > + > +set bp_main_return [gdb_get_line_number "TAG: main return" > $srcfile] > +gdb_breakpoint $srcfile:$bp_main_return > +gdb_continue_to_breakpoint "run to end of main, reverse-next > test" ".*$srcfile:$bp_main_return.*" > +gdb_test "display \$pc" ".*pc =.*" "display pc, reverse-next test" > + > +# At this point, GDB has already recorded the execution up until > the return > +# statement. Reverse-next and test if GDB transitions between > lines in the > +# expected order. It should reverse-next across lines 8, 5, 3, 2 > and 1. > +foreach line {8 5 3 2 1} { > + gdb_test "reverse-next" ".*TAG: line $line.*" "reverse next to > line $line" > +} > > It seems like the step and next tests are identical, so I guess it > could > be factored out using: > > foreach_with_prefix method {step next} { > ... > } > > ? > > Yup, redid the code using the foreach_with_prefix. > > Simon > > yR ^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH 2/2 v5] Fix reverse stepping multiple contiguous PC ranges over the line table. 2023-06-22 20:38 ` Carl Love 2023-06-22 20:39 ` Carl Love @ 2023-06-23 17:49 ` Simon Marchi 1 sibling, 0 replies; 41+ messages in thread From: Simon Marchi @ 2023-06-23 17:49 UTC (permalink / raw) To: Carl Love, Bruno Larsen, gdb-patches, UlrichWeigand, pedro; +Cc: luis.machado On 6/22/23 16:38, Carl Love wrote: > Simon: > > On Mon, 2023-06-19 at 13:58 -0400, Simon Marchi wrote: > <snip> > >> > 100644 >>> --- a/gdb/infrun.c >>> +++ b/gdb/infrun.c >>> @@ -114,6 +114,9 @@ static struct async_event_handler >>> *infrun_async_inferior_event_token; >>> Starts off as -1, indicating "never enabled/disabled". */ >>> static int infrun_is_async = -1; >>> >>> +static CORE_ADDR update_line_range_start (CORE_ADDR pc, >>> + struct >>> execution_control_state *ecs); >>> + >> >> This forward-declaration is not needed. > > I tried removing the forward-declaration and the compile fails with the > message: > > ../../binutils-gdb-reverse-multiple-contiguous/gdb/infrun.c:6773:1: > error: no previous declaration for ‘CORE_ADDR > update_line_range_start(CORE_ADDR, execution_control_state*)’ [- > Werror=missing-declarations] > 6773 | update_line_range_start (CORE_ADDR pc, struct > execution_control_state *ecs) > | ^~~~~~~~~~~~~~~~~~~~~~~ > cc1plus: all warnings being treated as errors > make[2]: *** [Makefile:1922: infrun.o] Error 1 > make[2]: Leaving directory '/home/carll/GDB/build-reverse-multiple- > contiguous/gdb' > make[1]: *** [Makefile:13569: all-gdb] Error 2 > make[1]: Leaving directory '/home/carll/GDB/build-reverse-multiple- > contiguous' > make: *** [Makefile:1005: all] Error 2 > > Leaving the forward declaration in the code. Because you need to put "static" at the other place (line 6813). Simon ^ permalink raw reply [flat|nested] 41+ messages in thread
* RE: [PATCH 2/2 v5] Fix reverse stepping multiple contiguous PC ranges over the line table. 2023-06-19 17:58 ` Simon Marchi 2023-06-22 20:38 ` Carl Love @ 2023-06-23 20:04 ` Carl Love 2023-06-23 20:04 ` [PATCH 2/2 v6] " Carl Love 2 siblings, 0 replies; 41+ messages in thread From: Carl Love @ 2023-06-23 20:04 UTC (permalink / raw) To: Bruno Larsen, gdb-patches, UlrichWeigand, pedro; +Cc: luis.machado, cel Simon: Thanks for the reply to my previous unintended send of this note before it was done. I did fix the forward declaration as you said. The issue with removing it was making sure the actual declaration is static. On Mon, 2023-06-19 at 13:58 -0400, Simon Marchi wrote: > On 5/16/23 18:54, Carl Love wrote: <snip> > > > > diff --git a/gdb/infrun.c b/gdb/infrun.c > > index efe2c00c489..31cd817c733 100644 > > --- a/gdb/infrun.c > > +++ b/gdb/infrun.c > > @@ -114,6 +114,9 @@ static struct async_event_handler > > *infrun_async_inferior_event_token; > > Starts off as -1, indicating "never enabled/disabled". */ > > static int infrun_is_async = -1; > > > > +static CORE_ADDR update_line_range_start (CORE_ADDR pc, > > + struct > > execution_control_state *ecs); > > + > > This forward-declaration is not needed. Per second email, removed the forward declaration and made the actual declaration static. > > > /* See infrun.h. */ > > > > void > > @@ -6769,6 +6772,25 @@ handle_signal_stop (struct > > execution_control_state *ecs) > > process_event_stop_test (ecs); > > } > > > > +CORE_ADDR > > +update_line_range_start (CORE_ADDR pc, struct > > execution_control_state *ecs) > > Please add a comment for the function. OK, added comment. > > > +{ > > + /* The line table may have multiple entries for the same source > > code line. > > + Given the PC, check the line table and return the PC that > > corresponds > > + to the line table entry for the source line that PC is > > in. */ > > + CORE_ADDR start_line_pc = ecs->event_thread- > > >control.step_range_start; > > + gdb::optional<CORE_ADDR> real_range_start; > > + > > + /* Call find_line_range_start to get the smallest address in the > > + linetable for multiple Line X entries in the line table. */ > > + real_range_start = find_line_range_start (pc); > > + > > + if (real_range_start.has_value ()) > > + start_line_pc = *real_range_start; > > + > > + return start_line_pc; > > When I read this, I wonder: why was control.step_range_start not set > to > the "real" range start in the first place (not only in the context of > reverse execution, every time it is set)? It would seem more robust > than patching it afterwards in some very specific spots. > > I could see some benefits for range-stepping uses cases too (relevant > when debugging remotely). Using your example here: > > Line X - [0x0 - 0x8] > Line X - [0x8 - 0x10] > Line X - [0x10 - 0x18] > > Imagine we are stopped at 0x14, and we type "next", and 0x14 is a > conditional jump to 0x5. It seems like current GDB would send a > "range > step" request to GDBserver, to step in the [0x10, 0x18[ range. When > reaching 0x5, execution would stop, and GDB would resume it again > with > the [0x0,0x8[ range. When reaching 0x8, it would stop again, GDB > would > resume it with [0x8,0x10[, and so on. If GDB could send a "range > step" > request with the [0x0,0x18[ range, it would avoid those unnecessary > intermediary stop. > We looked at trying to set control.step_range_start to the real start range initially. Ulrich brought this point up in our internal review of the patch. So, when I am in function finish_backward() in infcmd.c I have no way to determine what the previous PC was. If I assume it was the previous value, i.e. pc - 4byes (on PowerPC). I get a gdb internal error. It seems that I am not allowed to change the line range to something that does not include the current pc value. ../../binutils-gdb-reverse-multiple-contiguous/gdb/infrun.c:2740: internal-error: resume_1: Assertion `pc_in_thread_step_range (pc, tp)' failed. In order to make that work, we concluded that it would probably entail a much bigger change to how reverse execution works which would be beyond the scope of what this patch is trying to fix. So, being able to do what I believe you want to do is in theory possible but it would require a larger, independent change to what this patch is trying to fix. > > +} > > + > > /* Come here when we've got some debug event / signal we can > > explain > > (IOW, not a random signal), and test whether it should cause a > > stop, or whether we should resume the inferior (transparently). > > @@ -7570,6 +7592,28 @@ process_event_stop_test (struct > > execution_control_state *ecs) > > > > if (stop_pc_sal.is_stmt) > > { > > + if (execution_direction == EXEC_REVERSE) > > + { > > + /* We are stepping backwards make sure we have reached > > the > > + beginning of the line. */ > > + CORE_ADDR stop_pc = ecs->event_thread->stop_pc (); > > + CORE_ADDR start_line_pc > > + = update_line_range_start (stop_pc, ecs); > > + > > + if (stop_pc != start_line_pc) > > + { > > + /* Have not reached the beginning of the source code > > line. > > + Set a step range. Execution should stop in any > > function > > + calls we execute back into before reaching the > > beginning > > + of the line. */ > > + ecs->event_thread->control.step_range_start = > > start_line_pc; > > + ecs->event_thread->control.step_range_end = stop_pc; > > + set_step_info (ecs->event_thread, frame, > > stop_pc_sal); > > + keep_going (ecs); > > + return; > > + } > > + } > > + > > /* We are at the start of a statement. > > > > So stop. Note that we don't stop if we step into the > > middle of a > > @@ -7632,6 +7676,19 @@ process_event_stop_test (struct > > execution_control_state *ecs) > > set_step_info (ecs->event_thread, frame, stop_pc_sal); > > > > infrun_debug_printf ("keep going"); > > + > > + if (execution_direction == EXEC_REVERSE) > > + { > > + CORE_ADDR stop_pc = ecs->event_thread->stop_pc (); > > + > > + /* Make sure the stop_pc is set to the beginning of the > > line. */ > > + if (stop_pc != ecs->event_thread->control.step_range_start) > > + { > > + stop_pc = update_line_range_start (stop_pc, ecs); > > + ecs->event_thread->control.step_range_start = stop_pc; > > + } > > + } > > + > > keep_going (ecs); > > } > > > > diff --git a/gdb/symtab.c b/gdb/symtab.c > > index 27611a34ec4..91d35616eb9 100644 > > --- a/gdb/symtab.c > > +++ b/gdb/symtab.c > > @@ -3282,6 +3282,55 @@ find_pc_line (CORE_ADDR pc, int notcurrent) > > return sal; > > } > > > > +/* Compare two symtab_and_line entries. Return true if both have > > + the same line number and the same symtab pointer. That means > > we > > + are dealing with two entries from the same line and from the > > same > > + source file. > > + > > + Return false otherwise. */ > > + > > +static bool > > +sal_line_symtab_matches_p (const symtab_and_line &sal1, > > + const symtab_and_line &sal2) > > +{ > > + return (sal1.line == sal2.line && sal1.symtab == sal2.symtab); > > Unnecessary parenthesis. Fixed. > > > +} > > + > > +/* See symtah.h. */ > > + > > +gdb::optional<CORE_ADDR> > > +find_line_range_start (CORE_ADDR pc) > > +{ > > + struct symtab_and_line current_sal = find_pc_line (pc, 0); > > + > > + if (current_sal.line == 0) > > + return {}; > > + > > + struct symtab_and_line prev_sal = find_pc_line (current_sal.pc - > > 1, 0); > > + > > + /* If the previous entry is for a different line, that means we > > are already > > + at the entry with the start PC for this line. */ > > + if (!sal_line_symtab_matches_p (prev_sal, current_sal)) > > + return current_sal.pc; > > + > > + /* Otherwise, keep looking for entries for the same line but > > with > > + smaller PC's. */ > > + bool done = false; > > + CORE_ADDR prev_pc; > > + while (!done) > > + { > > + prev_pc = prev_sal.pc; > > + > > + prev_sal = find_pc_line (prev_pc - 1, 0); > > + > > + /* Did we notice a line change? If so, we are done with the > > search. */ > > + if (!sal_line_symtab_matches_p (prev_sal, current_sal)) > > + done = true; > > + } > > + > > + return prev_pc; > > Algorithmic complexity question: given that line tables are sorted by > address, would it work to start at the current line table item, and > go > look at the previous ones until we find one that is no longer > contiguous and same line? find_pc_line is somewhat heavy, so if we > don't need to do it repeatedly... Yes, line tables are sorted by address. The issue with just looking back thru the current line table is branches/function calls. Suppose we arrived at the current the source code line for a given entry in the line table via a branch or a function call. If we looked at the source code line number for the previous entry in the line table, it may be the same in this case if we jumped into the line. The pc for the previous entry in the line table would not for the line we came from because we arrived via a branch/function call. The address we came from could even be in another symbol table. We need to walk back one pc at a time, to see where we came from, and checking to see if the symbol table entry changed or the line number changed to determine where the beginning of the line is. So, we need to call find_pc_line(), to find out if the previous PC is in a different line or if it corresponds to a different symbol table. Just searching back in the "current" line table for a line number change to figure out the first pc in the line is not sufficient. I don't see anyway to avoid calling find_pc_line() to determine if the symbol table for the previous PC changed or not. Note, I did play with just walking back thru the line table and that does work for the test cases. But I don't believe it will work in general. The pointer to the base of the linetable for the symbol table and the maximum number of lines in the linetable are accessible in function find_pc_sect_line. Given that information, then we search the line table for the first linetable entry that corresponds to the line number we want and then return the pc for that linetable entry. We would need to do a binary search since we don't know the actual offset into the table for the current pc. The time (log base 2 of the linetable size) to do the search is obviously a function of the size of the linetable. If we have function find_pc_sect_line (which is called by our intial call to find_pc_line()) record the offset into the line table where the current line number occurs and store it in struct symtab, then we can very efficiently start at the correct linetable entry and walk back as you suggested. But what we can't tell is if the execution history actually came in via the pc values in the earlier linetable entries. If execution didn't come in via an earlier entry in the linetable, the returned pc for the first linetable entry of the desired source code line may never be reached during our reverse execution. Obviously in that case, gdb will not stop at the correct location. > > > +} > > + > > /* See symtab.h. */ > > > > struct symtab * > > diff --git a/gdb/symtab.h b/gdb/symtab.h > > index 404d0ab30a8..f54305636da 100644 > > --- a/gdb/symtab.h > > +++ b/gdb/symtab.h > > @@ -2346,6 +2346,22 @@ extern struct symtab_and_line find_pc_line > > (CORE_ADDR, int); > > extern struct symtab_and_line find_pc_sect_line (CORE_ADDR, > > struct obj_section *, > > int); > > > > +/* Given PC, and assuming it is part of a range of addresses that > > is part of a > > + line, go back through the linetable and find the starting PC of > > that > > + line. > > + > > + For example, suppose we have 3 PC ranges for line X: > > + > > + Line X - [0x0 - 0x8] > > + Line X - [0x8 - 0x10] > > + Line X - [0x10 - 0x18] > > + > > + If we call the function with PC == 0x14, we want to return 0x0, > > as that is > > + the starting PC of line X, and the ranges are contiguous. > > I think that putting this example in the comment is great. It makes > it > much more obvious what the function specifically does. > > > +*/ > > + > > +extern gdb::optional<CORE_ADDR> find_line_range_start (CORE_ADDR > > pc); > > + > > /* Wrapper around find_pc_line to just return the symtab. */ > > > > extern struct symtab *find_pc_line_symtab (CORE_ADDR); > > diff --git a/gdb/testsuite/gdb.reverse/func-map-to-same-line.c > > b/gdb/testsuite/gdb.reverse/func-map-to-same-line.c > > new file mode 100644 > > index 00000000000..da944874e86 > > --- /dev/null > > +++ b/gdb/testsuite/gdb.reverse/func-map-to-same-line.c > > @@ -0,0 +1,36 @@ > > +/* Copyright 2008-2023 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 < > > http://www.gnu.org/licenses/ > > >. > > + > > + This test is used to test the reverse-step and reverse-next > > instruction > > + execution for a source line that contains multiple function > > calls. */ > > + > > +void > > +func1 () > > +{ > > +} /* END FUNC1 */ > > + > > +void > > +func2 () > > +{ > > +} /* END FUNC2 */ > > + > > +int main () > > int > main (void) > Fixed. > > +{ > > + int a, b; > > + a = 1; > > + b = 2; > > + func1 (); func2 (); > > + a = a + b; /* START REVERSE TEST */ > > +} > > diff --git a/gdb/testsuite/gdb.reverse/func-map-to-same-line.exp > > b/gdb/testsuite/gdb.reverse/func-map-to-same-line.exp > > new file mode 100644 > > index 00000000000..89e226b0f84 > > --- /dev/null > > +++ b/gdb/testsuite/gdb.reverse/func-map-to-same-line.exp > > @@ -0,0 +1,140 @@ > > +# Copyright 2008-2023 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 < > > http://www.gnu.org/licenses/ > > >. */ > > + > > +# This file is part of the GDB testsuite. It tests reverse > > stepping. > > +# Lots of code borrowed from "step-test.exp". > > + > > +# This test checks to make sure there is no regression failures > > for > > +# the reverse-next command when stepping back over two functions > > in > > +# the same line. > > + > > +require supports_reverse > > + > > +# This test uses the gcc no-column-info command which was added in > > gcc 7.1. > > + > > +proc run_tests {} { > > + global srcfile > > + global executable > > + > > + runto_main > > We typically check for runto_main's success: Fixed a couple instances of runto_main in this test case and in the other test case. > > if { ![runto_main] } { > return > } > > runto_main logs a FAIL on failure. There are a few runto_mains in > the > patch. > > > + set target_remote [gdb_is_target_remote] > > target_remote seems unused Removed. > > > + > > + with_test_prefix "test1" { > > + gdb_test_no_output "record" "turn on process record" > > + } > > with_test_prefix with a single test in it is really just the same as: > > gdb_test_no_output "record" "test1: turn on process record" > > In fact, you have some other tests with the "test1:" or "test2:" > prefix, > I think they should be moved to the with_test_prefix. And maybe use > "next" and "step" instead of "test1" and "test2". Yup, cleaner to have the with_test_prefix cover the whole test. Changed test1 to next-test and test2 to step-next. > > > + > > + # This regression test verifies the reverse-step and reverse- > > next commands > > + # work properly when executing backwards thru a source line > > containing > > + # two function calls on the same source line, i.e. func1 (); > > func2 (); > > + # This test is compiled so the dwarf info not contain the line > > table > > + # information. > > + > > + # Test 1, reverse-next command > > + # Set breakpoint at the line after the function calls. > > + set bp_start_reverse_test [gdb_get_line_number "START REVERSE > > TEST" \ > > + $srcfile] > > + gdb_breakpoint $srcfile:$bp_start_reverse_test temporary > > + > > + # Continue to break point for reverse-next test. > > + # Command definition: reverse-next [count] > > + # Run backward to the beginning of the previous line > > executed in the > > + # current (innermost) stack frame. If the line contains > > function calls, > > + # they will be “un-executed” without stopping. Starting from > > the first > > + # line of a function, reverse-next will take you back to the > > caller of > > + # that function, before the function was called, just as the > > normal next > > + # command would take you from the last line of a function > > back to its > > + # return to its caller 2 . > > + gdb_continue_to_breakpoint \ > > + "test1: stopped at command reverse-next test start location" \ > > + ".*$srcfile:$bp_start_reverse_test\r\n.*" > > + > > + # The reverse-next should step all the way back to the > > beginning of the > > + # line, i.e. at the beginning of the func1 call. > > + gdb_test "reverse-next" ".*func1 \\(\\); func2 \\(\\);.*" \ > > + "test1: reverse-next to line with two functions" > > + > > + # We should be stopped at the first instruction of the line. A > > reverse-step > > + # should step back and stop at the beginning of the previous > > line b = 2, > > + # i.e. not in func1 (). > > + gdb_test "reverse-stepi" ".*b = 2;.*" \ > > + "test1: reverse-stepi to previous line b = 2" > > + > > + > > + # Setup for test 2 > > + clean_restart $executable > > + runto_main > > + > > + with_test_prefix "test2" { > > + gdb_test_no_output "record" "turn on process record" > > + } > > + > > + # Test 2, reverse-step command > > + # Set breakpoint at the line after the function calls. > > + gdb_breakpoint $srcfile:$bp_start_reverse_test temporary > > + > > + # Continue to the start of the reverse-step test. > > + # Command definition: reverse-step [count] > > + # Run the program backward until control reaches the start > > of a > > + # different source line; then stop it, and return control > > to gdb. > > + # Like the step command, reverse-step will only stop at the > > beginning > > + # of a source line. It “un-executes” the previously > > executed source > > + # line. If the previous source line included calls to > > debuggable > > + # functions, reverse-step will step (backward) into the > > called function, > > + # stopping at the beginning of the last statement in the > > called > > + # function (typically a return statement). Also, as with > > the step > > + # command, if non-debuggable functions are called, reverse- > > step will > > + # run thru them backward without stopping. > > + > > + gdb_continue_to_breakpoint \ > > + "test2: stopped at command reverse-step test start location" \ > > + ".*$srcfile:$bp_start_reverse_test\r\n.*" > > + > > + # The first reverse step should take us call of func2 (). > > + gdb_test "reverse-step" ".*END FUNC2.*" \ > > + "test2: reverse-step into func2 " > > + > > + # The second reverse step should take us into func1 (). > > + gdb_test "reverse-step" ".*END FUNC1.*" \ > > + "test2: reverse-step into func1 " > > + > > + # The third reverse step should take us call of func1 (). > > + gdb_test "reverse-step" ".*func1 \\(\\); func2 \\(\\);.*" \ > > + "test2: reverse-step to line func1(); func2(), at call for > > func1 " > > + > > + # We should be stopped at the first instruction of the line. A > > reverse > > + # stepi should take us to b = 2 (). > > + gdb_test "reverse-stepi" ".*b = 2;.*" \ > > + "test2: reverse-stepi to line b = 2 " > > +} > > + > > +set srcfile func-map-to-same-line.c > > +set executable func-map-to-same-line > > Wondering if this test should use standard_testfile (like almost > every > other tests) to set these. OK, changed to use the standard_testfile. > > > + > > +# test with and without gcc column info enabled > > +foreach_with_prefix with_column_info {yes no} { > > + if {$with_column_info == "yes"} { > > + set options [list debug column-info] > > + } else { > > + set options [list debug no-column-info] > > + } > > I didn't think of this when proposing the foreach_with_prefix, but > you > could perhaps use: > > foreach_with_prefix column_info_flag {column-info no-column-info} > > ... to avoid this boilerplate. You can then use $column_info_flag > directly when setting options. Yes, that makes things a bit cleaner. Changed. > > > + > > + if {[build_executable "failed to prepare" $executable $srcfile > > \ > > + $options] == -1} { > > + return -1 > > + } > > + > > + clean_restart $executable > > clean_restart can go in run_tests. Moved. > > > + run_tests > > +} > > diff --git a/gdb/testsuite/gdb.reverse/map-to-same-line.c > > b/gdb/testsuite/gdb.reverse/map-to-same-line.c > > new file mode 100644 > > index 00000000000..f20d778f40e > > --- /dev/null > > +++ b/gdb/testsuite/gdb.reverse/map-to-same-line.c > > @@ -0,0 +1,58 @@ > > +/* Copyright 2008-2023 Free Software Foundation, Inc. > > Just wondering if the copyright years are right. These are new test files so, yea, should start with 2023. > > > + > > + 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 < > > http://www.gnu.org/licenses/ > > >. */ > > + > > +/* The purpose of this test is to create a DWARF line table that > > contains two > > + or more entries for the same line. When stepping (forwards or > > backwards), > > + GDB should step over the entire line and not just a particular > > entry in the > > + line table. */ > > + > > +int > > +main () > > void in the parenthesis Fixed in both tests. > > > +{ /* TAG: main prologue */ > > + asm ("main_label: .globl main_label"); > > + int i = 1, j = 2, k; > > + float f1 = 2.0, f2 = 4.1, f3; > > + const char *str_1 = "foo", *str_2 = "bar", *str_3; > > + > > + asm ("line1: .globl line1"); > > + k = i; f3 = f1; str_3 = str_1; /* TAG: line 1 */ > > + > > + asm ("line2: .globl line2"); > > + k = j; f3 = f2; str_3 = str_2; /* TAG: line 2 */ > > + > > + asm ("line3: .globl line3"); > > + k = i; f3 = f1; str_3 = str_1; /* TAG: line 3 */ > > + > > + asm ("line4: .globl line4"); > > + k = j; f3 = f2; str_3 = str_2; /* TAG: line 4 */ > > + > > + asm ("line5: .globl line5"); > > + k = i; f3 = f1; str_3 = str_1; /* TAG: line 5 */ > > + > > + asm ("line6: .globl line6"); > > + k = j; f3 = f2; str_3 = str_2; /* TAG: line 6 */ > > + > > + asm ("line7: .globl line7"); > > + k = i; f3 = f1; str_3 = str_1; /* TAG: line 7 */ > > + > > + asm ("line8: .globl line8"); > > + k = j; f3 = f2; str_3 = str_2; /* TAG: line 8 */ > > + > > + asm ("main_return: .globl main_return"); > > + k = j; f3 = f2; str_3 = str_2; /* TAG: main return */ > > + > > + asm ("end_of_sequence: .globl end_of_sequence"); > > + return 0; /* TAG: main return */ > > +} > > diff --git a/gdb/testsuite/gdb.reverse/map-to-same-line.exp > > b/gdb/testsuite/gdb.reverse/map-to-same-line.exp > > new file mode 100644 > > index 00000000000..16a359d90ec > > --- /dev/null > > +++ b/gdb/testsuite/gdb.reverse/map-to-same-line.exp > > @@ -0,0 +1,156 @@ > > +# Copyright 2008-2023 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 < > > http://www.gnu.org/licenses/ > > = >. > > + > > +# When stepping (forwards or backwards), GDB should step over the > > entire line > > +# and not just a particular entry in the line table. This test was > > added to > > +# verify the find_line_range_start function properly sets the step > > range for a > > +# line that consists of multiple statements, i.e. multiple entries > > in the line > > +# table. This test creates a DWARF line table that contains two > > entries for > > +# the same line to do the needed testing. > > + > > +# This test can only be run on targets which support DWARF-2 and > > use gas. > > +load_lib dwarf.exp > > +require dwarf2_support > > + > > +# The DWARF assembler requires the gcc compiler. > > +require is_c_compiler_gcc > > + > > +# This test suitable only for process that can do reverse > > execution > > +require supports_reverse > > + > > +standard_testfile .c .S > > + > > +if { [prepare_for_testing "failed to prepare" ${testfile} > > ${srcfile}] } { > > + return -1 > > +} > > + > > +set asm_file [standard_output_file $srcfile2] > > +Dwarf::assemble $asm_file { > > + global srcdir subdir srcfile > > + declare_labels integer_label L > > + > > + # Find start address and length of program > > + lassign [function_range main [list > > ${srcdir}/${subdir}/$srcfile]] \ > > + main_start main_len > > + set main_end "$main_start + $main_len" > > + > > + cu {} { > > + compile_unit { > > + {language @DW_LANG_C} > > + {name map-to-same-line.c} > > + {stmt_list $L DW_FORM_sec_offset} > > + {low_pc 0 addr} > > + } { > > + subprogram { > > + {external 1 flag} > > + {name main} > > + {low_pc $main_start addr} > > + {high_pc $main_len DW_FORM_data4} > > + } > > + } > > + } > > + > > + lines {version 2 default_is_stmt 1} L { > > + include_dir "${srcdir}/${subdir}" > > + file_name "$srcfile" 1 > > + > > + # Generate the line table program with distinct source lines > > being > > + # mapped to the same line entry. Line 1, 5 and 8 contain 1 > > statement > > + # each. Line 2 contains 2 statements. Line 3 contains 3 > > statements. > > + program { > > + DW_LNE_set_address $main_start > > + line [gdb_get_line_number "TAG: main prologue"] > > + DW_LNS_copy > > + DW_LNE_set_address line1 > > + line [gdb_get_line_number "TAG: line 1" ] > > + DW_LNS_copy > > + DW_LNE_set_address line2 > > + line [gdb_get_line_number "TAG: line 2" ] > > + DW_LNS_copy > > + DW_LNE_set_address line3 > > + line [gdb_get_line_number "TAG: line 2" ] > > + DW_LNS_copy > > + DW_LNE_set_address line4 > > + line [gdb_get_line_number "TAG: line 3" ] > > + DW_LNS_copy > > + DW_LNE_set_address line5 > > + line [gdb_get_line_number "TAG: line 3" ] > > + DW_LNS_copy > > + DW_LNE_set_address line6 > > + line [gdb_get_line_number "TAG: line 3" ] > > + DW_LNS_copy > > + DW_LNE_set_address line7 > > + line [gdb_get_line_number "TAG: line 5" ] > > + DW_LNS_copy > > + DW_LNE_set_address line8 > > + line [gdb_get_line_number "TAG: line 8" ] > > + DW_LNS_copy > > + DW_LNE_set_address main_return > > + line [gdb_get_line_number "TAG: main return"] > > + DW_LNS_copy > > + DW_LNE_set_address end_of_sequence > > + DW_LNE_end_sequence > > + } > > + } > > +} > > + > > +if { [prepare_for_testing "failed to prepare" ${testfile} \ > > + [list $srcfile $asm_file] {nodebug} ] } { > > + return -1 > > +} > > + > > +runto_main > > + > > +# Print the line table > > +gdb_test_multiple "maint info line-table ${testfile}" "" { > > + -re "\r\n$decimal\[ \t\]+$decimal\[ \t\]+($hex)\[ > > \t\]+Y\[^\r\n\]*" { > > + lappend is_stmt $expect_out(1,string) > > + exp_continue > > + } > > + -re -wrap "" { > > + } > > +} > > + > > +# Do the reverse-step test > > +gdb_test_no_output "record" "turn on process record" > > + > > +set bp_main_return [gdb_get_line_number "TAG: main return" > > $srcfile] > > +gdb_breakpoint $srcfile:$bp_main_return > > +gdb_continue_to_breakpoint "run to end of main, reverse-step > > test" ".*$srcfile:$bp_main_return.*" > > +gdb_test "display \$pc" ".*pc =.*" "display pc, reverse-step test" > > + > > +# At this point, GDB has already recorded the execution up until > > the return > > +# statement. Reverse-step and test if GDB transitions between > > lines in the > > +# expected order. It should reverse-step across lines 8, 5, 3, 2 > > and 1. > > +foreach line {8 5 3 2 1} { > > + gdb_test "reverse-step" ".*TAG: line $line.*" "reverse step to > > line $line" > > +} > > + > > +## Clean restart, test reverse-next command > > +clean_restart ${testfile} > > +runto_main > > +gdb_test_no_output "record" "turn on process record, reverst-next > > test" > > + > > +set bp_main_return [gdb_get_line_number "TAG: main return" > > $srcfile] > > +gdb_breakpoint $srcfile:$bp_main_return > > +gdb_continue_to_breakpoint "run to end of main, reverse-next > > test" ".*$srcfile:$bp_main_return.*" > > +gdb_test "display \$pc" ".*pc =.*" "display pc, reverse-next test" > > + > > +# At this point, GDB has already recorded the execution up until > > the return > > +# statement. Reverse-next and test if GDB transitions between > > lines in the > > +# expected order. It should reverse-next across lines 8, 5, 3, 2 > > and 1. > > +foreach line {8 5 3 2 1} { > > + gdb_test "reverse-next" ".*TAG: line $line.*" "reverse next to > > line $line" > > +} > > It seems like the step and next tests are identical, so I guess it > could > be factored out using: > > foreach_with_prefix method {step next} { > ... > } > Yes, changed to use foreach_with_prefix. Carl ^ permalink raw reply [flat|nested] 41+ messages in thread
* [PATCH 2/2 v6] Fix reverse stepping multiple contiguous PC ranges over the line table. 2023-06-19 17:58 ` Simon Marchi 2023-06-22 20:38 ` Carl Love 2023-06-23 20:04 ` Carl Love @ 2023-06-23 20:04 ` Carl Love 2 siblings, 0 replies; 41+ messages in thread From: Carl Love @ 2023-06-23 20:04 UTC (permalink / raw) To: Simon Marchi, Bruno Larsen, gdb-patches, UlrichWeigand, pedro Cc: luis.machado, cel Bruno, Simon, GDB maintainers: Version 6, fixed various code style issues in the GDB source. The testcases were updated to use with_test_prefix for each gdb test in the step and next test cases, switch using the standard_testfile, use foreach_with_prefix to combine otherwise identical tests. Retested on Power 10. Version 5, changed comments in test case func-map-to-same-line.c. Patch 1/2 implemented the new options for gdb_compile. Updated the call to proc run_tests to use the new gdb_compile options in a foreach_with_prefix loop. Version 4, additional fixes for gcc version check, wrap function calls using "with_test_prefix", move load_lib dwarf.exe. Fixed typo noted by Luis. Version 3, added the gcc version check as discussed further from version 2 of the patch. Also updated the tests to check for supporting reverse execution rather than requiring recording. I also noticed there were a couple more instances of a requirement check, i.e. if [] which I changed to "require" per the current style for checking on the test requirements. The following patch fixes issues on PowerPC with the reverse-step and reverse-next instructions when there are multiple assignment statements on the same line and when there are multiple function calls on the same line. The commit log below discusses these issues in further depth. The discussion included what the correct operation should be for these commands based on the GDB documentation. The proposed patch at that time changed how the commands worked on other platforms such as X86 in a way they no longer matched the documentation. The issue is the line table contains multiple entries for the same source line. The patch adds a function to search the line table to find the address of the first instruction of a line. When setup up the reverse stepping range, the function is called to make sure the start of the range corresponds to the address of the first instruction for the line. This approach was used. When Luis initially developed the patch, he considered merging the contiguous ranges in the line table when reading the line tables. He decided it was better to work with the data directly in the line table rather than creating and using a modified version of the line table. The following patch fixes the execution of the reveres-step and reverse-next commands for both senarios of multiple statements on the same line for PowerPC and aarch64-linux. Unlike the previous patch, it does not change the operation of the commands on other platforms, i.e. X86. The patch adds new test cases for both scenarios to verify they work correctly. The patch has been tested on PowerPC, Intel X86 and aarch64-linux with no new regression failures. Please let me know if the patch is acceptable for mainline. Thanks. Carl -------------------------- Fix reverse stepping multiple contiguous PC ranges over the line table. There are a couple of scenarios where the GDB reverse-step and reverse-next commands do not work correctly. Scenario 1 issue description by Luis Machado: When running GDB's testsuite on aarch64-linux/Ubuntu 20.04 (also spotted on the ppc backend), I noticed some failures in gdb.reverse/solib-precsave.exp and gdb.reverse/solib-reverse.exp. The failure happens around the following code: 38 b[1] = shr2(17); /* middle part two */ 40 b[0] = 6; b[1] = 9; /* generic statement, end part two */ 42 shr1 ("message 1\n"); /* shr1 one */ Normal execution: - step from line 38 will land on line 40. - step from line 40 will land on line 42. Reverse execution: - step from line 42 will land on line 40. - step from line 40 will land on line 40. - step from line 40 will land on line 38. The problem here is that line 40 contains two contiguous but distinct PC ranges in the line table, like so: Line 40 - [0x7ec ~ 0x7f4] Line 40 - [0x7f4 ~ 0x7fc] The two distinct ranges are generated because GCC started outputting source column information, which GDB doesn't take into account at the moment. When stepping forward from line 40, we skip both of these ranges and land on line 42. When stepping backward from line 42, we stop at the start PC of the second (or first, going backwards) range of line 40. Since we've reached ecs->event_thread->control.step_range_start, we stop stepping backwards. --------------------------------------------------------- Scenario 2 issue described by Pedro Alves: The following explanation of the issue was taken from the gdb mailing list discussion of the withdrawn patch to change the behavior of the reverse-step and reverse-next commands. Specifically, message from Pedro Alves <pedro@palves.net> where he demonstrates the issue where you have multiple function calls on the same source code line: https://sourceware.org/pipermail/gdb-patches/2023-January/196110.html The source line looks like: func1 (); func2 (); so stepping backwards over that line should always stop at the first instruction of the line, not in the middle. Let's simplify this. Here's the full source code of my example: (gdb) list 1 1 void func1 () 2 { 3 } 4 5 void func2 () 6 { 7 } 8 9 int main () 10 { 11 func1 (); func2 (); 12 } Compiled with: $ gcc reverse.c -o reverse -g3 -O0 $ gcc -v ... gcc version 11.3.0 (Ubuntu 11.3.0-1ubuntu1~22.04) Now let's debug it with target record, using current gdb git master (f3d8ae90b236), without your patch: $ gdb ~/reverse GNU gdb (GDB) 14.0.50.20230124-git ... Reading symbols from /home/pedro/reverse... (gdb) start Temporary breakpoint 1 at 0x1147: file reverse.c, line 11. Starting program: /home/pedro/reverse [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". Temporary breakpoint 1, main () at reverse.c:11 11 func1 (); func2 (); (gdb) record (gdb) disassemble /s Dump of assembler code for function main: reverse.c: 10 { 0x000055555555513f <+0>: endbr64 0x0000555555555143 <+4>: push %rbp 0x0000555555555144 <+5>: mov %rsp,%rbp 11 func1 (); func2 (); => 0x0000555555555147 <+8>: mov $0x0,%eax 0x000055555555514c <+13>: call 0x555555555129 <func1> 0x0000555555555151 <+18>: mov $0x0,%eax 0x0000555555555156 <+23>: call 0x555555555134 <func2> 0x000055555555515b <+28>: mov $0x0,%eax 12 } 0x0000555555555160 <+33>: pop %rbp 0x0000555555555161 <+34>: ret End of assembler dump. (gdb) n 12 } So far so good, a "next" stepped over the whole of line 11 and stopped at line 12. Let's confirm where we are now: (gdb) disassemble /s Dump of assembler code for function main: reverse.c: 10 { 0x000055555555513f <+0>: endbr64 0x0000555555555143 <+4>: push %rbp 0x0000555555555144 <+5>: mov %rsp,%rbp 11 func1 (); func2 (); 0x0000555555555147 <+8>: mov $0x0,%eax 0x000055555555514c <+13>: call 0x555555555129 <func1> 0x0000555555555151 <+18>: mov $0x0,%eax 0x0000555555555156 <+23>: call 0x555555555134 <func2> 0x000055555555515b <+28>: mov $0x0,%eax 12 } => 0x0000555555555160 <+33>: pop %rbp 0x0000555555555161 <+34>: ret End of assembler dump. Good, we're at the first instruction of line 12. Now let's undo the "next", with "reverse-next": (gdb) reverse-next 11 func1 (); func2 (); Seemingly stopped at line 11. Let's see exactly where: (gdb) disassemble /s Dump of assembler code for function main: reverse.c: 10 { 0x000055555555513f <+0>: endbr64 0x0000555555555143 <+4>: push %rbp 0x0000555555555144 <+5>: mov %rsp,%rbp 11 func1 (); func2 (); 0x0000555555555147 <+8>: mov $0x0,%eax 0x000055555555514c <+13>: call 0x555555555129 <func1> => 0x0000555555555151 <+18>: mov $0x0,%eax 0x0000555555555156 <+23>: call 0x555555555134 <func2> 0x000055555555515b <+28>: mov $0x0,%eax 12 } 0x0000555555555160 <+33>: pop %rbp 0x0000555555555161 <+34>: ret End of assembler dump. (gdb) And lo, we stopped in the middle of line 11! That is a bug, we should have stepped back all the way to the beginning of the line. The "reverse-next" should have fully undone the prior "next" command. The above issues were fixed by introducing a new function that looks for adjacent PC ranges for the same line, until we notice a line change. Then we take that as the start PC of the range. The new start PC for the range is used for the control.step_range_start when setting up a step range. The test case gdb.reverse/map-to-same-line.exp is added to test the fix for the issues in scenario 1. The test case gdb.reverse/func-map-to-same-line.exp was added to test the fix for scenario 2 when the binary was compiled with and without line table information. bug: https://sourceware.org/bugzilla/show_bug.cgi?id=28426 Co-authored-by: Luis Machado <luis.machado@arm.com> Co-authored-by: Carl Love <cel@us.ibm.com> Reviewed-By: Bruno Larsen <blarsen@redhat.com> --- gdb/infrun.c | 59 +++++++ gdb/symtab.c | 49 ++++++ gdb/symtab.h | 16 ++ .../gdb.reverse/func-map-to-same-line.c | 37 +++++ .../gdb.reverse/func-map-to-same-line.exp | 139 ++++++++++++++++ gdb/testsuite/gdb.reverse/map-to-same-line.c | 58 +++++++ .../gdb.reverse/map-to-same-line.exp | 153 ++++++++++++++++++ 7 files changed, 511 insertions(+) create mode 100644 gdb/testsuite/gdb.reverse/func-map-to-same-line.c create mode 100644 gdb/testsuite/gdb.reverse/func-map-to-same-line.exp create mode 100644 gdb/testsuite/gdb.reverse/map-to-same-line.c create mode 100644 gdb/testsuite/gdb.reverse/map-to-same-line.exp diff --git a/gdb/infrun.c b/gdb/infrun.c index efe2c00c489..b06d6969ab8 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -114,6 +114,9 @@ static struct async_event_handler *infrun_async_inferior_event_token; Starts off as -1, indicating "never enabled/disabled". */ static int infrun_is_async = -1; +static CORE_ADDR update_line_range_start (CORE_ADDR pc, + struct execution_control_state *ecs); + /* See infrun.h. */ void @@ -6769,6 +6772,27 @@ handle_signal_stop (struct execution_control_state *ecs) process_event_stop_test (ecs); } +/* Return the address for the beginning of the line. */ + +CORE_ADDR +update_line_range_start (CORE_ADDR pc, struct execution_control_state *ecs) +{ + /* The line table may have multiple entries for the same source code line. + Given the PC, check the line table and return the PC that corresponds + to the line table entry for the source line that PC is in. */ + CORE_ADDR start_line_pc = ecs->event_thread->control.step_range_start; + gdb::optional<CORE_ADDR> real_range_start; + + /* Call find_line_range_start to get the smallest address in the + linetable for multiple Line X entries in the line table. */ + real_range_start = find_line_range_start (pc); + + if (real_range_start.has_value ()) + start_line_pc = *real_range_start; + + return start_line_pc; +} + /* Come here when we've got some debug event / signal we can explain (IOW, not a random signal), and test whether it should cause a stop, or whether we should resume the inferior (transparently). @@ -7570,6 +7594,28 @@ process_event_stop_test (struct execution_control_state *ecs) if (stop_pc_sal.is_stmt) { + if (execution_direction == EXEC_REVERSE) + { + /* We are stepping backwards make sure we have reached the + beginning of the line. */ + CORE_ADDR stop_pc = ecs->event_thread->stop_pc (); + CORE_ADDR start_line_pc + = update_line_range_start (stop_pc, ecs); + + if (stop_pc != start_line_pc) + { + /* Have not reached the beginning of the source code line. + Set a step range. Execution should stop in any function + calls we execute back into before reaching the beginning + of the line. */ + ecs->event_thread->control.step_range_start = start_line_pc; + ecs->event_thread->control.step_range_end = stop_pc; + set_step_info (ecs->event_thread, frame, stop_pc_sal); + keep_going (ecs); + return; + } + } + /* We are at the start of a statement. So stop. Note that we don't stop if we step into the middle of a @@ -7632,6 +7678,19 @@ process_event_stop_test (struct execution_control_state *ecs) set_step_info (ecs->event_thread, frame, stop_pc_sal); infrun_debug_printf ("keep going"); + + if (execution_direction == EXEC_REVERSE) + { + CORE_ADDR stop_pc = ecs->event_thread->stop_pc (); + + /* Make sure the stop_pc is set to the beginning of the line. */ + if (stop_pc != ecs->event_thread->control.step_range_start) + { + stop_pc = update_line_range_start (stop_pc, ecs); + ecs->event_thread->control.step_range_start = stop_pc; + } + } + keep_going (ecs); } diff --git a/gdb/symtab.c b/gdb/symtab.c index 27611a34ec4..fa8a892e530 100644 --- a/gdb/symtab.c +++ b/gdb/symtab.c @@ -3282,6 +3282,55 @@ find_pc_line (CORE_ADDR pc, int notcurrent) return sal; } +/* Compare two symtab_and_line entries. Return true if both have + the same line number and the same symtab pointer. That means we + are dealing with two entries from the same line and from the same + source file. + + Return false otherwise. */ + +static bool +sal_line_symtab_matches_p (const symtab_and_line &sal1, + const symtab_and_line &sal2) +{ + return sal1.line == sal2.line && sal1.symtab == sal2.symtab; +} + +/* See symtah.h. */ + +gdb::optional<CORE_ADDR> +find_line_range_start (CORE_ADDR pc) +{ + struct symtab_and_line current_sal = find_pc_line (pc, 0); + + if (current_sal.line == 0) + return {}; + + struct symtab_and_line prev_sal = find_pc_line (current_sal.pc - 1, 0); + + /* If the previous entry is for a different line, that means we are already + at the entry with the start PC for this line. */ + if (!sal_line_symtab_matches_p (prev_sal, current_sal)) + return current_sal.pc; + + /* Otherwise, keep looking for entries for the same line but with + smaller PC's. */ + bool done = false; + CORE_ADDR prev_pc; + while (!done) + { + prev_pc = prev_sal.pc; + + prev_sal = find_pc_line (prev_pc - 1, 0); + + /* Did we notice a line change? If so, we are done with the search. */ + if (!sal_line_symtab_matches_p (prev_sal, current_sal)) + done = true; + } + + return prev_pc; +} + /* See symtab.h. */ struct symtab * diff --git a/gdb/symtab.h b/gdb/symtab.h index 404d0ab30a8..f54305636da 100644 --- a/gdb/symtab.h +++ b/gdb/symtab.h @@ -2346,6 +2346,22 @@ extern struct symtab_and_line find_pc_line (CORE_ADDR, int); extern struct symtab_and_line find_pc_sect_line (CORE_ADDR, struct obj_section *, int); +/* Given PC, and assuming it is part of a range of addresses that is part of a + line, go back through the linetable and find the starting PC of that + line. + + For example, suppose we have 3 PC ranges for line X: + + Line X - [0x0 - 0x8] + Line X - [0x8 - 0x10] + Line X - [0x10 - 0x18] + + If we call the function with PC == 0x14, we want to return 0x0, as that is + the starting PC of line X, and the ranges are contiguous. +*/ + +extern gdb::optional<CORE_ADDR> find_line_range_start (CORE_ADDR pc); + /* Wrapper around find_pc_line to just return the symtab. */ extern struct symtab *find_pc_line_symtab (CORE_ADDR); diff --git a/gdb/testsuite/gdb.reverse/func-map-to-same-line.c b/gdb/testsuite/gdb.reverse/func-map-to-same-line.c new file mode 100644 index 00000000000..17fe17af267 --- /dev/null +++ b/gdb/testsuite/gdb.reverse/func-map-to-same-line.c @@ -0,0 +1,37 @@ +/* Copyright 2023 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 <http://www.gnu.org/licenses/>. + + This test is used to test the reverse-step and reverse-next instruction + execution for a source line that contains multiple function calls. */ + +void +func1 (void) +{ +} /* END FUNC1 */ + +void +func2 (void) +{ +} /* END FUNC2 */ + +int +main (void) +{ + int a, b; + a = 1; + b = 2; + func1 (); func2 (); + a = a + b; /* START REVERSE TEST */ +} diff --git a/gdb/testsuite/gdb.reverse/func-map-to-same-line.exp b/gdb/testsuite/gdb.reverse/func-map-to-same-line.exp new file mode 100644 index 00000000000..2890b5b1a70 --- /dev/null +++ b/gdb/testsuite/gdb.reverse/func-map-to-same-line.exp @@ -0,0 +1,139 @@ +# Copyright 2023 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 <http://www.gnu.org/licenses/>. */ + +# This file is part of the GDB testsuite. It tests reverse stepping. +# Lots of code borrowed from "step-test.exp". + +# This test checks to make sure there is no regression failures for +# the reverse-next command when stepping back over two functions in +# the same line. + +require supports_reverse + +# This test uses the gcc no-column-info command which was added in gcc 7.1. + +proc run_tests {} { + global testfile + + clean_restart ${testfile} + + if { ![runto_main] } { + return + } + + with_test_prefix "next-test" { + gdb_test_no_output "record" "turn on process record" + + # This regression test verifies the reverse-step and reverse-next + # commands work properly when executing backwards thru a source line + # containing two function calls on the same source line, i.e. func1 (); + # func2 ();. This test is compiled so the dwarf info not contain the + # line table information. + + # Test 1, reverse-next command + # Set breakpoint at the line after the function calls. + set bp_start_reverse_test [gdb_get_line_number "START REVERSE TEST"] + + gdb_breakpoint $bp_start_reverse_test temporary + + # Continue to break point for reverse-next test. + # Command definition: reverse-next [count] + # Run backward to the beginning of the previous line executed in the + # current (innermost) stack frame. If the line contains function + # calls,they will be “un-executed” without stopping. Starting from + # the first line of a function, reverse-next will take you back to + # the caller of that function, before the function was called, just + # as the normal next command would take you from the last line of a + # function back to its return to its caller 2 . + + gdb_continue_to_breakpoint \ + "stopped at command reverse-next test start location" \ + ".*$bp_start_reverse_test\r\n.*" + + # The reverse-next should step all the way back to the beginning of the + # line, i.e. at the beginning of the func1 call. + gdb_test "reverse-next" ".*func1 \\(\\); func2 \\(\\);.*" \ + " reverse-next to line with two functions" + + # We should be stopped at the first instruction of the line. A + # reverse-step should step back and stop at the beginning of the + # previous line b = 2, i.e. not in func1 (). + gdb_test "reverse-stepi" ".*b = 2;.*" \ + "reverse-stepi to previous line b = 2" + } + + # Setup for test 2 + clean_restart ${testfile} + + if { ![runto_main] } { + return + } + + with_test_prefix "step-test" { + gdb_test_no_output "record" "turn on process record" + + # Test 2, reverse-step command + # Set breakpoint at the line after the function calls. + gdb_breakpoint $bp_start_reverse_test temporary + + # Continue to the start of the reverse-step test. + # Command definition: reverse-step [count] + # Run the program backward until control reaches the start of a + # different source line; then stop it, and return control to gdb. + # Like the step command, reverse-step will only stop at the beginning + # of a source line. It “un-executes” the previously executed source + # line. If the previous source line included calls to debuggable + # functions, reverse-step will step (backward) into the called + # function, stopping at the beginning of the last statement in the + # called function (typically a return statement). Also, as with the + # step command, if non-debuggable functions are called, reverse-step + # will run thru them backward without stopping. + + gdb_continue_to_breakpoint \ + "stopped at command reverse-step test start location" \ + ".*$bp_start_reverse_test\r\n.*" + + # The first reverse step should take us call of func2 (). + gdb_test "reverse-step" ".*END FUNC2.*" \ + "reverse-step into func2 " + + # The second reverse step should take us into func1 (). + gdb_test "reverse-step" ".*END FUNC1.*" \ + "reverse-step into func1 " + + # The third reverse step should take us call of func1 (). + gdb_test "reverse-step" ".*func1 \\(\\); func2 \\(\\);.*" \ + "reverse-step to line func1(); func2(), at call for func1 " + + # We should be stopped at the first instruction of the line. A reverse + # stepi should take us to b = 2 (). + gdb_test "reverse-stepi" ".*b = 2;.*" \ + "reverse-stepi to line b = 2 " + } +} + +standard_testfile .c + +# test with and without gcc column info enabled +foreach_with_prefix column_info_flag {column-info no-column-info} { + set options [list debug $column_info_flag] + + if {[prepare_for_testing "failed to prepare" ${testfile} ${srcfile} \ + $options]} { + return -1 + } + + run_tests +} diff --git a/gdb/testsuite/gdb.reverse/map-to-same-line.c b/gdb/testsuite/gdb.reverse/map-to-same-line.c new file mode 100644 index 00000000000..5ae0a89b329 --- /dev/null +++ b/gdb/testsuite/gdb.reverse/map-to-same-line.c @@ -0,0 +1,58 @@ +/* Copyright 2023 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 <http://www.gnu.org/licenses/ >. */ + +/* The purpose of this test is to create a DWARF line table that contains two + or more entries for the same line. When stepping (forwards or backwards), + GDB should step over the entire line and not just a particular entry in the + line table. */ + +int +main (void) +{ /* TAG: main prologue */ + asm ("main_label: .globl main_label"); + int i = 1, j = 2, k; + float f1 = 2.0, f2 = 4.1, f3; + const char *str_1 = "foo", *str_2 = "bar", *str_3; + + asm ("line1: .globl line1"); + k = i; f3 = f1; str_3 = str_1; /* TAG: line 1 */ + + asm ("line2: .globl line2"); + k = j; f3 = f2; str_3 = str_2; /* TAG: line 2 */ + + asm ("line3: .globl line3"); + k = i; f3 = f1; str_3 = str_1; /* TAG: line 3 */ + + asm ("line4: .globl line4"); + k = j; f3 = f2; str_3 = str_2; /* TAG: line 4 */ + + asm ("line5: .globl line5"); + k = i; f3 = f1; str_3 = str_1; /* TAG: line 5 */ + + asm ("line6: .globl line6"); + k = j; f3 = f2; str_3 = str_2; /* TAG: line 6 */ + + asm ("line7: .globl line7"); + k = i; f3 = f1; str_3 = str_1; /* TAG: line 7 */ + + asm ("line8: .globl line8"); + k = j; f3 = f2; str_3 = str_2; /* TAG: line 8 */ + + asm ("main_return: .globl main_return"); + k = j; f3 = f2; str_3 = str_2; /* TAG: main return */ + + asm ("end_of_sequence: .globl end_of_sequence"); + return 0; /* TAG: main return */ +} diff --git a/gdb/testsuite/gdb.reverse/map-to-same-line.exp b/gdb/testsuite/gdb.reverse/map-to-same-line.exp new file mode 100644 index 00000000000..63f8c9c76b3 --- /dev/null +++ b/gdb/testsuite/gdb.reverse/map-to-same-line.exp @@ -0,0 +1,153 @@ +# Copyright 2023 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 <http://www.gnu.org/licenses/ >. + +# When stepping (forwards or backwards), GDB should step over the entire line +# and not just a particular entry in the line table. This test was added to +# verify the find_line_range_start function properly sets the step range for a +# line that consists of multiple statements, i.e. multiple entries in the line +# table. This test creates a DWARF line table that contains two entries for +# the same line to do the needed testing. + +# This test can only be run on targets which support DWARF-2 and use gas. +load_lib dwarf.exp +require dwarf2_support + +# The DWARF assembler requires the gcc compiler. +require is_c_compiler_gcc + +# This test suitable only for process that can do reverse execution +require supports_reverse + +standard_testfile .c .S + +if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile}] } { + return -1 +} + +set asm_file [standard_output_file $srcfile2] +Dwarf::assemble $asm_file { + global srcdir subdir srcfile + declare_labels integer_label L + + # Find start address and length of program + lassign [function_range main [list ${srcdir}/${subdir}/$srcfile]] \ + main_start main_len + set main_end "$main_start + $main_len" + + cu {} { + compile_unit { + {language @DW_LANG_C} + {name map-to-same-line.c} + {stmt_list $L DW_FORM_sec_offset} + {low_pc 0 addr} + } { + subprogram { + {external 1 flag} + {name main} + {low_pc $main_start addr} + {high_pc $main_len DW_FORM_data4} + } + } + } + + lines {version 2 default_is_stmt 1} L { + include_dir "${srcdir}/${subdir}" + file_name "$srcfile" 1 + + # Generate the line table program with distinct source lines being + # mapped to the same line entry. Line 1, 5 and 8 contain 1 statement + # each. Line 2 contains 2 statements. Line 3 contains 3 statements. + program { + DW_LNE_set_address $main_start + line [gdb_get_line_number "TAG: main prologue"] + DW_LNS_copy + DW_LNE_set_address line1 + line [gdb_get_line_number "TAG: line 1" ] + DW_LNS_copy + DW_LNE_set_address line2 + line [gdb_get_line_number "TAG: line 2" ] + DW_LNS_copy + DW_LNE_set_address line3 + line [gdb_get_line_number "TAG: line 2" ] + DW_LNS_copy + DW_LNE_set_address line4 + line [gdb_get_line_number "TAG: line 3" ] + DW_LNS_copy + DW_LNE_set_address line5 + line [gdb_get_line_number "TAG: line 3" ] + DW_LNS_copy + DW_LNE_set_address line6 + line [gdb_get_line_number "TAG: line 3" ] + DW_LNS_copy + DW_LNE_set_address line7 + line [gdb_get_line_number "TAG: line 5" ] + DW_LNS_copy + DW_LNE_set_address line8 + line [gdb_get_line_number "TAG: line 8" ] + DW_LNS_copy + DW_LNE_set_address main_return + line [gdb_get_line_number "TAG: main return"] + DW_LNS_copy + DW_LNE_set_address end_of_sequence + DW_LNE_end_sequence + } + } +} + +if { [prepare_for_testing "failed to prepare" ${testfile} \ + [list $srcfile $asm_file] {nodebug} ] } { + return -1 +} + +if { ![runto_main] } { + return +} + +# Print the line table +gdb_test_multiple "maint info line-table ${testfile}" "" { + -re "\r\n$decimal\[ \t\]+$decimal\[ \t\]+($hex)\[ \t\]+Y\[^\r\n\]*" { + lappend is_stmt $expect_out(1,string) + exp_continue + } + -re -wrap "" { + } +} + +# Do the reverse-step and reverse-next tests +foreach_with_prefix cmd {step next} { + gdb_test_no_output "record" "turn on process record, test $cmd" + + set bp_main_return [gdb_get_line_number "TAG: main return" $srcfile] + gdb_breakpoint $srcfile:$bp_main_return + gdb_continue_to_breakpoint "run to end of main, reverse-$cmd test" ".*$srcfile:$bp_main_return.*" + gdb_test "display \$pc" ".*pc =.*" "display pc, reverse-$cmd test" + + # At this point, GDB has already recorded the execution up until the return + # statement. Reverse and test if GDB transitions between lines in the + # expected order. It should reverse-step or reverse-next across lines 8, + # 5, 3, 2 and 1. + foreach line {8 5 3 2 1} { + gdb_test "reverse-$cmd" ".*TAG: line $line.*" "reverse $cmd to line $line" + } + + if {$cmd =="step"} { + ## Clean restart, test reverse-next command + clean_restart ${testfile} + + if { ![runto_main] } { + return + } + } +} -- 2.37.2 ^ permalink raw reply [flat|nested] 41+ messages in thread
* RE: [PATCH v4] Fix reverse stepping multiple contiguous PC ranges over the line table. 2023-05-11 16:01 ` Simon Marchi 2023-05-11 16:23 ` Bruno Larsen @ 2023-05-16 22:54 ` Carl Love 1 sibling, 0 replies; 41+ messages in thread From: Carl Love @ 2023-05-16 22:54 UTC (permalink / raw) To: Simon Marchi, Bruno Larsen, gdb-patches, UlrichWeigand, pedro Cc: luis.machado, cel Simon: On Thu, 2023-05-11 at 12:01 -0400, Simon Marchi wrote: > > <snip> > I'd like to help reviewing this, but I don't have much time at the > moment, so just a few comments on one test to start with. > > > + This test is used to test the reverse-step and reverse-next > > instruction > > + execution for a source line that contains multiple function > > calls. */ > > + > > +void > > +func1 () > > +{ > > +} // END FUNC1 > > Use /* */ for comments, for consistency with the rest of the code > base. > OK, changed all instances of // comment. > > + > > +void > > +func2 () > > +{ > > +} // END FUNC2 > > + > > +int main () > > +{ > > + int a, b; > > + a = 1; > > + b = 2; > > + func1 (); func2 (); > > + a = a + b; // START REVERSE TEST > > +} > > <snip> > > + > > +require supports_reverse > > + > > +# This test uses the gcc no-column-info command which was added in > > gcc 7.1. > > +if {![test_compiler_info {gcc-*}] > > + || [test_compiler_info {gcc-[1-6]-*}]} { > > + return > > +} > > I would prefer not to filter out by compiler explicitly like that. > It would be useful for the test to run with other compilers too. OK, implemented the gdb_compile options. Moved the specific gcc version test there. > > > + > > +proc run_tests {} { > > + global srcfile <snip> > > + > > +set srcfile func-map-to-same-line.c > > +set executable func-map-to-same-line > > + > > +# test with gcc column info enabled > > +set options [list debug additional_flags=] > > + > > +if {[build_executable "failed to prepare" $executable $srcfile > > $options] == -1}\ > > + { > > + return -1 > > +} > > + > > +clean_restart $executable > > + > > +with_test_prefix "with-column-info" { > > + run_tests > > +} > > So, the above assumes that the compiler generates column-info by > default, which has not historically been the case for GCC (it started > to > emit columns by default with version 8, according to my > tests). Other > compilers may choose to not emit them by default. > > I think it would make sense to make gdb_compile recognize the new > "column-info" and "no-column-info" options, which would translate to > the > right flags for the given compiler. gdb_compile already handles the > nitty gritty details of choosing compiler flags for specific compiler > versions. This way, individual tests don't contain compiler flags > that > are possibly compiler-specific. OK, I think I have this implemented as suggested. It does seem to work. I added both gcc and clang support. Per the link to clang which does have line info options. I put the new gdb_compile option support in a separate patch, followed by the updated "Fix reverse stepping multiple.... " patch. I tested the test case with: make check RUNTESTFLAGS='CC_FOR_TARGET=clang GDB=.../gdb gdb.reverse/func-map-to-same-line.exp' > out and make check RUNTESTFLAGS='GDB=.../gdb gdb.reverse/func-map-to-same-line.exp' > out I looked in the gdb/testsuite/gdb.log file to verify that the compiler line (gcc or clang) explicitly has the -gcolumn-info for the first test and -gno-column-info for the second test. The expected number of success were seen for the test with gcc and clang. It all looks like it works. > > > + > > +#test with gcc column info disabled > > +set options [list debug additional_flags=-gno-column-info] > > + > > +if {[build_executable "failed to prepare" $executable $srcfile > > $options] == -1}\ > > + { > > + return -1 > > +} > > + > > +set $executable executable_without_column_info > > +clean_restart $executable > > + > > +with_test_prefix "no-column-info" { > > + run_tests > > +} > > This would probably be a good use for foreach_with_prefix (if you can > make it work), to make things more compact: > > foreach_with_prefix with_column_info {yes no} { > } > > ... or something like that. Changed to the foreach_with_prefix. Thanks for the review. Will post version 5 as a series of two patches. Carl ^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v3] Fix reverse stepping multiple contiguous PC ranges over the line table. 2023-05-10 17:16 ` Carl Love 2023-05-10 17:32 ` [PATCH v4] " Carl Love @ 2023-05-11 7:52 ` Bruno Larsen 1 sibling, 0 replies; 41+ messages in thread From: Bruno Larsen @ 2023-05-11 7:52 UTC (permalink / raw) To: Carl Love, gdb-patches, UlrichWeigand, pedro; +Cc: luis.machado On 10/05/2023 19:16, Carl Love wrote: >>> + >>> +require supports_reverse >>> + >>> +# This test uses the gcc no-column-info command which was added in >>> gcc 7.1. >>> +require get_compiler_info "gcc-7-*" > I put the compiler check in last. When I ran it, I obviously didn't > double check gdb/testsuite/gdb.log to make sure it really worked. I > normally try to make a point of double checking the log file. I have > been burned before thinking it was OK when there were no errors visible > on the command line. The above command fails if you check the log file. >> By constructing your regex like this, you are only allowing this test >> to >> be run on gcc 7. Anything later is also not accepted. I would do >> something like (Warning, untested) >> >> require get_compiler_info "gcc" >> require !get_compiler_info "gcc-[1-6]-*" > I couldn't get require to work like that. The get_compiler_info > doesn't seem take "gcc" as an argument. > > I changed the test to: > > if {![test_compiler_info {gcc-*}] > || [test_compiler_info {gcc-[1-6]-*}]} { > return > } > > With this, I do see the correct number of passes in > gdb/testsuite/gdb.log. Yeah, this is perfectly fine IMO, since this is a more complicated use of "require". > >> Which requires gcc, but does not allow versions 1 to 6. There is >> probably a way to do it with a single require line, but I'm not the >> best >> with regexes. >> >>> + >>> +proc run_tests {msg} { >>> + global srcfile >>> + global executable >>> + >>> + runto_main >>> + set target_remote [gdb_is_target_remote] >> When probing for target remote, GDB will emit pass/fails with >> hardcoded >> names, so the current proc setup gives us some duplicated test names. >> >> I would suggest that, instead of passing a message as a parameter, >> you >> wrapped all function calls in a with_test_prefix scope, like: >> >> with_test_prefix "with-column-info" { >> run_test >> } > OK, I changed from passing in an argument and did the wrapped calls to > run_test instead. Note, this still didn't fix the duplicate test names > for turning on record. > >>> + >>> + gdb_test_no_output "record" "turn on process record" >>> + >>> + # This regression test verifies the reverse-step and reverse- >>> next commands >>> + # work properly when executing backwards thru a source line >>> containing >>> + # two function calls on the same source line, i.e. func1 (); >>> func2 (); >>> + # This test is compiled so the dwarf info not contain the line >>> table >>> + # information. >>> + >>> + # Test 1, reverse-next command >>> + # Set breakpoint at the line after the function calls. >>> + set bp_start_reverse_test [gdb_get_line_number "START REVERSE >>> TEST" \ >>> + $srcfile] >>> + gdb_breakpoint $srcfile:$bp_start_reverse_test temporary >>> + >>> + # Continue to break point for reverse-next test. >>> + # Command definition: reverse-next [count] >>> + # Run backward to the beginning of the previous line >>> executed in the >>> + # current (innermost) stack frame. If the line contains >>> function calls, >>> + # they will be “un-executed” without stopping. Starting from >>> the first >>> + # line of a function, reverse-next will take you back to the >>> caller of >>> + # that function, before the function was called, just as the >>> normal next >>> + # command would take you from the last line of a function >>> back to its >>> + # return to its caller 2 . >>> + gdb_continue_to_breakpoint \ >>> + "$msg: test1: stopped at command reverse-next test start >>> location" \ >>> + ".*$srcfile:$bp_start_reverse_test\r\n.*" >>> + >>> + # The reverse-next should step all the way back to the >>> beginning of the >>> + # line, i.e. at the beginning of the func1 call. >>> + gdb_test "reverse-next" ".*func1 \\(\\); func2 \\(\\);.*" \ >>> + "$msg: test1: reverse-next to line with two functions" >>> + >>> + # We should be stopped at the first instruction of the line. A >>> reverse-step >>> + # should step back and stop at the beginning of the previous >>> line b = 2, >>> + # i.e. not in func1 (). >>> + gdb_test "reverse-stepi" ".*b = 2;.*" \ >>> + "$msg: test1: reverse-stepi to previous line b = 2" >>> + >>> + >>> + # Setup for test 2 >>> + clean_restart $executable >>> + runto_main >>> + >>> + gdb_test_no_output "record" "turn on process record" >> This gives a duplicate test name from setting up for the first test. >> Adding "test 2:(...)" in here solves it. > I couldn't figure out how to get the above syntax to work. So I used > the with_test_prefix instead, i.e. oops, sorry, that's what I get for sending a patch review at the end of a long work day. That wasn't some arcane magic of TCL, it was just me being too lazy to type out the whole message. Sorry for the wild goose chase. > > with_test_prefix "test2" { > gdb_test_no_output "record" "turn on process record" > } > > That fixed the duplicate names. I also wrapped the first record with > "test1" for consistency. > -- Cheers, Bruno ^ permalink raw reply [flat|nested] 41+ messages in thread
* [PATCH 0/2] Fix reverse stepping multiple contiguous PC ranges over the line table. @ 2023-08-07 18:54 Carl Love 2023-08-07 19:03 ` [PATCH 1/2 ver 2] " Carl Love 0 siblings, 1 reply; 41+ messages in thread From: Carl Love @ 2023-08-07 18:54 UTC (permalink / raw) To: Simon Marchi, Bruno Larsen, gdb-patches, UlrichWeigand, pedro Cc: luis.machado, cel GDB maintainers: Per recent discussions with Bruno on how gdb should behave when reverse stepping from a function call to the previous line: https://sourceware.org/pipermail/gdb-patches/2023-July/201035.html I have updated the second patch in the series to address his comments. The current behavior of gdb when executing in reverse is gdb stops at the call to a function, then a reverse-step or reverse-next stops at the beginning of the same line instead of the previous line. Bruno pointed out that gdb on clang stops at the previous line not at the beginning of the line. The first patch in the series has not been changed. I am reposting it along with the second patch so it doesn't get lost. The patch series has been tested on Power10 LE and on X86-64. Carl ^ permalink raw reply [flat|nested] 41+ messages in thread
* [PATCH 1/2 ver 2] Fix reverse stepping multiple contiguous PC ranges over the line table. 2023-08-07 18:54 [PATCH 0/2] " Carl Love @ 2023-08-07 19:03 ` Carl Love 2023-08-08 10:04 ` Guinevere Larsen 0 siblings, 1 reply; 41+ messages in thread From: Carl Love @ 2023-08-07 19:03 UTC (permalink / raw) To: Simon Marchi, Bruno Larsen, gdb-patches, UlrichWeigand, pedro Cc: luis.machado, cel Simon, GDB maintainers: Version 2, updated the compiler check and handling for gcc version 6 and earlier. Retested on Power 10. Per the comments on version 4 for the gdb.reverse/func-map-to-same- line.exp, I have added support to proc gdb_compile to enable or disable generating line information as part of the debug information. The two new options are column-info and no-column-info. This patch implements the new options for gdb_compile. These options have been tested with patch 2 of 2 on PowerPC with the GCC and clang compilers. Please let me know if the patch is acceptable for mainline. Thanks. Carl -------------------------- Add gdb_compile options column-info and no-column-info This patch adds two new options to gdb_compile to specify if the compile should or should not generate the line table information. The options are supported on clang and gcc version 7 and newer. Patch has been tested on PowerPC with both gcc and clang. --- gdb/testsuite/lib/gdb.exp | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp index 36bf738c667..bffbbf38b09 100644 --- a/gdb/testsuite/lib/gdb.exp +++ b/gdb/testsuite/lib/gdb.exp @@ -4896,6 +4896,8 @@ proc quote_for_host { args } { # debug information # - text_segment=addr: Tell the linker to place the text segment at ADDR. # - build-id: Ensure the final binary includes a build-id. +# - no-column-info: Disable generation of column table information. +# - column-info: Enable generation of column table information. # # And here are some of the not too obscure options understood by DejaGnu that # influence the compilation: @@ -5105,6 +5107,38 @@ proc gdb_compile {source dest type options} { } else { error "Don't know how to handle text_segment option." } + } elseif { $opt == "column-info" } { + # If GCC or clang does not support column-info, compilation + # will fail and the usupported column-info option will be + # reported as such. + if {[test_compiler_info {gcc-*}]} { + lappend new_options "additional_flags=-gcolumn-info" + + } elseif {[test_compiler_info {clang-*}]} { + lappend new_options "additional_flags=-gcolumn-info" + + } else { + error "Don't know how to handle gcolumn-info option." + } + + } elseif { $opt == "no-column-info" } { + if {[test_compiler_info {gcc-*}]} { + if {[test_compiler_info {gcc-[1-6]-*}]} { + # In this case, don't add the compile line option and + # the result will be the same as using no-column-info + # on a version that supports the option. + warning "gdb_compile option no-column-info not supported, ignoring." + } else { + lappend new_options "additional_flags=-gno-column-info" + } + + } elseif {[test_compiler_info {clang-*}]} { + lappend new_options "additional_flags=-gno-column-info" + + } else { + error "Don't know how to handle gno-column-info option." + } + } else { lappend new_options $opt } -- 2.37.2 ^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH 1/2 ver 2] Fix reverse stepping multiple contiguous PC ranges over the line table. 2023-08-07 19:03 ` [PATCH 1/2 ver 2] " Carl Love @ 2023-08-08 10:04 ` Guinevere Larsen 2023-08-08 15:38 ` Carl Love 0 siblings, 1 reply; 41+ messages in thread From: Guinevere Larsen @ 2023-08-08 10:04 UTC (permalink / raw) To: Carl Love, Simon Marchi, gdb-patches, UlrichWeigand, pedro; +Cc: luis.machado On 07/08/2023 21:03, Carl Love wrote: > Simon, GDB maintainers: > > Version 2, updated the compiler check and handling for gcc version 6 > and earlier. Retested on Power 10. > > Per the comments on version 4 for the gdb.reverse/func-map-to-same- > line.exp, I have added support to proc gdb_compile to enable or disable > generating line information as part of the debug information. The two > new options are column-info and no-column-info. > > This patch implements the new options for gdb_compile. > > These options have been tested with patch 2 of 2 on PowerPC with the > GCC and clang compilers. > > Please let me know if the patch is acceptable for mainline. Thanks. > > Carl > > > -------------------------- > Add gdb_compile options column-info and no-column-info > > This patch adds two new options to gdb_compile to specify if the compile > should or should not generate the line table information. The > options are supported on clang and gcc version 7 and newer. > > Patch has been tested on PowerPC with both gcc and clang. > --- > gdb/testsuite/lib/gdb.exp | 34 ++++++++++++++++++++++++++++++++++ > 1 file changed, 34 insertions(+) > > diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp > index 36bf738c667..bffbbf38b09 100644 > --- a/gdb/testsuite/lib/gdb.exp > +++ b/gdb/testsuite/lib/gdb.exp > @@ -4896,6 +4896,8 @@ proc quote_for_host { args } { > # debug information > # - text_segment=addr: Tell the linker to place the text segment at ADDR. > # - build-id: Ensure the final binary includes a build-id. > +# - no-column-info: Disable generation of column table information. > +# - column-info: Enable generation of column table information. > # > # And here are some of the not too obscure options understood by DejaGnu that > # influence the compilation: > @@ -5105,6 +5107,38 @@ proc gdb_compile {source dest type options} { > } else { > error "Don't know how to handle text_segment option." > } > + } elseif { $opt == "column-info" } { > + # If GCC or clang does not support column-info, compilation > + # will fail and the usupported column-info option will be > + # reported as such. > + if {[test_compiler_info {gcc-*}]} { I think you missed a bit on an old comment from simon. Way back in may, in this email https://sourceware.org/pipermail/gdb-patches/2023-May/199523.html, he mentioned: For instance, if you used no-column-info with gcc 6 (which doesn't support column info at all), gdb_compile should succeed, even if there isn't an option to disable column info with that compiler. If you used column-info with gcc 6, gdb_compile would fail. So I think this bit should throw an error if it detects gcc-[1-6]. > + lappend new_options "additional_flags=-gcolumn-info" > + > + } elseif {[test_compiler_info {clang-*}]} { I did some digging, and column-info were added in llvm back in october 2012 (commit a2f7eb7c52cdc), which seems to mean support was added in llvm 3.2, but I don't see any mention in the release notes. In my opinion, this is old enough that we don't need to have a special case, but I wanted to mention, in case some maintainer thinks it should be dealt with. If we should, before then, it seems that clang WOULD add column info by default, so it should compile with a warning here, and fail if the user requested no column info -- Cheers, Guinevere Larsen She/Her/Hers > + lappend new_options "additional_flags=-gcolumn-info" > + > + } else { > + error "Don't know how to handle gcolumn-info option." > + } > + > + } elseif { $opt == "no-column-info" } { > + if {[test_compiler_info {gcc-*}]} { > + if {[test_compiler_info {gcc-[1-6]-*}]} { > + # In this case, don't add the compile line option and > + # the result will be the same as using no-column-info > + # on a version that supports the option. > + warning "gdb_compile option no-column-info not supported, ignoring." > + } else { > + lappend new_options "additional_flags=-gno-column-info" > + } > + > + } elseif {[test_compiler_info {clang-*}]} { > + lappend new_options "additional_flags=-gno-column-info" > + > + } else { > + error "Don't know how to handle gno-column-info option." > + } > + > } else { > lappend new_options $opt > } ^ permalink raw reply [flat|nested] 41+ messages in thread
* RE: [PATCH 1/2 ver 2] Fix reverse stepping multiple contiguous PC ranges over the line table. 2023-08-08 10:04 ` Guinevere Larsen @ 2023-08-08 15:38 ` Carl Love 2023-08-08 15:45 ` Guinevere Larsen 0 siblings, 1 reply; 41+ messages in thread From: Carl Love @ 2023-08-08 15:38 UTC (permalink / raw) To: Guinevere Larsen, Simon Marchi, gdb-patches, UlrichWeigand, pedro Cc: luis.machado, cel Guinevere: On Tue, 2023-08-08 at 12:04 +0200, Guinevere Larsen wrote: > > <snip> > On 07/08/2023 21:03, Carl Love wrote: > > > > diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp > > index 36bf738c667..bffbbf38b09 100644 > > --- a/gdb/testsuite/lib/gdb.exp > > +++ b/gdb/testsuite/lib/gdb.exp > > @@ -4896,6 +4896,8 @@ proc quote_for_host { args } { > > # debug information > > # - text_segment=addr: Tell the linker to place the text > > segment at ADDR. > > # - build-id: Ensure the final binary includes a build-id. > > +# - no-column-info: Disable generation of column table > > information. > > +# - column-info: Enable generation of column table information. > > # > > # And here are some of the not too obscure options understood by > > DejaGnu that > > # influence the compilation: > > @@ -5105,6 +5107,38 @@ proc gdb_compile {source dest type options} > > { > > } else { > > error "Don't know how to handle text_segment > > option." > > } > > + } elseif { $opt == "column-info" } { > > + # If GCC or clang does not support column-info, compilation > > + # will fail and the usupported column-info option will be > > + # reported as such. > > + if {[test_compiler_info {gcc-*}]} { > > I think you missed a bit on an old comment from simon. Way back in > may, > in this email > https://sourceware.org/pipermail/gdb-patches/2023-May/199523.html , > he > mentioned: > > For instance, if you used no-column-info with gcc 6 > (which doesn't support column info at all), gdb_compile should > succeed, > even if there isn't an option to disable column info with that > compiler. > If you used column-info with gcc 6, gdb_compile would fail. > > So I think this bit should throw an error if it detects gcc-[1-6]. It has been awhile, but as I recall, we decided that the we would specify column-info and if the compiler doesn't support it then the compiler will complain (i.e. fail) and we will let the failure be handled by the normal compiler failure path. I think that will work fine? If there is some concern that is not sufficient, I would be happy to put in the test if {[test_compiler_info {gcc-[1-6]-*}]} for the $opt == "column-info" to have the script flag the error. Thoughts? In the case where the compiler doesn't handle the no-column-info flag, i.e. gcc 1-6, we handle that case by not adding the flag so the compiler will not flag the error and fail. In that case, it isn't going to generate the column info anyways so we don't need to specify no-column info. > > > + lappend new_options "additional_flags=-gcolumn-info" > > + > > + } elseif {[test_compiler_info {clang-*}]} { > > I did some digging, and column-info were added in llvm back in > october > 2012 (commit a2f7eb7c52cdc), which seems to mean support was added > in > llvm 3.2, but I don't see any mention in the release notes. In my > opinion, this is old enough that we don't need to have a special > case, > but I wanted to mention, in case some maintainer thinks it should be > dealt with. > > If we should, before then, it seems that clang WOULD add column info > by > default, so it should compile with a warning here, and fail if the > user > requested no column info > Carl ^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH 1/2 ver 2] Fix reverse stepping multiple contiguous PC ranges over the line table. 2023-08-08 15:38 ` Carl Love @ 2023-08-08 15:45 ` Guinevere Larsen 0 siblings, 0 replies; 41+ messages in thread From: Guinevere Larsen @ 2023-08-08 15:45 UTC (permalink / raw) To: Carl Love, Simon Marchi, gdb-patches, UlrichWeigand, pedro; +Cc: luis.machado On 08/08/2023 17:38, Carl Love wrote: > Guinevere: > > On Tue, 2023-08-08 at 12:04 +0200, Guinevere Larsen wrote: > <snip> > >> On 07/08/2023 21:03, Carl Love wrote: >>> diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp >>> index 36bf738c667..bffbbf38b09 100644 >>> --- a/gdb/testsuite/lib/gdb.exp >>> +++ b/gdb/testsuite/lib/gdb.exp >>> @@ -4896,6 +4896,8 @@ proc quote_for_host { args } { >>> # debug information >>> # - text_segment=addr: Tell the linker to place the text >>> segment at ADDR. >>> # - build-id: Ensure the final binary includes a build-id. >>> +# - no-column-info: Disable generation of column table >>> information. >>> +# - column-info: Enable generation of column table information. >>> # >>> # And here are some of the not too obscure options understood by >>> DejaGnu that >>> # influence the compilation: >>> @@ -5105,6 +5107,38 @@ proc gdb_compile {source dest type options} >>> { >>> } else { >>> error "Don't know how to handle text_segment >>> option." >>> } >>> + } elseif { $opt == "column-info" } { >>> + # If GCC or clang does not support column-info, compilation >>> + # will fail and the usupported column-info option will be >>> + # reported as such. >>> + if {[test_compiler_info {gcc-*}]} { >> I think you missed a bit on an old comment from simon. Way back in >> may, >> in this email >> https://sourceware.org/pipermail/gdb-patches/2023-May/199523.html , >> he >> mentioned: >> >> For instance, if you used no-column-info with gcc 6 >> (which doesn't support column info at all), gdb_compile should >> succeed, >> even if there isn't an option to disable column info with that >> compiler. >> If you used column-info with gcc 6, gdb_compile would fail. >> >> So I think this bit should throw an error if it detects gcc-[1-6]. > It has been awhile, but as I recall, we decided that the we would > specify column-info and if the compiler doesn't support it then the > compiler will complain (i.e. fail) and we will let the failure be > handled by the normal compiler failure path. I think that will work > fine? If there is some concern that is not sufficient, I would be > happy to put in the test if {[test_compiler_info {gcc-[1-6]-*}]} for > the $opt == "column-info" to have the script flag the error. > Thoughts? oh, I see. I must have misread when looking back at that conversation. Sorry for the noise. Also, disregard my comments about LLVM. I checked on IRC and the oldest GCC we support compiling/using for usptream stuff is form 2014, so I think we dont have to worry about 2012 clang :) All in all, this patch looks good to go in. Reviewed-By: Guinevere Larsen <blarsen@redhat.com> I hope the maintainers approve this series soon, it is a long time coming! -- Cheers, Guinevere Larsen She/Her/Hers > > In the case where the compiler doesn't handle the no-column-info flag, > i.e. gcc 1-6, we handle that case by not adding the flag so the > compiler will not flag the error and fail. In that case, it isn't > going to generate the column info anyways so we don't need to specify > no-column info. > > >>> + lappend new_options "additional_flags=-gcolumn-info" >>> + >>> + } elseif {[test_compiler_info {clang-*}]} { >> I did some digging, and column-info were added in llvm back in >> october >> 2012 (commit a2f7eb7c52cdc), which seems to mean support was added >> in >> llvm 3.2, but I don't see any mention in the release notes. In my >> opinion, this is old enough that we don't need to have a special >> case, >> but I wanted to mention, in case some maintainer thinks it should be >> dealt with. >> >> If we should, before then, it seems that clang WOULD add column info >> by >> default, so it should compile with a warning here, and fail if the >> user >> requested no column info >> > Carl > ^ permalink raw reply [flat|nested] 41+ messages in thread
end of thread, other threads:[~2023-08-08 15:46 UTC | newest] Thread overview: 41+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2023-04-27 20:59 [PATCH] Fix reverse stepping multiple contiguous PC ranges over the line table Carl Love 2023-05-02 14:15 ` Bruno Larsen 2023-05-02 15:40 ` Carl Love 2023-05-02 15:42 ` Bruno Larsen 2023-05-11 15:11 ` Simon Marchi 2023-05-03 9:53 ` Bruno Larsen 2023-05-04 2:55 ` Carl Love 2023-05-04 9:24 ` Bruno Larsen 2023-05-04 14:52 ` Carl Love 2023-05-04 2:55 ` [PATCH v2] " Carl Love 2023-05-04 15:59 ` [PATCH v3] " Carl Love 2023-05-05 14:59 ` Luis Machado 2023-05-05 16:10 ` Carl Love 2023-05-10 13:47 ` Bruno Larsen 2023-05-10 17:16 ` Carl Love 2023-05-10 17:32 ` [PATCH v4] " Carl Love 2023-05-11 16:01 ` Simon Marchi 2023-05-11 16:23 ` Bruno Larsen 2023-05-11 17:28 ` Simon Marchi 2023-05-16 22:54 ` [PATCH 1/2] " Carl Love 2023-06-19 17:11 ` Simon Marchi 2023-06-22 16:52 ` Carl Love 2023-06-23 17:44 ` Simon Marchi 2023-06-23 19:41 ` Carl Love 2023-06-23 20:04 ` [PATCH 1/2 ver 2] " Carl Love 2023-07-06 15:07 ` Carl Love 2023-05-16 22:54 ` [PATCH 2/2 v5] " Carl Love 2023-05-25 15:08 ` Carl Love 2023-06-08 16:36 ` Carl Love 2023-06-19 17:58 ` Simon Marchi 2023-06-22 20:38 ` Carl Love 2023-06-22 20:39 ` Carl Love 2023-06-23 17:49 ` Simon Marchi 2023-06-23 20:04 ` Carl Love 2023-06-23 20:04 ` [PATCH 2/2 v6] " Carl Love 2023-05-16 22:54 ` [PATCH v4] " Carl Love 2023-05-11 7:52 ` [PATCH v3] " Bruno Larsen 2023-08-07 18:54 [PATCH 0/2] " Carl Love 2023-08-07 19:03 ` [PATCH 1/2 ver 2] " Carl Love 2023-08-08 10:04 ` Guinevere Larsen 2023-08-08 15:38 ` Carl Love 2023-08-08 15:45 ` Guinevere Larsen
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).