]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
Fix -var-update for registers in frames 1 and up users/palves/fix-var-update-for-registers
authorPedro Alves <palves@redhat.com>
Thu, 11 Aug 2016 18:14:11 +0000 (19:14 +0100)
committerPedro Alves <palves@redhat.com>
Thu, 11 Aug 2016 18:14:11 +0000 (19:14 +0100)
XXX palves:

Watchpoints on locals are quite similar in principle with varobjs on
locals.  Likewise wathpoints/varobjs on registers.

So this actually makes "watch $pc" in non-current frames be a local
watchpoint.

So

 up
 watch $pc
 down
 continue

now does the "right" thing.  I'm not sure this is really right,
though.

Haven't done anything on the varobjs side.

gdb/testsuite/ChangeLog:
2016-06-13  Don Breazeal  <dbreazea@my.domain.org>

* gdb.ada/mi_interface.exp: Add thread-id field to expected
output of -var-create and -var-list-children.
* gdb.ada/mi_var_array.exp: Add thread-id field to expected
output of -var-list-children.
* gdb.mi/mi-break.exp (test_error): Add thread-id field to
expected output of -var-create.
* gdb.mi/mi-frame-regs.exp: New test script.
* gdb.mi/mi-var-cmd.exp: Add thread-id field to expected
output of -var-create.
* gdb.mi/mi-var-create-rtti.exp: Likewise.

gdb/ChangeLog:
2016-06-13  Don Breazeal  <donb@codesourcery.com>
    Andrew Burgess <andrew.burgess@embecosm.com>

* varobj.c (varobj_create): Initialize innermost_block to
the global block instead of NULL.
(new_root_variable): Initialize the thread_id and next
fields.
(value_of_root_1): Set within_scope if the variable's
valid_block field is the global block.

gdb/breakpoint.c
gdb/testsuite/gdb.ada/mi_interface.exp
gdb/testsuite/gdb.ada/mi_var_array.exp
gdb/testsuite/gdb.mi/mi-break.exp
gdb/testsuite/gdb.mi/mi-frame-regs.exp [new file with mode: 0644]
gdb/testsuite/gdb.mi/mi-var-cmd.exp
gdb/testsuite/gdb.mi/mi-var-create-rtti.exp
gdb/varobj.c

index 6bb6bbfbef9ba573479b4fda575442026e9d3761..7ea55c2484f0f7e0baab1961b60e2b7b591192f4 100644 (file)
@@ -1867,7 +1867,7 @@ update_watchpoint (struct watchpoint *b, int reparse)
   frame_saved = 0;
 
   /* Determine if the watchpoint is within scope.  */
