From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTPS id EAB5C3858D20 for ; Thu, 17 Feb 2022 22:32:13 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org EAB5C3858D20 Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-298-9iDEMGrYOEqx3WeiAKI13g-1; Thu, 17 Feb 2022 17:32:08 -0500 X-MC-Unique: 9iDEMGrYOEqx3WeiAKI13g-1 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 mimecast-mx01.redhat.com (Postfix) with ESMTPS id D079F18B9F41; Thu, 17 Feb 2022 22:32:07 +0000 (UTC) Received: from localhost (unknown [10.33.36.25]) by smtp.corp.redhat.com (Postfix) with ESMTP id 7236F6AB84; Thu, 17 Feb 2022 22:32:07 +0000 (UTC) From: Jonathan Wakely To: libstdc++@gcc.gnu.org, gcc-patches@gcc.gnu.org Subject: [committed] libstdc++: Make std::error_code printer more robust Date: Thu, 17 Feb 2022 22:32:06 +0000 Message-Id: <20220217223206.4056618-1-jwakely@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.16 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: 8bit Content-Type: text/plain; charset="US-ASCII" X-Spam-Status: No, score=-13.5 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_LOW, SPF_HELO_NONE, SPF_NONE, TXREP, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.4 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 17 Feb 2022 22:32:16 -0000 Tested x86_64-linux, pushed to trunk. The StdErrorCodePrinter that crashes GDB is on gcc-11 too so this should be backported there. -- >8 -- This attempts to implement a partial workaround for the GDB bug https://sourceware.org/bugzilla/show_bug.cgi?id=28856 which causes GDB to crash when printing a frame with a std::error_code argument. By recognising the known error categories defined in the library and hardcoding their names we do not need to call cat->name() on the category. This has the additional benefit of also working when debugging a core file rather than a running process. For those known categories we can also cast the int value to the corresponding error code enum (e.g. future_errc) so that we show an enumerator instead of just an integer. For program-defined categories we just use the name of the dynamic type to identify the category, and print the value as an integer. Once the GDB bug is fixed and the virtual name() function can be called safely, that would be preferable. For now it's better to have an imperfect printer that doesn't crash GDB. This rewritten StdErrorCodePrinter needs gdb.Value.dynamic_type, so is only registered if that is supported, which means GDB 7.7 and later. libstdc++-v3/ChangeLog: * python/libstdcxx/v6/printers.py (StdErrorCodePrinter): Replace code that call cat->name() on std::error_category objects. Identify known categories by symbol name and use a hardcoded name. Print error code values as enumerators where appopriate. * testsuite/libstdc++-prettyprinters/cxx11.cc: Adjust expected name of custom category. Check io_errc and future_errc errors. --- libstdc++-v3/python/libstdcxx/v6/printers.py | 112 +++++++++++++++--- .../libstdc++-prettyprinters/cxx11.cc | 10 +- 2 files changed, 102 insertions(+), 20 deletions(-) diff --git a/libstdc++-v3/python/libstdcxx/v6/printers.py b/libstdc++-v3/python/libstdcxx/v6/printers.py index b3f4956381b..f7a7f9961a7 100644 --- a/libstdc++-v3/python/libstdcxx/v6/printers.py +++ b/libstdc++-v3/python/libstdcxx/v6/printers.py @@ -1518,41 +1518,113 @@ class StdCmpCatPrinter: class StdErrorCodePrinter: "Print a std::error_code or std::error_condition" - _errno_categories = None # List of categories that use errno values + _system_is_posix = None # Whether std::system_category() use errno values. def __init__ (self, typename, val): self.val = val self.typename = strip_versioned_namespace(typename) # Do this only once ... - if StdErrorCodePrinter._errno_categories is None: - StdErrorCodePrinter._errno_categories = ['generic'] + if StdErrorCodePrinter._system_is_posix is None: try: import posix - StdErrorCodePrinter._errno_categories.append('system') + StdErrorCodePrinter._system_is_posix = True except ImportError: - pass + StdErrorCodePrinter._system_is_posix = False @staticmethod - def _category_name(cat): - "Call the virtual function that overrides std::error_category::name()" - gdb.set_convenience_variable('__cat', cat) - return gdb.parse_and_eval('$__cat->name()').string() + def _find_errc_enum(name): + typ = gdb.lookup_type(name) + if typ is not None and typ.code == gdb.TYPE_CODE_ENUM: + return typ + return None + + @classmethod + def _match_net_ts_category(cls, cat): + net_cats = ['stream', 'socket', 'ip::resolver'] + for c in net_cats: + func = c + '_category()' + for ns in ['', _versioned_namespace]: + ns = 'std::{}experimental::net::v1'.format(ns) + sym = gdb.lookup_symbol('{}::{}::__c'.format(ns, func))[0] + if sym is not None: + if cat == sym.value().address: + name = 'net::' + func + enum = cls._find_errc_enum('{}::{}_errc'.format(ns, c)) + return (name, enum) + return (None, None) + + @classmethod + def _category_info(cls, cat): + "Return details of a std::error_category" + + name = None + enum = None + is_errno = False + + # Try these first, or we get "warning: RTTI symbol not found" when + # using cat.dynamic_type on the local class types for Net TS categories. + func, enum = cls._match_net_ts_category(cat) + if func is not None: + return (None, func, enum, is_errno) + + # This might give a warning for a program-defined category defined as + # a local class, but there doesn't seem to be any way to avoid that. + typ = cat.dynamic_type.target() + # Shortcuts for the known categories defined by libstdc++. + if typ.tag.endswith('::generic_error_category'): + name = 'generic' + is_errno = True + if typ.tag.endswith('::system_error_category'): + name = 'system' + is_errno = cls._system_is_posix + if typ.tag.endswith('::future_error_category'): + name = 'future' + enum = cls._find_errc_enum('std::future_errc') + if typ.tag.endswith('::io_error_category'): + name = 'io' + enum = cls._find_errc_enum('std::io_errc') + + if name is None: + try: + # Want to call std::error_category::name() override, but it's + # unsafe: https://sourceware.org/bugzilla/show_bug.cgi?id=28856 + # gdb.set_convenience_variable('__cat', cat) + # return '"%s"' % gdb.parse_and_eval('$__cat->name()').string() + pass + except: + pass + return (name, typ.tag, enum, is_errno) + + @staticmethod + def _unqualified_name(name): + "Strip any nested-name-specifier from NAME to give an unqualified name" + return name.split('::')[-1] def to_string (self): value = self.val['_M_value'] - category = self._category_name(self.val['_M_cat']) - strval = str(value) + cat = self.val['_M_cat'] + name, alt_name, enum, is_errno = self._category_info(cat) if value == 0: - default_cats = {'error_code':'system', 'error_condition':'generic'} - unqualified = self.typename.split('::')[-1] - if category == default_cats[unqualified]: + default_cats = { 'error_code' : 'system', + 'error_condition' : 'generic' } + if name == default_cats[self._unqualified_name(self.typename)]: return self.typename + ' = { }' # default-constructed value - if value > 0 and category in StdErrorCodePrinter._errno_categories: + + strval = str(value) + if is_errno and value != 0: try: strval = errno.errorcode[int(value)] except: pass - return '%s = {"%s": %s}' % (self.typename, category, strval) + elif enum is not None: + strval = self._unqualified_name(str(value.cast(enum))) + + if name is not None: + name = '"%s"' % name + else: + name = alt_name + return '%s = {%s: %s}' % (self.typename, name, strval) + class StdRegexStatePrinter: "Print a state node in the NFA for a std::regex" @@ -1984,8 +2056,6 @@ def build_libstdcxx_dictionary (): libstdcxx_printer.add_version('std::__cxx11::', 'basic_string', StdStringPrinter) libstdcxx_printer.add_container('std::', 'bitset', StdBitsetPrinter) libstdcxx_printer.add_container('std::', 'deque', StdDequePrinter) - libstdcxx_printer.add_version('std::', 'error_code', StdErrorCodePrinter) - libstdcxx_printer.add_version('std::', 'error_condition', StdErrorCodePrinter) libstdcxx_printer.add_container('std::', 'list', StdListPrinter) libstdcxx_printer.add_container('std::__cxx11::', 'list', StdListPrinter) libstdcxx_printer.add_container('std::', 'map', StdMapPrinter) @@ -2002,6 +2072,12 @@ def build_libstdcxx_dictionary (): libstdcxx_printer.add_container('std::', 'vector', StdVectorPrinter) # vector + if hasattr(gdb.Value, 'dynamic_type'): + libstdcxx_printer.add_version('std::', 'error_code', + StdErrorCodePrinter) + libstdcxx_printer.add_version('std::', 'error_condition', + StdErrorCodePrinter) + # Printer registrations for classes compiled with -D_GLIBCXX_DEBUG. libstdcxx_printer.add('std::__debug::bitset', StdBitsetPrinter) libstdcxx_printer.add('std::__debug::deque', StdDequePrinter) diff --git a/libstdc++-v3/testsuite/libstdc++-prettyprinters/cxx11.cc b/libstdc++-v3/testsuite/libstdc++-prettyprinters/cxx11.cc index 6285bd5c724..4262ca88bc3 100644 --- a/libstdc++-v3/testsuite/libstdc++-prettyprinters/cxx11.cc +++ b/libstdc++-v3/testsuite/libstdc++-prettyprinters/cxx11.cc @@ -24,6 +24,7 @@ #include #include #include +#include #include "../util/testsuite_allocator.h" // NullablePointer typedef std::tuple ExTuple; @@ -181,9 +182,14 @@ main() std::string message(int) const { return ""; } } cat; std::error_code emiaow(42, cat); - // { dg-final { note-test emiaow {std::error_code = {"miaow": 42}} } } + // { dg-final { note-test emiaow {std::error_code = {custom_cat: 42}} } } std::error_condition ecmiaow(42, cat); - // { dg-final { note-test ecmiaow {std::error_condition = {"miaow": 42}} } } + // { dg-final { note-test ecmiaow {std::error_condition = {custom_cat: 42}} } } + + std::error_code ecio = std::make_error_code(std::io_errc::stream); + // { dg-final { note-test ecio {std::error_code = {"io": stream}} } } + std::error_code ecfut0 = std::make_error_code(std::future_errc{}); + // { dg-final { note-test ecfut0 {std::error_code = {"future": 0}} } } placeholder(""); // Mark SPOT use(efl); -- 2.34.1