From patchwork Mon Nov 27 17:13:44 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pedro Alves X-Patchwork-Id: 24557 Received: (qmail 77921 invoked by alias); 27 Nov 2017 17:13:57 -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 77689 invoked by uid 89); 27 Nov 2017 17:13:56 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-25.2 required=5.0 tests=BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, KAM_SHORT, KAM_STOCKGEN, KB_WAM_FROM_NAME_SINGLEWORD, SPF_HELO_PASS, T_RP_MATCHES_RCVD 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; Mon, 27 Nov 2017 17:13:51 +0000 Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id C1B9D8047F for ; Mon, 27 Nov 2017 17:13:50 +0000 (UTC) Received: from cascais.lan (ovpn04.gateway.prod.ext.ams2.redhat.com [10.39.146.4]) by smtp.corp.redhat.com (Postfix) with ESMTP id B767C60C94 for ; Mon, 27 Nov 2017 17:13:49 +0000 (UTC) From: Pedro Alves To: gdb-patches@sourceware.org Subject: [PATCH v2 3/3] Breakpoints in symbols with ABI tags (PR c++/19436) Date: Mon, 27 Nov 2017 17:13:44 +0000 Message-Id: <1511802824-643-4-git-send-email-palves@redhat.com> In-Reply-To: <1511802824-643-1-git-send-email-palves@redhat.com> References: <1511802824-643-1-git-send-email-palves@redhat.com> In v2: - Nothing really changed. This is still here because it depends on infrastructure added by patch #1. - Documentation bits were already approved: https://sourceware.org/ml/gdb-patches/2017-06/msg00664.html - I've addressed Keith's nit at: https://sourceware.org/ml/gdb-patches/2017-08/msg00206.html ~~~~~~ Trying to set a breakpoint in a function with an ABI tag does not work currently. E.g., debugging gdb itself, we see this with the "string_printf" function: (top-gdb) b string_print [TAB] (top-gdb) b string_printf[abi:cxx11](char const*, ...) [RET] No source file named string_printf[abi. Make breakpoint pending on future shared library load? (y or [n]) Quoting doesn't help: (top-gdb) b 'string_printf[abi:cxx11]'(char const*, ...) malformed linespec error: unexpected string, "(char const*, ...)" (top-gdb) b 'string_printf[abi:cxx11](char const*, ...)' No source file named string_printf[abi. Make breakpoint pending on future shared library load? (y or [n]) n This patch fixes this, and takes it a bit further. The actual symbol name as demangled by libiberty's demangler is really string_printf[abi:cxx11](char const*, ...) however, this patch makes it possible to set the breakpoint with string_printf(char const*, ...) too. I.e., ignoring the ABI tag. And to match, it teaches the completer to complete the symbol name without the ABI tag, i.e., "string_pri" -> "string_printf(char const*, ...)" If however, you really want to break on a symbol with the tag, then you simply start writing the tag, and GDB will preserve it, like: "string_printf[a" -> "string_printf[abi:cxx11](char const*, ...)" Grows the gdb.linespec/ tests like this: -# of expected passes 8977 +# of expected passes 9176 gdb/ChangeLog: yyyy-mm-dd Pedro Alves PR c++/19436 * NEWS: Mention setting breakpoints on functions with C++ ABI tags. * completer.h (completion_match_for_lcd) : New methods. : Consider ignored ranges. : Clear ignored ranges. : New fields. * cp-support.c (cp_search_name_hash): Ignore ABI tags. (cp_symbol_name_matches_1, cp_fq_symbol_name_matches): Pass the completion_match_for_lcd pointer to strncmp_iw_with_mode. (test_cp_symbol_name_cmp): Add [abi:...] tags unit tests. * language.c (default_symbol_name_matcher): Pass the completion_match_for_lcd pointer to strncmp_iw_with_mode. * linespec.c (linespec_lexer_lex_string): Don't tokenize ABI tags. * utils.c (skip_abi_tag): New function. (strncmp_iw_with_mode): Add completion_match_for_lcd parameter. Handle ABI tags. * utils.h (strncmp_iw_with_mode): Add completion_match_for_lcd parameter. gdb/testsuite/ChangeLog: yyyy-mm-dd Pedro Alves PR c++/19436 * gdb.linespec/cpls-abi-tag.cc: New file. * gdb.linespec/cpls-abi-tag.exp: New file. gdb/doc/ChangeLog: yyyy-mm-dd Pedro Alves PR c++/19436 * gdb.texinfo (Debugging C Plus Plus): Document setting breakpoints in functions with ABI tags. --- gdb/doc/gdb.texinfo | 43 +++++ gdb/NEWS | 22 +++ gdb/completer.h | 62 +++++- gdb/cp-support.c | 53 +++++- gdb/language.c | 5 +- gdb/linespec.c | 5 + gdb/testsuite/gdb.linespec/cpls-abi-tag.cc | 93 +++++++++ gdb/testsuite/gdb.linespec/cpls-abi-tag.exp | 286 ++++++++++++++++++++++++++++ gdb/utils.c | 96 +++++++++- gdb/utils.h | 16 +- 10 files changed, 663 insertions(+), 18 deletions(-) create mode 100644 gdb/testsuite/gdb.linespec/cpls-abi-tag.cc create mode 100644 gdb/testsuite/gdb.linespec/cpls-abi-tag.exp diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 7a71739..4f55222 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -15128,6 +15128,49 @@ the same notation that is used to declare such symbols in C@t{++}: type also use the @value{GDBN} command-line word completion facilities to list the available choices, or to finish the type list for you. @xref{Completion,, Command Completion}, for details on how to do this. + +@item @r{Breakpoints in functions with ABI tags} + +The GNU C@t{++} compiler introduced the notion of ABI ``tags'', which +correspond to changes in the ABI of a type, function, or variable that +would not otherwise be reflected in a mangled name. See +@url{https://developers.redhat.com/blog/2015/02/05/gcc5-and-the-c11-abi/} +for more detail. + +The ABI tags are visible in C@t{++} demangled names. For example, a +function that returns a std::string: + +@smallexample +std::string function(int); +@end smallexample + +@noindent +when compiled for the C++11 ABI is marked with the @code{cxx11} ABI +tag, and @value{GDBN} displays the symbol like this: + +@smallexample +function[abi:cxx11](int) +@end smallexample + +You can set a breakpoint on such functions simply as if they had no +tag. For example: + +@smallexample +(gdb) b function(int) +Breakpoint 2 at 0x40060d: file main.cc, line 10. +(gdb) info breakpoints +Num Type Disp Enb Address What +1 breakpoint keep y 0x0040060d in function[abi:cxx11](int) + at main.cc:10 +@end smallexample + +On the rare occasion you need to disambiguate between different ABI +tags, you can do so by simply including the ABI tag in the function +name, like: + +@smallexample +(@value{GDBP}) b ambiguous[abi:other_tag](int) +@end smallexample @end table @node Decimal Floating Point diff --git a/gdb/NEWS b/gdb/NEWS index 984fd96..06df528 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -72,6 +72,28 @@ program, the "break -q B::func" command sets a breakpoint on "B::func", only. +* Breakpoints on functions marked with C++ ABI tags + + GDB can now set breakpoints on functions marked with C++ ABI tags + (e.g., [abi:cxx11]). See here for a description of ABI tags: + https://developers.redhat.com/blog/2015/02/05/gcc5-and-the-c11-abi/ + + Functions with a C++11 abi tag are demangled/displayed like this: + + function[abi:cxx11](int) + ^^^^^^^^^^^ + + You can now set a breakpoint on such functions simply as if they had + no tag, like: + + (gdb) b function(int) + + Or if you need to disambiguate between tags, like: + + (gdb) b function[abi:other_tag](int) + + Tab completion was adjusted accordingly as well. + * Python Scripting ** New events gdb.new_inferior, gdb.inferior_deleted, and diff --git a/gdb/completer.h b/gdb/completer.h index f756412..9ce70bf 100644 --- a/gdb/completer.h +++ b/gdb/completer.h @@ -122,28 +122,82 @@ private: "b push_ba" on a C++ program usually completes to std::vector<...>::push_back, std::string::push_back etc. In such case, the symbol comparison routine will set the LCD match to point - into the "push_back" substring within the symbol's name string. */ + into the "push_back" substring within the symbol's name string. + Also, in some cases, the symbol comparison routine will want to + ignore parts of the symbol name for LCD purposes, such as for + example symbols with abi tags in C++. In such cases, the symbol + comparison routine will set MARK_IGNORED_RANGE to mark the ignored + substrings of the matched string. The resulting LCD string with + the ignored parts stripped out is computed at the end of a + completion match sequence iff we had a positive match. */ class completion_match_for_lcd { public: + /* Get the resulting LCD, after a successful match. */ + const char *match () + { return m_match; } + /* Set the match for LCD. See m_match's description. */ void set_match (const char *match) { m_match = match; } - /* Get the resulting LCD, after a successful match. */ + /* Mark the range between [BEGIN, END) as ignored. */ + void mark_ignored_range (const char *begin, const char *end) + { m_ignored_ranges.emplace_back (begin, end); } + + /* Get the resulting LCD, after a successful match. If there are + ignored ranges, then this builds a new string with the ignored + parts removed (and stores it internally). As such, the result of + this call is only good for the current completion match + sequence. */ const char *finish () - { return m_match; } + { + if (m_ignored_ranges.empty ()) + return m_match; + else + { + m_finished_storage.clear (); + + const char *prev = m_match; + for (const auto &range : m_ignored_ranges) + { + m_finished_storage.append (prev, range.first); + prev = range.second; + } + m_finished_storage.append (prev); + + return m_finished_storage.c_str (); + } + } /* Prepare for another completion matching sequence. */ void clear () - { m_match = NULL; } + { + m_match = NULL; + m_ignored_ranges.clear (); + } private: /* The completion match result for LCD. This is usually either a pointer into to a substring within a symbol's name, or to the storage of the pairing completion_match object. */ const char *m_match; + + /* The ignored substring ranges within M_MATCH. E.g., if we were + looking for completion matches for C++ functions starting with + "functio" + and successfully match: + "function[abi:cxx11](int)" + the ignored ranges vector will contain an entry that delimits the + "[abi:cxx11]" substring, such that calling finish() results in: + "function(int)" + */ + std::vector> m_ignored_ranges; + + /* Storage used by the finish() method, if it has to compute a new + string. */ + std::string m_finished_storage; }; /* Convenience aggregate holding info returned by the symbol name diff --git a/gdb/cp-support.c b/gdb/cp-support.c index 172d821..34a8439 100644 --- a/gdb/cp-support.c +++ b/gdb/cp-support.c @@ -1629,7 +1629,23 @@ cp_search_name_hash (const char *search_name) if (prefix_len != 0) search_name += prefix_len + 2; - return default_search_name_hash (search_name); + unsigned int hash = 0; + for (const char *string = search_name; *string != '\0'; ++string) + { + string = skip_spaces (string); + + if (*string == '(') + break; + + /* Ignore ABI tags such as "[abi:cxx11]. */ + if (*string == '[' + && startswith (string + 1, "abi:") + && string[5] != ':') + break; + + hash = SYMBOL_HASH_NEXT (hash, *string); + } + return hash; } /* Helper for cp_symbol_name_matches (i.e., symbol_name_matcher_ftype @@ -1674,11 +1690,13 @@ cp_symbol_name_matches_1 (const char *symbol_search_name, completion_match_result *comp_match_res) { const char *sname = symbol_search_name; + completion_match_for_lcd *match_for_lcd + = (comp_match_res != NULL ? &comp_match_res->match_for_lcd : NULL); while (true) { if (strncmp_iw_with_mode (sname, lookup_name, lookup_name_len, - mode, language_cplus) == 0) + mode, language_cplus, match_for_lcd) == 0) { if (comp_match_res != NULL) { @@ -1728,14 +1746,15 @@ cp_fq_symbol_name_matches (const char *symbol_search_name, { /* Get the demangled name. */ const std::string &name = lookup_name.cplus ().lookup_name (); - + completion_match_for_lcd *match_for_lcd + = (comp_match_res != NULL ? &comp_match_res->match_for_lcd : NULL); strncmp_iw_mode mode = (lookup_name.completion_mode () ? strncmp_iw_mode::NORMAL : strncmp_iw_mode::MATCH_PARAMS); if (strncmp_iw_with_mode (symbol_search_name, name.c_str (), name.size (), - mode, language_cplus) == 0) + mode, language_cplus, match_for_lcd) == 0) { if (comp_match_res != NULL) comp_match_res->set_match (symbol_search_name); @@ -1950,6 +1969,32 @@ test_cp_symbol_name_matches () CHECK_NOT_MATCH_C ("foo::function()", "::function()"); CHECK_NOT_MATCH_C ("foo::function(int)", "::function()"); CHECK_NOT_MATCH_C ("foo::function(int)", "::function(int)"); + + /* Test ABI tag matching/ignoring. */ + + /* If the symbol name has an ABI tag, but the lookup name doesn't, + then the ABI tag in the symbol name is ignored. */ + CHECK_MATCH_C ("function[abi:foo]()", "function"); + CHECK_MATCH_C ("function[abi:foo](int)", "function"); + CHECK_MATCH_C ("function[abi:foo]()", "function ()"); + CHECK_NOT_MATCH_C ("function[abi:foo]()", "function (int)"); + + CHECK_MATCH_C ("function[abi:foo]()", "function[abi:foo]"); + CHECK_MATCH_C ("function[abi:foo](int)", "function[abi:foo]"); + CHECK_MATCH_C ("function[abi:foo]()", "function[abi:foo] ()"); + CHECK_MATCH_C ("function[abi:foo][abi:bar]()", "function"); + CHECK_MATCH_C ("function[abi:foo][abi:bar](int)", "function"); + CHECK_MATCH_C ("function[abi:foo][abi:bar]()", "function[abi:foo]"); + CHECK_MATCH_C ("function[abi:foo][abi:bar](int)", "function[abi:foo]"); + CHECK_MATCH_C ("function[abi:foo][abi:bar]()", "function[abi:foo] ()"); + CHECK_NOT_MATCH_C ("function[abi:foo][abi:bar]()", "function[abi:foo] (int)"); + + CHECK_MATCH_C ("function [abi:foo][abi:bar] ( )", "function [abi:foo]"); + + /* If the symbol name does not have an ABI tag, while the lookup + name has one, then there's no match. */ + CHECK_NOT_MATCH_C ("function()", "function[abi:foo]()"); + CHECK_NOT_MATCH_C ("function()", "function[abi:foo]"); } /* If non-NULL, return STR wrapped in quotes. Otherwise, return a diff --git a/gdb/language.c b/gdb/language.c index c05b703..c3872fc 100644 --- a/gdb/language.c +++ b/gdb/language.c @@ -707,13 +707,14 @@ default_symbol_name_matcher (const char *symbol_search_name, completion_match_result *comp_match_res) { const std::string &name = lookup_name.name (); - + completion_match_for_lcd *match_for_lcd + = (comp_match_res != NULL ? &comp_match_res->match_for_lcd : NULL); strncmp_iw_mode mode = (lookup_name.completion_mode () ? strncmp_iw_mode::NORMAL : strncmp_iw_mode::MATCH_PARAMS); if (strncmp_iw_with_mode (symbol_search_name, name.c_str (), name.size (), - mode, language_minimal) == 0) + mode, language_minimal, match_for_lcd) == 0) { if (comp_match_res != NULL) comp_match_res->set_match (symbol_search_name); diff --git a/gdb/linespec.c b/gdb/linespec.c index fca3efd..83600bb 100644 --- a/gdb/linespec.c +++ b/gdb/linespec.c @@ -748,6 +748,11 @@ linespec_lexer_lex_string (linespec_parser *parser) if (PARSER_STREAM (parser)[1] == ':') ++(PARSER_STREAM (parser)); + /* Do not tokenize ABI tags such as "[abi:cxx11]". */ + else if (PARSER_STREAM (parser) - start > 4 + && startswith (PARSER_STREAM (parser) - 4, "[abi")) + ++(PARSER_STREAM (parser)); + /* Do not tokenify if the input length so far is one (i.e, a single-letter drive name) and the next character is a directory separator. This allows Windows-style diff --git a/gdb/testsuite/gdb.linespec/cpls-abi-tag.cc b/gdb/testsuite/gdb.linespec/cpls-abi-tag.cc new file mode 100644 index 0000000..3916eda --- /dev/null +++ b/gdb/testsuite/gdb.linespec/cpls-abi-tag.cc @@ -0,0 +1,93 @@ +/* This testcase is part of GDB, the GNU debugger. + + 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 . */ + +#define ABI1 __attribute__ ((abi_tag ("tag1"))) +#define ABI2 __attribute__ ((abi_tag ("tag2"))) +#define ABI3 __attribute__ ((abi_tag ("tag3"))) + +void ABI1 +test_abi_tag_function (int) +{ +} + +void ABI1 +test_abi_tag_ovld_function () +{ +} + +void ABI1 +test_abi_tag_ovld_function (int) +{ +} + +/* Code for the overload functions, different ABI tag test. */ + +void +test_abi_tag_ovld2_function () +{ +} + +void ABI1 +test_abi_tag_ovld2_function (short) +{ +} + +void ABI2 +test_abi_tag_ovld2_function (int) +{ +} + +void ABI2 +test_abi_tag_ovld2_function (long) +{ +} + +struct ABI1 test_abi_tag_struct +{ + ABI2 test_abi_tag_struct (); + ABI2 ~test_abi_tag_struct (); +}; + +test_abi_tag_struct::test_abi_tag_struct () +{} + +test_abi_tag_struct::~test_abi_tag_struct () +{} + +ABI3 test_abi_tag_struct s; + +/* Code for the abi-tag in parameters test. */ + +struct ABI2 abi_tag_param_struct1 +{}; + +struct ABI2 abi_tag_param_struct2 +{}; + +void +test_abi_tag_in_params (abi_tag_param_struct1) +{} + +void +test_abi_tag_in_params (abi_tag_param_struct1, abi_tag_param_struct2) +{} + +int +main () +{ + return 0; +} diff --git a/gdb/testsuite/gdb.linespec/cpls-abi-tag.exp b/gdb/testsuite/gdb.linespec/cpls-abi-tag.exp new file mode 100644 index 0000000..9a0dab3 --- /dev/null +++ b/gdb/testsuite/gdb.linespec/cpls-abi-tag.exp @@ -0,0 +1,286 @@ +# 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 . + +# This file is part of the gdb testsuite. + +# Test ABI tag support in linespecs. + +load_lib completion-support.exp + +standard_testfile cpls-abi-tag.cc + +if {[prepare_for_testing "failed to prepare" $testfile \ + [list $srcfile] {c++ debug}]} { + return -1 +} + +gdb_test_no_output "set max-completions unlimited" + +# Check that the explicit location completer manages to find the next +# option name after a -function option, when the -function's argument +# is a function with an ABI tag. + +proc check_explicit_skips_function_argument {function} { + test_gdb_complete_unique \ + "b -function $function -sour" \ + "b -function $function -source" +} + +# The ABI tag tests. + +proc_with_prefix test_abi_tag {} { + with_test_prefix "completion" { + foreach cmd_prefix {"b" "b -function"} { + # Complete all prefixes between "_funcio" and the full + # prototype. The ABI tag is not considered for actual + # completion. + + with_test_prefix "skip tag" { + # set location "test_abi_tag_function\[abi:tag1\](int)" + set location "test_abi_tag_function(int)" + set line "$cmd_prefix $location" + set start [index_after "_functio" $line] + test_complete_prefix_range $line $start + } + + # Now the same, but start completing at the [. In that case, + # GDB considers the ABI tag as part of actual completion. + with_test_prefix "at tag" { + set location "test_abi_tag_function\[abi:tag1\](int)" + set line "$cmd_prefix $location" + set start [index_after "_function" $line] + test_complete_prefix_range $line $start + } + + # Same, but with extra spaces. Note that the original spaces in + # the input line are preserved after completion. + + with_test_prefix "spaces" { + test_gdb_complete_unique \ + "$cmd_prefix test_abi_tag_function \[abi:tag1\] (" \ + "$cmd_prefix test_abi_tag_function \[abi:tag1\] (int)" + + test_gdb_complete_unique \ + "$cmd_prefix test_abi_tag_function \[abi:tag1\] ( int " \ + "$cmd_prefix test_abi_tag_function \[abi:tag1\] ( int )" + } + } + } + + with_test_prefix "set breakpoints" { + foreach cmd_prefix {"b" "b -function"} { + # Test setting breakpoints. If the symbol name has an ABI + # tag, but the input linespec doesn't, then the ABI tag in the + # symbol name is ignored. + set linespec_list { + "test_abi_tag_function" + "test_abi_tag_function[abi:tag1]" + "test_abi_tag_function[abi:tag1](int)" + "test_abi_tag_function [abi:tag1]" + "test_abi_tag_function [abi:tag1] ( int )" + "test_abi_tag_function(int)" + "test_abi_tag_function (int)" + "test_abi_tag_function ( int )" + } + foreach linespec $linespec_list { + check_bp_locations_match_list \ + "$cmd_prefix $linespec" [list $location] + } + } + } + + with_test_prefix "set breakpoints wrong ABI tag" { + foreach cmd_prefix {"b" "b -function"} { + # Test setting breakpoints with the wrong ABI tag. Should + # fail to create the breakpoints. Completion should not find + # any match either. + set linespec_list { + "test_abi_tag_function[abi:tag2]" + "test_abi_tag_function[abi:tag2](int)" + "test_abi_tag_function [abi:tag2]" + "test_abi_tag_function [abi:tag2] ( int )" + } + foreach linespec $linespec_list { + check_setting_bp_fails "$cmd_prefix $linespec" + test_gdb_complete_none "$cmd_prefix $linespec" + } + + } + } + + # Test completion of overloaded functions with ABI tags. + with_test_prefix "completion of overloaded functions" { + foreach cmd_prefix {"b" "b -function"} { + set completion_list { + "test_abi_tag_ovld_function[abi:tag1]()" + "test_abi_tag_ovld_function[abi:tag1](int)" + } + + # If the input string does not include the ABI tag, then + # actual completion ignores it. + test_gdb_complete_multiple \ + "$cmd_prefix " "test_abi_tag_ovld_f" "unction(" \ + $completion_list + + # Otherwise, it's considered. + test_gdb_complete_multiple \ + "$cmd_prefix " "test_abi_tag_ovld_function\[" "abi:tag1\](" \ + $completion_list + + } + } + + # Test setting breakpoints on overloaded functions with ABI tags. + with_test_prefix "breakpoints on overloaded functions" { + foreach cmd_prefix {"b" "b -function"} { + set completion_list { + "test_abi_tag_ovld_function[abi:tag1]()" + "test_abi_tag_ovld_function[abi:tag1](int)" + } + set location_list { + "test_abi_tag_ovld_function" + "test_abi_tag_ovld_function[abi:tag1]" + } + foreach linespec $location_list { + check_bp_locations_match_list \ + "$cmd_prefix $linespec" $completion_list + } + + } + } + + with_test_prefix "completion of overloaded functions different abi" { + foreach cmd_prefix {"b" "b -function"} { + # Test completion of overloaded functions with ABI tags. + set completion_list { + "test_abi_tag_ovld2_function()" + "test_abi_tag_ovld2_function[abi:tag1](short)" + "test_abi_tag_ovld2_function[abi:tag2](int)" + "test_abi_tag_ovld2_function[abi:tag2](long)" + } + + # If the input string does not include the ABI tag, then + # actual completion ignores it. + test_gdb_complete_multiple \ + "$cmd_prefix " "test_abi_tag_ovld2_f" "unction(" \ + $completion_list + + # Otherwise, it's considered. Match stops at the part of + # the tag that diverges, and the completion list only + # shows matches with ABI tags. + set completion_list { + "test_abi_tag_ovld2_function[abi:tag1](short)" + "test_abi_tag_ovld2_function[abi:tag2](int)" + "test_abi_tag_ovld2_function[abi:tag2](long)" + } + test_gdb_complete_multiple \ + "$cmd_prefix " "test_abi_tag_ovld2_function\[" "abi:tag" \ + $completion_list + + # If you disambiguate, matches include only locations for + # the specified tag. + set completion_list { + "test_abi_tag_ovld2_function[abi:tag2](int)" + "test_abi_tag_ovld2_function[abi:tag2](long)" + } + test_gdb_complete_multiple \ + "$cmd_prefix " "test_abi_tag_ovld2_function\[abi:tag2" "\](" \ + $completion_list + + test_gdb_complete_unique \ + "$cmd_prefix test_abi_tag_ovld2_function\[abi:tag1" \ + "$cmd_prefix test_abi_tag_ovld2_function\[abi:tag1\](short)" + } + } + + with_test_prefix "completion of struct prefixes with tags" { + foreach cmd_prefix {"b" "b -function"} { + # Test completion of methods of structs with ABI tags. + set completion_list { + "test_abi_tag_struct[abi:tag1]::test_abi_tag_struct[abi:tag2]()" + "test_abi_tag_struct[abi:tag1]::~test_abi_tag_struct[abi:tag2]()" + } + + # If the input string does not include the ABI tag, then + # actual completion ignores it. + test_gdb_complete_multiple \ + "$cmd_prefix " "test_abi_tag_struc" "t::" \ + $completion_list + + # Otherwise, it's considered. + test_gdb_complete_multiple \ + "$cmd_prefix " "test_abi_tag_struct\[" "abi:tag1\]::" \ + $completion_list + + # Mix and match different abi tag positions. + test_gdb_complete_unique \ + "$cmd_prefix test_abi_tag_struct::t" \ + "$cmd_prefix test_abi_tag_struct::test_abi_tag_struct()" + + test_gdb_complete_unique \ + "$cmd_prefix test_abi_tag_struct\[abi:tag1\]::t" \ + "$cmd_prefix test_abi_tag_struct\[abi:tag1\]::test_abi_tag_struct()" + + test_gdb_complete_unique \ + "$cmd_prefix test_abi_tag_struct\[abi:tag1\]::test_abi_tag_struct\[" \ + "$cmd_prefix test_abi_tag_struct\[abi:tag1\]::test_abi_tag_struct\[abi:tag2\]()" + } + } + + with_test_prefix "abi tag in parameters" { + foreach cmd_prefix {"b" "b -function"} { + # Complete all prefixes between "_funcio" and the full + # prototype. The ABI tag is not considered for actual + # completion. + + set completion_list { + "test_abi_tag_in_params(abi_tag_param_struct1[abi:tag2])" + "test_abi_tag_in_params(abi_tag_param_struct1[abi:tag2], abi_tag_param_struct2[abi:tag2])" + } + # If the input string does not include the ABI tag, then + # actual completion ignores it. + test_gdb_complete_multiple \ + "$cmd_prefix " "test_abi_tag_in_para" "ms(abi_tag_param_struct1" \ + $completion_list + + # If OTOH the input string includes the ABI tag, then it + # is considered. + test_gdb_complete_multiple \ + "$cmd_prefix " "test_abi_tag_in_params(abi_tag_param_struct1\[ab" "i:tag2\]"\ + $completion_list + + set location_list { + "test_abi_tag_in_params(abi_tag_param_struct1[abi:tag2], abi_tag_param_struct2[abi:tag2])" + } + + set tags {"" "\[abi:tag2\]"} + foreach tag1 $tags { + foreach tag2 $tags { + set linespec "test_abi_tag_in_params(abi_tag_param_struct1${tag1}, abi_tag_param_struct2${tag2})" + check_bp_locations_match_list \ + "$cmd_prefix $linespec" $location_list + } + } + } + } + + # Check that the explicit location completer manages to find the + # option name after -function, when the -function's argument is a + # function with an ABI tag. + check_explicit_skips_function_argument \ + "test_abi_tag_function\[abi:unknown\](int)" +} + +test_abi_tag diff --git a/gdb/utils.c b/gdb/utils.c index 3e817ed..98ef6ac 100644 --- a/gdb/utils.c +++ b/gdb/utils.c @@ -2247,12 +2247,40 @@ cp_is_operator (const char *string, const char *start) && !valid_identifier_name_char (string[CP_OPERATOR_LEN])); } +/* If *NAME points at an ABI tag, skip it and return true. Otherwise + leave *NAME unmodified and return false. (see GCC's abi_tag + attribute), such names are demangled as e.g., + "function[abi:cxx11]()". */ + +static bool +skip_abi_tag (const char **name) +{ + const char *p = *name; + + if (startswith (p, "[abi:")) + { + p += 5; + + while (valid_identifier_name_char (*p)) + p++; + + if (*p == ']') + { + p++; + *name = p; + return true; + } + } + return false; +} + /* See utils.h. */ int strncmp_iw_with_mode (const char *string1, const char *string2, size_t string2_len, strncmp_iw_mode mode, - enum language language) + enum language language, + completion_match_for_lcd *match_for_lcd) { const char *string1_start = string1; const char *end_str2 = string2 + string2_len; @@ -2271,6 +2299,37 @@ strncmp_iw_with_mode (const char *string1, const char *string2, skip_spaces = false; } + /* Skip [abi:cxx11] tags in the symbol name if the lookup name + doesn't include them. E.g.: + + string1: function[abi:cxx1](int) + string2: function + + string1: function[abi:cxx1](int) + string2: function(int) + + string1: Struct[abi:cxx1]::function() + string2: Struct::function() + + string1: function(Struct[abi:cxx1], int) + string2: function(Struct, int) + */ + if (string2 == end_str2 + || (*string2 != '[' && !valid_identifier_name_char (*string2))) + { + const char *abi_start = string1; + + /* There can be more than one tag. */ + while (*string1 == '[' && skip_abi_tag (&string1)) + ; + + if (match_for_lcd != NULL && abi_start != string1) + match_for_lcd->mark_ignored_range (abi_start, string1); + + while (isspace (*string1)) + string1++; + } + if (*string1 == '\0' || string2 == end_str2) break; @@ -2405,7 +2464,40 @@ strncmp_iw_with_mode (const char *string1, const char *string2, if (string2 == end_str2) { if (mode == strncmp_iw_mode::NORMAL) - return 0; + { + /* Strip abi tag markers from the matched symbol name. + Usually the ABI marker will be found on function name + (automatically added because the function returns an + object marked with an ABI tag). However, it's also + possible to see a marker in one of the function + parameters, for example. + + string2 (lookup name): + func + symbol name: + function(some_struct[abi:cxx11], int) + + and for completion LCD computation we want to say that + the match was for: + function(some_struct, int) + */ + if (match_for_lcd != NULL) + { + while ((string1 = strstr (string1, "[abi:")) != NULL) + { + const char *abi_start = string1; + + /* There can be more than one tag. */ + while (skip_abi_tag (&string1) && *string1 == '[') + ; + + if (abi_start != string1) + match_for_lcd->mark_ignored_range (abi_start, string1); + } + } + + return 0; + } else return (*string1 != '\0' && *string1 != '('); } diff --git a/gdb/utils.h b/gdb/utils.h index dff4b17..349ab93 100644 --- a/gdb/utils.h +++ b/gdb/utils.h @@ -49,15 +49,19 @@ enum class strncmp_iw_mode /* Helper for strcmp_iw and strncmp_iw. Exported so that languages can implement both NORMAL and MATCH_PARAMS variants in a single function and defer part of the work to strncmp_iw_with_mode. + LANGUAGE is used to implement some context-sensitive language-specific comparisons. For example, for C++, "string1=operator()" should not match "string2=operator" even in - MATCH_PARAMS mode. */ -extern int strncmp_iw_with_mode (const char *string1, - const char *string2, - size_t string2_len, - strncmp_iw_mode mode, - enum language language); + MATCH_PARAMS mode. + + MATCH_FOR_LCD is passed down so that the function can mark parts of + the symbol name as ignored for completion matching purposes (e.g., + to handle abi tags). */ +extern int strncmp_iw_with_mode + (const char *string1, const char *string2, size_t string2_len, + strncmp_iw_mode mode, enum language language, + completion_match_for_lcd *match_for_lcd = NULL); /* Do a strncmp() type operation on STRING1 and STRING2, ignoring any differences in whitespace. STRING2_LEN is STRING2's length.