-  if (b->exp_valid_block == NULL)
+  if (!frame_id_p (b->watchpoint_frame))
     within_current_scope = 1;
   else
     {
@@ -4196,7 +4196,7 @@ breakpoint_init_inferior (enum inf_context context)
          struct watchpoint *w = (struct watchpoint *) b;
 
          /* Likewise for watchpoints on local expressions.  */
-         if (w->exp_valid_block != NULL)
+         if (frame_id_p (w->watchpoint_frame))
            delete_breakpoint (b);
          else
            {
@@ -5145,7 +5145,7 @@ watchpoint_check (void *p)
   if (!watchpoint_in_thread_scope (b))
     return WP_IGNORE;
 
-  if (b->exp_valid_block == NULL)
+  if (!frame_id_p (b->watchpoint_frame))
     within_current_scope = 1;
   else
     {
@@ -5170,7 +5170,7 @@ watchpoint_check (void *p)
 
       /* If we've gotten confused in the unwinder, we might have
         returned a frame that can't describe this variable.  */
-      if (within_current_scope)
+      if (within_current_scope && b->exp_valid_block != NULL)
        {
          struct symbol *function;
 
@@ -10529,13 +10529,16 @@ break_range_command (char *arg, int from_tty)
   update_global_location_list (UGLL_MAY_INSERT);
 }
 
+typedef int (for_each_exp_symbol_callback) (const struct expression *exp, int i, void *data);
+
 /*  Return non-zero if EXP is verified as constant.  Returned zero
     means EXP is variable.  Also the constant detection may fail for
     some constant expressions and in such case still falsely return
     zero.  */
 
 static int
-watchpoint_exp_is_const (const struct expression *exp)
+for_each_expression_symbol (const struct expression *exp,
+                           for_each_exp_symbol_callback *cb, void *cb_data)
 {
   int i = exp->nelts;
 
@@ -10613,34 +10616,86 @@ watchpoint_exp_is_const (const struct expression *exp)
          break;
 
        case OP_VAR_VALUE:
-         /* Check whether the associated symbol is a constant.
-
-            We use SYMBOL_CLASS rather than TYPE_CONST because it's
-            possible that a buggy compiler could mark a variable as
-            constant even when it is not, and TYPE_CONST would return
-            true in this case, while SYMBOL_CLASS wouldn't.
-
-            We also have to check for function symbols because they
-            are always constant.  */
-         {
-           struct symbol *s = exp->elts[i + 2].symbol;
-
-           if (SYMBOL_CLASS (s) != LOC_BLOCK
-               && SYMBOL_CLASS (s) != LOC_CONST
-               && SYMBOL_CLASS (s) != LOC_CONST_BYTES)
-             return 0;
-           break;
-         }
+         if (cb (exp, i, cb_data))
+           return 1;
+         break;
 
        /* The default action is to return 0 because we are using
           the optimistic approach here: If we don't know something,
           then it is not a constant.  */
        default:
-         return 0;
+         return 1;
        }
     }
 
-  return 1;
+  return 0;
+}
+
+static int
+exp_element_symbol_is_const (const struct expression *exp, int i,
+                            void *cb_data)
+{
+  if (exp->elts[i].opcode == OP_VAR_VALUE)
+    {
+      /* Check whether the associated symbol is a constant.
+
+        We use SYMBOL_CLASS rather than TYPE_CONST because it's
+        possible that a buggy compiler could mark a variable as
+        constant even when it is not, and TYPE_CONST would return
+        true in this case, while SYMBOL_CLASS wouldn't.
+
+        We also have to check for function symbols because they
+        are always constant.  */
+      struct symbol *s = exp->elts[i + 2].symbol;
+
+      if (SYMBOL_CLASS (s) != LOC_BLOCK
+         && SYMBOL_CLASS (s) != LOC_CONST
+         && SYMBOL_CLASS (s) != LOC_CONST_BYTES)
+       {
+         /* Stop looking.  */
+         return 1;
+       }
+    }
+
+  return 0;
+}
+
+static int
+watchpoint_exp_is_const (struct expression *exp)
+{
+  if (!for_each_expression_symbol (exp, exp_element_symbol_is_const, NULL))
+    return 1;
+  return 0;
+}
+
+
+static int
+exp_element_symbol_needs_frame (const struct expression *exp, int i,
+                               void *cb_data)
+{
+  switch (exp->elts[i].opcode)
+    {
+    case OP_VAR_VALUE:
+      {
+       struct symbol *s = exp->elts[i + 2].symbol;
+
+       if (symbol_read_needs_frame (s))
+         return 1;
+       break;
+      }
+    case OP_REGISTER:
+      return 1;
+    }
+
+  return 0;
+ }
+
+static int
+expression_needs_frame (const struct expression *exp)
+{
+  if (for_each_expression_symbol (exp, exp_element_symbol_needs_frame, NULL))
+    return 1;
+  return 0;
 }
 
 /* Implement the "dtor" breakpoint_ops method for watchpoints.  */
@@ -11331,13 +11386,16 @@ watch_command_1 (const char *arg, int accessflag, int from_tty,
   if (*tok)
     error (_("Junk at end of command."));
 
-  frame = block_innermost_frame (exp_valid_block);
+  if (exp_valid_block == NULL && expression_needs_frame (exp))
+    frame = get_selected_frame (NULL);
+  else
+    frame = block_innermost_frame (exp_valid_block);
 
   /* If the expression is "local", then set up a "watchpoint scope"
      breakpoint at the point where we've left the scope of the watchpoint
      expression.  Create the scope breakpoint before the watchpoint, so
      that we will encounter it first in bpstat_stop_status.  */
-  if (exp_valid_block && frame)
+  if (frame != NULL)
     {
       if (frame_id_p (frame_unwind_caller_id (frame)))
        {
index 6000ec862a1881816977c75ebd5c77b8b2d49802..b948cd5a7270027c9c0cd083977e8f2f904e186b 100644 (file)
@@ -44,9 +44,9 @@ mi_continue_to_line \
     "stop at start of main Ada procedure"
 
 mi_gdb_test "-var-create ggg1 * ggg1" \
-    "\\^done,name=\"ggg1\",numchild=\"1\",value=\"{...}\",type=\"<ref> pck.gadatatype\",has_more=\"0\"" \
+    "\\^done,name=\"ggg1\",numchild=\"1\",value=\"{...}\",type=\"<ref> pck.gadatatype\",thread-id=\"1\",has_more=\"0\"" \
     "Create ggg1 varobj"
 
 mi_gdb_test "-var-list-children 1 ggg1" \
-    "\\^done,numchild=\"1\",children=\\\[child={name=\"ggg1.i\",exp=\"i\",numchild=\"0\",value=\"42\",type=\"integer\"}\\\],has_more=\"0\"" \
+    "\\^done,numchild=\"1\",children=\\\[child={name=\"ggg1.i\",exp=\"i\",numchild=\"0\",value=\"42\",type=\"integer\",thread-id=\"1\"}\\\],has_more=\"0\"" \
     "list ggg1's children"
index c648e7e02ef3a6fb6428a3c18720a1ef2fc12af3..c02d4c93ce3df63b9296b7d32278aed825f01c32 100644 (file)
@@ -48,5 +48,5 @@ mi_gdb_test "-var-create vta * vta" \
     "Create bt varobj"
 
 mi_gdb_test "-var-list-children vta" \
-    "\\^done,numchild=\"2\",children=\\\[child={name=\"vta.n\",exp=\"n\",numchild=\"0\",type=\"bar\\.int\"},child={name=\"vta.f\",exp=\"f\",numchild=\"0\",type=\"array \\(1 .. n\\) of character\"}\\\],.*" \
+    "\\^done,numchild=\"2\",children=\\\[child={name=\"vta.n\",exp=\"n\",numchild=\"0\",type=\"bar\\.int\",thread-id=\"1\"},child={name=\"vta.f\",exp=\"f\",numchild=\"0\",type=\"array \\(1 .. n\\) of character\",thread-id=\"1\"}\\\],.*" \
     "list vta's children"
index 00293d7f44354c86eedf4bf952a44b289b40cb8c..3a5dd69e42a51d7e3787f709df3dcd4f3b900b39 100644 (file)
@@ -204,7 +204,7 @@ proc test_error {} {
     # containing function call, the internal breakpoint created to handle
     # function call would be reported, messing up MI output.
     mi_gdb_test "-var-create V * return_1()" \
-        "\\^done,name=\"V\",numchild=\"0\",value=\"1\",type=\"int\",has_more=\"0\"" \
+        "\\^done,name=\"V\",numchild=\"0\",value=\"1\",type=\"int\",thread-id=\"1\",has_more=\"0\"" \
         "create varobj for function call"
 
     mi_gdb_test "-var-update *" \
diff --git a/gdb/testsuite/gdb.mi/mi-frame-regs.exp b/gdb/testsuite/gdb.mi/mi-frame-regs.exp
new file mode 100644 (file)
index 0000000..45f81d6
--- /dev/null
@@ -0,0 +1,183 @@
+# Copyright 1999-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/>.
+
+# 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$" {
+           return ""
+       }
+       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 do_floating_varobj_test {} {
+    global srcfile
+    global hex
+    global expect_out
+
+    gdb_exit
+    if {[mi_gdb_start]} then {
+       continue
+    }
+
+    with_test_prefix "floating" {
+       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"
+
+           # 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 {
+               unresolved "get address of breakpoint $bpnum"
+               return
+           }
+
+           # Check that the addresses are the same.
+           if {[expr $bpaddr != $pcval]} then {
+               fail "\$pc does not equal address of breakpoint"
+           } else {
+               pass "\$pc equals address of breakpoint"
+           }
+       }
+    }
+}
+
+# Create a varobj for the pc register in each of the frames other
+# than frame 0.
+
+proc var_create_regs {nframes} {
+    global hex
+
+    for {set i 1} {$i < $nframes} {incr i} {
+       mi_gdb_test "-var-create --thread 1 --frame $i - \* \$pc" \
+                   "\\^done,.*value=\"$hex.*" \
+                   "create varobj for pc in frame $i"
+    }
+}
+
+# Check that -var-update reports that the value of the pc register
+# for each of the frames 1 and above is reported as unchanged.
+
+proc var_update_regs {nframes} {
+
+    for {set i 1} {$i < $nframes} {incr i} {
+       mi_gdb_test "-var-update 1 var$i" \
+                   "\\^done,(changelist=\\\[\\\])" \
+                   "pc unchanged in -var-update for frame $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 do_fixed_varobj_test {} {
+    global srcfile
+
+    gdb_exit
+    if {[mi_gdb_start]} then {
+       continue
+    }
+
+    with_test_prefix "fixed" {
+       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"
+
+       # At the breakpoint in callee3 there are 4 frames.  Create a
+       # varobj for the pc in each of frames 1 and above.
+       set nframes 4
+       var_create_regs $nframes
+
+       # 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 unchanged.
+       var_update_regs $nframes
+    }
+}
+
+do_fixed_varobj_test
+do_floating_varobj_test
+
+mi_gdb_exit
+return 0
index 558cd6c565461e0ddcd254f46a179fac3f864b6f..68e3cf87e12f4fc27ff732ff8a6fe2e57c511669 100644 (file)
@@ -381,7 +381,7 @@ mi_gdb_test "-var-update *" \
        "assign same value to func (update)"
 
 mi_gdb_test "-var-create array_ptr * array_ptr" \
-       "\\^done,name=\"array_ptr\",numchild=\"1\",value=\"$hex <array>\",type=\"int \\*\",has_more=\"0\"" \
+       "\\^done,name=\"array_ptr\",numchild=\"1\",value=\"$hex <array>\",type=\"int \\*\",thread-id=\"1\",has_more=\"0\"" \
        "create global variable array_ptr"
 
 mi_gdb_test "-var-assign array_ptr array2" \
@@ -608,7 +608,7 @@ mi_check_varobj_value F 7 "check F inside callee"
 # A varobj we fail to read during -var-update should be considered
 # out of scope.
 mi_gdb_test "-var-create null_ptr * **0" \
-    {\^done,name="null_ptr",numchild="0",value=".*",type="int",has_more="0"} \
+    {\^done,name="null_ptr",numchild="0",value=".*",type="int",thread-id="1"has_more="0"} \
     "create null_ptr"
 
 # Allow this to succeed, if address zero is readable, although it
index 3bcb36cdcce3a5f9f3467246fd072c50dba24e4f..a8cc76f7913e782fa8ebe090b20023c802292b74 100644 (file)
@@ -49,6 +49,6 @@ 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=\"1\",has_more=\"0\"" \
            "-var-create sp1 * \$sp"
 gdb_exit
index fb1349a85dba60374e6acf44f0f0f98489ae2b50..7cab707da8da8131d738e4a77a3a10b7f56a722d 100644 (file)
@@ -323,6 +323,7 @@ varobj_create (char *objname,
        }
 
       p = expression;
+      //      innermost_block = block_global_block (block);
       innermost_block = NULL;
       /* Wrap the call to parse expression, so we can 
          return a sensible error.  */
@@ -2103,6 +2104,8 @@ new_root_variable (void)
   var->root->floating = 0;
   var->root->rootvar = NULL;
   var->root->is_valid = 1;
+  var->root->thread_id = 0;
+  var->root->next = NULL;
 
   return var;
 }