From 9ebe2f9de70bfbadddccbf2723518e7feb38362d Mon Sep 17 00:00:00 2001 From: Siddhesh Poyarekar Date: Fri, 28 Feb 2025 13:13:09 -0500 Subject: [PATCH] Update libstdc++ pretty printer from latest GTS version Also add a script that makes the update more convenient in future. Resolves: RHEL-50290 --- gcc.spec | 30 +- ...-prettyprinter-update-14-tests-48362.patch | 30 + ...-prettyprinter-update-14-tests-cxx11.patch | 27 + ...-prettyprinter-update-14-tests-cxx17.patch | 44 + ...stdc++-prettyprinter-update-14-tests.patch | 458 ++ gcc8-libstdc++-prettyprinter-update-14.patch | 4166 +++++++++++++++++ update-libstdcxx-prettyprinter.sh | 75 + 7 files changed, 4823 insertions(+), 7 deletions(-) create mode 100644 gcc8-libstdc++-prettyprinter-update-14-tests-48362.patch create mode 100644 gcc8-libstdc++-prettyprinter-update-14-tests-cxx11.patch create mode 100644 gcc8-libstdc++-prettyprinter-update-14-tests-cxx17.patch create mode 100644 gcc8-libstdc++-prettyprinter-update-14-tests.patch create mode 100644 gcc8-libstdc++-prettyprinter-update-14.patch create mode 100755 update-libstdcxx-prettyprinter.sh diff --git a/gcc.spec b/gcc.spec index f89815b..eb00293 100644 --- a/gcc.spec +++ b/gcc.spec @@ -4,7 +4,7 @@ %global gcc_major 8 # Note, gcc_release must be integer, if you want to add suffixes to # %%{release}, append them after %%{gcc_release} on Release: line. -%global gcc_release 24 +%global gcc_release 25 %global nvptx_tools_gitrev c28050f60193b3b95a18866a96f03334e874e78f %global nvptx_newlib_gitrev aadc8eb0ec43b7cd0dd2dfb484bae63c8b05ef24 %global _unpackaged_files_terminate_build 0 @@ -309,9 +309,16 @@ Patch48: gcc8-pr111070.patch Patch49: gcc8-RHEL-32886.patch Patch50: gcc8-pr100508.patch -Patch1000: nvptx-tools-no-ptxas.patch -Patch1001: nvptx-tools-build.patch -Patch1002: nvptx-tools-glibc.patch +# Any patches changing libstdc++-v3/python and its tests should go after this. +Patch1000: gcc8-libstdc++-prettyprinter-update-14.patch +Patch1001: gcc8-libstdc++-prettyprinter-update-14-tests.patch +Patch1002: gcc8-libstdc++-prettyprinter-update-14-tests-48362.patch +Patch1003: gcc8-libstdc++-prettyprinter-update-14-tests-cxx11.patch +Patch1004: gcc8-libstdc++-prettyprinter-update-14-tests-cxx17.patch + +Patch2000: nvptx-tools-no-ptxas.patch +Patch2001: nvptx-tools-build.patch +Patch2002: nvptx-tools-glibc.patch # On ARM EABI systems, we do want -gnueabi to be part of the @@ -934,10 +941,16 @@ so that there cannot be any synchronization problems. %patch49 -p0 -b .32886~ %patch50 -p1 -b .pr100508~ +%patch1000 -p1 -b .libstdc++-prettyprinter-update-14~ +%patch1001 -p1 -b .libstdc++-prettyprinter-update-14-tests~ +%patch1002 -p1 -b .libstdc++-prettyprinter-update-14-tests-48362~ +%patch1003 -p1 -b .libstdc++-prettyprinter-update-14-tests-cxx11~ +%patch1004 -p1 -b .libstdc++-prettyprinter-update-14-tests-cxx17~ + cd nvptx-tools-%{nvptx_tools_gitrev} -%patch1000 -p1 -b .nvptx-tools-no-ptxas~ -%patch1001 -p1 -b .nvptx-tools-build~ -%patch1002 -p1 -b .nvptx-tools-glibc~ +%patch2000 -p1 -b .nvptx-tools-no-ptxas~ +%patch2001 -p1 -b .nvptx-tools-build~ +%patch2002 -p1 -b .nvptx-tools-glibc~ cd .. echo 'Red Hat %{version}-%{gcc_release}' > gcc/DEV-PHASE @@ -3340,6 +3353,9 @@ fi %{ANNOBIN_GCC_PLUGIN_DIR}/gcc-annobin.so.0.0.0 %changelog +* Fri Feb 28 2025 Siddhesh Poyarekar 8.5.0-25 +- Sync libstdc++ pretty printers to latest GTS (RHEL-50290). + * Mon Feb 24 2025 Marek Polacek 8.5.0-24 - don't reuse DEBUG_EXPRs with vector type (PR middle-end/100508, RHEL-79501) diff --git a/gcc8-libstdc++-prettyprinter-update-14-tests-48362.patch b/gcc8-libstdc++-prettyprinter-update-14-tests-48362.patch new file mode 100644 index 0000000..1f0239e --- /dev/null +++ b/gcc8-libstdc++-prettyprinter-update-14-tests-48362.patch @@ -0,0 +1,30 @@ +The change is based on: + +commit 190c644c06369766aa2537851ddbf83b1231b65b +Author: Philipp Fent +Date: Sun Sep 4 20:47:34 2022 +0200 + + libstdc++: Fix pretty printer tests of tuple indexes + +but it is adapted to GCC8 since it does not have the tuple inheritance change, +which is: + +commit 91e6226f880b048275a7ceedef716e159c7cefd9 +Author: Jonathan Wakely +Date: Fri Aug 7 17:13:56 2020 +0100 + + libstdc++: Remove inheritance from elements in std::tuple + +diff --git a/libstdc++-v3/testsuite/libstdc++-prettyprinters/48362.cc b/libstdc++-v3/testsuite/libstdc++-prettyprinters/48362.cc +index cc91803e247..af335d0d3c7 100644 +--- a/libstdc++-v3/testsuite/libstdc++-prettyprinters/48362.cc ++++ b/libstdc++-v3/testsuite/libstdc++-prettyprinters/48362.cc +@@ -29,7 +29,7 @@ main() + // { dg-final { note-test t1 {empty std::tuple} } } + + std::tuple> t2{ "Johnny", 5, {} }; +-// { dg-final { regexp-test t2 {std::tuple containing = {\[1\] = "Johnny", \[2\] = 5, \[3\] = {> = empty std::tuple, }}} } } ++// { dg-final { regexp-test t2 {std::tuple containing = {\[0\] = "Johnny", \[1\] = 5, \[2\] = {> = empty std::tuple, }}} } } + + std::cout << "\n"; + return 0; // Mark SPOT diff --git a/gcc8-libstdc++-prettyprinter-update-14-tests-cxx11.patch b/gcc8-libstdc++-prettyprinter-update-14-tests-cxx11.patch new file mode 100644 index 0000000..9aca53b --- /dev/null +++ b/gcc8-libstdc++-prettyprinter-update-14-tests-cxx11.patch @@ -0,0 +1,27 @@ +NullablePointer support not available, so drop these tests. + +diff --git a/libstdc++-v3/testsuite/libstdc++-prettyprinters/cxx11.cc b/libstdc++-v3/testsuite/libstdc++-prettyprinters/cxx11.cc.new +index 2f75d12..6fb19c3 100644 +--- a/libstdc++-v3/testsuite/libstdc++-prettyprinters/cxx11.cc ++++ b/libstdc++-v3/testsuite/libstdc++-prettyprinters/cxx11.cc +@@ -145,20 +145,6 @@ main() + std::unique_ptr& rarrptr = arrptr; + // { dg-final { regexp-test rarrptr {std::unique_ptr.datum \[\]. = {get\(\) = 0x.*}} } } + +- struct Deleter +- { +- int deleter_member = -1; +- using pointer = __gnu_test::NullablePointer; +- void operator()(pointer) const noexcept { } +- }; +- static_assert( !std::is_empty(), "Deleter is not empty" ); +- static_assert( std::is_empty(), "but pointer is empty" ); +- +- std::unique_ptr empty_ptr; +-// { dg-final { note-test empty_ptr {std::unique_ptr = {get() = {}}} } } +- std::unique_ptr& rempty_ptr = empty_ptr; +-// { dg-final { note-test rempty_ptr {std::unique_ptr = {get() = {}}} } } +- + struct Deleter_pr103086 + { + int deleter_member = -1; diff --git a/gcc8-libstdc++-prettyprinter-update-14-tests-cxx17.patch b/gcc8-libstdc++-prettyprinter-update-14-tests-cxx17.patch new file mode 100644 index 0000000..8928cc3 --- /dev/null +++ b/gcc8-libstdc++-prettyprinter-update-14-tests-cxx17.patch @@ -0,0 +1,44 @@ +Revert this commit for libstdc++-prettyprinters (only cxx17, 91997 +doesn't exist in this tree) since gcc8 does not default to c++17. The +context has been adapted but the change should be exact: + +commit 0498d2d09a2364aae1e6b5e085c8ebb8fc517684 +Author: Jonathan Wakely +Date: Mon May 10 16:22:54 2021 +0100 + + libstdc++: Remove redundant -std=gnu++17 option from remaining tests + +Also remove the filesystem tests since it's not supported by RHEL8 gcc. + +diff --git b/libstdc++-v3/testsuite/libstdc++-prettyprinters/cxx17.cc a/libstdc++-v3/testsuite/libstdc++-prettyprinters/cxx17.cc +index 72c66d3b785..98e21e963fe 100644 +--- b/libstdc++-v3/testsuite/libstdc++-prettyprinters/cxx17.cc ++++ a/libstdc++-v3/testsuite/libstdc++-prettyprinters/cxx17.cc +@@ -1,4 +1,4 @@ +-// { dg-options "-g -O0" } ++// { dg-options "-g -O0 -std=gnu++17" } + // { dg-do run { target c++17 } } + + // Copyright (C) 2014-2024 Free Software Foundation, Inc. +@@ -18,7 +18,6 @@ + // with this library; see the file COPYING3. If not see + // . + +-#include + #include + #include + #include +@@ -120,13 +119,6 @@ main() + // { dg-final { regexp-test q {std::shared_ptr.int \[2\]. \(use count 2, weak count 1\) = {get\(\) = 0x.*}} } } + // { dg-final { regexp-test wq {std::weak_ptr.int \[2\]. \(use count 2, weak count 1\) = {get\(\) = 0x.*}} } } + +- std::filesystem::path path0; +-// { dg-final { note-test path0 {filesystem::path ""} } } +- std::filesystem::path path1("filename"); +-// { dg-final { note-test path1 {filesystem::path "filename"} } } +- std::filesystem::path path2("/dir/."); +-// { dg-final { note-test path2 {filesystem::path "/dir/." = {[root-directory] = "/", [1] = "dir", [2] = "."}} } } +- + std::cout << "\n"; + return 0; // Mark SPOT + } diff --git a/gcc8-libstdc++-prettyprinter-update-14-tests.patch b/gcc8-libstdc++-prettyprinter-update-14-tests.patch new file mode 100644 index 0000000..5ba0a5d --- /dev/null +++ b/gcc8-libstdc++-prettyprinter-update-14-tests.patch @@ -0,0 +1,458 @@ + .../testsuite/libstdc++-prettyprinters/compat.cc | 11 +- + .../testsuite/libstdc++-prettyprinters/cxx11.cc | 129 +++++++++++++++------ + .../testsuite/libstdc++-prettyprinters/cxx17.cc | 50 +++++--- + .../libstdc++-prettyprinters/filesystem-ts.cc | 8 +- + .../libstdc++-prettyprinters/libfundts.cc | 26 +++-- + 5 files changed, 146 insertions(+), 78 deletions(-) + +diff --git a/libstdc++-v3/testsuite/libstdc++-prettyprinters/compat.cc b/libstdc++-v3/testsuite/libstdc++-prettyprinters/compat.cc +index 81e0ce7213f..604a6f6415b 100644 +--- a/libstdc++-v3/testsuite/libstdc++-prettyprinters/compat.cc ++++ b/libstdc++-v3/testsuite/libstdc++-prettyprinters/compat.cc +@@ -1,8 +1,7 @@ + // { dg-options "-g -O0" } + // { dg-do run { target c++11 } } +-// { dg-skip-if "" { *-*-* } { "-D_GLIBCXX_PROFILE" } } + +-// Copyright (C) 2014-2019 Free Software Foundation, Inc. ++// Copyright (C) 2014-2024 Free Software Foundation, Inc. + // + // This file is part of the GNU ISO C++ Library. This library is free + // software; you can redistribute it and/or modify it under the +@@ -103,13 +102,13 @@ main() + using std::optional; + + optional o; +-// { dg-final { note-test o {std::optional [no contained value]} } } ++// { dg-final { note-test o {std::optional [no contained value]} } } + optional ob{false}; +-// { dg-final { note-test ob {std::optional = {[contained value] = false}} } } ++// { dg-final { note-test ob {std::optional = {[contained value] = false}} } } + optional oi{5}; +-// { dg-final { note-test oi {std::optional = {[contained value] = 5}} } } ++// { dg-final { note-test oi {std::optional = {[contained value] = 5}} } } + optional op{nullptr}; +-// { dg-final { note-test op {std::optional = {[contained value] = 0x0}} } } ++// { dg-final { note-test op {std::optional = {[contained value] = 0x0}} } } + + __builtin_puts(""); + return 0; // Mark SPOT +diff --git a/libstdc++-v3/testsuite/libstdc++-prettyprinters/cxx11.cc b/libstdc++-v3/testsuite/libstdc++-prettyprinters/cxx11.cc +index 9a90d8d91db..2f75d12703c 100644 +--- a/libstdc++-v3/testsuite/libstdc++-prettyprinters/cxx11.cc ++++ b/libstdc++-v3/testsuite/libstdc++-prettyprinters/cxx11.cc +@@ -1,8 +1,7 @@ + // { dg-do run { target c++11 } } + // { dg-options "-g -O0" } +-// { dg-skip-if "" { *-*-* } { "-D_GLIBCXX_PROFILE" } } + +-// Copyright (C) 2011-2018 Free Software Foundation, Inc. ++// Copyright (C) 2011-2024 Free Software Foundation, Inc. + // + // This file is part of the GNU ISO C++ Library. This library is free + // software; you can redistribute it and/or modify it under the +@@ -25,6 +24,10 @@ + #include + #include + #include ++#include ++#include ++#include ++#include "../util/testsuite_allocator.h" // NullablePointer + + typedef std::tuple ExTuple; + +@@ -60,84 +63,74 @@ struct datum + + std::unique_ptr global; + +-struct Deleter +-{ +- // Deleter is not an empty class: +- int deleter_member = -1; +- // But pointer is an empty class: +- struct pointer +- { +- pointer(const void* = nullptr) { } +- explicit operator bool() const noexcept { return false; } +- friend bool operator==(pointer, pointer) noexcept { return true; } +- friend bool operator!=(pointer, pointer) noexcept { return false; } +- }; +- void operator()(pointer) const noexcept { } ++struct custom_cat : std::error_category { ++ const char* name() const noexcept { return "miaow"; } ++ std::string message(int) const { return ""; } + }; + + int + main() + { + std::forward_list efl; +-// { dg-final { note-test efl "empty std::forward_list" } } ++// { dg-final { regexp-test efl "empty std::(__debug::)?forward_list" } } + + std::forward_list &refl = efl; +-// { dg-final { note-test refl "empty std::forward_list" } } ++// { dg-final { regexp-test refl "empty std::(__debug::)?forward_list" } } + + std::forward_list fl; + fl.push_front(2); + fl.push_front(1); +-// { dg-final { note-test fl {std::forward_list = {[0] = 1, [1] = 2}} } } ++// { dg-final { regexp-test fl {std::(__debug::)?forward_list = {\[0\] = 1, \[1\] = 2}} } } + + std::forward_list &rfl = fl; +-// { dg-final { note-test rfl {std::forward_list = {[0] = 1, [1] = 2}} } } ++// { dg-final { regexp-test rfl {std::(__debug::)?forward_list = {\[0\] = 1, \[1\] = 2}} } } + + std::unordered_map eum; +-// { dg-final { note-test eum "std::unordered_map with 0 elements" } } ++// { dg-final { regexp-test eum "std::(__debug::)?unordered_map with 0 elements" } } + std::unordered_map &reum = eum; +-// { dg-final { note-test reum "std::unordered_map with 0 elements" } } ++// { dg-final { regexp-test reum "std::(__debug::)?unordered_map with 0 elements" } } + + std::unordered_multimap eumm; +-// { dg-final { note-test eumm "std::unordered_multimap with 0 elements" } } ++// { dg-final { regexp-test eumm "std::(__debug::)?unordered_multimap with 0 elements" } } + std::unordered_multimap &reumm = eumm; +-// { dg-final { note-test reumm "std::unordered_multimap with 0 elements" } } ++// { dg-final { regexp-test reumm "std::(__debug::)?unordered_multimap with 0 elements" } } + + std::unordered_set eus; +-// { dg-final { note-test eus "std::unordered_set with 0 elements" } } ++// { dg-final { regexp-test eus "std::(__debug::)?unordered_set with 0 elements" } } + std::unordered_set &reus = eus; +-// { dg-final { note-test reus "std::unordered_set with 0 elements" } } ++// { dg-final { regexp-test reus "std::(__debug::)?unordered_set with 0 elements" } } + + std::unordered_multiset eums; +-// { dg-final { note-test eums "std::unordered_multiset with 0 elements" } } ++// { dg-final { regexp-test eums "std::(__debug::)?unordered_multiset with 0 elements" } } + std::unordered_multiset &reums = eums; +-// { dg-final { note-test reums "std::unordered_multiset with 0 elements" } } ++// { dg-final { regexp-test reums "std::(__debug::)?unordered_multiset with 0 elements" } } + + std::unordered_map uom; + uom[5] = "three"; + uom[3] = "seven"; +-// { dg-final { note-test uom {std::unordered_map with 2 elements = {[3] = "seven", [5] = "three"}} } } ++// { dg-final { regexp-test uom {std::(__debug::)?unordered_map with 2 elements = {\[3\] = "seven", \[5\] = "three"}} } } + + std::unordered_map &ruom = uom; +-// { dg-final { note-test ruom {std::unordered_map with 2 elements = {[3] = "seven", [5] = "three"}} } } ++// { dg-final { regexp-test ruom {std::(__debug::)?unordered_map with 2 elements = {\[3\] = "seven", \[5\] = "three"}} } } + + std::unordered_multimap uomm; + uomm.insert(std::pair (5, "three")); + uomm.insert(std::pair (5, "seven")); +-// { dg-final { note-test uomm {std::unordered_multimap with 2 elements = {[5] = "seven", [5] = "three"}} } } ++// { dg-final { regexp-test uomm {std::(__debug::)?unordered_multimap with 2 elements = {\[5\] = "seven", \[5\] = "three"}} } } + std::unordered_multimap &ruomm = uomm; +-// { dg-final { note-test ruomm {std::unordered_multimap with 2 elements = {[5] = "seven", [5] = "three"}} } } ++// { dg-final { regexp-test ruomm {std::(__debug::)?unordered_multimap with 2 elements = {\[5\] = "seven", \[5\] = "three"}} } } + + std::unordered_set uos; + uos.insert(5); +-// { dg-final { note-test uos {std::unordered_set with 1 element = {[0] = 5}} } } ++// { dg-final { regexp-test uos {std::(__debug::)?unordered_set with 1 element = {\[0\] = 5}} } } + std::unordered_set &ruos = uos; +-// { dg-final { note-test ruos {std::unordered_set with 1 element = {[0] = 5}} } } ++// { dg-final { regexp-test ruos {std::(__debug::)?unordered_set with 1 element = {\[0\] = 5}} } } + + std::unordered_multiset uoms; + uoms.insert(5); +-// { dg-final { note-test uoms {std::unordered_multiset with 1 element = {[0] = 5}} } } ++// { dg-final { regexp-test uoms {std::(__debug::)?unordered_multiset with 1 element = {\[0\] = 5}} } } + std::unordered_multiset &ruoms = uoms; +-// { dg-final { note-test ruoms {std::unordered_multiset with 1 element = {[0] = 5}} } } ++// { dg-final { regexp-test ruoms {std::(__debug::)?unordered_multiset with 1 element = {\[0\] = 5}} } } + + std::unique_ptr uptr (new datum); + uptr->s = "hi bob"; +@@ -152,15 +145,77 @@ main() + std::unique_ptr& rarrptr = arrptr; + // { dg-final { regexp-test rarrptr {std::unique_ptr.datum \[\]. = {get\(\) = 0x.*}} } } + ++ struct Deleter ++ { ++ int deleter_member = -1; ++ using pointer = __gnu_test::NullablePointer; ++ void operator()(pointer) const noexcept { } ++ }; ++ static_assert( !std::is_empty(), "Deleter is not empty" ); ++ static_assert( std::is_empty(), "but pointer is empty" ); ++ + std::unique_ptr empty_ptr; + // { dg-final { note-test empty_ptr {std::unique_ptr = {get() = {}}} } } + std::unique_ptr& rempty_ptr = empty_ptr; + // { dg-final { note-test rempty_ptr {std::unique_ptr = {get() = {}}} } } + ++ struct Deleter_pr103086 ++ { ++ int deleter_member = -1; ++ void operator()(int*) const noexcept { } ++ }; ++ ++ std::unique_ptr uniq_ptr; ++// { dg-final { note-test uniq_ptr {std::unique_ptr = {get() = 0x0}} } } ++ std::unique_ptr& runiq_ptr = uniq_ptr; ++// { dg-final { note-test runiq_ptr {std::unique_ptr = {get() = 0x0}} } } ++ + ExTuple tpl(6,7); +-// { dg-final { note-test tpl {std::tuple containing = {[1] = 6, [2] = 7}} } } ++// { dg-final { note-test tpl {std::tuple containing = {[0] = 6, [1] = 7}} } } + ExTuple &rtpl = tpl; +-// { dg-final { note-test rtpl {std::tuple containing = {[1] = 6, [2] = 7}} } } ++// { dg-final { note-test rtpl {std::tuple containing = {[0] = 6, [1] = 7}} } } ++ ++ std::error_code e0; ++ // { dg-final { note-test e0 {std::error_code = { }} } } ++ std::error_condition ec0; ++ // { dg-final { note-test ec0 {std::error_condition = { }} } } ++ std::error_code einval = std::make_error_code(std::errc::invalid_argument); ++ // { dg-final { note-test einval {std::error_code = {"generic": EINVAL}} } } ++ std::error_condition ecinval = std::make_error_condition(std::errc::invalid_argument); ++ // { dg-final { note-test ecinval {std::error_condition = {"generic": EINVAL}} } } ++ ++ custom_cat cat; ++ std::error_code emiaow(42, cat); ++ // { 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 = {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}} } } ++ ++ std::initializer_list emptyIl = {}; ++ // { dg-final { note-test emptyIl {std::initializer_list of length 0} } } ++ std::initializer_list il = {3, 4}; ++ // { dg-final { note-test il {std::initializer_list of length 2 = {3, 4}} } } ++ ++ std::atomic ai{100}; ++ // { dg-final { note-test ai {std::atomic = { 100 }} } } ++ long l{}; ++ std::atomic ap{&l}; ++ // { dg-final { regexp-test ap {std::atomic.long \*. = { 0x.* }} } } ++ struct Value { int i, j; }; ++ std::atomic av{{8, 9}}; ++ // { dg-final { note-test av {std::atomic = { {i = 8, j = 9} }} } } ++ ++ std::integral_constant one; ++ // { dg-final { note-test one {std::integral_constant} } } ++ std::integral_constant truth; ++ // { dg-final { note-test truth {std::true_type} } } ++ std::integral_constant lies; ++ // { dg-final { note-test lies {std::false_type} } } ++ + placeholder(""); // Mark SPOT + use(efl); + use(fl); +diff --git a/libstdc++-v3/testsuite/libstdc++-prettyprinters/cxx17.cc b/libstdc++-v3/testsuite/libstdc++-prettyprinters/cxx17.cc +index 0c7cb4c9bb6..c8b70622bef 100644 +--- a/libstdc++-v3/testsuite/libstdc++-prettyprinters/cxx17.cc ++++ b/libstdc++-v3/testsuite/libstdc++-prettyprinters/cxx17.cc +@@ -1,8 +1,7 @@ +-// { dg-options "-g -O0 -std=gnu++17" } ++// { dg-options "-g -O0" } + // { dg-do run { target c++17 } } +-// { dg-skip-if "" { *-*-* } { "-D_GLIBCXX_PROFILE" } } + +-// Copyright (C) 2014-2018 Free Software Foundation, Inc. ++// Copyright (C) 2014-2024 Free Software Foundation, Inc. + // + // This file is part of the GNU ISO C++ Library. This library is free + // software; you can redistribute it and/or modify it under the +@@ -19,9 +18,7 @@ + // with this library; see the file COPYING3. If not see + // . + +-// Type printers only recognize the old std::string for now. +-#define _GLIBCXX_USE_CXX11_ABI 0 +- ++#include + #include + #include + #include +@@ -41,6 +38,11 @@ using std::unordered_set; + using std::shared_ptr; + using std::weak_ptr; + ++struct X { ++ X(int) { } ++ X(const X&) { } // not trivially-copyable ++}; ++ + int + main() + { +@@ -48,18 +50,18 @@ main() + // { dg-final { note-test str "\"string\"" } } + + optional o; +-// { dg-final { note-test o {std::optional [no contained value]} } } ++// { dg-final { note-test o {std::optional [no contained value]} } } + optional ob{false}; +-// { dg-final { note-test ob {std::optional = {[contained value] = false}} } } ++// { dg-final { note-test ob {std::optional = {[contained value] = false}} } } + optional oi{5}; +-// { dg-final { note-test oi {std::optional = {[contained value] = 5}} } } ++// { dg-final { note-test oi {std::optional = {[contained value] = 5}} } } + optional op{nullptr}; +-// { dg-final { note-test op {std::optional = {[contained value] = 0x0}} } } ++// { dg-final { note-test op {std::optional = {[contained value] = 0x0}} } } + optional> om; + om = std::map{ {1, 2.}, {3, 4.}, {5, 6.} }; +-// { dg-final { note-test om {std::optional> containing std::map with 3 elements = {[1] = 2, [3] = 4, [5] = 6}} } } ++// { dg-final { regexp-test om {std::optional containing std::(__debug::)?map with 3 elements = {\[1\] = 2, \[3\] = 4, \[5\] = 6}} } } + optional os{ "stringy" }; +-// { dg-final { note-test os {std::optional = {[contained value] = "stringy"}} } } ++// { dg-final { note-test os {std::optional = {[contained value] = "stringy"}} } } + + any a; + // { dg-final { note-test a {std::any [no contained value]} } } +@@ -74,22 +76,25 @@ main() + any as2("stringiest"); + // { dg-final { regexp-test as2 {std::any containing const char \* = {\[contained value\] = 0x[[:xdigit:]]+ "stringiest"}} } } + any am = *om; +-// { dg-final { note-test am {std::any containing std::map with 3 elements = {[1] = 2, [3] = 4, [5] = 6}} } } ++// { dg-final { regexp-test am {std::any containing std::(__debug::)?map with 3 elements = {\[1\] = 2, \[3\] = 4, \[5\] = 6}} } } ++ struct local_type { int i = 99; }; ++ any al = local_type{}; ++// { dg-final { note-test al {std::any containing local_type = {[contained value] = {i = 99}}} } } + + struct S { operator int() { throw 42; }}; + variant v0; +-// { dg-final { note-test v0 {std::variant [index 0] = {0}} } } ++// { dg-final { note-test v0 {std::variant [index 0] = {0}} } } + variant v1{ 0.5f }; +-// { dg-final { note-test v1 {std::variant [index 0] = {0.5}} } } +- variant v2; ++// { dg-final { note-test v1 {std::variant [index 0] = {0.5}} } } ++ variant v2; + try { + v2.emplace<1>(S()); + } catch (int) { } +-// { dg-final { note-test v2 {std::variant [no contained value]} } } ++// { dg-final { note-test v2 {std::variant [no contained value]} } } + variant v3{ 3 }; +-// { dg-final { note-test v3 {std::variant [index 1] = {3}} } } ++// { dg-final { note-test v3 {std::variant [index 1] = {3}} } } + variant v4{ str }; +-// { dg-final { note-test v4 {std::variant [index 2] = {"string"}} } } ++// { dg-final { note-test v4 {std::variant [index 2] = {"string"}} } } + + map m{ {1, "one"} }; + map::node_type n0; +@@ -115,6 +120,13 @@ main() + // { dg-final { regexp-test q {std::shared_ptr.int \[2\]. \(use count 2, weak count 1\) = {get\(\) = 0x.*}} } } + // { dg-final { regexp-test wq {std::weak_ptr.int \[2\]. \(use count 2, weak count 1\) = {get\(\) = 0x.*}} } } + ++ std::filesystem::path path0; ++// { dg-final { note-test path0 {filesystem::path ""} } } ++ std::filesystem::path path1("filename"); ++// { dg-final { note-test path1 {filesystem::path "filename"} } } ++ std::filesystem::path path2("/dir/."); ++// { dg-final { note-test path2 {filesystem::path "/dir/." = {[root-directory] = "/", [1] = "dir", [2] = "."}} } } ++ + std::cout << "\n"; + return 0; // Mark SPOT + } +diff --git a/libstdc++-v3/testsuite/libstdc++-prettyprinters/filesystem-ts.cc b/libstdc++-v3/testsuite/libstdc++-prettyprinters/filesystem-ts.cc +index 8a1398f6c85..9faeed02852 100644 +--- a/libstdc++-v3/testsuite/libstdc++-prettyprinters/filesystem-ts.cc ++++ b/libstdc++-v3/testsuite/libstdc++-prettyprinters/filesystem-ts.cc +@@ -2,7 +2,7 @@ + // { dg-do run { target c++11 } } + // { dg-require-filesystem-ts "" } + +-// Copyright (C) 2020 Free Software Foundation, Inc. ++// Copyright (C) 2020-2024 Free Software Foundation, Inc. + // + // This file is part of the GNU ISO C++ Library. This library is free + // software; you can redistribute it and/or modify it under the +@@ -26,11 +26,11 @@ int + main() + { + std::experimental::filesystem::path path0; +-// { dg-final { note-test path0 {filesystem::path ""} } } ++// { dg-final { note-test path0 {experimental::filesystem::path ""} } } + std::experimental::filesystem::path path1("filename"); +-// { dg-final { note-test path1 {filesystem::path "filename"} } } ++// { dg-final { note-test path1 {experimental::filesystem::path "filename"} } } + std::experimental::filesystem::path path2("/dir/."); +-// { dg-final { note-test path2 {filesystem::path "/dir/." = {[root-directory] = "/", [1] = "dir", [2] = "."}} } } ++// { dg-final { note-test path2 {experimental::filesystem::path "/dir/." = {[root-directory] = "/", [1] = "dir", [2] = "."}} } } + + std::cout << "\n"; + return 0; // Mark SPOT +diff --git a/libstdc++-v3/testsuite/libstdc++-prettyprinters/libfundts.cc b/libstdc++-v3/testsuite/libstdc++-prettyprinters/libfundts.cc +index ea13ebe71ee..7a6ee8d281e 100644 +--- a/libstdc++-v3/testsuite/libstdc++-prettyprinters/libfundts.cc ++++ b/libstdc++-v3/testsuite/libstdc++-prettyprinters/libfundts.cc +@@ -1,8 +1,7 @@ + // { dg-do run { target c++14 } } + // { dg-options "-g -O0" } +-// { dg-skip-if "" { *-*-* } { "-D_GLIBCXX_PROFILE" } } + +-// Copyright (C) 2014-2018 Free Software Foundation, Inc. ++// Copyright (C) 2014-2024 Free Software Foundation, Inc. + // + // This file is part of the GNU ISO C++ Library. This library is free + // software; you can redistribute it and/or modify it under the +@@ -19,9 +18,6 @@ + // with this library; see the file COPYING3. If not see + // . + +-// Type printers only recognize the old std::string for now. +-#define _GLIBCXX_USE_CXX11_ABI 0 +- + #include + #include + #include +@@ -36,22 +32,28 @@ using std::experimental::string_view; + int + main() + { ++ // Ensure debug info for std::string is issued in the local ++ // translation unit, so that GDB won't pick up any alternate ++ // std::string notion that might be present in libstdc++.so. ++ std::string bah = "hi"; ++ (void)bah; ++ + string_view str = "string"; + // { dg-final { note-test str "\"string\"" } } + + optional o; +-// { dg-final { note-test o {std::experimental::optional [no contained value]} } } ++// { dg-final { note-test o {std::experimental::optional [no contained value]} } } + optional ob{false}; +-// { dg-final { note-test ob {std::experimental::optional = {[contained value] = false}} } } ++// { dg-final { note-test ob {std::experimental::optional = {[contained value] = false}} } } + optional oi{5}; +-// { dg-final { note-test oi {std::experimental::optional = {[contained value] = 5}} } } ++// { dg-final { note-test oi {std::experimental::optional = {[contained value] = 5}} } } + optional op{nullptr}; +-// { dg-final { note-test op {std::experimental::optional = {[contained value] = 0x0}} } } ++// { dg-final { note-test op {std::experimental::optional = {[contained value] = 0x0}} } } + optional> om; + om = std::map{ {1, 2.}, {3, 4.}, {5, 6.} }; +-// { dg-final { note-test om {std::experimental::optional> containing std::map with 3 elements = {[1] = 2, [3] = 4, [5] = 6}} } } ++// { dg-final { regexp-test om {std::experimental::optional containing std::(__debug::)?map with 3 elements = {\[1\] = 2, \[3\] = 4, \[5\] = 6}} } } + optional os{ "stringy" }; +-// { dg-final { note-test os {std::experimental::optional = {[contained value] = "stringy"}} } } ++// { dg-final { note-test os {std::experimental::optional = {[contained value] = "stringy"}} } } + + any a; + // { dg-final { note-test a {std::experimental::any [no contained value]} } } +@@ -66,7 +68,7 @@ main() + any as2("stringiest"); + // { dg-final { regexp-test as2 {std::experimental::any containing const char \* = {\[contained value\] = 0x[[:xdigit:]]+ "stringiest"}} } } + any am = *om; +-// { dg-final { note-test am {std::experimental::any containing std::map with 3 elements = {[1] = 2, [3] = 4, [5] = 6}} } } ++// { dg-final { regexp-test am {std::experimental::any containing std::(__debug::)?map with 3 elements = {\[1\] = 2, \[3\] = 4, \[5\] = 6}} } } + + std::cout << "\n"; + return 0; // Mark SPOT diff --git a/gcc8-libstdc++-prettyprinter-update-14.patch b/gcc8-libstdc++-prettyprinter-update-14.patch new file mode 100644 index 0000000..f477ae1 --- /dev/null +++ b/gcc8-libstdc++-prettyprinter-update-14.patch @@ -0,0 +1,4166 @@ + libstdc++-v3/python/libstdcxx/v6/__init__.py | 4 +- + libstdc++-v3/python/libstdcxx/v6/printers.py | 2593 +++++++++++++++++++------- + libstdc++-v3/python/libstdcxx/v6/xmethods.py | 170 +- + 3 files changed, 2017 insertions(+), 750 deletions(-) + +diff --git a/libstdc++-v3/python/libstdcxx/v6/__init__.py b/libstdc++-v3/python/libstdcxx/v6/__init__.py +index b001c8520b6..f40acd922af 100644 +--- a/libstdc++-v3/python/libstdcxx/v6/__init__.py ++++ b/libstdc++-v3/python/libstdcxx/v6/__init__.py +@@ -1,4 +1,4 @@ +-# Copyright (C) 2014-2018 Free Software Foundation, Inc. ++# Copyright (C) 2014-2024 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 +@@ -13,8 +13,6 @@ + # You should have received a copy of the GNU General Public License + # along with this program. If not, see . + +-import gdb +- + # Load the xmethods if GDB supports them. + def gdb_has_xmethods(): + try: +diff --git a/libstdc++-v3/python/libstdcxx/v6/printers.py b/libstdc++-v3/python/libstdcxx/v6/printers.py +index ba9bbc096a0..3026de35bbd 100644 +--- a/libstdc++-v3/python/libstdcxx/v6/printers.py ++++ b/libstdc++-v3/python/libstdcxx/v6/printers.py +@@ -1,6 +1,6 @@ + # Pretty-printers for libstdc++. + +-# Copyright (C) 2008-2018 Free Software Foundation, Inc. ++# Copyright (C) 2008-2024 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 +@@ -19,8 +19,10 @@ import gdb + import itertools + import re + import sys ++import errno ++import datetime + +-### Python 2 + Python 3 compatibility code ++# Python 2 + Python 3 compatibility code + + # Resources about compatibility: + # +@@ -37,15 +39,16 @@ import sys + # + + if sys.version_info[0] > 2: +- ### Python 3 stuff ++ # Python 3 stuff + Iterator = object + # Python 3 folds these into the normal functions. + imap = map + izip = zip + # Also, int subsumes long + long = int ++ _utc_timezone = datetime.timezone.utc + else: +- ### Python 2 stuff ++ # Python 2 stuff + class Iterator: + """Compatibility mixin for iterators + +@@ -63,6 +66,20 @@ else: + # In Python 2, we still need these from itertools + from itertools import imap, izip + ++ # Python 2 does not provide the datetime.UTC singleton. ++ class UTC(datetime.tzinfo): ++ """Concrete tzinfo class representing the UTC time zone.""" ++ ++ def utcoffset(self, dt): ++ return datetime.timedelta(0) ++ ++ def tzname(self, dt): ++ return "UTC" ++ ++ def dst(self, dt): ++ return datetime.timedelta(0) ++ _utc_timezone = UTC() ++ + # Try to use the new-style pretty-printing if available. + _use_gdb_pp = True + try: +@@ -79,14 +96,22 @@ try: + except ImportError: + pass + ++# Use the base class if available. ++if hasattr(gdb, 'ValuePrinter'): ++ printer_base = gdb.ValuePrinter ++else: ++ printer_base = object ++ + # Starting with the type ORIG, search for the member type NAME. This + # handles searching upward through superclasses. This is needed to + # work around http://sourceware.org/bugzilla/show_bug.cgi?id=13615. ++ ++ + def find_type(orig, name): + typ = orig.strip_typedefs() + while True: +- # Strip cv-qualifiers. PR 67440. +- search = '%s::%s' % (typ.unqualified(), name) ++ # Use Type.tag to ignore cv-qualifiers. PR 67440. ++ search = '%s::%s' % (typ.tag, name) + try: + return gdb.lookup_type(search) + except RuntimeError: +@@ -94,39 +119,131 @@ def find_type(orig, name): + # The type was not found, so try the superclass. We only need + # to check the first superclass, so we don't bother with + # anything fancier here. +- field = typ.fields()[0] +- if not field.is_base_class: ++ fields = typ.fields() ++ if len(fields) and fields[0].is_base_class: ++ typ = fields[0].type ++ else: + raise ValueError("Cannot find type %s::%s" % (str(orig), name)) +- typ = field.type ++ + + _versioned_namespace = '__8::' + +-def is_specialization_of(type, template_name): +- "Test if a type is a given template instantiation." ++ ++def lookup_templ_spec(templ, *args): ++ """ ++ Lookup template specialization templ. ++ """ ++ t = '{}<{}>'.format(templ, ', '.join([str(a) for a in args])) ++ try: ++ return gdb.lookup_type(t) ++ except gdb.error as e: ++ # Type not found, try again in versioned namespace. ++ global _versioned_namespace ++ if _versioned_namespace not in templ: ++ t = t.replace('::', '::' + _versioned_namespace, 1) ++ try: ++ return gdb.lookup_type(t) ++ except gdb.error: ++ # If that also fails, rethrow the original exception ++ pass ++ raise e ++ ++# Use this to find container node types instead of find_type, ++# see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91997 for details. ++def lookup_node_type(nodename, containertype): ++ """ ++ Lookup specialization of template nodename corresponding to containertype. ++ ++ nodename - The name of a class template, as a String ++ containertype - The container, as a gdb.Type ++ ++ Return a gdb.Type for the corresponding specialization of nodename, ++ or None if the type cannot be found. ++ ++ e.g. lookup_node_type('_List_node', gdb.lookup_type('std::list')) ++ will return a gdb.Type for the type std::_List_node. ++ """ ++ # If nodename is unqualified, assume it's in namespace std. ++ if '::' not in nodename: ++ nodename = 'std::' + nodename ++ # Use either containertype's value_type or its first template argument. ++ try: ++ valtype = find_type(containertype, 'value_type') ++ except: ++ valtype = containertype.template_argument(0) ++ valtype = valtype.strip_typedefs() ++ try: ++ return lookup_templ_spec(nodename, valtype) ++ except gdb.error: ++ # For debug mode containers the node is in std::__cxx1998. ++ if is_member_of_namespace(nodename, 'std'): ++ if is_member_of_namespace(containertype, 'std::__cxx1998', ++ 'std::__debug', '__gnu_debug'): ++ nodename = nodename.replace('::', '::__cxx1998::', 1) ++ try: ++ return lookup_templ_spec(nodename, valtype) ++ except gdb.error: ++ pass ++ return None ++ ++ ++def is_member_of_namespace(typ, *namespaces): ++ """ ++ Test whether a type is a member of one of the specified namespaces. ++ The type can be specified as a string or a gdb.Type object. ++ """ ++ if isinstance(typ, gdb.Type): ++ typ = str(typ) ++ typ = strip_versioned_namespace(typ) ++ for namespace in namespaces: ++ if typ.startswith(namespace + '::'): ++ return True ++ return False ++ ++ ++def is_specialization_of(x, template_name): ++ """ ++ Test whether a type is a specialization of the named class template. ++ The type can be specified as a string or a gdb.Type object. ++ The template should be the name of a class template as a string, ++ without any 'std' qualification. ++ """ + global _versioned_namespace +- if _versioned_namespace: +- return re.match('^std::(%s)?%s<.*>$' % (_versioned_namespace, template_name), type) is not None +- return re.match('^std::%s<.*>$' % template_name, type) is not None ++ if isinstance(x, gdb.Type): ++ x = x.tag ++ template_name = '(%s)?%s' % (_versioned_namespace, template_name) ++ return re.match('^std::%s<.*>$' % template_name, x) is not None ++ + + def strip_versioned_namespace(typename): + global _versioned_namespace +- if _versioned_namespace: +- return typename.replace(_versioned_namespace, '') +- return typename ++ return typename.replace(_versioned_namespace, '') ++ ++ ++def strip_fundts_namespace(typ): ++ """Remove "fundamentals_vN" inline namespace from qualified type name.""" ++ pattern = r'^std::experimental::fundamentals_v\d::' ++ repl = 'std::experimental::' ++ if sys.version_info[0] == 2: ++ return re.sub(pattern, repl, typ, 1) ++ else: # Technically this needs Python 3.1 but nobody should be using 3.0 ++ return re.sub(pattern, repl, typ, count=1) ++ + + def strip_inline_namespaces(type_str): +- "Remove known inline namespaces from the canonical name of a type." ++ """Remove known inline namespaces from the canonical name of a type.""" + type_str = strip_versioned_namespace(type_str) + type_str = type_str.replace('std::__cxx11::', 'std::') + expt_ns = 'std::experimental::' + for lfts_ns in ('fundamentals_v1', 'fundamentals_v2'): +- type_str = type_str.replace(expt_ns+lfts_ns+'::', expt_ns) ++ type_str = type_str.replace(expt_ns + lfts_ns + '::', expt_ns) + fs_ns = expt_ns + 'filesystem::' +- type_str = type_str.replace(fs_ns+'v1::', fs_ns) ++ type_str = type_str.replace(fs_ns + 'v1::', fs_ns) + return type_str + ++ + def get_template_arg_list(type_obj): +- "Return a type's template arguments as a list" ++ """Return a type's template arguments as a list.""" + n = 0 + template_args = [] + while True: +@@ -136,78 +253,140 @@ def get_template_arg_list(type_obj): + return template_args + n += 1 + ++ + class SmartPtrIterator(Iterator): +- "An iterator for smart pointer types with a single 'child' value" ++ """An iterator for smart pointer types with a single 'child' value.""" + + def __init__(self, val): +- self.val = val ++ self._val = val + + def __iter__(self): + return self + + def __next__(self): +- if self.val is None: ++ if self._val is None: + raise StopIteration +- self.val, val = None, self.val ++ self._val, val = None, self._val + return ('get()', val) + +-class SharedPointerPrinter: +- "Print a shared_ptr or weak_ptr" + +- def __init__ (self, typename, val): +- self.typename = strip_versioned_namespace(typename) +- self.val = val +- self.pointer = val['_M_ptr'] ++class SharedPointerPrinter(printer_base): ++ """ ++ Print a shared_ptr, weak_ptr, atomic, or atomic. ++ """ ++ ++ def __init__(self, typename, val): ++ self._typename = strip_versioned_namespace(typename) ++ self._val = val ++ self._pointer = val['_M_ptr'] + +- def children (self): +- return SmartPtrIterator(self.pointer) ++ def children(self): ++ return SmartPtrIterator(self._pointer) ++ ++ # Return the _Sp_counted_base<>* that holds the refcounts. ++ def _get_refcounts(self): ++ if self._typename == 'std::atomic': ++ # A tagged pointer is stored as uintptr_t. ++ ptr_val = self._val['_M_refcount']['_M_val']['_M_i'] ++ ptr_val = ptr_val - (ptr_val % 2) # clear lock bit ++ ptr_type = find_type(self._val['_M_refcount'].type, 'pointer') ++ return ptr_val.cast(ptr_type) ++ return self._val['_M_refcount']['_M_pi'] + +- def to_string (self): ++ def to_string(self): + state = 'empty' +- refcounts = self.val['_M_refcount']['_M_pi'] ++ refcounts = self._get_refcounts() ++ targ = self._val.type.template_argument(0) ++ targ = strip_versioned_namespace(str(targ)) ++ + if refcounts != 0: + usecount = refcounts['_M_use_count'] + weakcount = refcounts['_M_weak_count'] + if usecount == 0: + state = 'expired, weak count %d' % weakcount + else: +- state = 'use count %d, weak count %d' % (usecount, weakcount - 1) +- return '%s<%s> (%s)' % (self.typename, str(self.val.type.template_argument(0)), state) +- +-class UniquePointerPrinter: +- "Print a unique_ptr" +- +- def __init__ (self, typename, val): +- self.val = val +- impl_type = val.type.fields()[0].type.strip_typedefs() +- if is_specialization_of(str(impl_type), '__uniq_ptr_impl'): # New implementation +- tuple_member = val['_M_t']['_M_t'] +- elif is_specialization_of(str(impl_type), 'tuple'): +- tuple_member = val['_M_t'] +- else: +- raise ValueError("Unsupported implementation for unique_ptr: %s" % str(impl_type)) +- tuple_impl_type = tuple_member.type.fields()[0].type # _Tuple_impl +- tuple_head_type = tuple_impl_type.fields()[1].type # _Head_base +- head_field = tuple_head_type.fields()[0] +- if head_field.name == '_M_head_impl': +- self.pointer = tuple_member['_M_head_impl'] +- elif head_field.is_base_class: +- self.pointer = tuple_member.cast(head_field.type) +- else: +- raise ValueError("Unsupported implementation for tuple in unique_ptr: %s" % str(impl_type)) ++ state = 'use count %d, weak count %d' % ( ++ usecount, weakcount - 1) ++ return '%s<%s> (%s)' % (self._typename, targ, state) ++ ++ ++def _tuple_impl_get(val): ++ """Return the tuple element stored in a _Tuple_impl base class.""" ++ bases = val.type.fields() ++ if not bases[-1].is_base_class: ++ raise ValueError( ++ "Unsupported implementation for std::tuple: %s" % str(val.type)) ++ # Get the _Head_base base class: ++ head_base = val.cast(bases[-1].type) ++ fields = head_base.type.fields() ++ if len(fields) == 0: ++ raise ValueError( ++ "Unsupported implementation for std::tuple: %s" % str(val.type)) ++ if fields[0].name == '_M_head_impl': ++ # The tuple element is the _Head_base::_M_head_impl data member. ++ return head_base['_M_head_impl'] ++ elif fields[0].is_base_class: ++ # The tuple element is an empty base class of _Head_base. ++ # Cast to that empty base class. ++ return head_base.cast(fields[0].type) ++ else: ++ raise ValueError( ++ "Unsupported implementation for std::tuple: %s" % str(val.type)) ++ ++ ++def tuple_get(n, val): ++ """Return the result of std::get(val) on a std::tuple.""" ++ tuple_size = len(get_template_arg_list(val.type)) ++ if n > tuple_size: ++ raise ValueError("Out of range index for std::get on std::tuple") ++ # Get the first _Tuple_impl<0, T...> base class: ++ node = val.cast(val.type.fields()[0].type) ++ while n > 0: ++ # Descend through the base classes until the Nth one. ++ node = node.cast(node.type.fields()[0].type) ++ n -= 1 ++ return _tuple_impl_get(node) ++ ++ ++def unique_ptr_get(val): ++ """Return the result of val.get() on a std::unique_ptr.""" ++ # std::unique_ptr contains a std::tuple, ++ # either as a direct data member _M_t (the old implementation) ++ # or within a data member of type __uniq_ptr_data. ++ impl_type = val.type.fields()[0].type.strip_typedefs() ++ # Check for new implementations first: ++ if is_specialization_of(impl_type, '__uniq_ptr_data') \ ++ or is_specialization_of(impl_type, '__uniq_ptr_impl'): ++ tuple_member = val['_M_t']['_M_t'] ++ elif is_specialization_of(impl_type, 'tuple'): ++ tuple_member = val['_M_t'] ++ else: ++ raise ValueError( ++ "Unsupported implementation for unique_ptr: %s" % str(impl_type)) ++ return tuple_get(0, tuple_member) ++ + +- def children (self): +- return SmartPtrIterator(self.pointer) ++class UniquePointerPrinter(printer_base): ++ """Print a unique_ptr.""" ++ ++ def __init__(self, typename, val): ++ self._val = val ++ ++ def children(self): ++ return SmartPtrIterator(unique_ptr_get(self._val)) ++ ++ def to_string(self): ++ t = self._val.type.template_argument(0) ++ return 'std::unique_ptr<{}>'.format(str(t)) + +- def to_string (self): +- return ('std::unique_ptr<%s>' % (str(self.val.type.template_argument(0)))) + + def get_value_from_aligned_membuf(buf, valtype): +- """Returns the value held in a __gnu_cxx::__aligned_membuf.""" ++ """Return the value held in a __gnu_cxx::__aligned_membuf.""" + return buf['_M_storage'].address.cast(valtype.pointer()).dereference() + ++ + def get_value_from_list_node(node): +- """Returns the value held in an _List_node<_Val>""" ++ """Return the value held in an _List_node<_Val>.""" + try: + member = node.type.fields()[1].name + if member == '_M_data': +@@ -221,240 +400,281 @@ def get_value_from_list_node(node): + pass + raise ValueError("Unsupported implementation for %s" % str(node.type)) + +-class StdListPrinter: +- "Print a std::list" ++ ++class StdListPrinter(printer_base): ++ """Print a std::list.""" + + class _iterator(Iterator): + def __init__(self, nodetype, head): +- self.nodetype = nodetype +- self.base = head['_M_next'] +- self.head = head.address +- self.count = 0 ++ self._nodetype = nodetype ++ self._base = head['_M_next'] ++ self._head = head.address ++ self._count = 0 + + def __iter__(self): + return self + + def __next__(self): +- if self.base == self.head: ++ if self._base == self._head: + raise StopIteration +- elt = self.base.cast(self.nodetype).dereference() +- self.base = elt['_M_next'] +- count = self.count +- self.count = self.count + 1 ++ elt = self._base.cast(self._nodetype).dereference() ++ self._base = elt['_M_next'] ++ count = self._count ++ self._count = self._count + 1 + val = get_value_from_list_node(elt) + return ('[%d]' % count, val) + + def __init__(self, typename, val): +- self.typename = strip_versioned_namespace(typename) +- self.val = val ++ self._typename = strip_versioned_namespace(typename) ++ self._val = val + + def children(self): +- nodetype = find_type(self.val.type, '_Node') +- nodetype = nodetype.strip_typedefs().pointer() +- return self._iterator(nodetype, self.val['_M_impl']['_M_node']) ++ nodetype = lookup_node_type('_List_node', self._val.type).pointer() ++ return self._iterator(nodetype, self._val['_M_impl']['_M_node']) + + def to_string(self): +- if self.val['_M_impl']['_M_node'].address == self.val['_M_impl']['_M_node']['_M_next']: +- return 'empty %s' % (self.typename) +- return '%s' % (self.typename) ++ headnode = self._val['_M_impl']['_M_node'] ++ if headnode['_M_next'] == headnode.address: ++ return 'empty %s' % (self._typename) ++ return '%s' % (self._typename) + +-class NodeIteratorPrinter: +- def __init__(self, typename, val, contname): +- self.val = val +- self.typename = typename +- self.contname = contname ++ ++class NodeIteratorPrinter(printer_base): ++ def __init__(self, typename, val, contname, nodename): ++ self._val = val ++ self._typename = typename ++ self._contname = contname ++ self._nodetype = lookup_node_type(nodename, val.type) + + def to_string(self): +- if not self.val['_M_node']: +- return 'non-dereferenceable iterator for std::%s' % (self.contname) +- nodetype = find_type(self.val.type, '_Node') +- nodetype = nodetype.strip_typedefs().pointer() +- node = self.val['_M_node'].cast(nodetype).dereference() ++ if not self._val['_M_node']: ++ return 'non-dereferenceable iterator for std::%s' % (self._contname) ++ node = self._val['_M_node'].cast( ++ self._nodetype.pointer()).dereference() + return str(get_value_from_list_node(node)) + ++ + class StdListIteratorPrinter(NodeIteratorPrinter): +- "Print std::list::iterator" ++ """Print std::list::iterator.""" + + def __init__(self, typename, val): +- NodeIteratorPrinter.__init__(self, typename, val, 'list') ++ NodeIteratorPrinter.__init__(self, typename, val, 'list', '_List_node') ++ + + class StdFwdListIteratorPrinter(NodeIteratorPrinter): +- "Print std::forward_list::iterator" ++ """Print std::forward_list::iterator.""" + + def __init__(self, typename, val): +- NodeIteratorPrinter.__init__(self, typename, val, 'forward_list') ++ NodeIteratorPrinter.__init__(self, typename, val, 'forward_list', ++ '_Fwd_list_node') + +-class StdSlistPrinter: +- "Print a __gnu_cxx::slist" ++ ++class StdSlistPrinter(printer_base): ++ """Print a __gnu_cxx::slist.""" + + class _iterator(Iterator): + def __init__(self, nodetype, head): +- self.nodetype = nodetype +- self.base = head['_M_head']['_M_next'] +- self.count = 0 ++ self._nodetype = nodetype ++ self._base = head['_M_head']['_M_next'] ++ self._count = 0 + + def __iter__(self): + return self + + def __next__(self): +- if self.base == 0: ++ if self._base == 0: + raise StopIteration +- elt = self.base.cast(self.nodetype).dereference() +- self.base = elt['_M_next'] +- count = self.count +- self.count = self.count + 1 ++ elt = self._base.cast(self._nodetype).dereference() ++ self._base = elt['_M_next'] ++ count = self._count ++ self._count = self._count + 1 + return ('[%d]' % count, elt['_M_data']) + + def __init__(self, typename, val): +- self.val = val ++ self._val = val + + def children(self): +- nodetype = find_type(self.val.type, '_Node') +- nodetype = nodetype.strip_typedefs().pointer() +- return self._iterator(nodetype, self.val) ++ nodetype = lookup_node_type('__gnu_cxx::_Slist_node', self._val.type) ++ return self._iterator(nodetype.pointer(), self._val) + + def to_string(self): +- if self.val['_M_head']['_M_next'] == 0: ++ if self._val['_M_head']['_M_next'] == 0: + return 'empty __gnu_cxx::slist' + return '__gnu_cxx::slist' + +-class StdSlistIteratorPrinter: +- "Print __gnu_cxx::slist::iterator" ++ ++class StdSlistIteratorPrinter(printer_base): ++ """Print __gnu_cxx::slist::iterator.""" + + def __init__(self, typename, val): +- self.val = val ++ self._val = val + + def to_string(self): +- if not self.val['_M_node']: ++ if not self._val['_M_node']: + return 'non-dereferenceable iterator for __gnu_cxx::slist' +- nodetype = find_type(self.val.type, '_Node') +- nodetype = nodetype.strip_typedefs().pointer() +- return str(self.val['_M_node'].cast(nodetype).dereference()['_M_data']) ++ nodetype = lookup_node_type( ++ '__gnu_cxx::_Slist_node', self._val.type).pointer() ++ return str(self._val['_M_node'].cast(nodetype).dereference()['_M_data']) ++ + +-class StdVectorPrinter: +- "Print a std::vector" ++class StdVectorPrinter(printer_base): ++ """Print a std::vector.""" + + class _iterator(Iterator): +- def __init__ (self, start, finish, bitvec): +- self.bitvec = bitvec ++ def __init__(self, start, finish, bitvec): ++ self._bitvec = bitvec + if bitvec: +- self.item = start['_M_p'] +- self.so = start['_M_offset'] +- self.finish = finish['_M_p'] +- self.fo = finish['_M_offset'] +- itype = self.item.dereference().type +- self.isize = 8 * itype.sizeof ++ self._item = start['_M_p'] ++ self._so = 0 ++ self._finish = finish['_M_p'] ++ self._fo = finish['_M_offset'] ++ itype = self._item.dereference().type ++ self._isize = 8 * itype.sizeof + else: +- self.item = start +- self.finish = finish +- self.count = 0 ++ self._item = start ++ self._finish = finish ++ self._count = 0 + + def __iter__(self): + return self + + def __next__(self): +- count = self.count +- self.count = self.count + 1 +- if self.bitvec: +- if self.item == self.finish and self.so >= self.fo: ++ count = self._count ++ self._count = self._count + 1 ++ if self._bitvec: ++ if self._item == self._finish and self._so >= self._fo: + raise StopIteration +- elt = self.item.dereference() +- if elt & (1 << self.so): +- obit = 1 +- else: +- obit = 0 +- self.so = self.so + 1 +- if self.so >= self.isize: +- self.item = self.item + 1 +- self.so = 0 +- return ('[%d]' % count, obit) ++ elt = bool(self._item.dereference() & (1 << self._so)) ++ self._so = self._so + 1 ++ if self._so >= self._isize: ++ self._item = self._item + 1 ++ self._so = 0 ++ return ('[%d]' % count, elt) + else: +- if self.item == self.finish: ++ if self._item == self._finish: + raise StopIteration +- elt = self.item.dereference() +- self.item = self.item + 1 ++ elt = self._item.dereference() ++ self._item = self._item + 1 + return ('[%d]' % count, elt) + + def __init__(self, typename, val): +- self.typename = strip_versioned_namespace(typename) +- self.val = val +- self.is_bool = val.type.template_argument(0).code == gdb.TYPE_CODE_BOOL ++ self._typename = strip_versioned_namespace(typename) ++ self._val = val ++ self._is_bool = val.type.template_argument( ++ 0).code == gdb.TYPE_CODE_BOOL + + def children(self): +- return self._iterator(self.val['_M_impl']['_M_start'], +- self.val['_M_impl']['_M_finish'], +- self.is_bool) ++ return self._iterator(self._val['_M_impl']['_M_start'], ++ self._val['_M_impl']['_M_finish'], ++ self._is_bool) + + def to_string(self): +- start = self.val['_M_impl']['_M_start'] +- finish = self.val['_M_impl']['_M_finish'] +- end = self.val['_M_impl']['_M_end_of_storage'] +- if self.is_bool: +- start = self.val['_M_impl']['_M_start']['_M_p'] +- so = self.val['_M_impl']['_M_start']['_M_offset'] +- finish = self.val['_M_impl']['_M_finish']['_M_p'] +- fo = self.val['_M_impl']['_M_finish']['_M_offset'] ++ start = self._val['_M_impl']['_M_start'] ++ finish = self._val['_M_impl']['_M_finish'] ++ end = self._val['_M_impl']['_M_end_of_storage'] ++ if self._is_bool: ++ start = self._val['_M_impl']['_M_start']['_M_p'] ++ finish = self._val['_M_impl']['_M_finish']['_M_p'] ++ fo = self._val['_M_impl']['_M_finish']['_M_offset'] + itype = start.dereference().type + bl = 8 * itype.sizeof +- length = (bl - so) + bl * ((finish - start) - 1) + fo ++ length = bl * (finish - start) + fo + capacity = bl * (end - start) + return ('%s of length %d, capacity %d' +- % (self.typename, int (length), int (capacity))) ++ % (self._typename, int(length), int(capacity))) + else: + return ('%s of length %d, capacity %d' +- % (self.typename, int (finish - start), int (end - start))) ++ % (self._typename, int(finish - start), int(end - start))) + + def display_hint(self): + return 'array' + +-class StdVectorIteratorPrinter: +- "Print std::vector::iterator" ++ ++class StdVectorIteratorPrinter(printer_base): ++ """Print std::vector::iterator.""" + + def __init__(self, typename, val): +- self.val = val ++ self._val = val + + def to_string(self): +- if not self.val['_M_current']: ++ if not self._val['_M_current']: + return 'non-dereferenceable iterator for std::vector' +- return str(self.val['_M_current'].dereference()) ++ return str(self._val['_M_current'].dereference()) ++ ++ ++class StdBitIteratorPrinter(printer_base): ++ """Print std::vector's _Bit_iterator and _Bit_const_iterator.""" ++ ++ def __init__(self, typename, val): ++ self._val = val ++ ++ def to_string(self): ++ if not self._val['_M_p']: ++ return 'non-dereferenceable iterator for std::vector' ++ return bool(self._val['_M_p'].dereference() ++ & (1 << self._val['_M_offset'])) + +-class StdTuplePrinter: +- "Print a std::tuple" ++ ++class StdBitReferencePrinter(printer_base): ++ """Print std::vector::reference.""" ++ ++ def __init__(self, typename, val): ++ self._val = val ++ ++ def to_string(self): ++ if not self._val['_M_p']: ++ return 'invalid std::vector::reference' ++ return bool(self._val['_M_p'].dereference() & (self._val['_M_mask'])) ++ ++ ++class StdTuplePrinter(printer_base): ++ """Print a std::tuple.""" + + class _iterator(Iterator): +- def __init__ (self, head): +- self.head = head ++ @staticmethod ++ def _is_nonempty_tuple(nodes): ++ if len(nodes) == 2: ++ if is_specialization_of(nodes[1].type, '__tuple_base'): ++ return True ++ elif len(nodes) == 1: ++ return True ++ elif len(nodes) == 0: ++ return False ++ raise ValueError( ++ "Top of tuple tree does not consist of a single node.") ++ ++ def __init__(self, head): ++ self._head = head + + # Set the base class as the initial head of the + # tuple. +- nodes = self.head.type.fields () +- if len (nodes) == 1: ++ nodes = self._head.type.fields() ++ if self._is_nonempty_tuple(nodes): + # Set the actual head to the first pair. +- self.head = self.head.cast (nodes[0].type) +- elif len (nodes) != 0: +- raise ValueError("Top of tuple tree does not consist of a single node.") +- self.count = 0 ++ self._head = self._head.cast(nodes[0].type) ++ self._count = 0 + +- def __iter__ (self): ++ def __iter__(self): + return self + +- def __next__ (self): ++ def __next__(self): + # Check for further recursions in the inheritance tree. +- # For a GCC 5+ tuple self.head is None after visiting all nodes: +- if not self.head: ++ # For a GCC 5+ tuple self._head is None after visiting all nodes: ++ if not self._head: + raise StopIteration +- nodes = self.head.type.fields () ++ nodes = self._head.type.fields() + # For a GCC 4.x tuple there is a final node with no fields: +- if len (nodes) == 0: ++ if len(nodes) == 0: + raise StopIteration + # Check that this iteration has an expected structure. +- if len (nodes) > 2: +- raise ValueError("Cannot parse more than 2 nodes in a tuple tree.") ++ if len(nodes) > 2: ++ raise ValueError( ++ "Cannot parse more than 2 nodes in a tuple tree.") + +- if len (nodes) == 1: ++ if len(nodes) == 1: + # This is the last node of a GCC 5+ std::tuple. +- impl = self.head.cast (nodes[0].type) +- self.head = None ++ impl = self._head.cast(nodes[0].type) ++ self._head = None + else: + # Either a node before the last node, or the last node of + # a GCC 4.x tuple (which has an empty parent). +@@ -463,53 +683,55 @@ class StdTuplePrinter: + # - Right node is the actual class contained in the tuple. + + # Process right node. +- impl = self.head.cast (nodes[1].type) ++ impl = self._head.cast(nodes[1].type) + + # Process left node and set it as head. +- self.head = self.head.cast (nodes[0].type) ++ self._head = self._head.cast(nodes[0].type) + +- self.count = self.count + 1 ++ self._count = self._count + 1 + + # Finally, check the implementation. If it is + # wrapped in _M_head_impl return that, otherwise return + # the value "as is". +- fields = impl.type.fields () +- if len (fields) < 1 or fields[0].name != "_M_head_impl": +- return ('[%d]' % self.count, impl) ++ fields = impl.type.fields() ++ if len(fields) < 1 or fields[0].name != "_M_head_impl": ++ return ('[%d]' % (self._count - 1), impl) + else: +- return ('[%d]' % self.count, impl['_M_head_impl']) ++ return ('[%d]' % (self._count - 1), impl['_M_head_impl']) + +- def __init__ (self, typename, val): +- self.typename = strip_versioned_namespace(typename) +- self.val = val; ++ def __init__(self, typename, val): ++ self._typename = strip_versioned_namespace(typename) ++ self._val = val + +- def children (self): +- return self._iterator (self.val) ++ def children(self): ++ return self._iterator(self._val) + +- def to_string (self): +- if len (self.val.type.fields ()) == 0: +- return 'empty %s' % (self.typename) +- return '%s containing' % (self.typename) ++ def to_string(self): ++ if len(self._val.type.fields()) == 0: ++ return 'empty %s' % (self._typename) ++ return '%s containing' % (self._typename) + +-class StdStackOrQueuePrinter: +- "Print a std::stack or std::queue" + +- def __init__ (self, typename, val): +- self.typename = strip_versioned_namespace(typename) +- self.visualizer = gdb.default_visualizer(val['c']) ++class StdStackOrQueuePrinter(printer_base): ++ """Print a std::stack or std::queue.""" + +- def children (self): +- return self.visualizer.children() ++ def __init__(self, typename, val): ++ self._typename = strip_versioned_namespace(typename) ++ self._visualizer = gdb.default_visualizer(val['c']) + +- def to_string (self): +- return '%s wrapping: %s' % (self.typename, +- self.visualizer.to_string()) ++ def children(self): ++ return self._visualizer.children() ++ ++ def to_string(self): ++ return '%s wrapping: %s' % (self._typename, ++ self._visualizer.to_string()) + +- def display_hint (self): +- if hasattr (self.visualizer, 'display_hint'): +- return self.visualizer.display_hint () ++ def display_hint(self): ++ if hasattr(self._visualizer, 'display_hint'): ++ return self._visualizer.display_hint() + return None + ++ + class RbtreeIterator(Iterator): + """ + Turn an RB-tree-based container (std::map, std::set etc.) into +@@ -517,24 +739,24 @@ class RbtreeIterator(Iterator): + """ + + def __init__(self, rbtree): +- self.size = rbtree['_M_t']['_M_impl']['_M_node_count'] +- self.node = rbtree['_M_t']['_M_impl']['_M_header']['_M_left'] +- self.count = 0 ++ self._size = rbtree['_M_t']['_M_impl']['_M_node_count'] ++ self._node = rbtree['_M_t']['_M_impl']['_M_header']['_M_left'] ++ self._count = 0 + + def __iter__(self): + return self + + def __len__(self): +- return int (self.size) ++ return int(self._size) + + def __next__(self): +- if self.count == self.size: ++ if self._count == self._size: + raise StopIteration +- result = self.node +- self.count = self.count + 1 +- if self.count < self.size: ++ result = self._node ++ self._count = self._count + 1 ++ if self._count < self._size: + # Compute the next node. +- node = self.node ++ node = self._node + if node.dereference()['_M_right']: + node = node.dereference()['_M_right'] + while node.dereference()['_M_left']: +@@ -546,11 +768,12 @@ class RbtreeIterator(Iterator): + parent = parent.dereference()['_M_parent'] + if node.dereference()['_M_right'] != parent: + node = parent +- self.node = node ++ self._node = node + return result + ++ + def get_value_from_Rb_tree_node(node): +- """Returns the value held in an _Rb_tree_node<_Val>""" ++ """Return the value held in an _Rb_tree_node<_Val>.""" + try: + member = node.type.fields()[1].name + if member == '_M_value_field': +@@ -567,143 +790,142 @@ def get_value_from_Rb_tree_node(node): + # This is a pretty printer for std::_Rb_tree_iterator (which is + # std::map::iterator), and has nothing to do with the RbtreeIterator + # class above. +-class StdRbtreeIteratorPrinter: +- "Print std::map::iterator, std::set::iterator, etc." +- +- def __init__ (self, typename, val): +- self.val = val +- valtype = self.val.type.template_argument(0).strip_typedefs() +- nodetype = '_Rb_tree_node<' + str(valtype) + '>' +- if _versioned_namespace and typename.startswith('std::' + _versioned_namespace): +- nodetype = _versioned_namespace + nodetype +- nodetype = gdb.lookup_type('std::' + nodetype) +- self.link_type = nodetype.strip_typedefs().pointer() +- +- def to_string (self): +- if not self.val['_M_node']: ++ ++ ++class StdRbtreeIteratorPrinter(printer_base): ++ """Print std::map::iterator, std::set::iterator, etc.""" ++ ++ def __init__(self, typename, val): ++ self._val = val ++ nodetype = lookup_node_type('_Rb_tree_node', self._val.type) ++ self._link_type = nodetype.pointer() ++ ++ def to_string(self): ++ if not self._val['_M_node']: + return 'non-dereferenceable iterator for associative container' +- node = self.val['_M_node'].cast(self.link_type).dereference() ++ node = self._val['_M_node'].cast(self._link_type).dereference() + return str(get_value_from_Rb_tree_node(node)) + +-class StdDebugIteratorPrinter: +- "Print a debug enabled version of an iterator" + +- def __init__ (self, typename, val): +- self.val = val ++class StdDebugIteratorPrinter(printer_base): ++ """Print a debug enabled version of an iterator.""" ++ ++ def __init__(self, typename, val): ++ self._val = val + + # Just strip away the encapsulating __gnu_debug::_Safe_iterator + # and return the wrapped iterator value. +- def to_string (self): ++ def to_string(self): + base_type = gdb.lookup_type('__gnu_debug::_Safe_iterator_base') +- itype = self.val.type.template_argument(0) +- safe_seq = self.val.cast(base_type)['_M_sequence'] ++ itype = self._val.type.template_argument(0) ++ safe_seq = self._val.cast(base_type)['_M_sequence'] + if not safe_seq: +- return str(self.val.cast(itype)) +- if self.val['_M_version'] != safe_seq['_M_version']: ++ return str(self._val.cast(itype)) ++ if self._val['_M_version'] != safe_seq['_M_version']: + return "invalid iterator" +- return str(self.val.cast(itype)) ++ return str(self._val.cast(itype)) ++ + + def num_elements(num): + """Return either "1 element" or "N elements" depending on the argument.""" + return '1 element' if num == 1 else '%d elements' % num + +-class StdMapPrinter: +- "Print a std::map or std::multimap" ++ ++class StdMapPrinter(printer_base): ++ """Print a std::map or std::multimap.""" + + # Turn an RbtreeIterator into a pretty-print iterator. + class _iter(Iterator): + def __init__(self, rbiter, type): +- self.rbiter = rbiter +- self.count = 0 +- self.type = type ++ self._rbiter = rbiter ++ self._count = 0 ++ self._type = type + + def __iter__(self): + return self + + def __next__(self): +- if self.count % 2 == 0: +- n = next(self.rbiter) +- n = n.cast(self.type).dereference() ++ if self._count % 2 == 0: ++ n = next(self._rbiter) ++ n = n.cast(self._type).dereference() + n = get_value_from_Rb_tree_node(n) +- self.pair = n ++ self._pair = n + item = n['first'] + else: +- item = self.pair['second'] +- result = ('[%d]' % self.count, item) +- self.count = self.count + 1 ++ item = self._pair['second'] ++ result = ('[%d]' % self._count, item) ++ self._count = self._count + 1 + return result + +- def __init__ (self, typename, val): +- self.typename = strip_versioned_namespace(typename) +- self.val = val ++ def __init__(self, typename, val): ++ self._typename = strip_versioned_namespace(typename) ++ self._val = val + +- def to_string (self): +- return '%s with %s' % (self.typename, +- num_elements(len(RbtreeIterator (self.val)))) ++ def to_string(self): ++ return '%s with %s' % (self._typename, ++ num_elements(len(RbtreeIterator(self._val)))) + +- def children (self): +- rep_type = find_type(self.val.type, '_Rep_type') +- node = find_type(rep_type, '_Link_type') +- node = node.strip_typedefs() +- return self._iter (RbtreeIterator (self.val), node) ++ def children(self): ++ node = lookup_node_type('_Rb_tree_node', self._val.type).pointer() ++ return self._iter(RbtreeIterator(self._val), node) + +- def display_hint (self): ++ def display_hint(self): + return 'map' + +-class StdSetPrinter: +- "Print a std::set or std::multiset" ++ ++class StdSetPrinter(printer_base): ++ """Print a std::set or std::multiset.""" + + # Turn an RbtreeIterator into a pretty-print iterator. + class _iter(Iterator): + def __init__(self, rbiter, type): +- self.rbiter = rbiter +- self.count = 0 +- self.type = type ++ self._rbiter = rbiter ++ self._count = 0 ++ self._type = type + + def __iter__(self): + return self + + def __next__(self): +- item = next(self.rbiter) +- item = item.cast(self.type).dereference() ++ item = next(self._rbiter) ++ item = item.cast(self._type).dereference() + item = get_value_from_Rb_tree_node(item) + # FIXME: this is weird ... what to do? + # Maybe a 'set' display hint? +- result = ('[%d]' % self.count, item) +- self.count = self.count + 1 ++ result = ('[%d]' % self._count, item) ++ self._count = self._count + 1 + return result + +- def __init__ (self, typename, val): +- self.typename = strip_versioned_namespace(typename) +- self.val = val ++ def __init__(self, typename, val): ++ self._typename = strip_versioned_namespace(typename) ++ self._val = val + +- def to_string (self): +- return '%s with %s' % (self.typename, +- num_elements(len(RbtreeIterator (self.val)))) ++ def to_string(self): ++ return '%s with %s' % (self._typename, ++ num_elements(len(RbtreeIterator(self._val)))) + +- def children (self): +- rep_type = find_type(self.val.type, '_Rep_type') +- node = find_type(rep_type, '_Link_type') +- node = node.strip_typedefs() +- return self._iter (RbtreeIterator (self.val), node) ++ def children(self): ++ node = lookup_node_type('_Rb_tree_node', self._val.type).pointer() ++ return self._iter(RbtreeIterator(self._val), node) + +-class StdBitsetPrinter: +- "Print a std::bitset" ++ ++class StdBitsetPrinter(printer_base): ++ """Print a std::bitset.""" + + def __init__(self, typename, val): +- self.typename = strip_versioned_namespace(typename) +- self.val = val ++ self._typename = strip_versioned_namespace(typename) ++ self._val = val + +- def to_string (self): ++ def to_string(self): + # If template_argument handled values, we could print the + # size. Or we could use a regexp on the type. +- return '%s' % (self.typename) ++ return '%s' % (self._typename) + +- def children (self): ++ def children(self): + try: + # An empty bitset may not have any members which will + # result in an exception being thrown. +- words = self.val['_M_w'] ++ words = self._val['_M_w'] + except: + return [] + +@@ -713,7 +935,7 @@ class StdBitsetPrinter: + # array. This depends on the template specialization used. + # If it is a single long, convert to a single element list. + if wtype.code == gdb.TYPE_CODE_ARRAY: +- tsize = wtype.target ().sizeof ++ tsize = wtype.target().sizeof + else: + words = [words] + tsize = wtype.sizeof +@@ -733,279 +955,349 @@ class StdBitsetPrinter: + byte = byte + 1 + return result + +-class StdDequePrinter: +- "Print a std::deque" ++ ++class StdDequePrinter(printer_base): ++ """Print a std::deque.""" + + class _iter(Iterator): + def __init__(self, node, start, end, last, buffer_size): +- self.node = node +- self.p = start +- self.end = end +- self.last = last +- self.buffer_size = buffer_size +- self.count = 0 ++ self._node = node ++ self._p = start ++ self._end = end ++ self._last = last ++ self._buffer_size = buffer_size ++ self._count = 0 + + def __iter__(self): + return self + + def __next__(self): +- if self.p == self.last: ++ if self._p == self._last: + raise StopIteration + +- result = ('[%d]' % self.count, self.p.dereference()) +- self.count = self.count + 1 ++ result = ('[%d]' % self._count, self._p.dereference()) ++ self._count = self._count + 1 + + # Advance the 'cur' pointer. +- self.p = self.p + 1 +- if self.p == self.end: ++ self._p = self._p + 1 ++ if self._p == self._end: + # If we got to the end of this bucket, move to the + # next bucket. +- self.node = self.node + 1 +- self.p = self.node[0] +- self.end = self.p + self.buffer_size ++ self._node = self._node + 1 ++ self._p = self._node[0] ++ self._end = self._p + self._buffer_size + + return result + + def __init__(self, typename, val): +- self.typename = strip_versioned_namespace(typename) +- self.val = val +- self.elttype = val.type.template_argument(0) +- size = self.elttype.sizeof ++ self._typename = strip_versioned_namespace(typename) ++ self._val = val ++ self._elttype = val.type.template_argument(0) ++ size = self._elttype.sizeof + if size < 512: +- self.buffer_size = int (512 / size) ++ self._buffer_size = int(512 / size) + else: +- self.buffer_size = 1 ++ self._buffer_size = 1 + + def to_string(self): +- start = self.val['_M_impl']['_M_start'] +- end = self.val['_M_impl']['_M_finish'] ++ start = self._val['_M_impl']['_M_start'] ++ end = self._val['_M_impl']['_M_finish'] + + delta_n = end['_M_node'] - start['_M_node'] - 1 + delta_s = start['_M_last'] - start['_M_cur'] + delta_e = end['_M_cur'] - end['_M_first'] + +- size = self.buffer_size * delta_n + delta_s + delta_e ++ size = self._buffer_size * delta_n + delta_s + delta_e + +- return '%s with %s' % (self.typename, num_elements(long(size))) ++ return '%s with %s' % (self._typename, num_elements(long(size))) + + def children(self): +- start = self.val['_M_impl']['_M_start'] +- end = self.val['_M_impl']['_M_finish'] ++ start = self._val['_M_impl']['_M_start'] ++ end = self._val['_M_impl']['_M_finish'] + return self._iter(start['_M_node'], start['_M_cur'], start['_M_last'], +- end['_M_cur'], self.buffer_size) ++ end['_M_cur'], self._buffer_size) + +- def display_hint (self): ++ def display_hint(self): + return 'array' + +-class StdDequeIteratorPrinter: +- "Print std::deque::iterator" ++ ++class StdDequeIteratorPrinter(printer_base): ++ """Print std::deque::iterator.""" + + def __init__(self, typename, val): +- self.val = val ++ self._val = val + + def to_string(self): +- if not self.val['_M_cur']: ++ if not self._val['_M_cur']: + return 'non-dereferenceable iterator for std::deque' +- return str(self.val['_M_cur'].dereference()) ++ return str(self._val['_M_cur'].dereference()) + +-class StdStringPrinter: +- "Print a std::basic_string of some kind" ++ ++class StdStringPrinter(printer_base): ++ """Print a std::basic_string of some kind.""" + + def __init__(self, typename, val): +- self.val = val +- self.new_string = typename.find("::__cxx11::basic_string") != -1 ++ self._val = val ++ self._new_string = typename.find("::__cxx11::basic_string") != -1 + + def to_string(self): + # Make sure &string works, too. +- type = self.val.type ++ type = self._val.type + if type.code == gdb.TYPE_CODE_REF: +- type = type.target () ++ type = type.target() + + # Calculate the length of the string so that to_string returns + # the string according to length, not according to first null + # encountered. +- ptr = self.val ['_M_dataplus']['_M_p'] +- if self.new_string: +- length = self.val['_M_string_length'] ++ ptr = self._val['_M_dataplus']['_M_p'] ++ if self._new_string: ++ length = self._val['_M_string_length'] + # https://sourceware.org/bugzilla/show_bug.cgi?id=17728 + ptr = ptr.cast(ptr.type.strip_typedefs()) + else: +- realtype = type.unqualified ().strip_typedefs () +- reptype = gdb.lookup_type (str (realtype) + '::_Rep').pointer () ++ realtype = type.unqualified().strip_typedefs() ++ reptype = gdb.lookup_type(str(realtype) + '::_Rep').pointer() + header = ptr.cast(reptype) - 1 +- length = header.dereference ()['_M_length'] ++ length = header.dereference()['_M_length'] + if hasattr(ptr, "lazy_string"): +- return ptr.lazy_string (length = length) +- return ptr.string (length = length) ++ return ptr.lazy_string(length=length) ++ return ptr.string(length=length) ++ ++ def display_hint(self): ++ return 'string' ++ ++ ++def access_streambuf_ptrs(streambuf): ++ """Access the streambuf put area pointers.""" ++ pbase = streambuf['_M_out_beg'] ++ pptr = streambuf['_M_out_cur'] ++ egptr = streambuf['_M_in_end'] ++ return pbase, pptr, egptr + +- def display_hint (self): ++ ++class StdStringBufPrinter(printer_base): ++ """Print a std::basic_stringbuf.""" ++ ++ def __init__(self, _, val): ++ self._val = val ++ ++ def to_string(self): ++ (pbase, pptr, egptr) = access_streambuf_ptrs(self._val) ++ # Logic from basic_stringbuf::_M_high_mark() ++ if pptr: ++ if not egptr or pptr > egptr: ++ return pbase.string(length=pptr - pbase) ++ else: ++ return pbase.string(length=egptr - pbase) ++ return self._val['_M_string'] ++ ++ def display_hint(self): + return 'string' + ++ ++class StdStringStreamPrinter(printer_base): ++ """Print a std::basic_stringstream.""" ++ ++ def __init__(self, typename, val): ++ self._val = val ++ self._typename = typename ++ ++ # Check if the stream was redirected. This is essentially: ++ # val['_M_streambuf'] != val['_M_stringbuf'].address ++ # However, GDB can't resolve the virtual inheritance, so we do that ++ # manually. ++ basetype = [f.type for f in val.type.fields() if f.is_base_class][0] ++ gdb.set_convenience_variable('__stream', val.cast(basetype).address) ++ self._streambuf = gdb.parse_and_eval('$__stream->rdbuf()') ++ self._was_redirected = self._streambuf != val['_M_stringbuf'].address ++ ++ def to_string(self): ++ if self._was_redirected: ++ return "%s redirected to %s" % ( ++ self._typename, self._streambuf.dereference()) ++ return self._val['_M_stringbuf'] ++ ++ def display_hint(self): ++ if self._was_redirected: ++ return None ++ return 'string' ++ ++ + class Tr1HashtableIterator(Iterator): +- def __init__ (self, hash): +- self.buckets = hash['_M_buckets'] +- self.bucket = 0 +- self.bucket_count = hash['_M_bucket_count'] +- self.node_type = find_type(hash.type, '_Node').pointer() +- self.node = 0 +- while self.bucket != self.bucket_count: +- self.node = self.buckets[self.bucket] +- if self.node: ++ def __init__(self, hashtable): ++ self._buckets = hashtable['_M_buckets'] ++ self._bucket = 0 ++ self._bucket_count = hashtable['_M_bucket_count'] ++ self._node_type = find_type(hashtable.type, '_Node').pointer() ++ self._node = 0 ++ while self._bucket != self._bucket_count: ++ self._node = self._buckets[self._bucket] ++ if self._node: + break +- self.bucket = self.bucket + 1 ++ self._bucket = self._bucket + 1 + +- def __iter__ (self): ++ def __iter__(self): + return self + +- def __next__ (self): +- if self.node == 0: ++ def __next__(self): ++ if self._node == 0: + raise StopIteration +- node = self.node.cast(self.node_type) ++ node = self._node.cast(self._node_type) + result = node.dereference()['_M_v'] +- self.node = node.dereference()['_M_next']; +- if self.node == 0: +- self.bucket = self.bucket + 1 +- while self.bucket != self.bucket_count: +- self.node = self.buckets[self.bucket] +- if self.node: ++ self._node = node.dereference()['_M_next'] ++ if self._node == 0: ++ self._bucket = self._bucket + 1 ++ while self._bucket != self._bucket_count: ++ self._node = self._buckets[self._bucket] ++ if self._node: + break +- self.bucket = self.bucket + 1 ++ self._bucket = self._bucket + 1 + return result + ++ + class StdHashtableIterator(Iterator): +- def __init__(self, hash): +- self.node = hash['_M_before_begin']['_M_nxt'] +- self.node_type = find_type(hash.type, '__node_type').pointer() ++ def __init__(self, hashtable): ++ self._node = hashtable['_M_before_begin']['_M_nxt'] ++ valtype = hashtable.type.template_argument(1) ++ cached = hashtable.type.template_argument(9).template_argument(0) ++ node_type = lookup_templ_spec('std::__detail::_Hash_node', str(valtype), ++ 'true' if cached else 'false') ++ self._node_type = node_type.pointer() + + def __iter__(self): + return self + + def __next__(self): +- if self.node == 0: ++ if self._node == 0: + raise StopIteration +- elt = self.node.cast(self.node_type).dereference() +- self.node = elt['_M_nxt'] ++ elt = self._node.cast(self._node_type).dereference() ++ self._node = elt['_M_nxt'] + valptr = elt['_M_storage'].address + valptr = valptr.cast(elt.type.template_argument(0).pointer()) + return valptr.dereference() + +-class Tr1UnorderedSetPrinter: +- "Print a tr1::unordered_set" + +- def __init__ (self, typename, val): +- self.typename = strip_versioned_namespace(typename) +- self.val = val ++class Tr1UnorderedSetPrinter(printer_base): ++ """Print a std::unordered_set or tr1::unordered_set.""" ++ ++ def __init__(self, typename, val): ++ self._typename = strip_versioned_namespace(typename) ++ self._val = val + +- def hashtable (self): +- if self.typename.startswith('std::tr1'): +- return self.val +- return self.val['_M_h'] ++ def _hashtable(self): ++ if self._typename.startswith('std::tr1'): ++ return self._val ++ return self._val['_M_h'] + +- def to_string (self): +- count = self.hashtable()['_M_element_count'] +- return '%s with %s' % (self.typename, num_elements(count)) ++ def to_string(self): ++ count = self._hashtable()['_M_element_count'] ++ return '%s with %s' % (self._typename, num_elements(count)) + + @staticmethod +- def format_count (i): ++ def _format_count(i): + return '[%d]' % i + +- def children (self): +- counter = imap (self.format_count, itertools.count()) +- if self.typename.startswith('std::tr1'): +- return izip (counter, Tr1HashtableIterator (self.hashtable())) +- return izip (counter, StdHashtableIterator (self.hashtable())) ++ def children(self): ++ counter = imap(self._format_count, itertools.count()) ++ if self._typename.startswith('std::tr1'): ++ return izip(counter, Tr1HashtableIterator(self._hashtable())) ++ return izip(counter, StdHashtableIterator(self._hashtable())) + +-class Tr1UnorderedMapPrinter: +- "Print a tr1::unordered_map" + +- def __init__ (self, typename, val): +- self.typename = strip_versioned_namespace(typename) +- self.val = val ++class Tr1UnorderedMapPrinter(printer_base): ++ """Print a std::unordered_map or tr1::unordered_map.""" + +- def hashtable (self): +- if self.typename.startswith('std::tr1'): +- return self.val +- return self.val['_M_h'] ++ def __init__(self, typename, val): ++ self._typename = strip_versioned_namespace(typename) ++ self._val = val ++ ++ def _hashtable(self): ++ if self._typename.startswith('std::tr1'): ++ return self._val ++ return self._val['_M_h'] + +- def to_string (self): +- count = self.hashtable()['_M_element_count'] +- return '%s with %s' % (self.typename, num_elements(count)) ++ def to_string(self): ++ count = self._hashtable()['_M_element_count'] ++ return '%s with %s' % (self._typename, num_elements(count)) + + @staticmethod +- def flatten (list): ++ def _flatten(list): + for elt in list: + for i in elt: + yield i + + @staticmethod +- def format_one (elt): ++ def _format_one(elt): + return (elt['first'], elt['second']) + + @staticmethod +- def format_count (i): ++ def _format_count(i): + return '[%d]' % i + +- def children (self): +- counter = imap (self.format_count, itertools.count()) ++ def children(self): ++ counter = imap(self._format_count, itertools.count()) + # Map over the hash table and flatten the result. +- if self.typename.startswith('std::tr1'): +- data = self.flatten (imap (self.format_one, Tr1HashtableIterator (self.hashtable()))) ++ if self._typename.startswith('std::tr1'): ++ data = self._flatten( ++ imap(self._format_one, Tr1HashtableIterator(self._hashtable()))) + # Zip the two iterators together. +- return izip (counter, data) +- data = self.flatten (imap (self.format_one, StdHashtableIterator (self.hashtable()))) ++ return izip(counter, data) ++ data = self._flatten( ++ imap(self._format_one, StdHashtableIterator(self._hashtable()))) + # Zip the two iterators together. +- return izip (counter, data) ++ return izip(counter, data) + +- def display_hint (self): ++ def display_hint(self): + return 'map' + +-class StdForwardListPrinter: +- "Print a std::forward_list" ++ ++class StdForwardListPrinter(printer_base): ++ """Print a std::forward_list.""" + + class _iterator(Iterator): + def __init__(self, nodetype, head): +- self.nodetype = nodetype +- self.base = head['_M_next'] +- self.count = 0 ++ self._nodetype = nodetype ++ self._base = head['_M_next'] ++ self._count = 0 + + def __iter__(self): + return self + + def __next__(self): +- if self.base == 0: ++ if self._base == 0: + raise StopIteration +- elt = self.base.cast(self.nodetype).dereference() +- self.base = elt['_M_next'] +- count = self.count +- self.count = self.count + 1 ++ elt = self._base.cast(self._nodetype).dereference() ++ self._base = elt['_M_next'] ++ count = self._count ++ self._count = self._count + 1 + valptr = elt['_M_storage'].address + valptr = valptr.cast(elt.type.template_argument(0).pointer()) + return ('[%d]' % count, valptr.dereference()) + + def __init__(self, typename, val): +- self.val = val +- self.typename = strip_versioned_namespace(typename) ++ self._val = val ++ self._typename = strip_versioned_namespace(typename) + + def children(self): +- nodetype = find_type(self.val.type, '_Node') +- nodetype = nodetype.strip_typedefs().pointer() +- return self._iterator(nodetype, self.val['_M_impl']['_M_head']) ++ nodetype = lookup_node_type('_Fwd_list_node', self._val.type).pointer() ++ return self._iterator(nodetype, self._val['_M_impl']['_M_head']) + + def to_string(self): +- if self.val['_M_impl']['_M_head']['_M_next'] == 0: +- return 'empty %s' % self.typename +- return '%s' % self.typename ++ if self._val['_M_impl']['_M_head']['_M_next'] == 0: ++ return 'empty %s' % self._typename ++ return '%s' % self._typename ++ + +-class SingleObjContainerPrinter(object): +- "Base class for printers of containers of single objects" ++class SingleObjContainerPrinter(printer_base): ++ """Base class for printers of containers of single objects.""" + +- def __init__ (self, val, viz, hint = None): +- self.contained_value = val +- self.visualizer = viz +- self.hint = hint ++ def __init__(self, val, viz, hint=None): ++ self._contained_value = val ++ self._visualizer = viz ++ self._hint = hint + + def _recognize(self, type): +- """Return TYPE as a string after applying type printers""" ++ """Return type as a string after applying type printers.""" + global _use_type_printing + if not _use_type_printing: + return str(type) +@@ -1013,35 +1305,37 @@ class SingleObjContainerPrinter(object): + type) or str(type) + + class _contained(Iterator): +- def __init__ (self, val): +- self.val = val ++ def __init__(self, val): ++ self._val = val + +- def __iter__ (self): ++ def __iter__(self): + return self + + def __next__(self): +- if self.val is None: ++ if self._val is None: + raise StopIteration +- retval = self.val +- self.val = None ++ retval = self._val ++ self._val = None + return ('[contained value]', retval) + +- def children (self): +- if self.contained_value is None: +- return self._contained (None) +- if hasattr (self.visualizer, 'children'): +- return self.visualizer.children () +- return self._contained (self.contained_value) ++ def children(self): ++ if self._contained_value is None: ++ return self._contained(None) ++ if hasattr(self._visualizer, 'children'): ++ return self._visualizer.children() ++ return self._contained(self._contained_value) ++ ++ def display_hint(self): ++ if (hasattr(self._visualizer, 'children') ++ and hasattr(self._visualizer, 'display_hint')): ++ # If contained value is a map we want to display in the same way. ++ return self._visualizer.display_hint() ++ return self._hint + +- def display_hint (self): +- # if contained value is a map we want to display in the same way +- if hasattr (self.visualizer, 'children') and hasattr (self.visualizer, 'display_hint'): +- return self.visualizer.display_hint () +- return self.hint + + def function_pointer_to_name(f): +- "Find the name of the function referred to by the gdb.Value f, " +- " which should contain a function pointer from the program." ++ """Find the name of the function referred to by the gdb.Value f, ++ which should contain a function pointer from the program.""" + + # Turn the function pointer into an actual address. + # This is needed to unpack ppc64 function descriptors. +@@ -1062,252 +1356,1003 @@ def function_pointer_to_name(f): + except: + return None + ++ + class StdExpAnyPrinter(SingleObjContainerPrinter): +- "Print a std::any or std::experimental::any" ++ """Print a std::any or std::experimental::any.""" + +- def __init__ (self, typename, val): +- self.typename = strip_versioned_namespace(typename) +- self.typename = re.sub('^std::experimental::fundamentals_v\d::', 'std::experimental::', self.typename, 1) +- self.val = val +- self.contained_type = None ++ def __init__(self, typename, val): ++ self._typename = strip_versioned_namespace(typename) ++ self._typename = strip_fundts_namespace(self._typename) ++ self._val = val ++ self._contained_type = None + contained_value = None + visualizer = None +- mgr = self.val['_M_manager'] ++ mgr = self._val['_M_manager'] + if mgr != 0: + func = function_pointer_to_name(mgr) + if not func: +- raise ValueError("Invalid function pointer in %s" % (self.typename)) +- rx = r"""({0}::_Manager_\w+<.*>)::_S_manage""".format(typename) ++ raise ValueError( ++ "Invalid function pointer in %s" % (self._typename)) ++ # We want to use this regular expression: ++ # T::_Manager_xxx<.*>::_S_manage\(T::_Op, const T\*, T::_Arg\*\) ++ # where T is std::any or std::experimental::any. ++ # But we need to account for variances in demangled names ++ # between GDB versions, e.g. 'enum T::_Op' instead of 'T::_Op'. ++ rx = ( ++ r"({0}::_Manager_\w+<.*>)::_S_manage\(" ++ r"(enum )?{0}::_Op, (const {0}|{0} const) ?\*, " ++ r"(union )?{0}::_Arg ?\*\)" ++ ).format(typename) + m = re.match(rx, func) + if not m: +- raise ValueError("Unknown manager function in %s" % self.typename) ++ raise ValueError( ++ "Unknown manager function in %s" % self._typename) + + mgrname = m.group(1) + # FIXME need to expand 'std::string' so that gdb.lookup_type works + if 'std::string' in mgrname: +- mgrname = re.sub("std::string(?!\w)", str(gdb.lookup_type('std::string').strip_typedefs()), m.group(1)) +- +- mgrtype = gdb.lookup_type(mgrname) +- self.contained_type = mgrtype.template_argument(0) ++ mgrtypes = [] ++ for s in StdExpAnyPrinter._string_types(): ++ try: ++ x = re.sub(r"std::string(?!\w)", s, m.group(1)) ++ # The following lookup might raise gdb.error if the ++ # manager function was never instantiated for 's' in ++ # the program, because there will be no such type. ++ mgrtypes.append(gdb.lookup_type(x)) ++ except gdb.error: ++ pass ++ if len(mgrtypes) != 1: ++ # FIXME: this is unlikely in practice, but possible for ++ # programs that use both old and new string types with ++ # std::any in a single program. Can we do better? ++ # Maybe find the address of each type's _S_manage and ++ # compare to the address stored in _M_manager? ++ raise ValueError( ++ 'Cannot uniquely determine std::string type ' ++ 'used in std::any' ++ ) ++ mgrtype = mgrtypes[0] ++ else: ++ mgrtype = gdb.lookup_type(mgrname) ++ self._contained_type = mgrtype.template_argument(0) + valptr = None + if '::_Manager_internal' in mgrname: +- valptr = self.val['_M_storage']['_M_buffer'].address ++ valptr = self._val['_M_storage']['_M_buffer'].address + elif '::_Manager_external' in mgrname: +- valptr = self.val['_M_storage']['_M_ptr'] ++ valptr = self._val['_M_storage']['_M_ptr'] + else: +- raise ValueError("Unknown manager function in %s" % self.typename) +- contained_value = valptr.cast(self.contained_type.pointer()).dereference() ++ raise ValueError( ++ "Unknown manager function in %s" % self._typename) ++ contained_value = valptr.cast( ++ self._contained_type.pointer()).dereference() + visualizer = gdb.default_visualizer(contained_value) +- super(StdExpAnyPrinter, self).__init__ (contained_value, visualizer) +- +- def to_string (self): +- if self.contained_type is None: +- return '%s [no contained value]' % self.typename +- desc = "%s containing " % self.typename +- if hasattr (self.visualizer, 'children'): +- return desc + self.visualizer.to_string () +- valtype = self._recognize (self.contained_type) ++ super(StdExpAnyPrinter, self).__init__(contained_value, visualizer) ++ ++ def to_string(self): ++ if self._contained_type is None: ++ return '%s [no contained value]' % self._typename ++ desc = "%s containing " % self._typename ++ if hasattr(self._visualizer, 'children'): ++ return desc + self._visualizer.to_string() ++ valtype = self._recognize(self._contained_type) + return desc + strip_versioned_namespace(str(valtype)) + ++ @staticmethod ++ def _string_types(): ++ # This lookup for std::string might return the __cxx11 version, ++ # but that's not necessarily the one used by the std::any ++ # manager function we're trying to find. ++ strings = {str(gdb.lookup_type('std::string').strip_typedefs())} ++ # So also consider all the other possible std::string types! ++ s = 'basic_string, std::allocator >' ++ quals = ['std::', 'std::__cxx11::', ++ 'std::' + _versioned_namespace] ++ strings |= {q + s for q in quals} # set of unique strings ++ return strings ++ ++ + class StdExpOptionalPrinter(SingleObjContainerPrinter): +- "Print a std::optional or std::experimental::optional" +- +- def __init__ (self, typename, val): +- valtype = self._recognize (val.type.template_argument(0)) +- self.typename = strip_versioned_namespace(typename) +- self.typename = re.sub('^std::(experimental::|)(fundamentals_v\d::|)(.*)', r'std::\1\3<%s>' % valtype, self.typename, 1) +- if not self.typename.startswith('std::experimental'): +- val = val['_M_payload'] +- self.val = val +- contained_value = val['_M_payload'] if self.val['_M_engaged'] else None +- visualizer = gdb.default_visualizer (val['_M_payload']) +- super (StdExpOptionalPrinter, self).__init__ (contained_value, visualizer) +- +- def to_string (self): +- if self.contained_value is None: +- return "%s [no contained value]" % self.typename +- if hasattr (self.visualizer, 'children'): +- return "%s containing %s" % (self.typename, +- self.visualizer.to_string()) +- return self.typename ++ """Print a std::optional or std::experimental::optional.""" ++ ++ def __init__(self, typename, val): ++ self._typename = strip_versioned_namespace(typename) ++ self._typename = strip_fundts_namespace(self._typename) ++ payload = val['_M_payload'] ++ if self._typename.startswith('std::experimental'): ++ engaged = val['_M_engaged'] ++ contained_value = payload ++ else: ++ engaged = payload['_M_engaged'] ++ contained_value = payload['_M_payload'] ++ try: ++ # Since GCC 9 ++ contained_value = contained_value['_M_value'] ++ except: ++ pass ++ visualizer = gdb.default_visualizer(contained_value) ++ if not engaged: ++ contained_value = None ++ super(StdExpOptionalPrinter, self).__init__( ++ contained_value, visualizer) ++ ++ def to_string(self): ++ if self._contained_value is None: ++ return "%s [no contained value]" % self._typename ++ if hasattr(self._visualizer, 'children'): ++ return "%s containing %s" % (self._typename, ++ self._visualizer.to_string()) ++ return self._typename ++ + + class StdVariantPrinter(SingleObjContainerPrinter): +- "Print a std::variant" ++ """Print a std::variant.""" + + def __init__(self, typename, val): + alternatives = get_template_arg_list(val.type) +- self.typename = strip_versioned_namespace(typename) +- self.typename = "%s<%s>" % (self.typename, ', '.join([self._recognize(alt) for alt in alternatives])) +- self.index = val['_M_index'] +- if self.index >= len(alternatives): +- self.contained_type = None ++ self._typename = strip_versioned_namespace(typename) ++ self._index = val['_M_index'] ++ if self._index >= len(alternatives): ++ self._contained_type = None + contained_value = None + visualizer = None + else: +- self.contained_type = alternatives[int(self.index)] ++ self._contained_type = alternatives[int(self._index)] + addr = val['_M_u']['_M_first']['_M_storage'].address +- contained_value = addr.cast(self.contained_type.pointer()).dereference() ++ contained_value = addr.cast( ++ self._contained_type.pointer()).dereference() + visualizer = gdb.default_visualizer(contained_value) +- super (StdVariantPrinter, self).__init__(contained_value, visualizer, 'array') ++ super(StdVariantPrinter, self).__init__( ++ contained_value, visualizer, 'array') + + def to_string(self): +- if self.contained_value is None: +- return "%s [no contained value]" % self.typename +- if hasattr(self.visualizer, 'children'): +- return "%s [index %d] containing %s" % (self.typename, self.index, +- self.visualizer.to_string()) +- return "%s [index %d]" % (self.typename, self.index) ++ if self._contained_value is None: ++ return "%s [no contained value]" % self._typename ++ if hasattr(self._visualizer, 'children'): ++ return "%s [index %d] containing %s" % (self._typename, self._index, ++ self._visualizer.to_string()) ++ return "%s [index %d]" % (self._typename, self._index) ++ + + class StdNodeHandlePrinter(SingleObjContainerPrinter): +- "Print a container node handle" ++ """Print a container node handle.""" + + def __init__(self, typename, val): +- self.value_type = val.type.template_argument(1) ++ self._value_type = val.type.template_argument(1) + nodetype = val.type.template_argument(2).template_argument(0) +- self.is_rb_tree_node = is_specialization_of(nodetype.name, '_Rb_tree_node') +- self.is_map_node = val.type.template_argument(0) != self.value_type ++ self._is_rb_tree_node = is_specialization_of( ++ nodetype.name, '_Rb_tree_node') ++ self._is_map_node = val.type.template_argument(0) != self._value_type + nodeptr = val['_M_ptr'] + if nodeptr: +- if self.is_rb_tree_node: +- contained_value = get_value_from_Rb_tree_node(nodeptr.dereference()) ++ if self._is_rb_tree_node: ++ contained_value = get_value_from_Rb_tree_node( ++ nodeptr.dereference()) + else: + contained_value = get_value_from_aligned_membuf(nodeptr['_M_storage'], +- self.value_type) ++ self._value_type) + visualizer = gdb.default_visualizer(contained_value) + else: + contained_value = None + visualizer = None + optalloc = val['_M_alloc'] +- self.alloc = optalloc['_M_payload'] if optalloc['_M_engaged'] else None ++ self._alloc = optalloc['_M_payload'] if optalloc['_M_engaged'] else None + super(StdNodeHandlePrinter, self).__init__(contained_value, visualizer, + 'array') + + def to_string(self): + desc = 'node handle for ' +- if not self.is_rb_tree_node: ++ if not self._is_rb_tree_node: + desc += 'unordered ' +- if self.is_map_node: +- desc += 'map'; ++ if self._is_map_node: ++ desc += 'map' + else: +- desc += 'set'; ++ desc += 'set' + +- if self.contained_value: ++ if self._contained_value: + desc += ' with element' +- if hasattr(self.visualizer, 'children'): +- return "%s = %s" % (desc, self.visualizer.to_string()) ++ if hasattr(self._visualizer, 'children'): ++ return "%s = %s" % (desc, self._visualizer.to_string()) + return desc + else: + return 'empty %s' % desc + +-class StdExpStringViewPrinter: +- "Print a std::basic_string_view or std::experimental::basic_string_view" + +- def __init__ (self, typename, val): +- self.val = val ++class StdExpStringViewPrinter(printer_base): ++ """ ++ Print a std::basic_string_view or std::experimental::basic_string_view ++ """ ++ ++ def __init__(self, typename, val): ++ self._val = val + +- def to_string (self): +- ptr = self.val['_M_str'] +- len = self.val['_M_len'] +- if hasattr (ptr, "lazy_string"): +- return ptr.lazy_string (length = len) +- return ptr.string (length = len) ++ def to_string(self): ++ ptr = self._val['_M_str'] ++ len = self._val['_M_len'] ++ if hasattr(ptr, "lazy_string"): ++ return ptr.lazy_string(length=len) ++ return ptr.string(length=len) + +- def display_hint (self): ++ def display_hint(self): + return 'string' + +-class StdExpPathPrinter: +- "Print a std::experimental::filesystem::path or std::filesystem::path" + +- def __init__ (self, typename, val): +- self.val = val +- self.typename = typename +- start = self.val['_M_cmpts']['_M_impl']['_M_start'] +- finish = self.val['_M_cmpts']['_M_impl']['_M_finish'] +- self.num_cmpts = int (finish - start) ++class StdExpPathPrinter(printer_base): ++ """Print a std::experimental::filesystem::path.""" ++ ++ def __init__(self, typename, val): ++ self._val = val ++ self._typename = typename ++ start = self._val['_M_cmpts']['_M_impl']['_M_start'] ++ finish = self._val['_M_cmpts']['_M_impl']['_M_finish'] ++ self._num_cmpts = int(finish - start) + + def _path_type(self): +- t = str(self.val['_M_type']) ++ t = str(self._val['_M_type']) + if t[-9:] == '_Root_dir': + return "root-directory" + if t[-10:] == '_Root_name': + return "root-name" + return None + +- def to_string (self): +- path = "%s" % self.val ['_M_pathname'] +- if self.num_cmpts == 0: ++ def to_string(self): ++ path = "%s" % self._val['_M_pathname'] ++ if self._num_cmpts == 0: + t = self._path_type() + if t: + path = '%s [%s]' % (path, t) +- return "filesystem::path %s" % path ++ return "experimental::filesystem::path %s" % path + + class _iterator(Iterator): + def __init__(self, cmpts, pathtype): +- self.pathtype = pathtype +- self.item = cmpts['_M_impl']['_M_start'] +- self.finish = cmpts['_M_impl']['_M_finish'] +- self.count = 0 ++ self._pathtype = pathtype ++ self._item = cmpts['_M_impl']['_M_start'] ++ self._finish = cmpts['_M_impl']['_M_finish'] ++ self._count = 0 ++ ++ def __iter__(self): ++ return self ++ ++ def __next__(self): ++ if self._item == self._finish: ++ raise StopIteration ++ item = self._item.dereference() ++ count = self._count ++ self._count = self._count + 1 ++ self._item = self._item + 1 ++ path = item['_M_pathname'] ++ t = StdExpPathPrinter(self._pathtype, item)._path_type() ++ if not t: ++ t = count ++ return ('[%s]' % t, path) ++ ++ def children(self): ++ return self._iterator(self._val['_M_cmpts'], self._typename) ++ ++ ++class StdPathPrinter(printer_base): ++ """Print a std::filesystem::path.""" ++ ++ def __init__(self, typename, val): ++ self._val = val ++ self._typename = typename ++ impl = unique_ptr_get(self._val['_M_cmpts']['_M_impl']) ++ self._type = impl.cast(gdb.lookup_type('uintptr_t')) & 3 ++ if self._type == 0: ++ self._impl = impl ++ else: ++ self._impl = None ++ ++ def _path_type(self): ++ t = str(self._type.cast(gdb.lookup_type(self._typename + '::_Type'))) ++ if t[-9:] == '_Root_dir': ++ return "root-directory" ++ if t[-10:] == '_Root_name': ++ return "root-name" ++ return None ++ ++ def to_string(self): ++ path = "%s" % self._val['_M_pathname'] ++ if self._type != 0: ++ t = self._path_type() ++ if t: ++ path = '%s [%s]' % (path, t) ++ return "filesystem::path %s" % path ++ ++ class _iterator(Iterator): ++ def __init__(self, impl, pathtype): ++ self._pathtype = pathtype ++ if impl: ++ # We can't access _Impl::_M_size because _Impl is incomplete ++ # so cast to int* to access the _M_size member at offset zero, ++ int_type = gdb.lookup_type('int') ++ cmpt_type = gdb.lookup_type(pathtype + '::_Cmpt') ++ char_type = gdb.lookup_type('char') ++ impl = impl.cast(int_type.pointer()) ++ size = impl.dereference() ++ #self._capacity = (impl + 1).dereference() ++ if hasattr(gdb.Type, 'alignof'): ++ sizeof_Impl = max(2 * int_type.sizeof, cmpt_type.alignof) ++ else: ++ sizeof_Impl = 2 * int_type.sizeof ++ begin = impl.cast(char_type.pointer()) + sizeof_Impl ++ self._item = begin.cast(cmpt_type.pointer()) ++ self._finish = self._item + size ++ self._count = 0 ++ else: ++ self._item = None ++ self._finish = None + + def __iter__(self): + return self + + def __next__(self): +- if self.item == self.finish: ++ if self._item == self._finish: + raise StopIteration +- item = self.item.dereference() +- count = self.count +- self.count = self.count + 1 +- self.item = self.item + 1 ++ item = self._item.dereference() ++ count = self._count ++ self._count = self._count + 1 ++ self._item = self._item + 1 + path = item['_M_pathname'] +- t = StdExpPathPrinter(self.pathtype, item)._path_type() ++ t = StdPathPrinter(self._pathtype, item)._path_type() + if not t: + t = count + return ('[%s]' % t, path) + + def children(self): +- return self._iterator(self.val['_M_cmpts'], self.typename) ++ return self._iterator(self._impl, self._typename) + + +-class StdPairPrinter: +- "Print a std::pair object, with 'first' and 'second' as children" ++class StdPairPrinter(printer_base): ++ """Print a std::pair object, with 'first' and 'second' as children.""" + + def __init__(self, typename, val): +- self.val = val ++ self._val = val + + class _iter(Iterator): +- "An iterator for std::pair types. Returns 'first' then 'second'." ++ """An iterator for std::pair types. Returns 'first' then 'second'.""" + + def __init__(self, val): +- self.val = val +- self.which = 'first' ++ self._val = val ++ self._which = 'first' + + def __iter__(self): + return self + + def __next__(self): +- if self.which is None: ++ if self._which is None: + raise StopIteration +- which = self.which ++ which = self._which + if which == 'first': +- self.which = 'second' ++ self._which = 'second' + else: +- self.which = None +- return (which, self.val[which]) ++ self._which = None ++ return (which, self._val[which]) + + def children(self): +- return self._iter(self.val) ++ return self._iter(self._val) + + def to_string(self): + return None + + ++class StdCmpCatPrinter(printer_base): ++ """Print a comparison category object.""" ++ ++ def __init__(self, typename, val): ++ self._typename = typename[typename.rfind(':') + 1:] ++ self._val = val['_M_value'] ++ ++ def to_string(self): ++ if self._typename == 'strong_ordering' and self._val == 0: ++ name = 'equal' ++ else: ++ names = {2: 'unordered', -1: 'less', 0: 'equivalent', 1: 'greater'} ++ name = names[int(self._val)] ++ return 'std::{}::{}'.format(self._typename, name) ++ ++ ++class StdErrorCodePrinter(printer_base): ++ """Print a std::error_code or std::error_condition.""" ++ ++ _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._system_is_posix is None: ++ try: ++ import posix ++ StdErrorCodePrinter._system_is_posix = True ++ except ImportError: ++ StdErrorCodePrinter._system_is_posix = False ++ ++ @staticmethod ++ 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 _find_standard_errc_enum(cls, name): ++ for ns in ['', _versioned_namespace]: ++ try: ++ qname = 'std::{}{}'.format(ns, name) ++ return cls._find_errc_enum(qname) ++ except RuntimeError: ++ pass ++ ++ @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_standard_errc_enum('future_errc') ++ if typ.tag.endswith('::io_error_category'): ++ name = 'io' ++ enum = cls._find_standard_errc_enum('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'] ++ 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'} ++ if name == default_cats[self._unqualified_name(self._typename)]: ++ return self._typename + ' = { }' # default-constructed value ++ ++ strval = str(value) ++ if is_errno and value != 0: ++ try: ++ strval = errno.errorcode[int(value)] ++ except: ++ pass ++ 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(printer_base): ++ """Print a state node in the NFA for a std::regex.""" ++ ++ def __init__(self, typename, val): ++ self._val = val ++ self._typename = typename ++ ++ def to_string(self): ++ opcode = str(self._val['_M_opcode']) ++ if opcode: ++ opcode = opcode[25:] ++ next_id = self._val['_M_next'] ++ ++ variants = {'repeat': 'alt', 'alternative': 'alt', ++ 'subexpr_begin': 'subexpr', 'subexpr_end': 'subexpr', ++ 'line_begin_assertion': None, 'line_end_assertion': None, ++ 'word_boundary': 'neg', 'subexpr_lookahead': 'neg', ++ 'backref': 'backref_index', ++ 'match': None, 'accept': None, ++ 'dummy': None, 'unknown': None ++ } ++ v = variants[opcode] ++ ++ s = "opcode={}, next={}".format(opcode, next_id) ++ if v is not None and self._val['_M_' + v] is not None: ++ s = "{}, {}={}".format(s, v, self._val['_M_' + v]) ++ return "{%s}" % (s) ++ ++ ++class StdSpanPrinter(printer_base): ++ """Print a std::span.""" ++ ++ class _iterator(Iterator): ++ def __init__(self, begin, size): ++ self._count = 0 ++ self._begin = begin ++ self._size = size ++ ++ def __iter__(self): ++ return self ++ ++ def __next__(self): ++ if self._count == self._size: ++ raise StopIteration ++ ++ count = self._count ++ self._count = self._count + 1 ++ return '[%d]' % count, (self._begin + count).dereference() ++ ++ def __init__(self, typename, val): ++ self._typename = strip_versioned_namespace(typename) ++ self._val = val ++ size_max = gdb.parse_and_eval('static_cast(-1)') ++ if val.type.template_argument(1) == size_max: ++ self._size = val['_M_extent']['_M_extent_value'] ++ else: ++ self._size = val.type.template_argument(1) ++ ++ def to_string(self): ++ return '%s of length %d' % (self._typename, self._size) ++ ++ def children(self): ++ return self._iterator(self._val['_M_ptr'], self._size) ++ ++ def display_hint(self): ++ return 'array' ++ ++ ++class StdInitializerListPrinter(printer_base): ++ """Print a std::initializer_list.""" ++ ++ def __init__(self, typename, val): ++ self._typename = typename ++ self._val = val ++ self._size = val['_M_len'] ++ ++ def to_string(self): ++ return '%s of length %d' % (self._typename, self._size) ++ ++ def children(self): ++ return StdSpanPrinter._iterator(self._val['_M_array'], self._size) ++ ++ def display_hint(self): ++ return 'array' ++ ++ ++class StdAtomicPrinter(printer_base): ++ """Print a std:atomic.""" ++ ++ def __init__(self, typename, val): ++ self._typename = strip_versioned_namespace(typename) ++ self._val = val ++ self._shptr_printer = None ++ self._value_type = self._val.type.template_argument(0) ++ if self._value_type.tag is not None: ++ typ = strip_versioned_namespace(self._value_type.tag) ++ if (typ.startswith('std::shared_ptr<') ++ or typ.startswith('std::weak_ptr<')): ++ impl = val['_M_impl'] ++ self._shptr_printer = SharedPointerPrinter(typename, impl) ++ self.children = self._shptr_children ++ ++ def _shptr_children(self): ++ return SmartPtrIterator(self._shptr_printer._pointer) ++ ++ def to_string(self): ++ if self._shptr_printer is not None: ++ return self._shptr_printer.to_string() ++ ++ if self._value_type.code == gdb.TYPE_CODE_INT: ++ val = self._val['_M_i'] ++ elif self._value_type.code == gdb.TYPE_CODE_FLT: ++ val = self._val['_M_fp'] ++ elif self._value_type.code == gdb.TYPE_CODE_PTR: ++ val = self._val['_M_b']['_M_p'] ++ elif self._value_type.code == gdb.TYPE_CODE_BOOL: ++ val = self._val['_M_base']['_M_i'] ++ else: ++ val = self._val['_M_i'] ++ return '%s<%s> = { %s }' % (self._typename, str(self._value_type), val) ++ ++ ++class StdFormatArgsPrinter(printer_base): ++ """Print a std::basic_format_args.""" ++ # TODO: add printer for basic_format_arg and print out children. ++ # TODO: add printer for __format::_ArgStore. ++ ++ def __init__(self, typename, val): ++ self._typename = strip_versioned_namespace(typename) ++ self._val = val ++ ++ def to_string(self): ++ targs = get_template_arg_list(self._val.type) ++ char_type = get_template_arg_list(targs[0])[1] ++ if char_type == gdb.lookup_type('char'): ++ typ = 'std::format_args' ++ elif char_type == gdb.lookup_type('wchar_t'): ++ typ = 'std::wformat_args' ++ else: ++ typ = 'std::basic_format_args' ++ ++ size = self._val['_M_packed_size'] ++ if size == 1: ++ return "%s with 1 argument" % (typ) ++ if size == 0: ++ size = self._val['_M_unpacked_size'] ++ return "%s with %d arguments" % (typ, size) ++ ++ ++class StdChronoDurationPrinter(printer_base): ++ """Print a std::chrono::duration.""" ++ ++ def __init__(self, typename, val): ++ self._typename = strip_versioned_namespace(typename) ++ self._val = val ++ ++ def _ratio(self): ++ # TODO use reduced period i.e. duration::period ++ period = self._val.type.template_argument(1) ++ num = period.template_argument(0) ++ den = period.template_argument(1) ++ return (num, den) ++ ++ def _suffix(self): ++ num, den = self._ratio() ++ if num == 1: ++ if den == 1: ++ return 's' ++ if den == 1000: ++ return 'ms' ++ if den == 1000000: ++ return 'us' ++ if den == 1000000000: ++ return 'ns' ++ elif den == 1: ++ if num == 60: ++ return 'min' ++ if num == 3600: ++ return 'h' ++ if num == 86400: ++ return 'd' ++ return '[{}]s'.format(num) ++ return "[{}/{}]s".format(num, den) ++ ++ def to_string(self): ++ r = self._val['__r'] ++ if r.type.strip_typedefs().code == gdb.TYPE_CODE_FLT: ++ r = "%g" % r ++ return "std::chrono::duration = {{ {}{} }}".format(r, self._suffix()) ++ ++ ++class StdChronoTimePointPrinter(printer_base): ++ """Print a std::chrono::time_point.""" ++ ++ def __init__(self, typename, val): ++ self._typename = strip_versioned_namespace(typename) ++ self._val = val ++ ++ def _clock(self): ++ clock = self._val.type.template_argument(0) ++ name = strip_versioned_namespace(clock.name) ++ if name == 'std::chrono::_V2::system_clock' \ ++ or name == 'std::chrono::system_clock': ++ return ('std::chrono::sys_time', 0) ++ # XXX need to remove leap seconds from utc, gps, and tai ++ if name == 'std::chrono::utc_clock': ++ return ('std::chrono::utc_time', None) # XXX ++ if name == 'std::chrono::gps_clock': ++ return ('std::chrono::gps_time', None) # XXX 315964809 ++ if name == 'std::chrono::tai_clock': ++ return ('std::chrono::tai_time', None) # XXX -378691210 ++ if name == 'std::filesystem::__file_clock': ++ return ('std::chrono::file_time', 6437664000) ++ if name == 'std::chrono::local_t': ++ return ('std::chrono::local_time', 0) ++ return ('{} time_point'.format(name), None) ++ ++ def to_string(self, abbrev=False): ++ clock, offset = self._clock() ++ d = self._val['__d'] ++ r = d['__r'] ++ printer = StdChronoDurationPrinter(d.type.name, d) ++ suffix = printer._suffix() ++ time = '' ++ if offset is not None: ++ num, den = printer._ratio() ++ secs = (r * num / den) + offset ++ try: ++ dt = datetime.datetime.fromtimestamp(secs, _utc_timezone) ++ time = ' [{:%Y-%m-%d %H:%M:%S}]'.format(dt) ++ except: ++ pass ++ s = '%d%s%s' % (r, suffix, time) ++ if abbrev: ++ return s ++ return '%s = { %s }' % (clock, s) ++ ++ ++class StdChronoZonedTimePrinter(printer_base): ++ """Print a std::chrono::zoned_time.""" ++ ++ def __init__(self, typename, val): ++ self._typename = strip_versioned_namespace(typename) ++ self._val = val ++ ++ def to_string(self): ++ zone = self._val['_M_zone'].dereference()['_M_name'] ++ time = self._val['_M_tp'] ++ printer = StdChronoTimePointPrinter(time.type.name, time) ++ time = printer.to_string(True) ++ return 'std::chrono::zoned_time = {{ {} {} }}'.format(zone, time) ++ ++ ++months = [None, 'January', 'February', 'March', 'April', 'May', 'June', ++ 'July', 'August', 'September', 'October', 'November', 'December'] ++ ++weekdays = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', ++ 'Saturday', 'Sunday'] ++ ++ ++class StdChronoCalendarPrinter(printer_base): ++ """Print a std::chrono::day, std::chrono::month, std::chrono::year etc.""" ++ ++ def __init__(self, typename, val): ++ self._typename = strip_versioned_namespace(typename) ++ self._val = val ++ ++ def to_string(self): ++ val = self._val ++ typ = self._typename ++ if 'month' in typ and typ != 'std::chrono::year_month_day_last': ++ m = val['_M_m'] ++ if typ.startswith('std::chrono::year'): ++ y = val['_M_y'] ++ ++ if typ == 'std::chrono::day': ++ return '{}'.format(int(val['_M_d'])) ++ if typ == 'std::chrono::month': ++ if m < 1 or m >= len(months): ++ return "%d is not a valid month" % m ++ return months[m] ++ if typ == 'std::chrono::year': ++ return '{}y'.format(y) ++ if typ == 'std::chrono::weekday': ++ wd = val['_M_wd'] ++ if wd < 0 or wd >= len(weekdays): ++ return "%d is not a valid weekday" % wd ++ return '{}'.format(weekdays[wd]) ++ if typ == 'std::chrono::weekday_indexed': ++ return '{}[{}]'.format(val['_M_wd'], int(val['_M_index'])) ++ if typ == 'std::chrono::weekday_last': ++ return '{}[last]'.format(val['_M_wd']) ++ if typ == 'std::chrono::month_day': ++ return '{}/{}'.format(m, val['_M_d']) ++ if typ == 'std::chrono::month_day_last': ++ return '{}/last'.format(m) ++ if typ == 'std::chrono::month_weekday': ++ return '{}/{}'.format(m, val['_M_wdi']) ++ if typ == 'std::chrono::month_weekday_last': ++ return '{}/{}'.format(m, val['_M_wdl']) ++ if typ == 'std::chrono::year_month': ++ return '{}/{}'.format(y, m) ++ if typ == 'std::chrono::year_month_day': ++ return '{}/{}/{}'.format(y, m, val['_M_d']) ++ if typ == 'std::chrono::year_month_day_last': ++ return '{}/{}'.format(y, val['_M_mdl']) ++ if typ == 'std::chrono::year_month_weekday': ++ return '{}/{}/{}'.format(y, m, val['_M_wdi']) ++ if typ == 'std::chrono::year_month_weekday_last': ++ return '{}/{}/{}'.format(y, m, val['_M_wdl']) ++ if typ.startswith('std::chrono::hh_mm_ss'): ++ fract = '' ++ if val['fractional_width'] != 0: ++ fract = '.{:0{}d}'.format(int(val['_M_ss']['_M_r']), ++ int(val['fractional_width'])) ++ h = int(val['_M_h']['__r']) ++ m = int(val['_M_m']['__r']) ++ s = int(val['_M_s']['__r']) ++ if val['_M_is_neg']: ++ h = -h ++ return '{:02}:{:02}:{:02}{}'.format(h, m, s, fract) ++ ++ ++class StdChronoTimeZonePrinter(printer_base): ++ """Print a chrono::time_zone or chrono::time_zone_link.""" ++ ++ def __init__(self, typename, val): ++ self._typename = strip_versioned_namespace(typename) ++ self._val = val ++ ++ def to_string(self): ++ str = '%s = %s' % (self._typename, self._val['_M_name']) ++ if self._typename.endswith("_link"): ++ str += ' -> %s' % (self._val['_M_target']) ++ return str ++ ++ ++class StdChronoLeapSecondPrinter(printer_base): ++ """Print a chrono::leap_second.""" ++ ++ def __init__(self, typename, val): ++ self._typename = strip_versioned_namespace(typename) ++ self._val = val ++ ++ def to_string(self): ++ date = self._val['_M_s']['__r'] ++ neg = '+-'[date < 0] ++ return '%s %d (%c)' % (self._typename, abs(date), neg) ++ ++ ++class StdChronoTzdbPrinter(printer_base): ++ """Print a chrono::tzdb.""" ++ ++ def __init__(self, typename, val): ++ self._typename = strip_versioned_namespace(typename) ++ self._val = val ++ ++ def to_string(self): ++ return '%s %s' % (self._typename, self._val['version']) ++ ++ ++class StdChronoTimeZoneRulePrinter(printer_base): ++ """Print a chrono::time_zone rule.""" ++ ++ def __init__(self, typename, val): ++ self._typename = strip_versioned_namespace(typename) ++ self._val = val ++ ++ def to_string(self): ++ on = self._val['on'] ++ kind = on['kind'] ++ month = months[on['month']] ++ suffixes = {1: 'st', 2: 'nd', 3: 'rd', ++ 21: 'st', 22: 'nd', 23: 'rd', 31: 'st'} ++ day = on['day_of_month'] ++ ordinal_day = '{}{}'.format(day, suffixes.get(day, 'th')) ++ if kind == 0: # DayOfMonth ++ start = '{} {}'.format(month, ordinal_day) ++ else: ++ weekday = weekdays[on['day_of_week']] ++ if kind == 1: # LastWeekDay ++ start = 'last {} in {}'.format(weekday, month) ++ else: ++ if kind == 2: # LessEq ++ direction = ('last', '<=') ++ else: ++ direction = ('first', '>=') ++ day = on['day_of_month'] ++ start = '{} {} {} {} {}'.format(direction[0], weekday, ++ direction[1], month, ++ ordinal_day) ++ return 'time_zone rule {} from {} to {} starting on {}'.format( ++ self._val['name'], self._val['from'], self._val['to'], start) ++ ++ ++class StdLocalePrinter(printer_base): ++ """Print a std::locale.""" ++ ++ def __init__(self, typename, val): ++ self._val = val ++ self._typename = typename ++ ++ def to_string(self): ++ names = self._val['_M_impl']['_M_names'] ++ mod = '' ++ if names[0] == 0: ++ name = '*' ++ else: ++ cats = gdb.parse_and_eval(self._typename + '::_S_categories') ++ ncat = gdb.parse_and_eval(self._typename + '::_S_categories_size') ++ n = names[0].string() ++ cat = cats[0].string() ++ name = '{}={}'.format(cat, n) ++ cat_names = {cat: n} ++ i = 1 ++ while i < ncat and names[i] != 0: ++ n = names[i].string() ++ cat = cats[i].string() ++ name = '{};{}={}'.format(name, cat, n) ++ cat_names[cat] = n ++ i = i + 1 ++ uniq_names = set(cat_names.values()) ++ if len(uniq_names) == 1: ++ name = n ++ elif len(uniq_names) == 2: ++ n1, n2 = (uniq_names) ++ name_list = list(cat_names.values()) ++ other = None ++ if name_list.count(n1) == 1: ++ name = n2 ++ other = n1 ++ elif name_list.count(n2) == 1: ++ name = n1 ++ other = n2 ++ if other is not None: ++ cat = next(c for c, n in cat_names.items() if n == other) ++ mod = ' with "{}={}"'.format(cat, other) ++ return 'std::locale = "{}"{}'.format(name, mod) ++ ++class StdIntegralConstantPrinter(printer_base): ++ """Print a std::true_type or std::false_type.""" ++ ++ def __init__(self, typename, val): ++ self._val = val ++ self._typename = typename ++ ++ def to_string(self): ++ value_type = self._val.type.template_argument(0) ++ value = self._val.type.template_argument(1) ++ if value_type.code == gdb.TYPE_CODE_BOOL: ++ if value: ++ return "std::true_type" ++ else: ++ return "std::false_type" ++ typename = strip_versioned_namespace(self._typename) ++ return "{}<{}, {}>".format(typename, value_type, value) ++ ++class StdTextEncodingPrinter(printer_base): ++ """Print a std::text_encoding.""" ++ ++ def __init__(self, typename, val): ++ self._val = val ++ self._typename = typename ++ ++ def to_string(self): ++ rep = self._val['_M_rep'].dereference() ++ if rep['_M_id'] == 1: ++ return self._val['_M_name'] ++ if rep['_M_id'] == 2: ++ return 'unknown' ++ return rep['_M_name'] ++ + # A "regular expression" printer which conforms to the + # "SubPrettyPrinter" protocol from gdb.printing. + class RxPrinter(object): + def __init__(self, name, function): + super(RxPrinter, self).__init__() + self.name = name +- self.function = function ++ self._function = function + self.enabled = True + + def invoke(self, value): +@@ -1315,36 +2360,40 @@ class RxPrinter(object): + return None + + if value.type.code == gdb.TYPE_CODE_REF: +- if hasattr(gdb.Value,"referenced_value"): ++ if hasattr(gdb.Value, "referenced_value"): + value = value.referenced_value() + +- return self.function(self.name, value) ++ return self._function(self.name, value) + + # A pretty-printer that conforms to the "PrettyPrinter" protocol from + # gdb.printing. It can also be used directly as an old-style printer. ++ ++ + class Printer(object): + def __init__(self, name): + super(Printer, self).__init__() + self.name = name +- self.subprinters = [] +- self.lookup = {} ++ self._subprinters = [] ++ self._lookup = {} + self.enabled = True +- self.compiled_rx = re.compile('^([a-zA-Z0-9_:]+)(<.*>)?$') ++ self._compiled_rx = re.compile('^([a-zA-Z0-9_:]+)(<.*>)?$') + + def add(self, name, function): + # A small sanity check. + # FIXME +- if not self.compiled_rx.match(name): +- raise ValueError('libstdc++ programming error: "%s" does not match' % name) ++ if not self._compiled_rx.match(name): ++ raise ValueError( ++ 'libstdc++ programming error: "%s" does not match' % name) + printer = RxPrinter(name, function) +- self.subprinters.append(printer) +- self.lookup[name] = printer ++ self._subprinters.append(printer) ++ self._lookup[name] = printer + + # Add a name using _GLIBCXX_BEGIN_NAMESPACE_VERSION. + def add_version(self, base, name, function): + self.add(base + name, function) +- if _versioned_namespace: +- vbase = re.sub('^(std|__gnu_cxx)::', r'\g<0>%s' % _versioned_namespace, base) ++ if '__cxx11' not in base: ++ vbase = re.sub('^(std|__gnu_cxx)::', r'\g<0>%s' % ++ _versioned_namespace, base) + self.add(vbase + name, function) + + # Add a name using _GLIBCXX_BEGIN_NAMESPACE_CONTAINER. +@@ -1356,10 +2405,10 @@ class Printer(object): + def get_basic_type(type): + # If it points to a reference, get the reference. + if type.code == gdb.TYPE_CODE_REF: +- type = type.target () ++ type = type.target() + + # Get the unqualified type, stripped of typedefs. +- type = type.unqualified ().strip_typedefs () ++ type = type.unqualified().strip_typedefs() + + return type.tag + +@@ -1370,47 +2419,49 @@ class Printer(object): + + # All the types we match are template types, so we can use a + # dictionary. +- match = self.compiled_rx.match(typename) ++ match = self._compiled_rx.match(typename) + if not match: + return None + + basename = match.group(1) + + if val.type.code == gdb.TYPE_CODE_REF: +- if hasattr(gdb.Value,"referenced_value"): ++ if hasattr(gdb.Value, "referenced_value"): + val = val.referenced_value() + +- if basename in self.lookup: +- return self.lookup[basename].invoke(val) ++ if basename in self._lookup: ++ return self._lookup[basename].invoke(val) + + # Cannot find a pretty printer. Return None. + return None + ++ + libstdcxx_printer = None + ++ + class TemplateTypePrinter(object): +- r""" ++ """ + A type printer for class templates with default template arguments. + + Recognizes specializations of class templates and prints them without + any template arguments that use a default template argument. + Type printers are recursively applied to the template arguments. + +- e.g. replace "std::vector >" with "std::vector". ++ e.g. replace 'std::vector >' with 'std::vector'. + """ + + def __init__(self, name, defargs): + self.name = name +- self.defargs = defargs ++ self._defargs = defargs + self.enabled = True + + class _recognizer(object): +- "The recognizer class for TemplateTypePrinter." ++ """The recognizer class for TemplateTypePrinter.""" + + def __init__(self, name, defargs): + self.name = name +- self.defargs = defargs +- # self.type_obj = None ++ self._defargs = defargs ++ # self._type_obj = None + + def recognize(self, type_obj): + """ +@@ -1433,7 +2484,7 @@ class TemplateTypePrinter(object): + # The actual template argument in the type: + targ = template_args[n] + # The default template argument for the class template: +- defarg = self.defargs.get(n) ++ defarg = self._defargs.get(n) + if defarg is not None: + # Substitute other template arguments into the default: + defarg = defarg.format(*template_args) +@@ -1470,7 +2521,7 @@ class TemplateTypePrinter(object): + if type_obj.code == gdb.TYPE_CODE_ARRAY: + type_str = self._recognize_subtype(type_obj.target()) + if str(type_obj.strip_typedefs()).endswith('[]'): +- return type_str + '[]' # array of unknown bound ++ return type_str + '[]' # array of unknown bound + return "%s[%d]" % (type_str, type_obj.range()[1] + 1) + if type_obj.code == gdb.TYPE_CODE_REF: + return self._recognize_subtype(type_obj.target()) + '&' +@@ -1479,17 +2530,18 @@ class TemplateTypePrinter(object): + return self._recognize_subtype(type_obj.target()) + '&&' + + type_str = gdb.types.apply_type_recognizers( +- gdb.types.get_type_recognizers(), type_obj) ++ gdb.types.get_type_recognizers(), type_obj) + if type_str: + return type_str + return str(type_obj) + + def instantiate(self): +- "Return a recognizer object for this type printer." +- return self._recognizer(self.name, self.defargs) ++ """Return a recognizer object for this type printer.""" ++ return self._recognizer(self.name, self._defargs) ++ + + def add_one_template_type_printer(obj, name, defargs): +- r""" ++ """ + Add a type printer for a class template with default template arguments. + + Args: +@@ -1503,78 +2555,121 @@ def add_one_template_type_printer(obj, name, defargs): + { 2: 'std::hash<{0}>', + 3: 'std::equal_to<{0}>', + 4: 'std::allocator >' } +- + """ +- printer = TemplateTypePrinter('std::'+name, defargs) ++ printer = TemplateTypePrinter('std::' + name, defargs) ++ gdb.types.register_type_printer(obj, printer) ++ ++ # Add type printer for same type in debug namespace: ++ printer = TemplateTypePrinter('std::__debug::' + name, defargs) + gdb.types.register_type_printer(obj, printer) +- if _versioned_namespace: ++ ++ if '__cxx11' not in name: + # Add second type printer for same type in versioned namespace: + ns = 'std::' + _versioned_namespace + # PR 86112 Cannot use dict comprehension here: +- defargs = dict((n, d.replace('std::', ns)) for (n,d) in defargs.items()) +- printer = TemplateTypePrinter(ns+name, defargs) ++ defargs = dict((n, d.replace('std::', ns)) ++ for (n, d) in defargs.items()) ++ printer = TemplateTypePrinter(ns + name, defargs) ++ gdb.types.register_type_printer(obj, printer) ++ ++ # Add type printer for same type in debug namespace: ++ printer = TemplateTypePrinter('std::__debug::' + name, defargs) + gdb.types.register_type_printer(obj, printer) + ++ + class FilteringTypePrinter(object): +- r""" ++ """ + A type printer that uses typedef names for common template specializations. + + Args: +- match (str): The class template to recognize. ++ template (str): The class template to recognize. + name (str): The typedef-name that will be used instead. ++ targ1 (str, optional): The first template argument. Defaults to None. + +- Checks if a specialization of the class template 'match' is the same type ++ Checks if a specialization of the class template 'template' is the same type + as the typedef 'name', and prints it as 'name' instead. + + e.g. if an instantiation of std::basic_istream is the same type as + std::istream then print it as std::istream. ++ ++ If targ1 is provided (not None), match only template specializations with ++ this type as the first template argument, e.g. if template='basic_string' ++ and targ1='char' then only match 'basic_string' and not ++ 'basic_string'. This rejects non-matching specializations ++ more quickly, without needing to do GDB type lookups. + """ + +- def __init__(self, match, name): +- self.match = match ++ def __init__(self, template, name, targ1=None): ++ self._template = template + self.name = name ++ self._targ1 = targ1 + self.enabled = True + + class _recognizer(object): +- "The recognizer class for TemplateTypePrinter." ++ """The recognizer class for FilteringTypePrinter.""" + +- def __init__(self, match, name): +- self.match = match ++ def __init__(self, template, name, targ1): ++ self._template = template + self.name = name +- self.type_obj = None ++ self._targ1 = targ1 ++ self._type_obj = None + + def recognize(self, type_obj): + """ +- If type_obj starts with self.match and is the same type as ++ If type_obj starts with self._template and is the same type as + self.name then return self.name, otherwise None. + """ + if type_obj.tag is None: + return None + +- if self.type_obj is None: +- if not type_obj.tag.startswith(self.match): ++ if self._type_obj is None: ++ if self._targ1 is not None: ++ s = '{}<{}'.format(self._template, self._targ1) ++ if not type_obj.tag.startswith(s): ++ # Filter didn't match. ++ return None ++ elif not type_obj.tag.startswith(self._template): + # Filter didn't match. + return None ++ + try: +- self.type_obj = gdb.lookup_type(self.name).strip_typedefs() ++ self._type_obj = gdb.lookup_type( ++ self.name).strip_typedefs() + except: + pass +- if self.type_obj == type_obj: ++ ++ if self._type_obj is None: ++ return None ++ ++ t1 = gdb.types.get_basic_type(self._type_obj) ++ t2 = gdb.types.get_basic_type(type_obj) ++ if t1 == t2: + return strip_inline_namespaces(self.name) ++ ++ # Workaround ambiguous typedefs matching both std:: and ++ # std::__cxx11:: symbols. ++ if self._template.split('::')[-1] == 'basic_string': ++ s1 = self._type_obj.tag.replace('__cxx11::', '') ++ s2 = type_obj.tag.replace('__cxx11::', '') ++ if s1 == s2: ++ return strip_inline_namespaces(self.name) ++ + return None + + def instantiate(self): +- "Return a recognizer object for this type printer." +- return self._recognizer(self.match, self.name) ++ """Return a recognizer object for this type printer.""" ++ return self._recognizer(self._template, self.name, self._targ1) + +-def add_one_type_printer(obj, match, name): +- printer = FilteringTypePrinter('std::' + match, 'std::' + name) ++ ++def add_one_type_printer(obj, template, name, targ1=None): ++ printer = FilteringTypePrinter('std::' + template, 'std::' + name, targ1) + gdb.types.register_type_printer(obj, printer) +- if _versioned_namespace: ++ if '__cxx11' not in template: + ns = 'std::' + _versioned_namespace +- printer = FilteringTypePrinter(ns + match, ns + name) ++ printer = FilteringTypePrinter(ns + template, ns + name, targ1) + gdb.types.register_type_printer(obj, printer) + ++ + def register_type_printers(obj): + global _use_type_printing + +@@ -1582,29 +2677,39 @@ def register_type_printers(obj): + return + + # Add type printers for typedefs std::string, std::wstring etc. +- for ch in ('', 'w', 'u16', 'u32'): +- add_one_type_printer(obj, 'basic_string', ch + 'string') ++ for ch in (('', 'char'), ++ ('w', 'wchar_t'), ++ ('u8', 'char8_t'), ++ ('u16', 'char16_t'), ++ ('u32', 'char32_t')): ++ add_one_type_printer(obj, 'basic_string', ch[0] + 'string', ch[1]) ++ add_one_type_printer(obj, '__cxx11::basic_string', ++ ch[0] + 'string', ch[1]) ++ # Typedefs for __cxx11::basic_string used to be in namespace __cxx11: + add_one_type_printer(obj, '__cxx11::basic_string', +- '__cxx11::' + ch + 'string') +- add_one_type_printer(obj, 'basic_string_view', ch + 'string_view') ++ '__cxx11::' + ch[0] + 'string', ch[1]) ++ add_one_type_printer(obj, 'basic_string_view', ++ ch[0] + 'string_view', ch[1]) + + # Add type printers for typedefs std::istream, std::wistream etc. +- for ch in ('', 'w'): ++ for ch in (('', 'char'), ('w', 'wchar_t')): + for x in ('ios', 'streambuf', 'istream', 'ostream', 'iostream', + 'filebuf', 'ifstream', 'ofstream', 'fstream'): +- add_one_type_printer(obj, 'basic_' + x, ch + x) ++ add_one_type_printer(obj, 'basic_' + x, ch[0] + x, ch[1]) + for x in ('stringbuf', 'istringstream', 'ostringstream', + 'stringstream'): +- add_one_type_printer(obj, 'basic_' + x, ch + x) +- # types are in __cxx11 namespace, but typedefs aren'x: +- add_one_type_printer(obj, '__cxx11::basic_' + x, ch + x) ++ add_one_type_printer(obj, 'basic_' + x, ch[0] + x, ch[1]) ++ # types are in __cxx11 namespace, but typedefs aren't: ++ add_one_type_printer(obj, '__cxx11::basic_' + x, ch[0] + x, ch[1]) + + # Add type printers for typedefs regex, wregex, cmatch, wcmatch etc. + for abi in ('', '__cxx11::'): +- for ch in ('', 'w'): +- add_one_type_printer(obj, abi + 'basic_regex', abi + ch + 'regex') ++ for ch in (('', 'char'), ('w', 'wchar_t')): ++ add_one_type_printer(obj, abi + 'basic_regex', ++ abi + ch[0] + 'regex', ch[1]) + for ch in ('c', 's', 'wc', 'ws'): +- add_one_type_printer(obj, abi + 'match_results', abi + ch + 'match') ++ add_one_type_printer( ++ obj, abi + 'match_results', abi + ch + 'match') + for x in ('sub_match', 'regex_iterator', 'regex_token_iterator'): + add_one_type_printer(obj, abi + x, abi + ch + x) + +@@ -1613,9 +2718,9 @@ def register_type_printers(obj): + add_one_type_printer(obj, 'fpos', 'streampos') + + # Add type printers for typedefs. +- for dur in ('nanoseconds', 'microseconds', 'milliseconds', +- 'seconds', 'minutes', 'hours'): +- add_one_type_printer(obj, 'duration', dur) ++ for dur in ('nanoseconds', 'microseconds', 'milliseconds', 'seconds', ++ 'minutes', 'hours', 'days', 'weeks', 'years', 'months'): ++ add_one_type_printer(obj, 'chrono::duration', 'chrono::' + dur) + + # Add type printers for typedefs. + add_one_type_printer(obj, 'linear_congruential_engine', 'minstd_rand0') +@@ -1630,47 +2735,54 @@ def register_type_printers(obj): + + # Add type printers for experimental::basic_string_view typedefs. + ns = 'experimental::fundamentals_v1::' +- for ch in ('', 'w', 'u16', 'u32'): ++ for ch in (('', 'char'), ++ ('w', 'wchar_t'), ++ ('u8', 'char8_t'), ++ ('u16', 'char16_t'), ++ ('u32', 'char32_t')): + add_one_type_printer(obj, ns + 'basic_string_view', +- ns + ch + 'string_view') ++ ns + ch[0] + 'string_view', ch[1]) + + # Do not show defaulted template arguments in class templates. + add_one_template_type_printer(obj, 'unique_ptr', +- { 1: 'std::default_delete<{0}>' }) +- add_one_template_type_printer(obj, 'deque', { 1: 'std::allocator<{0}>'}) +- add_one_template_type_printer(obj, 'forward_list', { 1: 'std::allocator<{0}>'}) +- add_one_template_type_printer(obj, 'list', { 1: 'std::allocator<{0}>'}) +- add_one_template_type_printer(obj, '__cxx11::list', { 1: 'std::allocator<{0}>'}) +- add_one_template_type_printer(obj, 'vector', { 1: 'std::allocator<{0}>'}) ++ {1: 'std::default_delete<{0}>'}) ++ add_one_template_type_printer(obj, 'deque', {1: 'std::allocator<{0}>'}) ++ add_one_template_type_printer( ++ obj, 'forward_list', {1: 'std::allocator<{0}>'}) ++ add_one_template_type_printer(obj, 'list', {1: 'std::allocator<{0}>'}) ++ add_one_template_type_printer( ++ obj, '__cxx11::list', {1: 'std::allocator<{0}>'}) ++ add_one_template_type_printer(obj, 'vector', {1: 'std::allocator<{0}>'}) + add_one_template_type_printer(obj, 'map', +- { 2: 'std::less<{0}>', +- 3: 'std::allocator>' }) ++ {2: 'std::less<{0}>', ++ 3: 'std::allocator>'}) + add_one_template_type_printer(obj, 'multimap', +- { 2: 'std::less<{0}>', +- 3: 'std::allocator>' }) ++ {2: 'std::less<{0}>', ++ 3: 'std::allocator>'}) + add_one_template_type_printer(obj, 'set', +- { 1: 'std::less<{0}>', 2: 'std::allocator<{0}>' }) ++ {1: 'std::less<{0}>', 2: 'std::allocator<{0}>'}) + add_one_template_type_printer(obj, 'multiset', +- { 1: 'std::less<{0}>', 2: 'std::allocator<{0}>' }) ++ {1: 'std::less<{0}>', 2: 'std::allocator<{0}>'}) + add_one_template_type_printer(obj, 'unordered_map', +- { 2: 'std::hash<{0}>', +- 3: 'std::equal_to<{0}>', +- 4: 'std::allocator>'}) ++ {2: 'std::hash<{0}>', ++ 3: 'std::equal_to<{0}>', ++ 4: 'std::allocator>'}) + add_one_template_type_printer(obj, 'unordered_multimap', +- { 2: 'std::hash<{0}>', +- 3: 'std::equal_to<{0}>', +- 4: 'std::allocator>'}) ++ {2: 'std::hash<{0}>', ++ 3: 'std::equal_to<{0}>', ++ 4: 'std::allocator>'}) + add_one_template_type_printer(obj, 'unordered_set', +- { 1: 'std::hash<{0}>', +- 2: 'std::equal_to<{0}>', +- 3: 'std::allocator<{0}>'}) ++ {1: 'std::hash<{0}>', ++ 2: 'std::equal_to<{0}>', ++ 3: 'std::allocator<{0}>'}) + add_one_template_type_printer(obj, 'unordered_multiset', +- { 1: 'std::hash<{0}>', +- 2: 'std::equal_to<{0}>', +- 3: 'std::allocator<{0}>'}) ++ {1: 'std::hash<{0}>', ++ 2: 'std::equal_to<{0}>', ++ 3: 'std::allocator<{0}>'}) + +-def register_libstdcxx_printers (obj): +- "Register libstdc++ pretty-printers with objfile Obj." ++ ++def register_libstdcxx_printers(obj): ++ """Register libstdc++ pretty-printers with objfile Obj.""" + + global _use_gdb_pp + global libstdcxx_printer +@@ -1684,7 +2796,8 @@ def register_libstdcxx_printers (obj): + + register_type_printers(obj) + +-def build_libstdcxx_dictionary (): ++ ++def build_libstdcxx_dictionary(): + global libstdcxx_printer + + libstdcxx_printer = Printer("libstdc++-v6") +@@ -1693,7 +2806,8 @@ def build_libstdcxx_dictionary (): + # In order from: + # http://gcc.gnu.org/onlinedocs/libstdc++/latest-doxygen/a01847.html + libstdcxx_printer.add_version('std::', 'basic_string', StdStringPrinter) +- libstdcxx_printer.add_version('std::__cxx11::', 'basic_string', StdStringPrinter) ++ 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_container('std::', 'list', StdListPrinter) +@@ -1711,6 +2825,18 @@ def build_libstdcxx_dictionary (): + libstdcxx_printer.add_version('std::', 'unique_ptr', UniquePointerPrinter) + libstdcxx_printer.add_container('std::', 'vector', StdVectorPrinter) + # vector ++ libstdcxx_printer.add_version('std::', 'locale', StdLocalePrinter) ++ ++ libstdcxx_printer.add_version('std::', 'integral_constant', ++ StdIntegralConstantPrinter) ++ libstdcxx_printer.add_version('std::', 'text_encoding', ++ StdTextEncodingPrinter) ++ ++ 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) +@@ -1719,12 +2845,7 @@ def build_libstdcxx_dictionary (): + libstdcxx_printer.add('std::__debug::map', StdMapPrinter) + libstdcxx_printer.add('std::__debug::multimap', StdMapPrinter) + libstdcxx_printer.add('std::__debug::multiset', StdSetPrinter) +- libstdcxx_printer.add('std::__debug::priority_queue', +- StdStackOrQueuePrinter) +- libstdcxx_printer.add('std::__debug::queue', StdStackOrQueuePrinter) + libstdcxx_printer.add('std::__debug::set', StdSetPrinter) +- libstdcxx_printer.add('std::__debug::stack', StdStackOrQueuePrinter) +- libstdcxx_printer.add('std::__debug::unique_ptr', UniquePointerPrinter) + libstdcxx_printer.add('std::__debug::vector', StdVectorPrinter) + + # These are the TR1 and C++11 printers. +@@ -1742,8 +2863,10 @@ def build_libstdcxx_dictionary (): + libstdcxx_printer.add_container('std::', 'forward_list', + StdForwardListPrinter) + +- libstdcxx_printer.add_version('std::tr1::', 'shared_ptr', SharedPointerPrinter) +- libstdcxx_printer.add_version('std::tr1::', 'weak_ptr', SharedPointerPrinter) ++ libstdcxx_printer.add_version( ++ 'std::tr1::', 'shared_ptr', SharedPointerPrinter) ++ libstdcxx_printer.add_version( ++ 'std::tr1::', 'weak_ptr', SharedPointerPrinter) + libstdcxx_printer.add_version('std::tr1::', 'unordered_map', + Tr1UnorderedMapPrinter) + libstdcxx_printer.add_version('std::tr1::', 'unordered_set', +@@ -1753,6 +2876,28 @@ def build_libstdcxx_dictionary (): + libstdcxx_printer.add_version('std::tr1::', 'unordered_multiset', + Tr1UnorderedSetPrinter) + ++ libstdcxx_printer.add_version('std::', 'initializer_list', ++ StdInitializerListPrinter) ++ libstdcxx_printer.add_version('std::', 'atomic', StdAtomicPrinter) ++ libstdcxx_printer.add_version( ++ 'std::', 'basic_stringbuf', StdStringBufPrinter) ++ libstdcxx_printer.add_version( ++ 'std::__cxx11::', 'basic_stringbuf', StdStringBufPrinter) ++ for sstream in ('istringstream', 'ostringstream', 'stringstream'): ++ libstdcxx_printer.add_version( ++ 'std::', 'basic_' + sstream, StdStringStreamPrinter) ++ libstdcxx_printer.add_version( ++ 'std::__cxx11::', 'basic_' + sstream, StdStringStreamPrinter) ++ ++ libstdcxx_printer.add_version('std::chrono::', 'duration', ++ StdChronoDurationPrinter) ++ libstdcxx_printer.add_version('std::chrono::', 'time_point', ++ StdChronoTimePointPrinter) ++ ++ # std::regex components ++ libstdcxx_printer.add_version('std::__detail::', '_State', ++ StdRegexStatePrinter) ++ + # These are the C++11 printer registrations for -D_GLIBCXX_DEBUG cases. + # The tr1 namespace containers do not have any debug equivalents, + # so do not register printers for them. +@@ -1780,9 +2925,9 @@ def build_libstdcxx_dictionary (): + libstdcxx_printer.add_version('std::experimental::filesystem::v1::__cxx11::', + 'path', StdExpPathPrinter) + libstdcxx_printer.add_version('std::filesystem::', +- 'path', StdExpPathPrinter) ++ 'path', StdPathPrinter) + libstdcxx_printer.add_version('std::filesystem::__cxx11::', +- 'path', StdExpPathPrinter) ++ 'path', StdPathPrinter) + + # C++17 components + libstdcxx_printer.add_version('std::', +@@ -1796,6 +2941,33 @@ def build_libstdcxx_dictionary (): + libstdcxx_printer.add_version('std::', + '_Node_handle', StdNodeHandlePrinter) + ++ # C++20 components ++ libstdcxx_printer.add_version( ++ 'std::', 'partial_ordering', StdCmpCatPrinter) ++ libstdcxx_printer.add_version('std::', 'weak_ordering', StdCmpCatPrinter) ++ libstdcxx_printer.add_version('std::', 'strong_ordering', StdCmpCatPrinter) ++ libstdcxx_printer.add_version('std::', 'span', StdSpanPrinter) ++ libstdcxx_printer.add_version('std::', 'basic_format_args', ++ StdFormatArgsPrinter) ++ for c in ['day', 'month', 'year', 'weekday', 'weekday_indexed', 'weekday_last', ++ 'month_day', 'month_day_last', 'month_weekday', 'month_weekday_last', ++ 'year_month', 'year_month_day', 'year_month_day_last', ++ 'year_month_weekday', 'year_month_weekday_last', 'hh_mm_ss']: ++ libstdcxx_printer.add_version('std::chrono::', c, ++ StdChronoCalendarPrinter) ++ libstdcxx_printer.add_version('std::chrono::', 'time_zone', ++ StdChronoTimeZonePrinter) ++ libstdcxx_printer.add_version('std::chrono::', 'time_zone_link', ++ StdChronoTimeZonePrinter) ++ libstdcxx_printer.add_version('std::chrono::', 'zoned_time', ++ StdChronoZonedTimePrinter) ++ libstdcxx_printer.add_version('std::chrono::', 'leap_second', ++ StdChronoLeapSecondPrinter) ++ libstdcxx_printer.add_version( ++ 'std::chrono::', 'tzdb', StdChronoTzdbPrinter) ++ # libstdcxx_printer.add_version('std::chrono::(anonymous namespace)', 'Rule', ++ # StdChronoTimeZoneRulePrinter) ++ + # Extensions. + libstdcxx_printer.add_version('__gnu_cxx::', 'slist', StdSlistPrinter) + +@@ -1816,6 +2988,12 @@ def build_libstdcxx_dictionary (): + StdDequeIteratorPrinter) + libstdcxx_printer.add_version('__gnu_cxx::', '__normal_iterator', + StdVectorIteratorPrinter) ++ libstdcxx_printer.add_container('std::', '_Bit_iterator', ++ StdBitIteratorPrinter) ++ libstdcxx_printer.add_container('std::', '_Bit_const_iterator', ++ StdBitIteratorPrinter) ++ libstdcxx_printer.add_container('std::', '_Bit_reference', ++ StdBitReferencePrinter) + libstdcxx_printer.add_version('__gnu_cxx::', '_Slist_iterator', + StdSlistIteratorPrinter) + libstdcxx_printer.add_container('std::', '_Fwd_list_iterator', +@@ -1828,4 +3006,5 @@ def build_libstdcxx_dictionary (): + libstdcxx_printer.add('__gnu_debug::_Safe_iterator', + StdDebugIteratorPrinter) + +-build_libstdcxx_dictionary () ++ ++build_libstdcxx_dictionary() +diff --git a/libstdc++-v3/python/libstdcxx/v6/xmethods.py b/libstdc++-v3/python/libstdcxx/v6/xmethods.py +index 12fefdb041c..436c866e001 100644 +--- a/libstdc++-v3/python/libstdcxx/v6/xmethods.py ++++ b/libstdc++-v3/python/libstdcxx/v6/xmethods.py +@@ -1,6 +1,6 @@ + # Xmethods for libstdc++. + +-# Copyright (C) 2014-2018 Free Software Foundation, Inc. ++# Copyright (C) 2014-2024 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 +@@ -21,12 +21,27 @@ import re + + matcher_name_prefix = 'libstdc++::' + ++ + def get_bool_type(): + return gdb.lookup_type('bool') + + def get_std_size_type(): + return gdb.lookup_type('std::size_t') + ++_versioned_namespace = '__8::' ++ ++def is_specialization_of(x, template_name): ++ """ ++ Test whether a type is a specialization of the named class template. ++ The type can be specified as a string or a gdb.Type object. ++ The template should be the name of a class template as a string, ++ without any 'std' qualification. ++ """ ++ if isinstance(x, gdb.Type): ++ x = x.tag ++ template_name = '(%s)?%s' % (_versioned_namespace, template_name) ++ return re.match(r'^std::(__\d::)?%s<.*>$' % template_name, x) is not None ++ + class LibStdCxxXMethod(gdb.xmethod.XMethod): + def __init__(self, name, worker_class): + gdb.xmethod.XMethod.__init__(self, name) +@@ -34,6 +49,7 @@ class LibStdCxxXMethod(gdb.xmethod.XMethod): + + # Xmethods for std::array + ++ + class ArrayWorkerBase(gdb.xmethod.XMethodWorker): + def __init__(self, val_type, size): + self._val_type = val_type +@@ -43,6 +59,7 @@ class ArrayWorkerBase(gdb.xmethod.XMethodWorker): + nullptr = gdb.parse_and_eval('(void *) 0') + return nullptr.cast(self._val_type.pointer()).dereference() + ++ + class ArraySizeWorker(ArrayWorkerBase): + def __init__(self, val_type, size): + ArrayWorkerBase.__init__(self, val_type, size) +@@ -56,6 +73,7 @@ class ArraySizeWorker(ArrayWorkerBase): + def __call__(self, obj): + return self._size + ++ + class ArrayEmptyWorker(ArrayWorkerBase): + def __init__(self, val_type, size): + ArrayWorkerBase.__init__(self, val_type, size) +@@ -69,6 +87,7 @@ class ArrayEmptyWorker(ArrayWorkerBase): + def __call__(self, obj): + return (int(self._size) == 0) + ++ + class ArrayFrontWorker(ArrayWorkerBase): + def __init__(self, val_type, size): + ArrayWorkerBase.__init__(self, val_type, size) +@@ -85,6 +104,7 @@ class ArrayFrontWorker(ArrayWorkerBase): + else: + return self.null_value() + ++ + class ArrayBackWorker(ArrayWorkerBase): + def __init__(self, val_type, size): + ArrayWorkerBase.__init__(self, val_type, size) +@@ -101,6 +121,7 @@ class ArrayBackWorker(ArrayWorkerBase): + else: + return self.null_value() + ++ + class ArrayAtWorker(ArrayWorkerBase): + def __init__(self, val_type, size): + ArrayWorkerBase.__init__(self, val_type, size) +@@ -117,6 +138,7 @@ class ArrayAtWorker(ArrayWorkerBase): + ((int(index), self._size))) + return obj['_M_elems'][index] + ++ + class ArraySubscriptWorker(ArrayWorkerBase): + def __init__(self, val_type, size): + ArrayWorkerBase.__init__(self, val_type, size) +@@ -133,6 +155,7 @@ class ArraySubscriptWorker(ArrayWorkerBase): + else: + return self.null_value() + ++ + class ArrayMethodsMatcher(gdb.xmethod.XMethodMatcher): + def __init__(self): + gdb.xmethod.XMethodMatcher.__init__(self, +@@ -148,7 +171,7 @@ class ArrayMethodsMatcher(gdb.xmethod.XMethodMatcher): + self.methods = [self._method_dict[m] for m in self._method_dict] + + def match(self, class_type, method_name): +- if not re.match('^std::(__\d+::)?array<.*>$', class_type.tag): ++ if not is_specialization_of(class_type, 'array'): + return None + method = self._method_dict.get(method_name) + if method is None or not method.enabled: +@@ -160,24 +183,34 @@ class ArrayMethodsMatcher(gdb.xmethod.XMethodMatcher): + return None + return method.worker_class(value_type, size) + ++ + # Xmethods for std::deque + ++ + class DequeWorkerBase(gdb.xmethod.XMethodWorker): + def __init__(self, val_type): + self._val_type = val_type + self._bufsize = 512 // val_type.sizeof or 1 + + def size(self, obj): +- first_node = obj['_M_impl']['_M_start']['_M_node'] +- last_node = obj['_M_impl']['_M_finish']['_M_node'] +- cur = obj['_M_impl']['_M_finish']['_M_cur'] +- first = obj['_M_impl']['_M_finish']['_M_first'] +- return (last_node - first_node) * self._bufsize + (cur - first) ++ start = obj['_M_impl']['_M_start'] ++ finish = obj['_M_impl']['_M_finish'] ++ if start['_M_cur'] == finish['_M_cur']: ++ return 0 ++ return (self._bufsize ++ * (finish['_M_node'] - start['_M_node'] - 1) ++ + (finish['_M_cur'] - finish['_M_first']) ++ + (start['_M_last'] - start['_M_cur'])) + + def index(self, obj, idx): +- first_node = obj['_M_impl']['_M_start']['_M_node'] +- index_node = first_node + int(idx) // self._bufsize +- return index_node[0][idx % self._bufsize] ++ start = obj['_M_impl']['_M_start'] ++ first_node_size = start['_M_last'] - start['_M_cur'] ++ if idx < first_node_size: ++ return start['_M_cur'][idx] ++ idx = idx - first_node_size ++ index_node = start['_M_node'][1 + int(idx) // self._bufsize] ++ return index_node[idx % self._bufsize] ++ + + class DequeEmptyWorker(DequeWorkerBase): + def get_arg_types(self): +@@ -190,6 +223,7 @@ class DequeEmptyWorker(DequeWorkerBase): + return (obj['_M_impl']['_M_start']['_M_cur'] == + obj['_M_impl']['_M_finish']['_M_cur']) + ++ + class DequeSizeWorker(DequeWorkerBase): + def get_arg_types(self): + return None +@@ -200,6 +234,7 @@ class DequeSizeWorker(DequeWorkerBase): + def __call__(self, obj): + return self.size(obj) + ++ + class DequeFrontWorker(DequeWorkerBase): + def get_arg_types(self): + return None +@@ -210,6 +245,7 @@ class DequeFrontWorker(DequeWorkerBase): + def __call__(self, obj): + return obj['_M_impl']['_M_start']['_M_cur'][0] + ++ + class DequeBackWorker(DequeWorkerBase): + def get_arg_types(self): + return None +@@ -219,12 +255,13 @@ class DequeBackWorker(DequeWorkerBase): + + def __call__(self, obj): + if (obj['_M_impl']['_M_finish']['_M_cur'] == +- obj['_M_impl']['_M_finish']['_M_first']): ++ obj['_M_impl']['_M_finish']['_M_first']): + prev_node = obj['_M_impl']['_M_finish']['_M_node'] - 1 + return prev_node[0][self._bufsize - 1] + else: + return obj['_M_impl']['_M_finish']['_M_cur'][-1] + ++ + class DequeSubscriptWorker(DequeWorkerBase): + def get_arg_types(self): + return get_std_size_type() +@@ -235,6 +272,7 @@ class DequeSubscriptWorker(DequeWorkerBase): + def __call__(self, obj, subscript): + return self.index(obj, subscript) + ++ + class DequeAtWorker(DequeWorkerBase): + def get_arg_types(self): + return get_std_size_type() +@@ -248,7 +286,8 @@ class DequeAtWorker(DequeWorkerBase): + raise IndexError('Deque index "%d" should not be >= %d.' % + (int(index), deque_size)) + else: +- return self.index(obj, index) ++ return self.index(obj, index) ++ + + class DequeMethodsMatcher(gdb.xmethod.XMethodMatcher): + def __init__(self): +@@ -265,7 +304,7 @@ class DequeMethodsMatcher(gdb.xmethod.XMethodMatcher): + self.methods = [self._method_dict[m] for m in self._method_dict] + + def match(self, class_type, method_name): +- if not re.match('^std::(__\d+::)?deque<.*>$', class_type.tag): ++ if not is_specialization_of(class_type, 'deque'): + return None + method = self._method_dict.get(method_name) + if method is None or not method.enabled: +@@ -274,6 +313,7 @@ class DequeMethodsMatcher(gdb.xmethod.XMethodMatcher): + + # Xmethods for std::forward_list + ++ + class ForwardListWorkerBase(gdb.xmethod.XMethodMatcher): + def __init__(self, val_type, node_type): + self._val_type = val_type +@@ -282,6 +322,7 @@ class ForwardListWorkerBase(gdb.xmethod.XMethodMatcher): + def get_arg_types(self): + return None + ++ + class ForwardListEmptyWorker(ForwardListWorkerBase): + def get_result_type(self, obj): + return get_bool_type() +@@ -289,6 +330,7 @@ class ForwardListEmptyWorker(ForwardListWorkerBase): + def __call__(self, obj): + return obj['_M_impl']['_M_head']['_M_next'] == 0 + ++ + class ForwardListFrontWorker(ForwardListWorkerBase): + def get_result_type(self, obj): + return self._val_type +@@ -298,6 +340,7 @@ class ForwardListFrontWorker(ForwardListWorkerBase): + val_address = node['_M_storage']['_M_storage'].address + return val_address.cast(self._val_type.pointer()).dereference() + ++ + class ForwardListMethodsMatcher(gdb.xmethod.XMethodMatcher): + def __init__(self): + matcher_name = matcher_name_prefix + 'forward_list' +@@ -309,7 +352,7 @@ class ForwardListMethodsMatcher(gdb.xmethod.XMethodMatcher): + self.methods = [self._method_dict[m] for m in self._method_dict] + + def match(self, class_type, method_name): +- if not re.match('^std::(__\d+::)?forward_list<.*>$', class_type.tag): ++ if not is_specialization_of(class_type, 'forward_list'): + return None + method = self._method_dict.get(method_name) + if method is None or not method.enabled: +@@ -320,6 +363,7 @@ class ForwardListMethodsMatcher(gdb.xmethod.XMethodMatcher): + + # Xmethods for std::list + ++ + class ListWorkerBase(gdb.xmethod.XMethodWorker): + def __init__(self, val_type, node_type): + self._val_type = val_type +@@ -337,6 +381,7 @@ class ListWorkerBase(gdb.xmethod.XMethodWorker): + addr = node['_M_storage'].address + return addr.cast(self._val_type.pointer()).dereference() + ++ + class ListEmptyWorker(ListWorkerBase): + def get_result_type(self, obj): + return get_bool_type() +@@ -348,6 +393,7 @@ class ListEmptyWorker(ListWorkerBase): + else: + return False + ++ + class ListSizeWorker(ListWorkerBase): + def get_result_type(self, obj): + return get_std_size_type() +@@ -361,6 +407,7 @@ class ListSizeWorker(ListWorkerBase): + size += 1 + return size + ++ + class ListFrontWorker(ListWorkerBase): + def get_result_type(self, obj): + return self._val_type +@@ -369,6 +416,7 @@ class ListFrontWorker(ListWorkerBase): + node = obj['_M_impl']['_M_node']['_M_next'].cast(self._node_type) + return self.get_value_from_node(node) + ++ + class ListBackWorker(ListWorkerBase): + def get_result_type(self, obj): + return self._val_type +@@ -377,6 +425,7 @@ class ListBackWorker(ListWorkerBase): + prev_node = obj['_M_impl']['_M_node']['_M_prev'].cast(self._node_type) + return self.get_value_from_node(prev_node) + ++ + class ListMethodsMatcher(gdb.xmethod.XMethodMatcher): + def __init__(self): + gdb.xmethod.XMethodMatcher.__init__(self, +@@ -390,7 +439,7 @@ class ListMethodsMatcher(gdb.xmethod.XMethodMatcher): + self.methods = [self._method_dict[m] for m in self._method_dict] + + def match(self, class_type, method_name): +- if not re.match('^std::(__\d+::)?(__cxx11::)?list<.*>$', class_type.tag): ++ if not is_specialization_of(class_type, '(__cxx11::)?list'): + return None + method = self._method_dict.get(method_name) + if method is None or not method.enabled: +@@ -401,6 +450,7 @@ class ListMethodsMatcher(gdb.xmethod.XMethodMatcher): + + # Xmethods for std::vector + ++ + class VectorWorkerBase(gdb.xmethod.XMethodWorker): + def __init__(self, val_type): + self._val_type = val_type +@@ -425,6 +475,7 @@ class VectorWorkerBase(gdb.xmethod.XMethodWorker): + else: + return obj['_M_impl']['_M_start'][index] + ++ + class VectorEmptyWorker(VectorWorkerBase): + def get_arg_types(self): + return None +@@ -435,6 +486,7 @@ class VectorEmptyWorker(VectorWorkerBase): + def __call__(self, obj): + return int(self.size(obj)) == 0 + ++ + class VectorSizeWorker(VectorWorkerBase): + def get_arg_types(self): + return None +@@ -445,6 +497,7 @@ class VectorSizeWorker(VectorWorkerBase): + def __call__(self, obj): + return self.size(obj) + ++ + class VectorFrontWorker(VectorWorkerBase): + def get_arg_types(self): + return None +@@ -455,6 +508,7 @@ class VectorFrontWorker(VectorWorkerBase): + def __call__(self, obj): + return self.get(obj, 0) + ++ + class VectorBackWorker(VectorWorkerBase): + def get_arg_types(self): + return None +@@ -465,6 +519,7 @@ class VectorBackWorker(VectorWorkerBase): + def __call__(self, obj): + return self.get(obj, int(self.size(obj)) - 1) + ++ + class VectorAtWorker(VectorWorkerBase): + def get_arg_types(self): + return get_std_size_type() +@@ -479,6 +534,7 @@ class VectorAtWorker(VectorWorkerBase): + ((int(index), size))) + return self.get(obj, int(index)) + ++ + class VectorSubscriptWorker(VectorWorkerBase): + def get_arg_types(self): + return get_std_size_type() +@@ -489,6 +545,7 @@ class VectorSubscriptWorker(VectorWorkerBase): + def __call__(self, obj, subscript): + return self.get(obj, int(subscript)) + ++ + class VectorMethodsMatcher(gdb.xmethod.XMethodMatcher): + def __init__(self): + gdb.xmethod.XMethodMatcher.__init__(self, +@@ -505,7 +562,7 @@ class VectorMethodsMatcher(gdb.xmethod.XMethodMatcher): + self.methods = [self._method_dict[m] for m in self._method_dict] + + def match(self, class_type, method_name): +- if not re.match('^std::(__\d+::)?vector<.*>$', class_type.tag): ++ if not is_specialization_of(class_type, 'vector'): + return None + method = self._method_dict.get(method_name) + if method is None or not method.enabled: +@@ -514,6 +571,7 @@ class VectorMethodsMatcher(gdb.xmethod.XMethodMatcher): + + # Xmethods for associative containers + ++ + class AssociativeContainerWorkerBase(gdb.xmethod.XMethodWorker): + def __init__(self, unordered): + self._unordered = unordered +@@ -527,6 +585,7 @@ class AssociativeContainerWorkerBase(gdb.xmethod.XMethodWorker): + def get_arg_types(self): + return None + ++ + class AssociativeContainerEmptyWorker(AssociativeContainerWorkerBase): + def get_result_type(self, obj): + return get_bool_type() +@@ -534,6 +593,7 @@ class AssociativeContainerEmptyWorker(AssociativeContainerWorkerBase): + def __call__(self, obj): + return int(self.node_count(obj)) == 0 + ++ + class AssociativeContainerSizeWorker(AssociativeContainerWorkerBase): + def get_result_type(self, obj): + return get_std_size_type() +@@ -541,6 +601,7 @@ class AssociativeContainerSizeWorker(AssociativeContainerWorkerBase): + def __call__(self, obj): + return self.node_count(obj) + ++ + class AssociativeContainerMethodsMatcher(gdb.xmethod.XMethodMatcher): + def __init__(self, name): + gdb.xmethod.XMethodMatcher.__init__(self, +@@ -554,7 +615,7 @@ class AssociativeContainerMethodsMatcher(gdb.xmethod.XMethodMatcher): + self.methods = [self._method_dict[m] for m in self._method_dict] + + def match(self, class_type, method_name): +- if not re.match('^std::(__\d+::)?%s<.*>$' % self._name, class_type.tag): ++ if not is_specialization_of(class_type, self._name): + return None + method = self._method_dict.get(method_name) + if method is None or not method.enabled: +@@ -564,8 +625,11 @@ class AssociativeContainerMethodsMatcher(gdb.xmethod.XMethodMatcher): + + # Xmethods for std::unique_ptr + ++ + class UniquePtrGetWorker(gdb.xmethod.XMethodWorker): +- "Implements std::unique_ptr::get() and std::unique_ptr::operator->()" ++ """ ++ Implement std::unique_ptr::get() and std::unique_ptr::operator->(). ++ """ + + def __init__(self, elem_type): + self._is_array = elem_type.code == gdb.TYPE_CODE_ARRAY +@@ -581,19 +645,31 @@ class UniquePtrGetWorker(gdb.xmethod.XMethodWorker): + return self._elem_type.pointer() + + def _supports(self, method_name): +- "operator-> is not supported for unique_ptr" ++ # operator-> is not supported for unique_ptr + return method_name == 'get' or not self._is_array + + def __call__(self, obj): + impl_type = obj.dereference().type.fields()[0].type.tag +- if re.match('^std::(__\d+::)?__uniq_ptr_impl<.*>$', impl_type): # New implementation +- return obj['_M_t']['_M_t']['_M_head_impl'] +- elif re.match('^std::(__\d+::)?tuple<.*>$', impl_type): +- return obj['_M_t']['_M_head_impl'] +- return None ++ # Check for new implementations first: ++ if is_specialization_of(impl_type, '__uniq_ptr_(data|impl)'): ++ tuple_member = obj['_M_t']['_M_t'] ++ elif is_specialization_of(impl_type, 'tuple'): ++ tuple_member = obj['_M_t'] ++ else: ++ return None ++ tuple_impl_type = tuple_member.type.fields()[0].type # _Tuple_impl ++ tuple_head_type = tuple_impl_type.fields()[1].type # _Head_base ++ head_field = tuple_head_type.fields()[0] ++ if head_field.name == '_M_head_impl': ++ return tuple_member.cast(tuple_head_type)['_M_head_impl'] ++ elif head_field.is_base_class: ++ return tuple_member.cast(head_field.type) ++ else: ++ return None ++ + + class UniquePtrDerefWorker(UniquePtrGetWorker): +- "Implements std::unique_ptr::operator*()" ++ """Implement std::unique_ptr::operator*().""" + + def __init__(self, elem_type): + UniquePtrGetWorker.__init__(self, elem_type) +@@ -602,14 +678,15 @@ class UniquePtrDerefWorker(UniquePtrGetWorker): + return self._elem_type + + def _supports(self, method_name): +- "operator* is not supported for unique_ptr" ++ # operator* is not supported for unique_ptr + return not self._is_array + + def __call__(self, obj): + return UniquePtrGetWorker.__call__(self, obj).dereference() + ++ + class UniquePtrSubscriptWorker(UniquePtrGetWorker): +- "Implements std::unique_ptr::operator[](size_t)" ++ """Implement std::unique_ptr::operator[](size_t).""" + + def __init__(self, elem_type): + UniquePtrGetWorker.__init__(self, elem_type) +@@ -621,12 +698,13 @@ class UniquePtrSubscriptWorker(UniquePtrGetWorker): + return self._elem_type + + def _supports(self, method_name): +- "operator[] is only supported for unique_ptr" ++ # operator[] is only supported for unique_ptr + return self._is_array + + def __call__(self, obj, index): + return UniquePtrGetWorker.__call__(self, obj)[index] + ++ + class UniquePtrMethodsMatcher(gdb.xmethod.XMethodMatcher): + def __init__(self): + gdb.xmethod.XMethodMatcher.__init__(self, +@@ -640,7 +718,7 @@ class UniquePtrMethodsMatcher(gdb.xmethod.XMethodMatcher): + self.methods = [self._method_dict[m] for m in self._method_dict] + + def match(self, class_type, method_name): +- if not re.match('^std::(__\d+::)?unique_ptr<.*>$', class_type.tag): ++ if not is_specialization_of(class_type, 'unique_ptr'): + return None + method = self._method_dict.get(method_name) + if method is None or not method.enabled: +@@ -652,8 +730,11 @@ class UniquePtrMethodsMatcher(gdb.xmethod.XMethodMatcher): + + # Xmethods for std::shared_ptr + ++ + class SharedPtrGetWorker(gdb.xmethod.XMethodWorker): +- "Implements std::shared_ptr::get() and std::shared_ptr::operator->()" ++ """ ++ Implements std::shared_ptr::get() and std::shared_ptr::operator->(). ++ """ + + def __init__(self, elem_type): + self._is_array = elem_type.code == gdb.TYPE_CODE_ARRAY +@@ -669,14 +750,15 @@ class SharedPtrGetWorker(gdb.xmethod.XMethodWorker): + return self._elem_type.pointer() + + def _supports(self, method_name): +- "operator-> is not supported for shared_ptr" ++ # operator-> is not supported for shared_ptr + return method_name == 'get' or not self._is_array + + def __call__(self, obj): + return obj['_M_ptr'] + ++ + class SharedPtrDerefWorker(SharedPtrGetWorker): +- "Implements std::shared_ptr::operator*()" ++ """Implement std::shared_ptr::operator*().""" + + def __init__(self, elem_type): + SharedPtrGetWorker.__init__(self, elem_type) +@@ -685,14 +767,15 @@ class SharedPtrDerefWorker(SharedPtrGetWorker): + return self._elem_type + + def _supports(self, method_name): +- "operator* is not supported for shared_ptr" ++ # operator* is not supported for shared_ptr + return not self._is_array + + def __call__(self, obj): + return SharedPtrGetWorker.__call__(self, obj).dereference() + ++ + class SharedPtrSubscriptWorker(SharedPtrGetWorker): +- "Implements std::shared_ptr::operator[](size_t)" ++ """Implement std::shared_ptr::operator[](size_t).""" + + def __init__(self, elem_type): + SharedPtrGetWorker.__init__(self, elem_type) +@@ -704,22 +787,23 @@ class SharedPtrSubscriptWorker(SharedPtrGetWorker): + return self._elem_type + + def _supports(self, method_name): +- "operator[] is only supported for shared_ptr" ++ # operator[] is only supported for shared_ptr + return self._is_array + + def __call__(self, obj, index): + # Check bounds if _elem_type is an array of known bound +- m = re.match('.*\[(\d+)]$', str(self._elem_type)) ++ m = re.match(r'.*\[(\d+)]$', str(self._elem_type)) + if m and index >= int(m.group(1)): + raise IndexError('shared_ptr<%s> index "%d" should not be >= %d.' % + (self._elem_type, int(index), int(m.group(1)))) + return SharedPtrGetWorker.__call__(self, obj)[index] + ++ + class SharedPtrUseCountWorker(gdb.xmethod.XMethodWorker): +- "Implements std::shared_ptr::use_count()" ++ """Implement std::shared_ptr::use_count().""" + + def __init__(self, elem_type): +- SharedPtrUseCountWorker.__init__(self, elem_type) ++ pass + + def get_arg_types(self): + return None +@@ -727,12 +811,16 @@ class SharedPtrUseCountWorker(gdb.xmethod.XMethodWorker): + def get_result_type(self, obj): + return gdb.lookup_type('long') + ++ def _supports(self, method_name): ++ return True ++ + def __call__(self, obj): + refcounts = obj['_M_refcount']['_M_pi'] + return refcounts['_M_use_count'] if refcounts else 0 + ++ + class SharedPtrUniqueWorker(SharedPtrUseCountWorker): +- "Implements std::shared_ptr::unique()" ++ """Implement std::shared_ptr::unique().""" + + def __init__(self, elem_type): + SharedPtrUseCountWorker.__init__(self, elem_type) +@@ -743,6 +831,7 @@ class SharedPtrUniqueWorker(SharedPtrUseCountWorker): + def __call__(self, obj): + return SharedPtrUseCountWorker.__call__(self, obj) == 1 + ++ + class SharedPtrMethodsMatcher(gdb.xmethod.XMethodMatcher): + def __init__(self): + gdb.xmethod.XMethodMatcher.__init__(self, +@@ -758,7 +847,7 @@ class SharedPtrMethodsMatcher(gdb.xmethod.XMethodMatcher): + self.methods = [self._method_dict[m] for m in self._method_dict] + + def match(self, class_type, method_name): +- if not re.match('^std::(__\d+::)?shared_ptr<.*>$', class_type.tag): ++ if not is_specialization_of(class_type, 'shared_ptr'): + return None + method = self._method_dict.get(method_name) + if method is None or not method.enabled: +@@ -768,6 +857,7 @@ class SharedPtrMethodsMatcher(gdb.xmethod.XMethodMatcher): + return worker + return None + ++ + def register_libstdcxx_xmethods(locus): + gdb.xmethod.register_xmethod_matcher(locus, ArrayMethodsMatcher()) + gdb.xmethod.register_xmethod_matcher(locus, ForwardListMethodsMatcher()) diff --git a/update-libstdcxx-prettyprinter.sh b/update-libstdcxx-prettyprinter.sh new file mode 100755 index 0000000..670a537 --- /dev/null +++ b/update-libstdcxx-prettyprinter.sh @@ -0,0 +1,75 @@ +#!/bin/bash + +if [ "x$1" != "x" ] && ! [ -d "$1" ]; then + echo "Usage: $0 [git_reference_dir_to_speed_up]" + exit 1 +fi + +# Variables to set up for a new GTS update. +# The latest GTS supported on this branch. +gts_major=14 +# Tests that need to be synced in for this change. These are required to sync +# in adjustments to tests due to changes to pretty printer output. +# Occasionally, a test adjustment may be due to an ABI change, in which case +# the test may fail with the system gcc, since we do not backport ABI changes. +# This is expected, in which case we adjust back the expected output for the +# test to match the system libstdc++ ABI in a subsequent, hand-written patch to +# the tests. +tests=" +filesystem-ts.cc +compat.cc +cxx11.cc +cxx17.cc +libfundts.cc +" + +system_major=`sed -n 's/^%global gcc_major //p' gcc.spec` +systemrev=`sed -n 's/^%global gitrev //p' gcc.spec` + +if [ $gts_major -eq $system_major ]; then echo "GTS same version as system, nothing to do."; exit 1; fi +if ! [ -f gcc.spec ]; then echo Must be run in the directory with gcc.spec file.; exit 1; fi +branch=`git branch --show-current` + +if ! [[ $branch =~ ^c[0-9]+s$ ]]; then echo "Must be run on a valid centos stream branch, not '$branch'"; exit 1; fi + +gtsrev=`curl -s https://gitlab.com/redhat/centos-stream/rpms/gcc-toolset-$gts_major-gcc/-/raw/$(git branch --show-current)/gcc.spec?ref_type=heads | sed -n 's/^%global gitrev //p'` + +echo "system at $systemrev and GTS at $gtsrev on the vendor branch" + +cwd=$PWD +cleanup() { + rm -rf $cwd/gcc-dir.tmp +} + +trap cleanup EXIT + +# Get vendor branch contents from upstream. git-fetch doesn't have the +# --reference feature, so do a full clone referencing a local upstream repo if +# present. Otherwise fetch only those specific refs. +url=https://gcc.gnu.org/git/gcc.git +echo -n "Getting sources from $url" +if [ "$#" -eq 1 ]; then + echo " (using $1 as cache)" + git clone -q --dissociate --reference $1 $url gcc-dir.tmp + pushd gcc-dir.tmp +else + echo + mkdir gcc-dir.tmp + pushd gcc-dir.tmp + git init -q + git remote add origin $url +fi + +pp_patchfile=gcc$system_major-libstdc++-prettyprinter-update-$gts_major.patch +tests_patchfile=gcc$system_major-libstdc++-prettyprinter-update-$gts_major-tests.patch +git fetch -q origin $systemrev $gtsrev +# We limit the update to just the printer, retain the system gcc hook and +# make-foo. +git diff --stat --patch $systemrev..$gtsrev -- libstdc++-v3/python/libstdcxx > ../$pp_patchfile +echo "Updated $pp_patchfile" + +testfiles=$(echo "$tests" | awk '/.+/{printf "libstdc++-v3/testsuite/libstdc++-prettyprinters/%s ", $1}') +git diff --stat --patch $systemrev..$gtsrev -- $testfiles > ../$tests_patchfile +echo "Updated $tests_patchfile" + +popd