diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index 829b60eebc7..bd887c3191a 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,18 @@ +2019-07-27 Kevin Buettner + + * gdb.dwarf2/dw2-ranges-func.c: Rename to... + * gdb.dwarf2/dw2-ranges-func-lo-cold.c: ...this. + * gdb.dwarf2/dw2-ranges-func-lo-cold.c (foo_low): Change name to + foo_cold. Revise comments to match. + * gdb.dwarf2/dw2-ranges-func-hi-cold.c: New file. + * gdb.dwarf2/dw2-ranges-func.exp (do_test): New proc. Existing tests + were wrapped into this proc; Call do_test in loop from outermost + level. + (foo_low): Rename all occurrences to "foo_cold". + (backtrace from baz): New test. + (x2/i foo_cold): New test. + (info line *foo_cold): New test. + 2019-07-26 Tom de Vries * gdb.arch/i386-pkru.exp: Fix unterminated string. diff --git a/gdb/testsuite/gdb.dwarf2/dw2-ranges-func.c b/gdb/testsuite/gdb.dwarf2/dw2-ranges-func-hi-cold.c similarity index 85% rename from gdb/testsuite/gdb.dwarf2/dw2-ranges-func.c rename to gdb/testsuite/gdb.dwarf2/dw2-ranges-func-hi-cold.c index fa1b7bab415..06c3a3485ae 100644 --- a/gdb/testsuite/gdb.dwarf2/dw2-ranges-func.c +++ b/gdb/testsuite/gdb.dwarf2/dw2-ranges-func-hi-cold.c @@ -16,7 +16,7 @@ /* The idea here is to, via use of the dwarf assembler, create a function which occupies two non-contiguous address ranges. - foo_low and foo will be combined into a single function foo with a + foo_cold and foo will be combined into a single function foo with a function bar in between these two ranges. This test case was motivated by a bug in which a function which @@ -37,19 +37,19 @@ volatile int e = 0; -void -baz (void) -{ - asm ("baz_label: .globl baz_label"); -} /* baz end */ +void bar (void); +void foo_cold (void); +void baz (void); void -foo_low (void) -{ /* foo_low prologue */ - asm ("foo_low_label: .globl foo_low_label"); - baz (); /* foo_low baz call */ - asm ("foo_low_label2: .globl foo_low_label2"); -} /* foo_low end */ +foo (void) +{ /* foo prologue */ + asm ("foo_label: .globl foo_label"); + bar (); /* foo bar call */ + asm ("foo_label2: .globl foo_label2"); + if (e) foo_cold (); /* foo foo_cold call */ + asm ("foo_label3: .globl foo_label3"); +} /* foo end */ void bar (void) @@ -58,14 +58,18 @@ bar (void) } /* bar end */ void -foo (void) -{ /* foo prologue */ - asm ("foo_label: .globl foo_label"); - bar (); /* foo bar call */ - asm ("foo_label2: .globl foo_label2"); - if (e) foo_low (); /* foo foo_low call */ - asm ("foo_label3: .globl foo_label3"); -} /* foo end */ +foo_cold (void) +{ /* foo_cold prologue */ + asm ("foo_cold_label: .globl foo_cold_label"); + baz (); /* foo_cold baz call */ + asm ("foo_cold_label2: .globl foo_cold_label2"); +} /* foo_cold end */ + +void +baz (void) +{ + asm ("baz_label: .globl baz_label"); +} /* baz end */ int main (void) diff --git a/gdb/testsuite/gdb.dwarf2/dw2-ranges-func-lo-cold.c b/gdb/testsuite/gdb.dwarf2/dw2-ranges-func-lo-cold.c new file mode 100644 index 00000000000..40966ce6dcf --- /dev/null +++ b/gdb/testsuite/gdb.dwarf2/dw2-ranges-func-lo-cold.c @@ -0,0 +1,82 @@ +/* Copyright 2018-2019 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 . */ + +/* The idea here is to, via use of the dwarf assembler, create a function + which occupies two non-contiguous address ranges. + + foo_cold and foo will be combined into a single function foo with a + function bar in between these two ranges. + + This test case was motivated by a bug in which a function which + occupied two non-contiguous address ranges was calling another + function which resides in between these ranges. So we end up with + a situation in which the low/start address of our constructed foo + (in this case) will be less than any of the addresses in bar, but + the high/end address of foo will be greater than any of bar's + addresses. + + This situation was causing a problem in the caching code of + find_pc_partial_function: When the low and high addresses of foo + are placed in the cache, the simple check that was used to see if + the cache was applicable would (incorrectly) succeed when presented + with an address in bar. I.e. an address in bar presented as an + input to find_pc_partial_function could produce the answer "this + address belongs to foo". */ + +volatile int e = 0; + +void bar (void); +void foo_cold (void); +void baz (void); + +void +baz (void) +{ + asm ("baz_label: .globl baz_label"); +} /* baz end */ + +void +foo_cold (void) +{ /* foo_cold prologue */ + asm ("foo_cold_label: .globl foo_cold_label"); + baz (); /* foo_cold baz call */ + asm ("foo_cold_label2: .globl foo_cold_label2"); +} /* foo_cold end */ + +void +bar (void) +{ + asm ("bar_label: .globl bar_label"); +} /* bar end */ + +void +foo (void) +{ /* foo prologue */ + asm ("foo_label: .globl foo_label"); + bar (); /* foo bar call */ + asm ("foo_label2: .globl foo_label2"); + if (e) foo_cold (); /* foo foo_cold call */ + asm ("foo_label3: .globl foo_label3"); +} /* foo end */ + +int +main (void) +{ /* main prologue */ + asm ("main_label: .globl main_label"); + foo (); /* main foo call */ + asm ("main_label2: .globl main_label2"); + return 0; /* main return */ +} /* main end */ + diff --git a/gdb/testsuite/gdb.dwarf2/dw2-ranges-func.exp b/gdb/testsuite/gdb.dwarf2/dw2-ranges-func.exp index ccf18b69830..fdc488ae926 100644 --- a/gdb/testsuite/gdb.dwarf2/dw2-ranges-func.exp +++ b/gdb/testsuite/gdb.dwarf2/dw2-ranges-func.exp @@ -30,376 +30,431 @@ if !$gcc_compiled { return 0 } -standard_testfile dw2-ranges-func.c dw2-ranges-func-dw.S +proc do_test {suffix} { + global gdb_test_file_name + global testfile binfile srcfile srcfile2 gdb_prompt hex -# We need to know the size of integer and address types in order to -# write some of the debugging info we'd like to generate. -# -# For that, we ask GDB by debugging our test program. Any program -# would do, but since we already have it specifically for this -# testcase, might as well use that. + # Don't use standard_testfile; we want different binaries for + # each suffix. + set testfile $gdb_test_file_name-$suffix + set binfile [standard_output_file ${testfile}] + set srcfile $testfile.c + set srcfile2 $testfile-dw2.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 srcfile2 - declare_labels integer_label volatile_label func_ranges_label cu_ranges_label L - set int_size [get_sizeof "int" 4] - - # Find start address and length for our functions. - lassign [function_range main [list ${srcdir}/${subdir}/$srcfile]] \ - main_start main_len - set main_end "$main_start + $main_len" - lassign [function_range foo [list ${srcdir}/${subdir}/$srcfile]] \ - foo_start foo_len - set foo_end "$foo_start + $foo_len" - lassign [function_range foo_low [list ${srcdir}/${subdir}/$srcfile]] \ - foo_low_start foo_low_len - set foo_low_end "$foo_low_start + $foo_low_len" - lassign [function_range bar [list ${srcdir}/${subdir}/$srcfile]] \ - bar_start bar_len - set bar_end "$bar_start + $bar_len" - lassign [function_range baz [list ${srcdir}/${subdir}/$srcfile]] \ - baz_start baz_len - set baz_end "$baz_start + $baz_len" - - set e_var [gdb_target_symbol e] - - cu {} { - compile_unit { - {language @DW_LANG_C} - {name dw-ranges-func.c} - {stmt_list $L DW_FORM_sec_offset} - {low_pc 0 addr} - {ranges ${cu_ranges_label} DW_FORM_sec_offset} - } { - integer_label: DW_TAG_base_type { - {DW_AT_byte_size $int_size DW_FORM_sdata} - {DW_AT_encoding @DW_ATE_signed} - {DW_AT_name integer} - } - volatile_label: DW_TAG_volatile_type { - {type :$integer_label} - } - DW_TAG_variable { - {name e} - {external 1 flag} - {type :$volatile_label} - {location {addr $e_var} SPECIAL_expr} - } - subprogram { - {external 1 flag} - {name main} - {DW_AT_type :$integer_label} - {low_pc $main_start addr} - {high_pc $main_len DW_FORM_data4} - } - subprogram { - {external 1 flag} - {name foo} - {ranges ${func_ranges_label} DW_FORM_sec_offset} - } - subprogram { - {external 1 flag} - {name bar} - {low_pc $bar_start addr} - {high_pc $bar_len DW_FORM_data4} - } - subprogram { - {external 1 flag} - {name baz} - {low_pc $baz_start addr} - {high_pc $baz_len DW_FORM_data4} - } - } - } - - lines {version 2} L { - include_dir "${srcdir}/${subdir}" - file_name "$srcfile" 1 - - # Generate a line table program. An attempt was made to make it - # reasonably accurate as it made debugging the test case easier. - program { - {DW_LNE_set_address $main_start} - {DW_LNS_advance_line [expr [gdb_get_line_number "main prologue"] - 1]} - {DW_LNS_copy} - {DW_LNE_set_address main_label} - {DW_LNS_advance_line [expr [gdb_get_line_number "main foo call"] - [gdb_get_line_number "main prologue"]]} - {DW_LNS_copy} - {DW_LNE_set_address main_label2} - {DW_LNS_advance_line [expr [gdb_get_line_number "main return"] - [gdb_get_line_number "main foo call"]]} - {DW_LNS_copy} - {DW_LNE_set_address $main_end} - {DW_LNS_advance_line [expr [gdb_get_line_number "main end"] - [gdb_get_line_number "main return"] + 1]} - {DW_LNS_copy} - {DW_LNE_end_sequence} - - {DW_LNE_set_address $foo_start} - {DW_LNS_advance_line [expr [gdb_get_line_number "foo prologue"] - 1] } - {DW_LNS_copy} - {DW_LNE_set_address foo_label} - {DW_LNS_advance_line [expr [gdb_get_line_number "foo bar call"] - [gdb_get_line_number "foo prologue"]]} - {DW_LNS_copy} - {DW_LNE_set_address foo_label2} - {DW_LNS_advance_line [expr [gdb_get_line_number "foo foo_low call"] - [gdb_get_line_number "foo bar call"]]} - {DW_LNS_copy} - {DW_LNE_set_address foo_label3} - {DW_LNS_advance_line [expr [gdb_get_line_number "foo end"] - [gdb_get_line_number "foo foo_low call"]]} - {DW_LNS_copy} - {DW_LNE_set_address $foo_end} - {DW_LNS_advance_line 1} - {DW_LNS_copy} - {DW_LNE_end_sequence} - - {DW_LNE_set_address $bar_start} - {DW_LNS_advance_line [expr [gdb_get_line_number "bar end"] - 1]} - {DW_LNS_copy} - {DW_LNS_advance_pc $bar_len} - {DW_LNS_advance_line 1} - {DW_LNS_copy} - {DW_LNE_end_sequence} - - {DW_LNE_set_address $baz_start} - {DW_LNS_advance_line [expr [gdb_get_line_number "baz end"] - 1]} - {DW_LNS_copy} - {DW_LNS_advance_pc $baz_len} - {DW_LNS_advance_line 1} - {DW_LNS_copy} - {DW_LNE_end_sequence} - - {DW_LNE_set_address $foo_low_start} - {DW_LNS_advance_line [expr [gdb_get_line_number "foo_low prologue"] - 1]} - {DW_LNS_copy} - {DW_LNE_set_address foo_low_label} - {DW_LNS_advance_line [expr [gdb_get_line_number "foo_low baz call"] - [gdb_get_line_number "foo_low prologue"]]} - {DW_LNS_copy} - {DW_LNE_set_address foo_low_label2} - {DW_LNS_advance_line [expr [gdb_get_line_number "foo_low end"] - [gdb_get_line_number "foo_low baz call"]]} - {DW_LNS_copy} - {DW_LNE_set_address $foo_low_end} - {DW_LNS_advance_line 1} - {DW_LNS_copy} - {DW_LNE_end_sequence} - } - } - - # Generate ranges data. - ranges {is_64 [is_64_target]} { - func_ranges_label: sequence { - {range {$foo_start } $foo_end} - {range {$foo_low_start} $foo_low_end} - } - cu_ranges_label: sequence { - {range {$foo_start } $foo_end} - {range {$foo_low_start} $foo_low_end} - {range {$main_start} $main_end} - {range {$bar_start} $bar_end} - {range {$baz_start} $baz_end} - } - } -} - -if { [prepare_for_testing "failed to prepare" ${testfile} \ - [list $srcfile $asm_file] {nodebug}] } { - return -1 -} - -if ![runto_main] { - return -1 -} - -set main_prologue_line_num [gdb_get_line_number "main prologue"] -# Do a sanity check to make sure that line number info is available. -gdb_test "info line main" \ - "Line ${main_prologue_line_num} of .* starts at address .* and ends at .*" - -with_test_prefix "step-test-1" { - set bp_foo_bar [gdb_get_line_number "foo bar call"] - - gdb_test "break $bp_foo_bar" \ - "Breakpoint.*at.* file .*$srcfile, line $bp_foo_bar\\." \ - "break at call to bar" - - gdb_test "continue" \ - "Continuing\\..*Breakpoint \[0-9\]+, foo \\(\\).*$bp_foo_bar\\s+bar\\s\\(\\);.*foo bar call.*" \ - "continue to call of bar" - - gdb_test "step" \ - "bar \\(\\).*bar end.*" \ - "step into bar" - - gdb_test "step" \ - "foo \\(\\).*foo foo_low call.*" \ - "step out of bar, back into foo" -} - -with_test_prefix "step-test-2" { - clean_restart ${testfile} - if ![runto_main] { - return -1 - } - - # Note that the RE used for the following test will fail when the - # breakpoint has been set on multiple locations. E.g. "(2 locations)". - # This is intentional since that behavior is one of the bugs that - # this test case tests for. - gdb_test "break foo" \ - "Breakpoint.*at.* file .*$srcfile, line \\d+\\." \ - "break foo" - - # Continue to foo. Allow execution to stop either on the prologue - # or on the call to bar since either behavior is acceptable though - # the latter is preferred. - set test "continue to foo" - gdb_test_multiple "continue" $test { - -re "Breakpoint \\d+, foo \\(\\).*foo prologue.*${gdb_prompt}" { - pass $test - gdb_test "step" \ - "foo bar call .*" \ - "step to call of bar after landing on prologue" - } - -re "Breakpoint \\d+, foo \\(\\).*foo bar call.*${gdb_prompt}" { - pass $test - } - } - - gdb_test "step" \ - "bar \\(\\).*bar end.*" \ - "step into bar" - - gdb_test "step" \ - "foo \\(\\).*foo foo_low call.*" \ - "step out of bar, back into foo" -} - -clean_restart ${testfile} -if ![runto_main] { - return -1 -} - -# Disassembly of foo should have multiple address ranges. -gdb_test_sequence "disassemble foo" "" [list \ - "Dump of assembler code for function foo:" \ - "Address range $hex to $hex:" \ - " $hex <\\+0>:" \ - "Address range $hex to $hex:" \ - " $hex <(.+?)>:" \ - "End of assembler dump\\." \ -] - -set foo_low_addr -1 -set test "x/i foo_low" -gdb_test_multiple $test $test { - -re " ($hex) .*${gdb_prompt}" { - set foo_low_addr $expect_out(1,string) - pass $test - } -} - -set foo_addr -1 -set test "x/i foo" -gdb_test_multiple $test $test { - -re " ($hex) .*${gdb_prompt}" { - set foo_addr $expect_out(1,string) - pass $test - } -} - -gdb_assert {$foo_low_addr != $foo_addr} "foo and foo_low are at different addresses" - -# This more permissive RE for "break foo" will allow a breakpoint on -# multiple locations to PASS. */ -gdb_test "break foo" \ - "Breakpoint.*at.*" \ - "break foo" - -gdb_test "break baz" \ - "Breakpoint.*at.* file .*$srcfile, line \\d+\\." - -gdb_test "continue" \ - "Breakpoint \\d+, foo \\(\\).*" \ - "continue to foo" - -gdb_test_no_output "set variable e=1" - -# If GDB incorrectly places the foo breakpoint on multiple locations, -# then GDB will (incorrectly) stop in foo_low instead of in baz. -gdb_test "continue" \ - "Breakpoint \\d+, (?:$hex in )?baz \\(\\).*" \ - "continue to baz" - -with_test_prefix "step-test-3" { - clean_restart ${testfile} - if ![runto_main] { - return -1 - } - - gdb_test "step" \ - "foo \\(\\).*bar \\(\\);.*foo bar call.*" \ - "step into foo from main" - - gdb_test "step" \ - "bar \\(\\).*\}.* bar end.*" \ - "step into bar from foo" - - gdb_test "step" \ - "foo(_label2)? \\(\\).*foo_low \\(\\);.*foo foo_low call.*" \ - "step out of bar to foo" - - # The tests in the "enable_foo_low_stepping" section, below, work - # with some versions of gcc, though it's not clear that they - # should. This test case causes foo_low, originally a separate - # function invoked via a subroutine call, to be considered as part - # of foo via use of DW_AT_ranges. Real code that I've looked at - # uses a branch instruction to cause code in the "cold" range to - # be executed. + # We need to know the size of integer and address types in order to + # write some of the debugging info we'd like to generate. # - # For the moment though, these tests have been left in place, but - # disabled, in case we decide that making such a subroutine call - # is a reasonable thing to do that should also be supported by - # GDB. + # For that, we ask GDB by debugging our test program. Any program + # would do, but since we already have it specifically for this + # testcase, might as well use that. - set enable_foo_low_stepping false + if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile}] } { + return -1 + } - if { $enable_foo_low_stepping } { - gdb_test_no_output "set variable e=1" + set asm_file [standard_output_file $srcfile2] + Dwarf::assemble $asm_file { + global srcdir subdir srcfile srcfile2 + declare_labels integer_label volatile_label func_ranges_label cu_ranges_label L + set int_size [get_sizeof "int" 4] - set test "step into foo_low from foo" - gdb_test_multiple "step" $test { - -re "foo(_low)? \\(\\).*\{.*foo_low prologue.*${gdb_prompt}" { + # Find start address and length for our functions. + lassign [function_range main [list ${srcdir}/${subdir}/$srcfile]] \ + main_start main_len + set main_end "$main_start + $main_len" + lassign [function_range foo [list ${srcdir}/${subdir}/$srcfile]] \ + foo_start foo_len + set foo_end "$foo_start + $foo_len" + lassign [function_range foo_cold [list ${srcdir}/${subdir}/$srcfile]] \ + foo_cold_start foo_cold_len + set foo_cold_end "$foo_cold_start + $foo_cold_len" + lassign [function_range bar [list ${srcdir}/${subdir}/$srcfile]] \ + bar_start bar_len + set bar_end "$bar_start + $bar_len" + lassign [function_range baz [list ${srcdir}/${subdir}/$srcfile]] \ + baz_start baz_len + set baz_end "$baz_start + $baz_len" + + set e_var [gdb_target_symbol e] + + cu {} { + compile_unit { + {language @DW_LANG_C} + {name dw-ranges-func2.c} + {stmt_list $L DW_FORM_sec_offset} + {low_pc 0 addr} + {ranges ${cu_ranges_label} DW_FORM_sec_offset} + } { + integer_label: DW_TAG_base_type { + {DW_AT_byte_size $int_size DW_FORM_sdata} + {DW_AT_encoding @DW_ATE_signed} + {DW_AT_name integer} + } + volatile_label: DW_TAG_volatile_type { + {type :$integer_label} + } + DW_TAG_variable { + {name e} + {external 1 flag} + {type :$volatile_label} + {location {addr $e_var} SPECIAL_expr} + } + subprogram { + {external 1 flag} + {name main} + {DW_AT_type :$integer_label} + {low_pc $main_start addr} + {high_pc $main_len DW_FORM_data4} + } + subprogram { + {external 1 flag} + {name foo} + {ranges ${func_ranges_label} DW_FORM_sec_offset} + } + subprogram { + {external 1 flag} + {name bar} + {low_pc $bar_start addr} + {high_pc $bar_len DW_FORM_data4} + } + subprogram { + {external 1 flag} + {name baz} + {low_pc $baz_start addr} + {high_pc $baz_len DW_FORM_data4} + } + } + } + + lines {version 2} L { + include_dir "${srcdir}/${subdir}" + file_name "$srcfile" 1 + + # Generate a line table program. An attempt was made to make it + # reasonably accurate as it made debugging the test case easier. + program { + {DW_LNE_set_address $main_start} + {DW_LNS_advance_line [expr [gdb_get_line_number "main prologue"] - 1]} + {DW_LNS_copy} + {DW_LNE_set_address main_label} + {DW_LNS_advance_line [expr [gdb_get_line_number "main foo call"] - [gdb_get_line_number "main prologue"]]} + {DW_LNS_copy} + {DW_LNE_set_address main_label2} + {DW_LNS_advance_line [expr [gdb_get_line_number "main return"] - [gdb_get_line_number "main foo call"]]} + {DW_LNS_copy} + {DW_LNE_set_address $main_end} + {DW_LNS_advance_line [expr [gdb_get_line_number "main end"] - [gdb_get_line_number "main return"] + 1]} + {DW_LNS_copy} + {DW_LNE_end_sequence} + + {DW_LNE_set_address $foo_start} + {DW_LNS_advance_line [expr [gdb_get_line_number "foo prologue"] - 1] } + {DW_LNS_copy} + {DW_LNE_set_address foo_label} + {DW_LNS_advance_line [expr [gdb_get_line_number "foo bar call"] - [gdb_get_line_number "foo prologue"]]} + {DW_LNS_copy} + {DW_LNE_set_address foo_label2} + {DW_LNS_advance_line [expr [gdb_get_line_number "foo foo_cold call"] - [gdb_get_line_number "foo bar call"]]} + {DW_LNS_copy} + {DW_LNE_set_address foo_label3} + {DW_LNS_advance_line [expr [gdb_get_line_number "foo end"] - [gdb_get_line_number "foo foo_cold call"]]} + {DW_LNS_copy} + {DW_LNE_set_address $foo_end} + {DW_LNS_advance_line 1} + {DW_LNS_copy} + {DW_LNE_end_sequence} + + {DW_LNE_set_address $bar_start} + {DW_LNS_advance_line [expr [gdb_get_line_number "bar end"] - 1]} + {DW_LNS_copy} + {DW_LNS_advance_pc $bar_len} + {DW_LNS_advance_line 1} + {DW_LNS_copy} + {DW_LNE_end_sequence} + + {DW_LNE_set_address $baz_start} + {DW_LNS_advance_line [expr [gdb_get_line_number "baz end"] - 1]} + {DW_LNS_copy} + {DW_LNS_advance_pc $baz_len} + {DW_LNS_advance_line 1} + {DW_LNS_copy} + {DW_LNE_end_sequence} + + {DW_LNE_set_address $foo_cold_start} + {DW_LNS_advance_line [expr [gdb_get_line_number "foo_cold prologue"] - 1]} + {DW_LNS_copy} + {DW_LNE_set_address foo_cold_label} + {DW_LNS_advance_line [expr [gdb_get_line_number "foo_cold baz call"] - [gdb_get_line_number "foo_cold prologue"]]} + {DW_LNS_copy} + {DW_LNE_set_address foo_cold_label2} + {DW_LNS_advance_line [expr [gdb_get_line_number "foo_cold end"] - [gdb_get_line_number "foo_cold baz call"]]} + {DW_LNS_copy} + {DW_LNE_set_address $foo_cold_end} + {DW_LNS_advance_line 1} + {DW_LNS_copy} + {DW_LNE_end_sequence} + } + } + + # Generate ranges data. + ranges {is_64 [is_64_target]} { + func_ranges_label: sequence { + {range {$foo_start } $foo_end} + {range {$foo_cold_start} $foo_cold_end} + } + cu_ranges_label: sequence { + {range {$foo_start } $foo_end} + {range {$foo_cold_start} $foo_cold_end} + {range {$main_start} $main_end} + {range {$bar_start} $bar_end} + {range {$baz_start} $baz_end} + } + } + } + + if { [prepare_for_testing "failed to prepare" ${testfile} \ + [list $srcfile $asm_file] {nodebug}] } { + return -1 + } + + if ![runto_main] { + return -1 + } + + set main_prologue_line_num [gdb_get_line_number "main prologue"] + # Do a sanity check to make sure that line number info is available. + gdb_test "info line main" \ + "Line ${main_prologue_line_num} of .* starts at address .* and ends at .*" + + with_test_prefix "step-test-1" { + set bp_foo_bar [gdb_get_line_number "foo bar call"] + + gdb_test "break $bp_foo_bar" \ + "Breakpoint.*at.* file .*$srcfile, line $bp_foo_bar\\." \ + "break at call to bar" + + gdb_test "continue" \ + "Continuing\\..*Breakpoint \[0-9\]+, foo \\(\\).*$bp_foo_bar\\s+bar\\s\\(\\);.*foo bar call.*" \ + "continue to call of bar" + + gdb_test "step" \ + "bar \\(\\).*bar end.*" \ + "step into bar" + + gdb_test "step" \ + "foo \\(\\).*foo foo_cold call.*" \ + "step out of bar, back into foo" + } + + with_test_prefix "step-test-2" { + clean_restart ${testfile} + if ![runto_main] { + return -1 + } + + # Note that the RE used for the following test will fail when the + # breakpoint has been set on multiple locations. E.g. "(2 locations)". + # This is intentional since that behavior is one of the bugs that + # this test case tests for. + gdb_test "break foo" \ + "Breakpoint.*at.* file .*$srcfile, line \\d+\\." \ + "break foo" + + # Continue to foo. Allow execution to stop either on the prologue + # or on the call to bar since either behavior is acceptable though + # the latter is preferred. + set test "continue to foo" + gdb_test_multiple "continue" $test { + -re "Breakpoint \\d+, foo \\(\\).*foo prologue.*${gdb_prompt}" { pass $test gdb_test "step" \ - "foo \\(\\).*baz \\(\\);.*foo_low baz call.*" \ - "step to baz call in foo_low" - + "foo bar call .*" \ + "step to call of bar after landing on prologue" } - -re "foo(_low)? \\(\\).*baz \\(\\);.*foo_low baz call.*${gdb_prompt}" { + -re "Breakpoint \\d+, foo \\(\\).*foo bar call.*${gdb_prompt}" { pass $test } } gdb_test "step" \ - "baz \\(\\).*\}.*baz end.*" \ - "step into baz from foo_low" + "bar \\(\\).*bar end.*" \ + "step into bar" gdb_test "step" \ - "foo(?:_low(?:_label2)?)? \\(\\).*\}.*foo_low end.*" \ - "step out of baz to foo_low" - - gdb_test "step" \ - "foo(?:_label3)? \\(\\).*\}.*foo end.*" \ - "step out of foo_low to foo" - } else { - gdb_test "next" \ - ".*foo end.*" \ - "next over foo_low call" + "foo \\(\\).*foo foo_cold call.*" \ + "step out of bar, back into foo" } - gdb_test "step" \ - "main(?:_label2)? \\(\\).*" \ - "step out of foo to main" + clean_restart ${testfile} + if ![runto_main] { + return -1 + } + + # Disassembly of foo should have multiple address ranges. + gdb_test_sequence "disassemble foo" "" [list \ + "Dump of assembler code for function foo:" \ + "Address range $hex to $hex:" \ + " $hex <\\+0>:" \ + "Address range $hex to $hex:" \ + " $hex <(.+?)>:" \ + "End of assembler dump\\." \ + ] + + set foo_cold_addr -1 + set test "x/i foo_cold" + gdb_test_multiple $test $test { + -re " ($hex) .*${gdb_prompt}" { + set foo_cold_addr $expect_out(1,string) + pass $test + } + } + + set foo_addr -1 + set test "x/i foo" + gdb_test_multiple $test $test { + -re " ($hex) .*${gdb_prompt}" { + set foo_addr $expect_out(1,string) + pass $test + } + } + + gdb_assert {$foo_cold_addr != $foo_addr} "foo and foo_cold are at different addresses" + + # This more permissive RE for "break foo" will allow a breakpoint on + # multiple locations to PASS. */ + gdb_test "break foo" \ + "Breakpoint.*at.*" \ + "break foo" + + gdb_test "break baz" \ + "Breakpoint.*at.* file .*$srcfile, line \\d+\\." + + gdb_test "continue" \ + "Breakpoint \\d+, foo \\(\\).*" \ + "continue to foo" + + gdb_test_no_output "set variable e=1" + + # If GDB incorrectly places the foo breakpoint on multiple locations, + # then GDB will (incorrectly) stop in foo_cold instead of in baz. + gdb_test "continue" \ + "Breakpoint \\d+, (?:$hex in )?baz \\(\\).*" \ + "continue to baz" + + with_test_prefix "no-cold-names" { + + # Due to the calling sequence, this backtrace would normally + # show function foo_cold for frame #1. However, we don't want + # this to be the case due to placing it in the same block + # (albeit at a different range) as foo. Thus it is correct to + # see foo for frames #1 and #2. It is incorrect to see + # foo_cold at frame #1. + gdb_test_sequence "bt" "backtrace from baz" { + "\[\r\n\]#0 .*? baz \\(\\) " + "\[\r\n\]#1 .*? foo \\(\\) " + "\[\r\n\]#2 .*? foo \\(\\) " + "\[\r\n\]#3 .*? main \\(\\) " + } + + # Doing x/2i foo_cold should show foo_cold as the first symbolic + # address and an offset from foo for the second. We also check to + # make sure that the offset is not too large - we don't GDB to + # display really large offsets that would (try to) wrap around the + # address space. + set foo_cold_offset 0 + set test "x/2i foo_cold" + gdb_test_multiple $test $test { + -re " (?:$hex) .*?\n (?:$hex) .*${gdb_prompt}" { + set foo_cold_offset $expect_out(1,string) + pass $test + } + } + gdb_assert {$foo_cold_offset <= 10000} "offset to foo_cold is not too large" + + # Likewise, verify that second address shown by "info line" is at + # and offset from foo instead of foo_cold. + gdb_test "info line *foo_cold" "starts at address $hex and ends at $hex .*" + + } + + with_test_prefix "step-test-3" { + clean_restart ${testfile} + if ![runto_main] { + return -1 + } + + gdb_test "step" \ + "foo \\(\\).*bar \\(\\);.*foo bar call.*" \ + "step into foo from main" + + gdb_test "step" \ + "bar \\(\\).*\}.* bar end.*" \ + "step into bar from foo" + + gdb_test "step" \ + "foo(_label2)? \\(\\).*foo_cold \\(\\);.*foo foo_cold call.*" \ + "step out of bar to foo" + + # The tests in the "enable_foo_cold_stepping" section, below, work + # with some versions of gcc, though it's not clear that they + # should. This test case causes foo_cold, originally a separate + # function invoked via a subroutine call, to be considered as part + # of foo via use of DW_AT_ranges. Real code that I've looked at + # uses a branch instruction to cause code in the "cold" range to + # be executed. + # + # For the moment though, these tests have been left in place, but + # disabled, in case we decide that making such a subroutine call + # is a reasonable thing to do that should also be supported by + # GDB. + + set enable_foo_cold_stepping false + + if { $enable_foo_cold_stepping } { + gdb_test_no_output "set variable e=1" + + set test "step into foo_cold from foo" + gdb_test_multiple "step" $test { + -re "foo(_low)? \\(\\).*\{.*foo_cold prologue.*${gdb_prompt}" { + pass $test + gdb_test "step" \ + "foo \\(\\).*baz \\(\\);.*foo_cold baz call.*" \ + "step to baz call in foo_cold" + + } + -re "foo(_low)? \\(\\).*baz \\(\\);.*foo_cold baz call.*${gdb_prompt}" { + pass $test + } + } + + gdb_test "step" \ + "baz \\(\\).*\}.*baz end.*" \ + "step into baz from foo_cold" + + gdb_test "step" \ + "foo(?:_low(?:_label2)?)? \\(\\).*\}.*foo_cold end.*" \ + "step out of baz to foo_cold" + + gdb_test "step" \ + "foo(?:_label3)? \\(\\).*\}.*foo end.*" \ + "step out of foo_cold to foo" + } else { + gdb_test "next" \ + ".*foo end.*" \ + "next over foo_cold call" + } + + gdb_test "step" \ + "main(?:_label2)? \\(\\).*" \ + "step out of foo to main" + } +} + +# foreach_with_prefix could be used here, but the log file output is somewhat +# less verbose when using an explicit "with_test_prefix". + +foreach test_suffix { "lo-cold" "hi-cold" } { + with_test_prefix $test_suffix { + do_test $test_suffix + } }