summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPedro Alves <palves@redhat.com>2017-07-12 13:32:10 +0100
committerPedro Alves <palves@redhat.com>2017-07-13 16:20:43 +0100
commite0a8936aa4a2f61c9f71a0d307dd937b77eeb6e4 (patch)
tree6f404cc7701d5420ebbd0c1b331e292bacd62d79
parent3ce9de7516c54ff3c5279976d0915550905c85d1 (diff)
downloadbinutils-gdb-e0a8936aa4a2f61c9f71a0d307dd937b77eeb6e4.tar.gz
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: yyyy-mm-dd 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: yyyy-mm-dd Pedro Alves <palves@redhat.com> * gdb.base/local-static.c: New. * gdb.base/local-static.cc: New. * gdb.base/local-static.exp: New.
-rw-r--r--gdb/c-exp.y43
-rw-r--r--gdb/eval.c21
-rw-r--r--gdb/expprint.c18
-rw-r--r--gdb/parse.c6
-rw-r--r--gdb/std-operator.def21
-rw-r--r--gdb/testsuite/gdb.cp/local-static.c142
-rw-r--r--gdb/testsuite/gdb.cp/local-static.cc1
-rw-r--r--gdb/testsuite/gdb.cp/local-static.exp136
8 files changed, 386 insertions, 2 deletions
diff --git a/gdb/c-exp.y b/gdb/c-exp.y
index 48b5a537866..51024703ba6 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 e70328ba1dd..7d129f0e774 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 0697a773b9d..fad20e81519 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 ee6b6f72cc4..97ddc7281de 100644
--- a/gdb/parse.c
+++ b/gdb/parse.c
@@ -900,6 +900,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 3eed2cc3dff..56a9af91150 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/gdb.cp/local-static.c b/gdb/testsuite/gdb.cp/local-static.c
new file mode 100644
index 00000000000..5bfff8d662d
--- /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 00000000000..f2bffbef3a9
--- /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 00000000000..ba0c5ef13e5
--- /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
+ }
+}