Compare commits

...

4 Commits

Author SHA1 Message Date
Bernhard Heckel
bee226f9b2 Fortran: Document scope operator.
Document scope operator in GDB manual.

2016-08-10  Bernhard Heckel  <bernhard.heckel@intel.com>

gdb/Changelog:
	* doc/gdb.texinfo: Describe scope operator.

Change-Id: I15ff901b4a729f0994410a971bba639ebc361d2a
2016-12-23 12:20:09 +01:00
Bernhard Heckel
1c04575472 Fortran: Nested functions, add scope parameter.
In order to avoid name clashing in GDB, we add a scope
to nested subroutines. Enveloping function gives the
scope.

Change-Id: I7d424b1e3039613d938aae56ec1a3b3d1cdda744
2016-12-23 12:20:06 +01:00
Bernhard Heckel
671c004cc4 Fortran: Enable setting breakpoint on nested functions.
Like in Ada, we want to be able to set a breakpoint on
nested functions, called "contained routines" in Fortran.

2016-06-30  Bernhard Heckel  <bernhard.heckel@intel.com>

gdb/Changelog:
	* dwarf2read.c (todo)

gdb/Changelog:
	* gdb.fortran/nested-funcs.exp: Set breakpoint on contained routines.

Change-Id: I7f2897f8f3160c0d5618850273d87579cc2479c8
2016-12-23 12:20:04 +01:00
Bernhard Heckel
57ed8b65cc Dwarf: Fortran, support DW_TAG_entry_point.
Fortran provides additional entry-points to an subprogram.
Those entry-points may have only a subset of parameters
of the original subprogram as well.
Add support for parsing DW_TAG_entry_point's for Fortran.

2016-06-01  Bernhard Heckel  <bernhard.heckel@intel.com>

gdb/Changelog:
	* gdb/dwarf2read.c (add_partial_symbol): Handle DW_TAG_entry_point.
	(add_partial_entry_point): New.
	(add_partial_subprogram): Search for entry_points.
	(process_die): Handle DW_TAG_entry_point.
	(dwarf2_get_pc_bounds): Update low pc from DWARF.
	(load_partial_dies): Save DW_TAG_entry_point's.
	(load_partial_dies): Save DW_TAG_entry_point to hash table.
	(load_partial_dies): Look into child's of DW_TAG_sub_program
	for fortran.
	(new_symbol_full): Process DW_TAG_entry_point.
	(read_type_die_1): Handle DW_TAG_entry_point.

gdb/Testsuite/Changelog:
	* gdb.fortran/entry_point.f90: New.
	* gdb.fortran/entry_point.exp: New.

Change-Id: I886699802fc940cd9b995806c32a85a05cf57dc4
2016-12-23 12:20:01 +01:00
6 changed files with 328 additions and 7 deletions

3
gdb/doc/gdb.texinfo Normal file → Executable file
View File

@@ -15228,6 +15228,9 @@ The access component operator. Normally used to access elements in derived
types. Also suitable for unions. As unions aren't part of regular Fortran,
this can only happen when accessing a register that uses a gdbarch-defined
union type.
@item ::
The scope operator. Normally used to access variables in modules or to set breakpoints
on subroutines nested in modules or in other subroutines (internal subroutines).
@end table
@node Fortran Defaults

View File

