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.
This commit is contained in:
Pedro Alves 2017-09-04 20:21:15 +01:00
parent dd5901a6a5
commit 858be34c5a
10 changed files with 405 additions and 2 deletions

View File

@ -1,3 +1,16 @@
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> 2017-09-04 Pedro Alves <palves@redhat.com>
* eval.c (evaluate_subexp_standard): Remove UNOP_MEMVAL_TLS * eval.c (evaluate_subexp_standard): Remove UNOP_MEMVAL_TLS

View File

@ -127,7 +127,7 @@ static void c_print_token (FILE *file, int type, YYSTYPE value);
#endif #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 <lval> rcurly
%type <tval> type typebase %type <tval> type typebase
%type <tvec> nonempty_typelist func_mod parameter_typelist %type <tvec> nonempty_typelist func_mod parameter_typelist
@ -498,6 +498,18 @@ exp : exp '('
write_exp_elt_opcode (pstate, OP_FUNCALL); } 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 '(' exp : UNKNOWN_CPP_NAME '('
{ {
/* This could potentially be a an argument defined /* This could potentially be a an argument defined
@ -539,7 +551,7 @@ arglist : arglist ',' exp %prec ABOVE_COMMA
{ arglist_len++; } { arglist_len++; }
; ;
exp : exp '(' parameter_typelist ')' const_or_volatile function_method: exp '(' parameter_typelist ')' const_or_volatile
{ int i; { int i;
VEC (type_ptr) *type_list = $3; VEC (type_ptr) *type_list = $3;
struct type *type_elt; 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 : '}' rcurly : '}'
{ $$ = end_arglist () - 1; } { $$ = end_arglist () - 1; }
; ;

View File

@ -847,6 +847,27 @@ evaluate_subexp_standard (struct type *expect_type,
return SYMBOL_COMPUTED_OPS (sym)->read_variable_at_entry (sym, frame); 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: case OP_LAST:
(*pos) += 2; (*pos) += 2;
return return

View File

@ -141,6 +141,14 @@ print_subexp_standard (struct expression *exp, int *pos,
} }
return; 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: case OP_VAR_ENTRY_VALUE:
{ {
(*pos) += 2; (*pos) += 2;
@ -999,6 +1007,16 @@ dump_subexp_body_standard (struct expression *exp,
elt += 4 + BYTES_TO_EXP_ELEM (len + 1); elt += 4 + BYTES_TO_EXP_ELEM (len + 1);
} }
break; 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: case TYPE_INSTANCE:
{ {
LONGEST len; LONGEST len;

View File

@ -901,6 +901,12 @@ operator_length_standard (const struct expression *expr, int endpos,
oplen = 4; oplen = 4;
break; 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_TYPE:
case OP_BOOL: case OP_BOOL:
case OP_LAST: case OP_LAST:

View File

@ -280,6 +280,27 @@ OP (OP_OBJC_SELECTOR)
a string, which, of course, is variable length. */ a string, which, of course, is variable length. */
OP (OP_SCOPE) 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 /* 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 so we can look up types that are qualified by scope, either with
the GDB "::" operator, or the Modula-2 '.' operator. */ the GDB "::" operator, or the Modula-2 '.' operator. */

View File

@ -1,3 +1,9 @@
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> 2017-09-04 Pedro Alves <palves@redhat.com>
* gdb.asm/asm-source.exp: Add casts to int. * gdb.asm/asm-source.exp: Add casts to int.

View File

@ -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;
}

View File

@ -0,0 +1 @@
#include "local-static.c"

View File

@ -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
}
}