Improve test gdb.dwarf2/dw2-ranges-func.exp

The original dw2-ranges-func.exp test caused a function named foo to be
created with two non-contiguous address ranges.  In the C source file,
a function named foo_low was incorporated into the function foo which
was also defined in that file.  The DWARF assembler is used to do this
manipulation.  The source file had been laid out so that foo_low would
likely be placed (by the compiler and linker) at a lower address than
foo().

The case where a range at a higher set of addresses (than foo) was not
being tested.  In a recent discussion on gdb-patches, it became clear
that performing such tests are desirable because bugs were discovered
which only became evident when the other range was located at high(er)
addresses than the range containing the entry point for the function.

This other (non entry pc) address range is typically used for "cold"
code which executes less frequently.  Thus, I renamed foo_low to
foo_cold and renamed the C source file from dw-ranges-func.c to
dw-ranges-func-lo.c.  I then made a copy of this file, naming it
dw-ranges-func-hi.c.  (That was my intent anyway.  According to git,
I renamed dw-ranges-func.c to dw-ranges-func-hi.c and then modified it.
dw-ranges-func-lo.c shows up as an entirely new file.)

Within dw-ranges-func-hi.c, I changed the placement of foo_cold()
along with some of the other functions so that foo_cold() would be at
a higher address than foo() while also remaining non-contiguous.  The
two files, dw-ranges-func-lo.c and dw-ranges-func-hi.c, are
essentially the same except for the placement of some of the functions
therein.

The tests in dw2-ranges-func.exp where then wrapped in a new proc named
do_test which was then called in a loop from the outermost level.  The
loop causes each of the source files to have the same tests run upon
them.

I also added a few new tests which test functionality fixed by the other
commits to this patch series.  Due to the reorganization of the file,
it's hard to identify these changes in the patch.  So, here are the
tests which were added:

    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) <foo_cold>.*?\n   (?:$hex) <foo\[+-\](\[0-9\]+)>.*${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 <foo_cold> and ends at $hex <foo\[+-\].*?>.*"

    }

When run against a GDB without the requisite bug fixes (from this patch
series), these 6 failures should be seen:

FAIL: gdb.dwarf2/dw2-ranges-func.exp: lo-cold: no-cold-names: backtrace from baz (pattern 4)
FAIL: gdb.dwarf2/dw2-ranges-func.exp: lo-cold: no-cold-names: x/2i foo_cold
FAIL: gdb.dwarf2/dw2-ranges-func.exp: lo-cold: no-cold-names: info line *foo_cold
FAIL: gdb.dwarf2/dw2-ranges-func.exp: hi-cold: no-cold-names: backtrace from baz (pattern 3)
FAIL: gdb.dwarf2/dw2-ranges-func.exp: hi-cold: no-cold-names: x/2i foo_cold
FAIL: gdb.dwarf2/dw2-ranges-func.exp: hi-cold: no-cold-names: info line *foo_cold

gdb/testsuite/ChangeLog:

	* 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.
This commit is contained in:
Kevin Buettner
2019-06-02 18:31:22 -07:00
parent 1aff717310
commit 5c076da45c
4 changed files with 530 additions and 374 deletions

View File

@ -1,3 +1,18 @@
2019-07-27 Kevin Buettner <kevinb@redhat.com>
* 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 <tdevries@suse.de>
* gdb.arch/i386-pkru.exp: Fix unterminated string.

View File

@ -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)

View File

@ -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 <http://www.gnu.org/licenses/>. */
/* 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 */

View File

@ -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) <foo.*?>.*${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) <foo.*?>.*${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) <foo.*?>.*${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) <foo.*?>.*${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) <foo_cold>.*?\n (?:$hex) <foo\[+-\](\[0-9\]+)>.*${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 <foo_cold> and ends at $hex <foo\[+-\].*?>.*"
}
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
}
}