Message ID | 20140714142159.GF4871@redhat.com |
---|---|
State | New |
Headers | show |
On 14/07/14 15:21 +0100, Jonathan Wakely wrote: >This defines a new style of Python type printer that recognizes >templates and can be used to omit default template arguments from the >typename GDB prints, e.g. showing std::vector<T, std::allocator<T>> as >simply std::vector<T>. Additionally, T will get processed by the type >recognizers so if it's a standard typedef or template it will also get >displayed in its abbreviated form. > >e.g. with current trunk: > >Breakpoint 1, main () at p.cc:45 >45 std::vector<std::deque<std::list<int>>> nested; >(gdb) whatis nested >type = std::vector<std::deque<std::list<int, std::allocator<int> >, std::allocator<std::list<int, std::allocator<int> > > >, std::allocator<std::deque<std::list<int, std::allocator<int> >, std::allocator<std::list<int, std::allocator<int> > > > > > > >and with this patch: > >(gdb) whatis nested >type = std::vector<std::deque<std::list<int>>> > >N.B. I am not printing spaces between the closing angle brackets. If >people prefer I can put them in, or only do it for C++11 types, so >that copying and pasting types from GDB will always work (if you're >copying a C++11 type then you must be planning to use it with a C++11 >compiler, which will handle >> without spaces). > >This passes the python testsuite but I'll wait for comments before >committing, in case my use of the GDB API or Python can be improved by >anyone. I forgot to say that with these type recognizers we might want to revisit the output of the existing printers for the containers, so that instead of: std::map with 3 elements = { ... } GDB could print the map type: std::map<int, int> with 3 elements = { ... }
>>>>> "Jonathan" == Jonathan Wakely <jwakely@redhat.com> writes:
Jonathan> This defines a new style of Python type printer that
Jonathan> recognizes templates and can be used to omit default template
Jonathan> arguments from the typename GDB prints, e.g. showing
Jonathan> std::vector<T, std::allocator<T>> as simply std::vector<T>.
Jonathan> Additionally, T will get processed by the type recognizers so
Jonathan> if it's a standard typedef or template it will also get
Jonathan> displayed in its abbreviated form.
Nice.
I'd hoped to eventually implement this automatically using some
information from gcc; see:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=49090
https://sourceware.org/bugzilla/show_bug.cgi?id=15251
However I haven't gotten around to it; and anyway your approach is nice
and not only doesn't conflict with the above, but may continue to be
useful even if the above is implemented.
Jonathan> This passes the python testsuite but I'll wait for comments before
Jonathan> committing, in case my use of the GDB API or Python can be improved by
Jonathan> anyone.
It looked fine to me.
Tom
>>>>> "Jonathan" == Jonathan Wakely <jwakely@redhat.com> writes:
Jonathan> I forgot to say that with these type recognizers we might want to
Jonathan> revisit the output of the existing printers for the containers, so
Jonathan> that instead of:
Jonathan> std::map with 3 elements = { ... }
Jonathan> GDB could print the map type:
Jonathan> std::map<int, int> with 3 elements = { ... }
I think the reason for the brevity right now is just that, even eliding
the defaults, the type names can get rather long. It's not absurd
though, just maybe not always desirable. One possible approach would be
to make a new gdb.Parameter in the libstdc++ code to control it.
Tom
On 14/07/14 14:08 -0600, Tom Tromey wrote: >Jonathan> This passes the python testsuite but I'll wait for comments before >Jonathan> committing, in case my use of the GDB API or Python can be improved by >Jonathan> anyone. > >It looked fine to me. Thanks for checking it. One thing I should have mentioned is the inconsistency between regex subgroups and string replacement fields: add_one_template_type_printer(obj, 'unique_ptr<T>', 'unique_ptr<(.*), std::default_delete<\\1 ?> >', 'unique_ptr<{1}>') It might seem odd that the last argument uses {1} not \\1 but the reason is that what gets substituted there is not actually \\1 but rather the result of applying type printers to \\1, and I implemented the substitution via string.format(), rather than reimplementing most of re.sub() by hand.
On 14/07/14 14:11 -0600, Tom Tromey wrote: >Jonathan> I forgot to say that with these type recognizers we might want to >Jonathan> revisit the output of the existing printers for the containers, so >Jonathan> that instead of: >Jonathan> std::map with 3 elements = { ... } >Jonathan> GDB could print the map type: >Jonathan> std::map<int, int> with 3 elements = { ... } > >I think the reason for the brevity right now is just that, even eliding >the defaults, the type names can get rather long. It's not absurd >though, just maybe not always desirable. One possible approach would be >to make a new gdb.Parameter in the libstdc++ code to control it. I think a parameter would make sense. I can look into that some time. One part of the patch I wasn't sure about was this, where 'mgr' is a function pointer: func = gdb.block_for_pc(int(mgr.cast(gdb.lookup_type('intptr_t')))) Is there a better way to get a pc from the function pointer? I tried simply int(mgr) but it didn't work.
On 15/07/14 00:50 +0100, Jonathan Wakely wrote: >One part of the patch I wasn't sure about was this, where 'mgr' is a >function pointer: > > func = gdb.block_for_pc(int(mgr.cast(gdb.lookup_type('intptr_t')))) > >Is there a better way to get a pc from the function pointer? >I tried simply int(mgr) but it didn't work. I've committed the patch, but would still like to improve the cast above if there's a better way to do it.
Jonathan Wakely <jwakely@redhat.com> writes: > One part of the patch I wasn't sure about was this, where 'mgr' is a > function pointer: > > func = gdb.block_for_pc(int(mgr.cast(gdb.lookup_type('intptr_t')))) > > Is there a better way to get a pc from the function pointer? > I tried simply int(mgr) but it didn't work. Well, long() WFM; e.g. with Python 2 and gdb 7.8.50.20140706-cvs I can do: (gdb) python print(long(gdb.parse_and_eval("(void(*)())-1"))) 4294967295 The fact that int() does not work the same way may indicate a shortcoming in gdb.Value.__int__(); I'm told that in recent 2.x versions, int() is perfectly happy returning a long instead of an int. Of course, this still presumably won't work on architectures where function pointers don't point straight to the code, e.g. most PowerPC, where as I understand things it actually points at a "function descriptor", which in turn references the actual code. So it seems we're missing an API for this.
commit 538e6eddc52681d9b3ca8fb5d97f194492ee68da Author: Jonathan Wakely <jwakely@redhat.com> Date: Thu Jul 10 20:31:11 2014 +0100 * python/libstdcxx/v6/printers.py (TemplateTypePrinter): Add type printer for class templates. (register_type_printers): Use TemplateTypePrinter for containers and other class templates with default template arguments. * testsuite/libstdc++-prettyprinters/whatis.cc: Test new recognizers. diff --git a/libstdc++-v3/python/libstdcxx/v6/printers.py b/libstdc++-v3/python/libstdcxx/v6/printers.py index 1fa08fb..ea34f22 100644 --- a/libstdc++-v3/python/libstdcxx/v6/printers.py +++ b/libstdc++-v3/python/libstdcxx/v6/printers.py @@ -922,6 +922,57 @@ class Printer(object): libstdcxx_printer = None +class TemplateTypePrinter(object): + """A type printer for class templates. + + Recognizes type names that match a regular expression. + Replaces them with a formatted string which can use replacement field + {N} to refer to the \N subgroup of the regex match. + Type printers are recusively applied to the subgroups. + + This allows recognizing e.g. "std::vector<(.*), std::allocator<\\1> >" + and replacing it with "std::vector<{1}>", omitting the template argument + that uses the default type. + """ + + def __init__(self, name, pattern, subst): + self.name = name + self.pattern = re.compile(pattern) + self.subst = subst + self.enabled = True + + class _recognizer(object): + def __init__(self, pattern, subst): + self.pattern = pattern + self.subst = subst + self.type_obj = None + + def recognize(self, type_obj): + if type_obj.tag is None: + return None + + m = self.pattern.match(type_obj.tag) + if m: + subs = list(m.groups()) + for i, sub in enumerate(subs): + if ('{%d}' % (i+1)) in self.subst: + # apply recognizers to subgroup + rep = gdb.types.apply_type_recognizers( + gdb.types.get_type_recognizers(), + gdb.lookup_type(sub)) + if rep: + subs[i] = rep + subs = [None] + subs + return self.subst.format(*subs) + return None + + def instantiate(self): + return self._recognizer(self.pattern, self.subst) + +def add_one_template_type_printer(obj, name, match, subst): + printer = TemplateTypePrinter(name, '^std::' + match + '$', 'std::' + subst) + gdb.types.register_type_printer(obj, printer) + class FilteringTypePrinter(object): def __init__(self, match, name): self.match = match @@ -1013,6 +1064,56 @@ def register_type_printers(obj): add_one_type_printer(obj, 'discard_block_engine', 'ranlux48') add_one_type_printer(obj, 'shuffle_order_engine', 'knuth_b') + # Do not show defaulted template arguments in class templates + add_one_template_type_printer(obj, 'unique_ptr<T>', + 'unique_ptr<(.*), std::default_delete<\\1 ?> >', + 'unique_ptr<{1}>') + + add_one_template_type_printer(obj, 'deque<T>', + 'deque<(.*), std::allocator<\\1 ?> >', + 'deque<{1}>') + add_one_template_type_printer(obj, 'forward_list<T>', + 'forward_list<(.*), std::allocator<\\1 ?> >', + 'forward_list<{1}>') + add_one_template_type_printer(obj, 'list<T>', + 'list<(.*), std::allocator<\\1 ?> >', + 'list<{1}>') + add_one_template_type_printer(obj, 'vector<T>', + 'vector<(.*), std::allocator<\\1 ?> >', + 'vector<{1}>') + add_one_template_type_printer(obj, 'map<Key, T>', + 'map<(.*), (.*), std::less<\\1 ?>, std::allocator<std::pair<\\1 const, \\2 ?> > >', + 'map<{1}, {2}>') + add_one_template_type_printer(obj, 'multimap<Key, T>', + 'multimap<(.*), (.*), std::less<\\1 ?>, std::allocator<std::pair<\\1 const, \\2 ?> > >', + 'multimap<{1}, {2}>') + add_one_template_type_printer(obj, 'set<T>', + 'set<(.*), std::less<\\1 ?>, std::allocator<\\1 ?> >', + 'set<{1}>') + add_one_template_type_printer(obj, 'multiset<T>', + 'multiset<(.*), std::less<\\1 ?>, std::allocator<\\1 ?> >', + 'multiset<{1}>') + add_one_template_type_printer(obj, 'unordered_map<Key, T>', + 'unordered_map<(.*), (.*), std::hash<\\1 ?>, std::equal_to<\\1 ?>, std::allocator<std::pair<\\1 const, \\2 ?> > >', + 'unordered_map<{1}, {2}>') + add_one_template_type_printer(obj, 'unordered_multimap<Key, T>', + 'unordered_multimap<(.*), (.*), std::hash<\\1 ?>, std::equal_to<\\1 ?>, std::allocator<std::pair<\\1 const, \\2 ?> > >', + 'unordered_multimap<{1}, {2}>') + add_one_template_type_printer(obj, 'unordered_set<T>', + 'unordered_set<(.*), std::hash<\\1 ?>, std::equal_to<\\1 ?>, std::allocator<\\1 ?> >', + 'unordered_set<{1}>') + add_one_template_type_printer(obj, 'unordered_multiset<T>', + 'unordered_multiset<(.*), std::hash<\\1 ?>, std::equal_to<\\1 ?>, std::allocator<\\1 ?> >', + 'unordered_multiset<{1}>') + + # strip the "fundamentals_v1" inline namespace from these types + add_one_template_type_printer(obj, 'optional<T>', + 'experimental::fundamentals_v1::optional<(.*)>', + 'experimental::optional<\\1>') + add_one_template_type_printer(obj, 'basic_string_view<C>', + 'experimental::fundamentals_v1::basic_string_view<(.*), std::char_traits<\\1> >', + 'experimental::basic_string_view<\\1>') + def register_libstdcxx_printers (obj): "Register libstdc++ pretty-printers with objfile Obj." diff --git a/libstdc++-v3/testsuite/libstdc++-prettyprinters/whatis.cc b/libstdc++-v3/testsuite/libstdc++-prettyprinters/whatis.cc index fbbb772..b398972 100644 --- a/libstdc++-v3/testsuite/libstdc++-prettyprinters/whatis.cc +++ b/libstdc++-v3/testsuite/libstdc++-prettyprinters/whatis.cc @@ -21,6 +21,15 @@ #include <string> #include <iostream> #include <regex> +#include <memory> +#include <deque> +#include <forward_list> +#include <list> +#include <vector> +#include <map> +#include <set> +#include <unordered_map> +#include <unordered_set> template<class T> void @@ -159,6 +168,31 @@ std::basic_string<signed char> *sstring_ptr; holder< std::basic_string<signed char> > sstring_holder; // { dg-final { whatis-test sstring_holder "holder<std::basic_string<signed char, std::char_traits<signed char>, std::allocator<signed char> > >" } } +std::vector<std::deque<std::unique_ptr<char>>> *seq1_ptr; +holder< std::vector<std::deque<std::unique_ptr<char>>> > seq1_holder; +// { dg-final { whatis-test seq1_holder "holder<std::vector<std::deque<std::unique_ptr<char>>> >" } } + +std::list<std::forward_list<std::unique_ptr<char>>> *seq2_ptr; +holder< std::list<std::forward_list<std::unique_ptr<char>>> > seq2_holder; +// { dg-final { whatis-test seq2_holder "holder<std::list<std::forward_list<std::unique_ptr<char>>> >" } } + +std::map<int, std::set<int>> *assoc1_ptr; +holder< std::map<int, std::set<int>> > assoc1_holder; +// { dg-final { whatis-test assoc1_holder "holder<std::map<int, std::set<int>> >" } } + +std::multimap<int, std::multiset<int>> *assoc2_ptr; +holder< std::multimap<int, std::multiset<int>> > assoc2_holder; +// { dg-final { whatis-test assoc2_holder "holder<std::multimap<int, std::multiset<int>> >" } } + +std::unordered_map<int, std::unordered_set<int>> *unord1_ptr; +holder< std::unordered_map<int, std::unordered_set<int>> > unord1_holder; +// { dg-final { whatis-test unord1_holder "holder<std::unordered_map<int, std::unordered_set<int>> >" } } + +std::unordered_multimap<int, std::unordered_multiset<int>> *unord2_ptr; +holder< std::unordered_multimap<int, std::unordered_multiset<int>> > unord2_holder; +// { dg-final { whatis-test unord2_holder "holder<std::unordered_multimap<int, std::unordered_multiset<int>> >" } } + + int main() { @@ -236,6 +270,18 @@ main() placeholder(&ustring_holder); placeholder(&sstring_ptr); placeholder(&sstring_holder); + placeholder(&seq1_ptr); + placeholder(&seq1_holder); + placeholder(&seq2_ptr); + placeholder(&seq2_holder); + placeholder(&assoc1_ptr); + placeholder(&assoc1_holder); + placeholder(&assoc2_ptr); + placeholder(&assoc2_holder); + placeholder(&unord1_ptr); + placeholder(&unord1_holder); + placeholder(&unord2_ptr); + placeholder(&unord2_holder); return 0; }