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:
parent
dd5901a6a5
commit
858be34c5a
@ -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>
|
||||
|
||||
* eval.c (evaluate_subexp_standard): Remove UNOP_MEMVAL_TLS
|
||||
|
43
gdb/c-exp.y
43
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; }
|
||||
;
|
||||
|
21
gdb/eval.c
21
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
|
||||
|
@ -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;
|
||||
|
@ -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:
|
||||
|
@ -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. */
|
||||
|
@ -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>
|
||||
|
||||
* gdb.asm/asm-source.exp: Add casts to int.
|
||||
|
142
gdb/testsuite/gdb.cp/local-static.c
Normal file
142
gdb/testsuite/gdb.cp/local-static.c
Normal 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;
|
||||
}
|
1
gdb/testsuite/gdb.cp/local-static.cc
Normal file
1
gdb/testsuite/gdb.cp/local-static.cc
Normal file
@ -0,0 +1 @@
|
||||
#include "local-static.c"
|
136
gdb/testsuite/gdb.cp/local-static.exp
Normal file
136
gdb/testsuite/gdb.cp/local-static.exp
Normal 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
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user