From patchwork Thu Jul 13 15:32:06 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pedro Alves X-Patchwork-Id: 21597 Received: (qmail 112509 invoked by alias); 13 Jul 2017 15:32:28 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Delivered-To: mailing list gdb-patches@sourceware.org Received: (qmail 112376 invoked by uid 89); 13 Jul 2017 15:32:27 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-26.8 required=5.0 tests=BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, KAM_STOCKGEN, RP_MATCHES_RCVD, SPF_HELO_PASS autolearn=ham version=3.3.2 spammy= X-HELO: mx1.redhat.com Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Thu, 13 Jul 2017 15:32:22 +0000 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.phx2.redhat.com [10.5.11.16]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id EF7A37F6A5 for ; Thu, 13 Jul 2017 15:32:20 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com EF7A37F6A5 Authentication-Results: ext-mx01.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com Authentication-Results: ext-mx01.extmail.prod.ext.phx2.redhat.com; spf=pass smtp.mailfrom=palves@redhat.com DKIM-Filter: OpenDKIM Filter v2.11.0 mx1.redhat.com EF7A37F6A5 Received: from cascais.lan (ovpn04.gateway.prod.ext.ams2.redhat.com [10.39.146.4]) by smtp.corp.redhat.com (Postfix) with ESMTP id 0B1984DA78 for ; Thu, 13 Jul 2017 15:32:19 +0000 (UTC) From: Pedro Alves To: gdb-patches@sourceware.org Subject: [PATCH v2 10/13] Handle "p S::method()::static_var" in the C++ parser Date: Thu, 13 Jul 2017 16:32:06 +0100 Message-Id: <1499959929-29497-11-git-send-email-palves@redhat.com> In-Reply-To: <1499959929-29497-1-git-send-email-palves@redhat.com> References: <1499959929-29497-1-git-send-email-palves@redhat.com> 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 = 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 (%type ): 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 * gdb.base/local-static.c: New. * gdb.base/local-static.cc: New. * gdb.base/local-static.exp: New. --- gdb/c-exp.y | 43 +++++++++- gdb/eval.c | 21 +++++ gdb/expprint.c | 18 +++++ gdb/parse.c | 6 ++ gdb/std-operator.def | 21 +++++ gdb/testsuite/gdb.cp/local-static.c | 142 ++++++++++++++++++++++++++++++++++ gdb/testsuite/gdb.cp/local-static.cc | 1 + gdb/testsuite/gdb.cp/local-static.exp | 136 ++++++++++++++++++++++++++++++++ 8 files changed, 386 insertions(+), 2 deletions(-) create mode 100644 gdb/testsuite/gdb.cp/local-static.c create mode 100644 gdb/testsuite/gdb.cp/local-static.cc create mode 100644 gdb/testsuite/gdb.cp/local-static.exp diff --git a/gdb/c-exp.y b/gdb/c-exp.y index 48b5a53..5102470 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 exp exp1 type_exp start variable qualified_name lcurly +%type exp exp1 type_exp start variable qualified_name lcurly function_method %type rcurly %type type typebase %type 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 ee6b6f7..97ddc72 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 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/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 . */ + +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 +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 +void +S2::method () +{ + DEF_STATICS (S2_M); +} + +template +void +S2::static_method () +{ + DEF_STATICS (S2_SM); +} + +S2 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::static_method (); + S2::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 . + +# 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::method()" "S2_M"} + {"S2::static_method()" "S2_SM"} + {"S2::inline_method()" "S2_IM"} + {"S2::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 + } +}