This is the mail archive of the gdb-cvs@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[binutils-gdb] Handle "p S::method()::static_var" in the C++ parser


https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=858be34c5a03bb8973679ebf00d360182434dc00

commit 858be34c5a03bb8973679ebf00d360182434dc00
Author: Pedro Alves <palves@redhat.com>
Date:   Mon Sep 4 20:21:15 2017 +0100

    Handle "p S::method()::static_var" in the C++ parser
    
    This commit makes "print S::method()::static_var" actually find the
    debug symbol for static_var.  Currently, you get:
    
      (gdb) print S::method()::static_var
      A syntax error in expression, near `'.
    
    Quoting the whole string would seemingly work before the previous
    patch that made GDB stop assuming int for no-debug-info variables:
    
      (gdb) p 'S::method()::static_var'
      $1 = 1
    
    ... except that's incorrect output, because:
    
      (gdb) ptype 'S::method()::static_var'
      type = <data variable, no debug info>
    
    The way to make it work correctly currently is by quoting the
    function/method part, like this:
    
      (gdb) print 'S::method()'::static_var
      $1 = {i1 = 1, i2 = 2, i3 = 3}
      (gdb) ptype 'S::method()'::static_var
      type = struct aggregate {
          int i1;
          int i2;
          int i3;
      }
    
    At least after the "stop assuming int" patch, this is what we
    now get:
    
      (gdb) p 'S::method()::static_var'
      'S::method()::static_var' has unknown type; cast it to its declared type
      (gdb) p (struct aggregate) 'S::method()::static_var'
      $1 = {i1 = 1, i2 = 2, i3 = 3}
    
    However, IMO, users shouldn't really have to care about any of this.
    GDB should Just Work, without quoting, IMO.
    
    So here's a patch that implements support for that in the C++ parser.
    With this patch, you now get:
    
      (gdb) p S::method()::S_M_s_var_aggregate
      $1 = {i1 = 1, i2 = 2, i3 = 3}
      (gdb) ptype S::method()::S_M_s_var_aggregate
      type = struct aggregate {
          int i1;
          int i2;
          int i3;
      }
    
    gdb/ChangeLog:
    2017-09-04  Pedro Alves  <palves@redhat.com>
    
    	(%type <voidval>): Add function_method.
    	* c-exp.y (exp): New production for calls with no arguments.
    	(function_method, function_method_void_or_typelist): New
    	productions.
    	(exp): New production for "method()::static_var".
    	* eval.c (evaluate_subexp_standard): Handle OP_FUNC_STATIC_VAR.
    	* expprint.c (print_subexp_standard, dump_subexp_body_standard):
    	Handle OP_FUNC_STATIC_VAR.
    	* parse.c (operator_length_standard):
    	Handle OP_FUNC_STATIC_VAR.
    	* std-operator.def (OP_FUNC_STATIC_VAR): New.
    
    gdb/testsuite/ChangeLog:
    2017-09-04  Pedro Alves  <palves@redhat.com>
    
    	* gdb.base/local-static.c: New.
    	* gdb.base/local-static.cc: New.
    	* gdb.base/local-static.exp:  New.

Diff:
---
 gdb/ChangeLog                         |  13 ++++
 gdb/c-exp.y                           |  43 +++++++++-
 gdb/eval.c                            |  21 +++++
 gdb/expprint.c                        |  18 +++++
 gdb/parse.c                           |   6 ++
 gdb/std-operator.def                  |  21 +++++
 gdb/testsuite/ChangeLog               |   6 ++
 gdb/testsuite/gdb.cp/local-static.c   | 142 ++++++++++++++++++++++++++++++++++
 gdb/testsuite/gdb.cp/local-static.cc  |   1 +
 gdb/testsuite/gdb.cp/local-static.exp | 136 ++++++++++++++++++++++++++++++++
 10 files changed, 405 insertions(+), 2 deletions(-)

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index cc6f76d..a7b4e57 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,5 +1,18 @@
 2017-09-04  Pedro Alves  <palves@redhat.com>
 
+	(%type <voidval>): Add function_method.
+	* c-exp.y (exp): New production for calls with no arguments.
+	(function_method, function_method_void_or_typelist): New
+	productions.
+	(exp): New production for "method()::static_var".
+	* eval.c (evaluate_subexp_standard): Handle OP_FUNC_STATIC_VAR.
+	* expprint.c (print_subexp_standard, dump_subexp_body_standard):
+	Handle OP_FUNC_STATIC_VAR.
+	* parse.c (operator_length_standard):
+	Handle OP_FUNC_STATIC_VAR.
+
+2017-09-04  Pedro Alves  <palves@redhat.com>
+
 	* eval.c (evaluate_subexp_standard): Remove UNOP_MEMVAL_TLS
 	handling.
 	* expprint.c (print_subexp_standard, dump_subexp_body_standard):
diff --git a/gdb/c-exp.y b/gdb/c-exp.y
index a1f9fee..f7f098b 100644
--- a/gdb/c-exp.y
+++ b/gdb/c-exp.y
@@ -127,7 +127,7 @@ static void c_print_token (FILE *file, int type, YYSTYPE value);
 #endif
 %}
 
-%type <voidval> exp exp1 type_exp start variable qualified_name lcurly
+%type <voidval> exp exp1 type_exp start variable qualified_name lcurly function_method
 %type <lval> rcurly
 %type <tval> type typebase
 %type <tvec> nonempty_typelist func_mod parameter_typelist
@@ -498,6 +498,18 @@ exp	:	exp '('
 			  write_exp_elt_opcode (pstate, OP_FUNCALL); }
 	;
 
+/* This is here to disambiguate with the production for
+   "func()::static_var" further below, which uses
+   function_method_void.  */
+exp	:	exp '(' ')' %prec ARROW
+			{ start_arglist ();
+			  write_exp_elt_opcode (pstate, OP_FUNCALL);
+			  write_exp_elt_longcst (pstate,
+						 (LONGEST) end_arglist ());
+			  write_exp_elt_opcode (pstate, OP_FUNCALL); }
+	;
+
+
 exp	:	UNKNOWN_CPP_NAME '('
 			{
 			  /* This could potentially be a an argument defined
@@ -539,7 +551,7 @@ arglist	:	arglist ',' exp   %prec ABOVE_COMMA
 			{ arglist_len++; }
 	;
 
-exp     :       exp '(' parameter_typelist ')' const_or_volatile
+function_method:       exp '(' parameter_typelist ')' const_or_volatile
 			{ int i;
 			  VEC (type_ptr) *type_list = $3;
 			  struct type *type_elt;
@@ -557,6 +569,33 @@ exp     :       exp '(' parameter_typelist ')' const_or_volatile
 			}
 	;
 
+function_method_void:	    exp '(' ')' const_or_volatile
+		       { write_exp_elt_opcode (pstate, TYPE_INSTANCE);
+			 write_exp_elt_longcst (pstate, 0);
+			 write_exp_elt_longcst (pstate, 0);
+			 write_exp_elt_opcode (pstate, TYPE_INSTANCE);
+		       }
+       ;
+
+exp     :       function_method
+	;
+
+/* Normally we must interpret "func()" as a function call, instead of
+   a type.  The user needs to write func(void) to disambiguate.
+   However, in the "func()::static_var" case, there's no
+   ambiguity.  */
+function_method_void_or_typelist: function_method
+	|               function_method_void
+	;
+
+exp     :       function_method_void_or_typelist COLONCOLON name
+			{
+			  write_exp_elt_opcode (pstate, OP_FUNC_STATIC_VAR);
+			  write_exp_string (pstate, $3);
+			  write_exp_elt_opcode (pstate, OP_FUNC_STATIC_VAR);
+			}
+	;
+
 rcurly	:	'}'
 			{ $$ = end_arglist () - 1; }
 	;
diff --git a/gdb/eval.c b/gdb/eval.c
index e70328b..7d129f0 100644
--- a/gdb/eval.c
+++ b/gdb/eval.c
@@ -847,6 +847,27 @@ evaluate_subexp_standard (struct type *expect_type,
 	return SYMBOL_COMPUTED_OPS (sym)->read_variable_at_entry (sym, frame);
       }
 
+    case OP_FUNC_STATIC_VAR:
+      tem = longest_to_int (exp->elts[pc + 1].longconst);
+      (*pos) += 3 + BYTES_TO_EXP_ELEM (tem + 1);
+      if (noside == EVAL_SKIP)
+	return eval_skip_value (exp);
+
+      {
+	value *func = evaluate_subexp_standard (NULL, exp, pos, noside);
+	CORE_ADDR addr = value_address (func);
+
+	const block *blk = block_for_pc (addr);
+	const char *var = &exp->elts[pc + 2].string;
+
+	struct block_symbol sym = lookup_symbol (var, blk, VAR_DOMAIN, NULL);
+
+	if (sym.symbol == NULL)
+	  error (_("No symbol \"%s\" in specified context."), var);
+
+	return evaluate_var_value (noside, sym.block, sym.symbol);
+      }
+
     case OP_LAST:
       (*pos) += 2;
       return
diff --git a/gdb/expprint.c b/gdb/expprint.c
index 0697a77..fad20e8 100644
--- a/gdb/expprint.c
+++ b/gdb/expprint.c
@@ -141,6 +141,14 @@ print_subexp_standard (struct expression *exp, int *pos,
       }
       return;
 
+    case OP_FUNC_STATIC_VAR:
+      {
+	tem = longest_to_int (exp->elts[pc + 1].longconst);
+	(*pos) += 3 + BYTES_TO_EXP_ELEM (tem + 1);
+	fputs_filtered (&exp->elts[pc + 1].string, stream);
+      }
+      return;
+
     case OP_VAR_ENTRY_VALUE:
       {
 	(*pos) += 2;
@@ -999,6 +1007,16 @@ dump_subexp_body_standard (struct expression *exp,
 	elt += 4 + BYTES_TO_EXP_ELEM (len + 1);
       }
       break;
+
+    case OP_FUNC_STATIC_VAR:
+      {
+	int len = longest_to_int (exp->elts[elt].longconst);
+	const char *var_name = &exp->elts[elt + 1].string;
+	fprintf_filtered (stream, "Field name: `%.*s'", len, var_name);
+	elt += 3 + BYTES_TO_EXP_ELEM (len + 1);
+      }
+      break;
+
     case TYPE_INSTANCE:
       {
 	LONGEST len;
diff --git a/gdb/parse.c b/gdb/parse.c
index 3db9735..e1fa229 100644
--- a/gdb/parse.c
+++ b/gdb/parse.c
@@ -901,6 +901,12 @@ operator_length_standard (const struct expression *expr, int endpos,
       oplen = 4;
       break;
 
+    case OP_FUNC_STATIC_VAR:
+      oplen = longest_to_int (expr->elts[endpos - 2].longconst);
+      oplen = 4 + BYTES_TO_EXP_ELEM (oplen + 1);
+      args = 1;
+      break;
+
     case OP_TYPE:
     case OP_BOOL:
     case OP_LAST:
diff --git a/gdb/std-operator.def b/gdb/std-operator.def
index 3eed2cc..56a9af9 100644
--- a/gdb/std-operator.def
+++ b/gdb/std-operator.def
@@ -280,6 +280,27 @@ OP (OP_OBJC_SELECTOR)
    a string, which, of course, is variable length.  */
 OP (OP_SCOPE)
 
+/* OP_FUNC_STATIC_VAR refers to a function local static variable.  The
+   function is taken from the following subexpression.  The length of
+   the variable name as a string follows the opcode, followed by
+   BYTES_TO_EXP_ELEM(length) elements containing the data of the
+   string, followed by the length again and the opcode again.
+
+   Note this is used by C++, but not C.  The C parser handles local
+   static variables in the parser directly.  Also, this is only used
+   in C++ if the function/method name is not quoted, like e.g.:
+
+     p S:method()::var
+     p S:method() const::var
+
+   If the function/method is quoted like instead:
+
+     p 'S:method() const'::var
+
+   then the C-specific handling directly in the parser takes over (see
+   "block/variable productions).  */
+OP (OP_FUNC_STATIC_VAR)
+
 /* OP_TYPE is for parsing types, and used with the "ptype" command
    so we can look up types that are qualified by scope, either with
    the GDB "::" operator, or the Modula-2 '.' operator.  */
diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog
index 072423d..58ff7f3 100644
--- a/gdb/testsuite/ChangeLog
+++ b/gdb/testsuite/ChangeLog
@@ -1,5 +1,11 @@
 2017-09-04  Pedro Alves  <palves@redhat.com>
 
+	* gdb.base/local-static.c: New.
+	* gdb.base/local-static.cc: New.
+	* gdb.base/local-static.exp:  New.
+
+2017-09-04  Pedro Alves  <palves@redhat.com>
+
 	* gdb.asm/asm-source.exp: Add casts to int.
 	* gdb.base/nodebug.c (dataglobal8, dataglobal32_1, dataglobal32_2)
 	(dataglobal64_1, dataglobal64_2): New globals.
diff --git a/gdb/testsuite/gdb.cp/local-static.c b/gdb/testsuite/gdb.cp/local-static.c
new file mode 100644
index 0000000..5bfff8d
--- /dev/null
+++ b/gdb/testsuite/gdb.cp/local-static.c
@@ -0,0 +1,142 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2002-2017 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/>.  */
+
+struct aggregate
+{
+  int i1;
+  int i2;
+  int i3;
+};
+
+void keepalive_float (double *var) { }
+void keepalive_int (int *var) { }
+void keepalive_aggregate (struct aggregate *var) { }
+
+#define PREFIXIFY(PREFIX, VAR)			\
+  PREFIX ## _ ## VAR
+
+#define DEF_STATICS(PREFIX)						\
+  static int PREFIXIFY(PREFIX, s_var_int) = 4;				\
+  static double PREFIXIFY(PREFIX, s_var_float) = 3.14f;			\
+  static struct aggregate PREFIXIFY(PREFIX, s_var_aggregate)		\
+    = { 1, 2, 3 };							\
+									\
+  keepalive_int (&PREFIXIFY(PREFIX, s_var_int));			\
+  keepalive_float (&PREFIXIFY(PREFIX, s_var_float));			\
+  keepalive_aggregate (&PREFIXIFY(PREFIX, s_var_aggregate));
+
+#ifdef __cplusplus
+
+struct S
+{
+  void inline_method ()
+  {
+    DEF_STATICS (S_IM);
+  }
+  static void static_inline_method ()
+  {
+    DEF_STATICS (S_SIM);
+  }
+
+  void method ();
+  static void static_method ();
+};
+
+S s;
+
+void
+S::method ()
+{
+  DEF_STATICS (S_M);
+}
+
+void
+S::static_method ()
+{
+  DEF_STATICS (S_SM);
+}
+
+template <typename T>
+struct S2
+{
+  void method ();
+  static void static_method ();
+
+  void inline_method ()
+  {
+    DEF_STATICS (S2_IM);
+  }
+
+  static void static_inline_method ()
+  {
+    DEF_STATICS (S2_SIM);
+  }
+};
+
+template<typename T>
+void
+S2<T>::method ()
+{
+  DEF_STATICS (S2_M);
+}
+
+template<typename T>
+void
+S2<T>::static_method ()
+{
+  DEF_STATICS (S2_SM);
+}
+
+S2<int> s2;
+
+#endif
+
+void
+free_func (void)
+{
+  DEF_STATICS (FF);
+}
+
+static inline void
+free_inline_func (void)
+{
+  DEF_STATICS (FIF);
+}
+
+int
+main ()
+{
+  for (int i = 0; i < 1000; i++)
+    {
+      free_func ();
+      free_inline_func ();
+
+#ifdef __cplusplus
+      s.method ();
+      s.inline_method ();
+      S::static_method ();
+      S::static_inline_method ();
+
+      s2.method ();
+      s2.inline_method ();
+      S2<int>::static_method ();
+      S2<int>::static_inline_method ();
+#endif
+    }
+
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.cp/local-static.cc b/gdb/testsuite/gdb.cp/local-static.cc
new file mode 100644
index 0000000..f2bffbe
--- /dev/null
+++ b/gdb/testsuite/gdb.cp/local-static.cc
@@ -0,0 +1 @@
+#include "local-static.c"
diff --git a/gdb/testsuite/gdb.cp/local-static.exp b/gdb/testsuite/gdb.cp/local-static.exp
new file mode 100644
index 0000000..ba0c5ef
--- /dev/null
+++ b/gdb/testsuite/gdb.cp/local-static.exp
@@ -0,0 +1,136 @@
+# Copyright 2017 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/>.
+
+# Tests for function local static variables, both C and C++.
+
+# This file is part of the gdb testsuite.
+
+standard_testfile .c
+
+# A list of scopes that have the static variables that we want to
+# print.  Each entry has, in order, the scope/function name, and the
+# prefix used by the static variables.  (The prefix exists to make it
+# easier to debug the test if something goes wrong.)
+
+     #SCOPE				#PREFIX
+set cxx_scopes_list {
+    {"S::method()"			"S_M"}
+    {"S::static_method()"		"S_SM"}
+    {"S::inline_method()"		"S_IM"}
+    {"S::static_inline_method()"	"S_SIM"}
+    {"S2<int>::method()"		"S2_M"}
+    {"S2<int>::static_method()"		"S2_SM"}
+    {"S2<int>::inline_method()"		"S2_IM"}
+    {"S2<int>::static_inline_method()"	"S2_SIM"}
+    {"free_func()"			"FF"}
+    {"free_inline_func()"		"FIF"}
+}
+
+set c_scopes_list {
+    {"free_func"			"FF"}
+    {"free_inline_func"			"FIF"}
+}
+
+# A list of all the static varibles defined in each scope.  The first
+# column is the name of the variable, without the prefix, and the
+# second column is a regex matching what printing the variable should
+# output.
+
+     #VAR		#PRINT
+set vars_list {
+    {"s_var_int"	" = 4"}
+    {"s_var_float"	" = 3.14.*"}
+    {"s_var_aggregate"	" = \\{i1 = 1, i2 = 2, i3 = 3\\}"}
+}
+
+proc do_test {lang} {
+    global c_scopes_list
+    global cxx_scopes_list
+    global vars_list
+    global srcfile testfile
+
+    set options {debug}
+
+    if {$lang == "c++"} {
+	if { [skip_cplus_tests] } {
+	    return
+	}
+	lappend options $lang
+	set src ${srcfile}c
+    } else {
+	set src ${srcfile}
+    }
+
+    if {[prepare_for_testing "failed to prepare" $testfile-$lang \
+	     [list $src] $options]} {
+	return -1
+    }
+
+    if ![runto_main] then {
+	fail "couldn't run to breakpoint"
+	return
+    }
+
+    gdb_test "show language" " currently [string_to_regexp $lang]\"\\."
+
+    if {$lang == "c"} {
+	set scopes_list $c_scopes_list
+    } else {
+	set scopes_list $cxx_scopes_list
+    }
+
+    # Print each variable using these syntaxes:
+    #
+    #  'func()'::var
+    #  func()::var
+
+    foreach scope_line $scopes_list  {
+	set scope [lindex $scope_line 0]
+	set var_prefix [lindex $scope_line 1]
+	foreach var_line $vars_list {
+	    set var [lindex $var_line 0]
+	    set print_re [lindex $var_line 1]
+
+	    gdb_test "print '${scope}'::${var_prefix}_${var}" $print_re
+	    gdb_test "print ${scope}::${var_prefix}_${var}" $print_re
+	}
+    }
+
+    # Now run to each function, and print its variables using the
+    # localy-visible name.
+    foreach scope_line $scopes_list {
+	set scope [lindex $scope_line 0]
+	set var_prefix [lindex $scope_line 1]
+
+	with_test_prefix "$scope" {
+	    delete_breakpoints
+	    gdb_breakpoint "$scope"
+	    gdb_continue_to_breakpoint "$scope"
+
+	    foreach var_line $vars_list {
+		set var [lindex $var_line 0]
+		set print_re [lindex $var_line 1]
+
+		gdb_test "print ${var_prefix}_${var}" $print_re
+	    }
+	}
+    }
+}
+
+foreach lang {"c" "c++"} {
+    with_test_prefix $lang {
+	do_test $lang
+    }
+}


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]