@@ -1425,6 +1425,10 @@ static void add_partial_module (struct partial_die_info *pdi, CORE_ADDR *lowpc,
static void add_partial_enumeration (struct partial_die_info *enum_pdi,
struct dwarf2_cu *cu);
static void add_partial_entry_point (struct partial_die_info *pdi,
CORE_ADDR *lowpc, CORE_ADDR *highpc,
int need_pc, struct dwarf2_cu *cu);
static void add_partial_subprogram (struct partial_die_info *pdi,
CORE_ADDR *lowpc, CORE_ADDR *highpc,
int need_pc, struct dwarf2_cu *cu);
@@ -6817,6 +6821,7 @@ partial_die_parent_scope (struct partial_die_info *pdi,
return NULL;
}
/* Internal (nested) subroutines in Fortran get a prefix. */
if (pdi->tag == DW_TAG_enumerator)
/* Enumerators should not get the name of the enumeration as a prefix. */
parent->scope = grandparent_scope;
@@ -6826,7 +6831,10 @@ partial_die_parent_scope (struct partial_die_info *pdi,
|| parent->tag == DW_TAG_class_type
|| parent->tag == DW_TAG_interface_type
|| parent->tag == DW_TAG_union_type
|| parent->tag == DW_TAG_enumeration_type)
|| parent->tag == DW_TAG_enumeration_type
|| (cu->language == language_fortran
&& parent->tag == DW_TAG_subprogram
&& pdi->tag == DW_TAG_subprogram))
{
if (grandparent_scope == NULL)
parent->scope = parent->name;
@@ -6911,9 +6919,30 @@ add_partial_symbol (struct partial_die_info *pdi, struct dwarf2_cu *cu)
switch (pdi->tag)
{
case DW_TAG_entry_point:
addr = gdbarch_adjust_dwarf2_addr (gdbarch, pdi->lowpc + baseaddr);
/* DW_TAG_entry_point provides an additional entry_point to an
existing sub_program. Therefore, we inherit the "external"
attribute from the sub_program to which the entry_point
belongs to. */
if (pdi->die_parent->is_external)
add_psymbol_to_list (actual_name, strlen (actual_name),
built_actual_name != NULL,
VAR_DOMAIN, LOC_BLOCK,
&objfile->global_psymbols,
addr, cu->language, objfile);
else
add_psymbol_to_list (actual_name, strlen (actual_name),
built_actual_name != NULL,
VAR_DOMAIN, LOC_BLOCK,
&objfile->static_psymbols,
addr, cu->language, objfile);
break;
case DW_TAG_subprogram:
addr = gdbarch_adjust_dwarf2_addr (gdbarch, pdi->lowpc + baseaddr);
if (pdi->is_external || cu->language == language_ada)
if (pdi->is_external
|| cu->language == language_ada
|| cu->language == language_fortran)
{
/* brobecker/2007-12-26: Normally, only "external" DIEs are part
of the global scope. But in Ada, we want to be able to access
@@ -7108,6 +7137,17 @@ add_partial_module (struct partial_die_info *pdi, CORE_ADDR *lowpc,
scan_partial_symbols (pdi->die_child, lowpc, highpc, set_addrmap, cu);
}
static void
add_partial_entry_point (struct partial_die_info *pdi,
CORE_ADDR *p_lowpc, CORE_ADDR *p_highpc,
int set_addrmap, struct dwarf2_cu *cu)
{
if (pdi->name == NULL)
complaint (&symfile_complaints, _("DW_TAG_entry_point have to have a name"));
else
add_partial_symbol (pdi, cu);
}
/* Read a partial die corresponding to a subprogram and create a partial
symbol for that subprogram. When the CU language allows it, this
routine also defines a partial symbol for each nested subprogram
@@ -7178,6 +7218,18 @@ add_partial_subprogram (struct partial_die_info *pdi,
pdi = pdi->die_sibling;
}
}
else if (cu->language == language_fortran)
{
pdi = pdi->die_child;
while (pdi != NULL)
{
if (pdi->tag == DW_TAG_entry_point)
add_partial_entry_point (pdi, lowpc, highpc, set_addrmap, cu);
else if (pdi->tag == DW_TAG_subprogram)
add_partial_subprogram (pdi, lowpc, highpc, set_addrmap, cu);
pdi = pdi->die_sibling;
}
}
}
/* Read a partial die corresponding to an enumeration type. */
@@ -8283,6 +8335,12 @@ process_die (struct die_info *die, struct dwarf2_cu *cu)
read_type_unit_scope (die, cu);
break;
case DW_TAG_subprogram:
/* Internal subprograms in Fortran get a prefix. */
if (cu->language == language_fortran
&& die->parent != NULL
&& die->parent->tag == DW_TAG_subprogram)
cu->processing_has_namespace_info = 1;
case DW_TAG_entry_point:
case DW_TAG_inlined_subroutine:
read_func_scope (die, cu);
break;
@@ -12063,6 +12121,27 @@ dwarf2_get_pc_bounds (struct die_info *die, CORE_ADDR *lowpc,
CORE_ADDR high = 0;
enum pc_bounds_kind ret;
if (die->tag == DW_TAG_entry_point)
{
/* Entry_point is embedded in an subprogram. Therefore, we can use
the highpc from it's enveloping subprogram and get the
lowpc from DWARF. */
if (PC_BOUNDS_INVALID == dwarf2_get_pc_bounds (die->parent, lowpc, highpc, cu, pst))
return PC_BOUNDS_INVALID;
attr = dwarf2_attr (die, DW_AT_low_pc, cu);
if (!attr)
{
complaint (&symfile_complaints,
_("DW_TAG_entry_point is missing DW_AT_low_pc"));
return PC_BOUNDS_INVALID;
}
low = attr_value_as_address (attr);
*lowpc = low;
return PC_BOUNDS_HIGH_LOW;
}
attr_high = dwarf2_attr (die, DW_AT_high_pc, cu);
if (attr_high)
{
@@ -15632,6 +15711,7 @@ load_partial_dies (const struct die_reader_specs *reader,
&& abbrev->tag != DW_TAG_constant
&& abbrev->tag != DW_TAG_enumerator
&& abbrev->tag != DW_TAG_subprogram
&& abbrev->tag != DW_TAG_entry_point
&& abbrev->tag != DW_TAG_lexical_block
&& abbrev->tag != DW_TAG_variable
&& abbrev->tag != DW_TAG_namespace
@@ -15758,6 +15838,7 @@ load_partial_dies (const struct die_reader_specs *reader,
if (load_all
|| abbrev->tag == DW_TAG_constant
|| abbrev->tag == DW_TAG_subprogram
|| abbrev->tag == DW_TAG_entry_point
|| abbrev->tag == DW_TAG_variable
|| abbrev->tag == DW_TAG_namespace
|| part_die->is_declaration)
@@ -15799,7 +15880,9 @@ load_partial_dies (const struct die_reader_specs *reader,
|| last_die->tag == DW_TAG_union_type))
|| (cu->language == language_ada
&& (last_die->tag == DW_TAG_subprogram
|| last_die->tag == DW_TAG_lexical_block))))
|| last_die->tag == DW_TAG_lexical_block))
|| (cu->language == language_fortran
&& last_die->tag == DW_TAG_subprogram)))
{
nesting_level++;
parent_die = last_die;
@@ -18440,13 +18523,28 @@ new_symbol_full (struct die_info *die, struct type *type, struct dwarf2_cu *cu,
SYMBOL_ACLASS_INDEX (sym) = LOC_LABEL;
add_symbol_to_list (sym, cu->list_in_scope);
break;
case DW_TAG_entry_point:
/* SYMBOL_BLOCK_VALUE (sym) will be filled in later by
finish_block. */
SYMBOL_ACLASS_INDEX (sym) = LOC_BLOCK;
/* DW_TAG_entry_point provides an additional entry_point to an
existing sub_program. Therefore, we inherit the "external"
attribute from the sub_program to which the entry_point
belongs to. */
attr2 = dwarf2_attr (die->parent, DW_AT_external, cu);
if (attr2 && (DW_UNSND (attr2) != 0))
list_to_add = &global_symbols;
else
list_to_add = cu->list_in_scope;
break;
case DW_TAG_subprogram:
/* SYMBOL_BLOCK_VALUE (sym) will be filled in later by
finish_block. */
SYMBOL_ACLASS_INDEX (sym) = LOC_BLOCK;
attr2 = dwarf2_attr (die, DW_AT_external, cu);
if ((attr2 && (DW_UNSND (attr2) != 0))
|| cu->language == language_ada)
|| cu->language == language_ada
|| cu->language == language_fortran)
{
/* Subprograms marked external are stored as a global symbol.
Ada subprograms, whether marked external or not, are always
@@ -19124,6 +19222,7 @@ read_type_die_1 (struct die_info *die, struct dwarf2_cu *cu)
case DW_TAG_enumeration_type:
this_type = read_enumeration_type (die, cu);
break;
case DW_TAG_entry_point:
case DW_TAG_subprogram:
case DW_TAG_subroutine_type:
case DW_TAG_inlined_subroutine:
@@ -19450,6 +19549,19 @@ determine_prefix (struct die_info *die, struct dwarf2_cu *cu)
return TYPE_TAG_NAME (parent_type);
return "";
}
case DW_TAG_subprogram:
/* Only internal subroutines in Fortran get a prefix with the name
of the parent's subroutine. */
if (cu->language == language_fortran)
{
if ((die->tag == DW_TAG_subprogram)
&& (dwarf2_name (parent, cu) != NULL))
return dwarf2_name (parent, cu);
else
return "";
}
else
return determine_prefix (parent, cu);
/* Fall through. */
default:
return determine_prefix (parent, cu);

View File

@@ -0,0 +1,70 @@
# Copyright 2016 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/>.
if { [skip_fortran_tests] } { return -1 }
standard_testfile .f90
load_lib "fortran.exp"
if {[prepare_for_testing $testfile.exp $testfile $srcfile {debug f90}]} {
return -1
}
if ![runto MAIN__] then {
perror "couldn't run to breakpoint MAIN__"
continue
}
# Test if we can set a breakpoint via entry-point name
set ept_name "foo"
gdb_breakpoint $ept_name
gdb_test "continue" \
[multi_line "Breakpoint $decimal, $ept_name \\(j=1, k=2, l=3, i1=4\\) at .*" \
".*"] \
"continue to breakpoint: $ept_name"
gdb_test "print j" "= 1" "print j, entered via $ept_name"
gdb_test "print k" "= 2" "print k, entered via $ept_name"
gdb_test "print l" "= 3" "print l, entered via $ept_name"
gdb_test "print i1" "= 4" "print i1, entered via $ept_name"
gdb_test "info args" \
[multi_line "j = 1" \
"k = 2" \
"l = 3" \
"i1 = 4"] \
"info args, entered via $ept_name"
# Test if we can set a breakpoint via function name
set ept_name "bar"
gdb_breakpoint $ept_name
gdb_test "continue" \
[multi_line "Breakpoint $decimal, $ept_name \\(i=4, j=5, k=6, i1=7\\) at .*" \
".*"] \
"continue to breakpoint: $ept_name"
gdb_test "print i" "= 4" "print i, entered via $ept_name"
gdb_test "print j" "= 5" "print j, entered via $ept_name"
gdb_test "print k" "= 6" "print k, entered via $ept_name"
gdb_test "print i1" "= 7" "print i1, entered via $ept_name"
set ept_name "tim"
gdb_breakpoint $ept_name
gdb_test "continue" \
[multi_line "Breakpoint $decimal, $ept_name \\(j=1\\) at .*" \
".*"] \
"continue to breakpoint: $ept_name"
gdb_test "print j" "= 1" "print j, entered via $ept_name"
gdb_test "info args" "j = 1" "info args, entered via $ept_name"

View File

@@ -0,0 +1,48 @@
! Copyright 2016 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/>.
program TestEntryPoint
call foo(1,2,3,4)
call bar(4,5,6,7)
call tim(1)
end program TestEntryPoint
subroutine bar(I,J,K,I1)
INTEGER I,J,K,L,I1
INTEGER A
REAL C
A = 0
C = 0.0
A = I + K + I1
goto 1000
entry foo(J,K,L,I1)
A = J + K + L + I1
200 C = J
goto 1000
entry tim(J)
goto 200
1000 A = C + 1
C = J * 1.5
return
end subroutine

View File

@@ -30,6 +30,10 @@ if ![runto MAIN__] then {
continue
}
# Test if we can set a breakpoint in a nested function
gdb_breakpoint "testnestedfuncs::sub_nested_outer"
gdb_continue_to_breakpoint "testnestedfuncs::sub_nested_outer" ".*local_int = 19"
# Test if we can access local and
# non-local variables defined one level up.
gdb_breakpoint [gdb_get_line_number "! BP_outer"]
@@ -39,10 +43,17 @@ gdb_test "set index = 42"
gdb_test "print index" "= 42" "print index at BP_outer, manipulated"
gdb_test "print local_int" "= 19" "print local_int in outer function"
# Non-local variable should be affected in one frame up as well.
gdb_test "up"
gdb_test "print index" "= 42" "print index at BP1, one frame up"
# Test if we can set a breakpoint in a nested function
gdb_breakpoint "testnestedfuncs::sub_nested_inner"
gdb_continue_to_breakpoint "testnestedfuncs::sub_nested_inner" ".*local_int = 17"
# Test if we can access local and
# non-local variables defined two level up.
gdb_breakpoint [gdb_get_line_number "! BP_inner"]
@@ -51,12 +62,29 @@ gdb_test "print index" "= 42" "print index at BP_inner"
gdb_test "print v_state%code" "= 61" "print v_state%code at BP_inner"
gdb_test "print local_int" "= 17" "print local_int in inner function"
# Test if local variable is still correct.
gdb_breakpoint [gdb_get_line_number "! BP_outer_2"]
gdb_continue_to_breakpoint "! BP_outer_2" ".*! BP_outer_2"
gdb_test "print local_int" "= 19" \
"print local_int in outer function, after sub_nested_inner"
# Test if we can set a breakpoint in public routine with the same name as the internal
gdb_breakpoint "sub_nested_outer"
gdb_continue_to_breakpoint "sub_nested_outer" ".*name = 'sub_nested_outer external'"
# Test if we can set a breakpoint in public routine with the same name as the internal
gdb_breakpoint "sub_with_sub_nested_outer::sub_nested_outer"
gdb_continue_to_breakpoint "sub_with_sub_nested_outer::sub_nested_outer" ".*local_int = 11"
# Test if we can set a breakpoint in public routine with the same name as the internal
gdb_breakpoint "mod1::sub_nested_outer"
gdb_continue_to_breakpoint "mod1::sub_nested_outer" ".*name = 'sub_nested_outer_mod1'"
# Sanity check in main.
gdb_breakpoint [gdb_get_line_number "! BP_main"]
gdb_continue_to_breakpoint "! BP_main" ".*! BP_main"

View File

@@ -13,8 +13,64 @@
! You should have received a copy of the GNU General Public License
! along with this program. If not, see <http://www.gnu.org/licenses/>.
program TestNestedFuncs
module mod1
integer :: var_i = 1
integer :: var_const
parameter (var_const = 20)
CONTAINS
SUBROUTINE sub_nested_outer
integer :: local_int
character (len=20) :: name
name = 'sub_nested_outer_mod1'
local_int = 11
END SUBROUTINE sub_nested_outer
end module mod1
! Public sub_nested_outer
SUBROUTINE sub_nested_outer
integer :: local_int
character (len=16) :: name
name = 'sub_nested_outer external'
local_int = 11
END SUBROUTINE sub_nested_outer
! Needed indirection to call public sub_nested_outer from main
SUBROUTINE sub_nested_outer_ind
character (len=20) :: name
name = 'sub_nested_outer_ind'
CALL sub_nested_outer
END SUBROUTINE sub_nested_outer_ind
! public routine with internal subroutine
SUBROUTINE sub_with_sub_nested_outer()
integer :: local_int
character (len=16) :: name
name = 'subroutine_with_int_sub'
local_int = 1
CALL sub_nested_outer ! Should call the internal fct
CONTAINS
SUBROUTINE sub_nested_outer
integer :: local_int
local_int = 11
END SUBROUTINE sub_nested_outer
END SUBROUTINE sub_with_sub_nested_outer
! Main
program TestNestedFuncs
USE mod1, sub_nested_outer_use_mod1 => sub_nested_outer
IMPLICIT NONE
TYPE :: t_State
@@ -22,10 +78,14 @@ program TestNestedFuncs
END TYPE t_State
TYPE (t_State) :: v_state
integer index
integer index, local_int
local_int = 14
index = 13
CALL sub_nested_outer
CALL sub_nested_outer ! Call internal sub_nested_outer
CALL sub_nested_outer_ind ! Call external sub_nested_outer via sub_nested_outer_ind
CALL sub_with_sub_nested_outer ! Call external routine with nested sub_nested_outer
CALL sub_nested_outer_use_mod1 ! Call sub_nested_outer imported via module
index = 11 ! BP_main
v_state%code = 27