diff --git a/gdb/ChangeLog b/gdb/ChangeLog index d6bf7e3a27..9da1d3bcb8 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,21 @@ +2018-01-21 Andrew Burgess + + PR mi/20395 + * ada-exp.y (write_var_from_sym): Pass extra parameter when + updating innermost block. + * parse.c (innermost_block_tracker::update): Take extra type + parameter, and check types match before updating innermost block. + (write_dollar_variable): Update innermost block for registers. + * parser-defs.h (enum innermost_block_tracker_type): New enum. + (innermost_block_tracker::innermost_block_tracker): Initialise + m_types member. + (innermost_block_tracker::reset): Take type parameter. + (innermost_block_tracker::update): Take type parameter, and pass + type through as needed. + (innermost_block_tracker::m_types): New member. + * varobj.c (varobj_create): Pass type when reseting innermost + block. + 2018-01-21 Andrew Burgess * ada-exp.y (write_var_from_sym): Switch to innermost_block API. diff --git a/gdb/ada-exp.y b/gdb/ada-exp.y index 56113186b9..ac4c341bfe 100644 --- a/gdb/ada-exp.y +++ b/gdb/ada-exp.y @@ -757,7 +757,7 @@ write_var_from_sym (struct parser_state *par_state, struct symbol *sym) { if (symbol_read_needs_frame (sym)) - innermost_block.update (block); + innermost_block.update (block, INNERMOST_BLOCK_FOR_SYMBOLS); write_exp_elt_opcode (par_state, OP_VAR_VALUE); write_exp_elt_block (par_state, block); diff --git a/gdb/parse.c b/gdb/parse.c index ca5eb02386..e3f1306a17 100644 --- a/gdb/parse.c +++ b/gdb/parse.c @@ -124,9 +124,12 @@ static expression_up parse_exp_in_context_1 (const char **, CORE_ADDR, /* Documented at it's declaration. */ void -innermost_block_tracker::update (const struct block *b) +innermost_block_tracker::update (const struct block *b, + innermost_block_tracker_types t) { - if (m_innermost_block == NULL || contained_in (b, m_innermost_block)) + if ((m_types & t) != 0 + && (m_innermost_block == NULL + || contained_in (b, m_innermost_block))) m_innermost_block = b; } @@ -691,6 +694,8 @@ handle_register: str.ptr++; write_exp_string (ps, str); write_exp_elt_opcode (ps, OP_REGISTER); + innermost_block.update (expression_context_block, + INNERMOST_BLOCK_FOR_REGISTERS); return; } diff --git a/gdb/parser-defs.h b/gdb/parser-defs.h index 01ac0cd363..c67b8d5691 100644 --- a/gdb/parser-defs.h +++ b/gdb/parser-defs.h @@ -75,6 +75,24 @@ extern const struct block *expression_context_block; then look up the macro definitions active at that point. */ extern CORE_ADDR expression_context_pc; +/* While parsing expressions we need to track the innermost lexical block + that we encounter. In some situations we need to track the innermost + block just for symbols, and in other situations we want to track the + innermost block for symbols and registers. These flags are used by the + innermost block tracker to control which blocks we consider for the + innermost block. These flags can be combined together as needed. */ + +enum innermost_block_tracker_type +{ + /* Track the innermost block for symbols within an expression. */ + INNERMOST_BLOCK_FOR_SYMBOLS = (1 << 0), + + /* Track the innermost block for registers within an expression. */ + INNERMOST_BLOCK_FOR_REGISTERS = (1 << 1) +}; +DEF_ENUM_FLAGS_TYPE (enum innermost_block_tracker_type, + innermost_block_tracker_types); + /* When parsing expressions we track the innermost block that was referenced. */ @@ -82,24 +100,32 @@ class innermost_block_tracker { public: innermost_block_tracker () - : m_innermost_block (NULL) + : m_types (INNERMOST_BLOCK_FOR_SYMBOLS), + m_innermost_block (NULL) { /* Nothing. */ } /* Reset the currently stored innermost block. Usually called before - parsing a new expression. */ - void reset () + parsing a new expression. As the most common case is that we only + want to gather the innermost block for symbols in an expression, this + becomes the default block tracker type. */ + void reset (innermost_block_tracker_types t = INNERMOST_BLOCK_FOR_SYMBOLS) { - m_innermost_block = nullptr; + m_types = t; + m_innermost_block = NULL; } /* Update the stored innermost block if the new block B is more inner - than the currently stored block, or if no block is stored yet. */ - void update (const struct block *b); + than the currently stored block, or if no block is stored yet. The + type T tells us whether the block B was for a symbol or for a + register. The stored innermost block is only updated if the type T is + a type we are interested in, the types we are interested in are held + in M_TYPES and set during RESET. */ + void update (const struct block *b, innermost_block_tracker_types t); /* Overload of main UPDATE method which extracts the block from BS. */ void update (const struct block_symbol &bs) { - update (bs.block); + update (bs.block, INNERMOST_BLOCK_FOR_SYMBOLS); } /* Return the stored innermost block. Can be nullptr if no symbols or @@ -111,13 +137,16 @@ public: } private: + /* The type of innermost block being looked for. */ + innermost_block_tracker_types m_types; + /* The currently stored innermost block found while parsing an expression. */ const struct block *m_innermost_block; }; -/* The innermost context required by the stack and register variables we've - encountered so far. This should be cleared before parsing an +/* The innermost context required by the stack and register variables + we've encountered so far. This should be cleared before parsing an expression, and queried once the parse is complete. */ extern innermost_block_tracker innermost_block; diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index db860b0a1f..3a8e6ed30d 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,11 @@ +2018-01-21 Don Breazeal + Andrew Burgess + + * gdb.mi/basics.c: Add new global. + * gdb.mi/mi-frame-regs.exp: New file. + * gdb.mi/mi-var-create-rtti.exp: Update expected results, add new + case. + 2018-01-21 Andrew Burgess * gdb.arch/amd64-entry-value.exp: Test using @entry on a diff --git a/gdb/testsuite/gdb.mi/basics.c b/gdb/testsuite/gdb.mi/basics.c index 08d9ccae17..327f33c6ec 100644 --- a/gdb/testsuite/gdb.mi/basics.c +++ b/gdb/testsuite/gdb.mi/basics.c @@ -20,6 +20,8 @@ * on function calls. Useful to test printing frames, stepping, etc. */ +unsigned long long global_zero = 0; + int callee4 (void) { int A=1; diff --git a/gdb/testsuite/gdb.mi/mi-frame-regs.exp b/gdb/testsuite/gdb.mi/mi-frame-regs.exp new file mode 100644 index 0000000000..413fa5c3c9 --- /dev/null +++ b/gdb/testsuite/gdb.mi/mi-frame-regs.exp @@ -0,0 +1,186 @@ +# Copyright 2018 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 . + +# Test essential Machine interface (MI) operations +# +# Verify that -var-update will provide the correct values for floating +# and fixed varobjs that represent the pc register. +# + +load_lib mi-support.exp +set MIFLAGS "-i=mi" + +standard_testfile basics.c + +if {[gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" \ + executable {debug}] != "" } then { + untested mi-frame-regs.exp + return -1 +} + +# Return the address of the specified breakpoint. + +proc breakpoint_address {bpnum} { + global hex + global expect_out + global mi_gdb_prompt + + send_gdb "info breakpoint $bpnum\n" + gdb_expect { + -re ".*($hex).*$mi_gdb_prompt$" { + return $expect_out(1,string) + } + -re ".*$mi_gdb_prompt$" { + unresolved "get address of breakpoint $bpnum" + return "" + } + timeout { + unresolved "get address of breakpoint $bpnum (timeout)" + return "" + } + } +} + +# Test that a floating varobj representing $pc will provide the +# correct value via -var-update as the program stops at +# breakpoints in different functions. + +proc_with_prefix do_floating_varobj_test {} { + global srcfile + global hex + global expect_out + + gdb_exit + if {[mi_gdb_start]} then { + fail "couldn't start gdb" + return + } + + mi_run_to_main + + # Create a floating varobj for $pc. + mi_gdb_test "-var-create --thread 1 --frame 0 - @ \$pc" \ + "\\^done,.*value=\"$hex.*" \ + "create varobj for pc in frame 0" + + set nframes 4 + for {set i 1} {$i < $nframes} {incr i} { + + # Run to a breakpoint in each callee function in succession. + # Note that we can't use mi_runto because we need the + # breakpoint to be persistent, so we can use its address. + set bpnum [expr $i + 1] + mi_create_breakpoint \ + "basics.c:callee$i" \ + "insert breakpoint at basics.c:callee$i" \ + -number $bpnum -func callee$i -file ".*basics.c" + + mi_execute_to "exec-continue" "breakpoint-hit" \ + "callee$i" ".*" ".*${srcfile}" ".*" \ + { "" "disp=\"keep\"" } "breakpoint hit in callee$i" + + # Get the value of $pc from the floating varobj. + mi_gdb_test "-var-update 1 var1" \ + "\\^done,.*value=\"($hex) .*" \ + "-var-update for frame $i" + set pcval $expect_out(3,string) + + # Get the address of the current breakpoint. + set bpaddr [breakpoint_address $bpnum] + if {$bpaddr == ""} then { return } + + # Check that the addresses are the same. + gdb_assert [expr $bpaddr == $pcval] "\$pc equals address of breakpoint in callee$i" + } +} + +# Test that fixed varobjs representing $pc in different stack frames +# will provide the correct value via -var-update after the program +# counter changes (without substantially changing the stack). + +proc_with_prefix do_fixed_varobj_test {} { + global srcfile + global hex + + gdb_exit + if {[mi_gdb_start]} then { + fail "couldn't start gdb" + return + } + + mi_run_to_main + + # Run to the function 'callee3' so we have several frames. + mi_create_breakpoint "basics.c:callee3" \ + "insert breakpoint at basics.c:callee3" \ + -number 2 -func callee3 -file ".*basics.c" + + mi_execute_to "exec-continue" "breakpoint-hit" \ + "callee3" ".*" ".*${srcfile}" ".*" \ + { "" "disp=\"keep\"" } "breakpoint hit in callee3" + + # At the breakpoint in callee3 there are 4 frames. + # + # Create some varobj based on $pc in all frames. When we single + # step we expect the varobj for frame 0 to change, while the + # varobj for all other frames should be unchanged. + # + # Track in FIRST_UNCHANGING_VARNUM the number of the first varobj + # that is not in frame 0, varobj with a lower number we expect to + # change, while this and later varobj should not change. + # + # Track the number of the next varobj to be created in VARNUM. + set first_unchanging_varnum 0 + set varnum 1 + + for {set i 0} {$i < 4} {incr i} { + + if { $i == 1 } then { set first_unchanging_varnum $varnum } + + mi_gdb_test "-var-create --thread 1 --frame $i - \* \$pc" \ + "\\^done,.*value=\"$hex.*" \ + "create varobj for \$pc in frame $i" + incr varnum + + mi_gdb_test "-var-create --thread 1 --frame $i - \* \"global_zero + \$pc\"" \ + "\\^done,.*value=\"$hex.*" \ + "create varobj for 'global_zero + \$pc' in frame $i" + incr varnum + } + + # Step one instruction to change the program counter. + mi_execute_to "exec-next-instruction" "end-stepping-range" \ + "callee3" ".*" ".*${srcfile}" ".*" "" \ + "next instruction in callee3" + + # Check that -var-update reports that the values are changed for + # varobj in frame 0. + for {set i 1} {$i < $first_unchanging_varnum} {incr i} { + mi_gdb_test "-var-update 1 var$i" \ + "\\^done,(changelist=\\\[\{name=\"var$i\"\[^\\\]\]+\\\])" \ + "varobj var$i has changed" + } + + # Check that -var-update reports that the values are unchanged for + # varobj in frames other than 0. + for {set i $first_unchanging_varnum} {$i < $varnum} {incr i} { + mi_gdb_test "-var-update 1 var$i" \ + "\\^done,(changelist=\\\[\\\])" \ + "varobj var$i has not changed" + } +} + +do_fixed_varobj_test +do_floating_varobj_test diff --git a/gdb/testsuite/gdb.mi/mi-var-create-rtti.exp b/gdb/testsuite/gdb.mi/mi-var-create-rtti.exp index b61c495ab4..9ea5784bca 100644 --- a/gdb/testsuite/gdb.mi/mi-var-create-rtti.exp +++ b/gdb/testsuite/gdb.mi/mi-var-create-rtti.exp @@ -49,6 +49,9 @@ mi_gdb_test "-gdb-set print object on" ".*" # We use a explicit cast to (void *) as that is the # type that caused the bug this testcase is testing for. mi_gdb_test "-var-create sp1 * ((void*)\$sp)" \ - "\\^done,name=\"sp1\",numchild=\"0\",value=\"$hex\",type=\"void \\*\",has_more=\"0\"" \ + "\\^done,name=\"sp1\",numchild=\"0\",value=\"$hex\",type=\"void \\*\",thread-id=\"$decimal\",has_more=\"0\"" \ "-var-create sp1 * \$sp" +mi_gdb_test "-var-create sp2 @ ((void*)\$sp)" \ + "\\^done,name=\"sp2\",numchild=\"0\",value=\"$hex\",type=\"void \\*\",thread-id=\"$decimal\",has_more=\"0\"" \ + "-var-create sp2 @ \$sp" gdb_exit diff --git a/gdb/varobj.c b/gdb/varobj.c index 20dd09bd58..6c0145402a 100644 --- a/gdb/varobj.c +++ b/gdb/varobj.c @@ -311,7 +311,8 @@ varobj_create (const char *objname, } p = expression; - innermost_block.reset (); + innermost_block.reset (INNERMOST_BLOCK_FOR_SYMBOLS + | INNERMOST_BLOCK_FOR_REGISTERS); /* Wrap the call to parse expression, so we can return a sensible error. */ TRY