632 changes: 632 additions & 0 deletions libcxx/test/pretty_printers/gdb_pretty_printer_test.sh.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,632 @@
// -*- C++ -*-
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: system-windows
// REQUIRES: libcxx_gdb
//
// RUN: %cxx %flags %s -o %t.exe %compile_flags -g %link_flags
// Ensure locale-independence for unicode tests.
// RUN: %libcxx_gdb -nx -batch -iex "set autoload off" -ex "source %libcxx_src_root/utils/gdb/libcxx/printers.py" -ex "python register_libcxx_printer_loader()" -ex "source %libcxx_src_root/test/pretty_printers/gdb_pretty_printer_test.py" %t.exe

#include <bitset>
#include <deque>
#include <list>
#include <map>
#include <memory>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <string>
#include <tuple>
#include <unordered_map>
#include <unordered_set>

// To write a pretty-printer test:
//
// 1. Declare a variable of the type you want to test
//
// 2. Set its value to something which will test the pretty printer in an
// interesting way.
//
// 3. Call ComparePrettyPrintToChars with that variable, and a "const char*"
// value to compare to the printer's output.
//
// Or
//
// Call ComparePrettyPrintToChars with that variable, and a "const char*"
// *python* regular expression to match against the printer's output.
// The set of special characters in a Python regular expression overlaps
// with a lot of things the pretty printers print--brackets, for
// example--so take care to escape appropriately.
//
// Alternatively, construct a string that gdb can parse as an expression,
// so that printing the value of the expression will test the pretty printer
// in an interesting way. Then, call CompareExpressionPrettyPrintToChars or
// CompareExpressionPrettyPrintToRegex to compare the printer's output.

// Avoids setting a breakpoint in every-single instantiation of
// ComparePrettyPrintTo*. Also, make sure neither it, nor the
// variables we need present in the Compare functions are optimized
// away.
void StopForDebugger(void *value, void *check) __attribute__((optnone)) { }

// Prevents the compiler optimizing away the parameter in the caller function.
template <typename Type>
void MarkAsLive(Type &&t) __attribute__((optnone)) { }

// In all of the Compare(Expression)PrettyPrintTo(Regex/Chars) functions below,
// the python script sets a breakpoint just before the call to StopForDebugger,
// compares the result to the expectation.
//
// The expectation is a literal string to be matched exactly in
// *PrettyPrintToChars functions, and is a python regular expression in
// *PrettyPrintToRegex functions.
//
// In ComparePrettyPrint* functions, the value is a variable of any type. In
// CompareExpressionPrettyPrint functions, the value is a string expression that
// gdb will parse and print the result.
//
// The python script will print either "PASS", or a detailed failure explanation
// along with the line that has invoke the function. The testing will continue
// in either case.

template <typename TypeToPrint> void ComparePrettyPrintToChars(
TypeToPrint value,
const char *expectation) {
StopForDebugger(&value, &expectation);
}

template <typename TypeToPrint> void ComparePrettyPrintToRegex(
TypeToPrint value,
const char *expectation) {
StopForDebugger(&value, &expectation);
}

void CompareExpressionPrettyPrintToChars(
std::string value,
const char *expectation) {
StopForDebugger(&value, &expectation);
}

void CompareExpressionPrettyPrintToRegex(
std::string value,
const char *expectation) {
StopForDebugger(&value, &expectation);
}

namespace example {
struct example_struct {
int a = 0;
int arr[1000];
};
}

// If enabled, the self test will "fail"--because we want to be sure it properly
// diagnoses tests that *should* fail. Evaluate the output by hand.
void framework_self_test() {
#ifdef FRAMEWORK_SELF_TEST
// Use the most simple data structure we can.
const char a = 'a';

// Tests that should pass
ComparePrettyPrintToChars(a, "97 'a'");
ComparePrettyPrintToRegex(a, ".*");

// Tests that should fail.
ComparePrettyPrintToChars(a, "b");
ComparePrettyPrintToRegex(a, "b");
#endif
}

// A simple pass-through allocator to check that we handle CompressedPair
// correctly.
template <typename T> class UncompressibleAllocator : public std::allocator<T> {
public:
char X;
};

void string_test() {
std::string short_string("kdjflskdjf");
// The display_hint "string" adds quotes the printed result.
ComparePrettyPrintToChars(short_string, "\"kdjflskdjf\"");

std::basic_string<char, std::char_traits<char>, UncompressibleAllocator<char>>
long_string("mehmet bizim dostumuz agzi kirik testimiz");
ComparePrettyPrintToChars(long_string,
"\"mehmet bizim dostumuz agzi kirik testimiz\"");
}

void u16string_test() {
std::u16string test0 = u"Hello World";
ComparePrettyPrintToChars(test0, "u\"Hello World\"");
std::u16string test1 = u"\U00010196\u20AC\u00A3\u0024";
ComparePrettyPrintToChars(test1, "u\"\U00010196\u20AC\u00A3\u0024\"");
std::u16string test2 = u"\u0024\u0025\u0026\u0027";
ComparePrettyPrintToChars(test2, "u\"\u0024\u0025\u0026\u0027\"");
std::u16string test3 = u"mehmet bizim dostumuz agzi kirik testimiz";
ComparePrettyPrintToChars(test3,
("u\"mehmet bizim dostumuz agzi kirik testimiz\""));
}

void u32string_test() {
std::u32string test0 = U"Hello World";
ComparePrettyPrintToChars(test0, "U\"Hello World\"");
std::u32string test1 =
U"\U0001d552\U0001d553\U0001d554\U0001d555\U0001d556\U0001d557";
ComparePrettyPrintToChars(
test1,
("U\"\U0001d552\U0001d553\U0001d554\U0001d555\U0001d556\U0001d557\""));
std::u32string test2 = U"\U00004f60\U0000597d";
ComparePrettyPrintToChars(test2, ("U\"\U00004f60\U0000597d\""));
std::u32string test3 = U"mehmet bizim dostumuz agzi kirik testimiz";
ComparePrettyPrintToChars(test3, ("U\"mehmet bizim dostumuz agzi kirik testimiz\""));
}

void tuple_test() {
std::tuple<int, int, int> test0(2, 3, 4);
ComparePrettyPrintToChars(
test0,
"std::tuple containing = {[1] = 2, [2] = 3, [3] = 4}");

std::tuple<> test1;
ComparePrettyPrintToChars(
test1,
"empty std::tuple");
}

void unique_ptr_test() {
std::unique_ptr<std::string> matilda(new std::string("Matilda"));
ComparePrettyPrintToRegex(
std::move(matilda),
R"(std::unique_ptr<std::string> containing = {__ptr_ = 0x[a-f0-9]+})");
std::unique_ptr<int> forty_two(new int(42));
ComparePrettyPrintToRegex(std::move(forty_two),
R"(std::unique_ptr<int> containing = {__ptr_ = 0x[a-f0-9]+})");

std::unique_ptr<int> this_is_null;
ComparePrettyPrintToChars(std::move(this_is_null),
R"(std::unique_ptr is nullptr)");
}

void bitset_test() {
std::bitset<258> i_am_empty(0);
ComparePrettyPrintToChars(i_am_empty, "std::bitset<258>");

std::bitset<0> very_empty;
ComparePrettyPrintToChars(very_empty, "std::bitset<0>");

std::bitset<15> b_000001111111100(1020);
ComparePrettyPrintToChars(b_000001111111100,
"std::bitset<15> = {[2] = 1, [3] = 1, [4] = 1, [5] = 1, [6] = 1, "
"[7] = 1, [8] = 1, [9] = 1}");

std::bitset<258> b_0_129_132(0);
b_0_129_132[0] = true;
b_0_129_132[129] = true;
b_0_129_132[132] = true;
ComparePrettyPrintToChars(b_0_129_132,
"std::bitset<258> = {[0] = 1, [129] = 1, [132] = 1}");
}

void list_test() {
std::list<int> i_am_empty{};
ComparePrettyPrintToChars(i_am_empty, "std::list is empty");

std::list<int> one_two_three {1, 2, 3};
ComparePrettyPrintToChars(one_two_three,
"std::list with 3 elements = {1, 2, 3}");

std::list<std::string> colors {"red", "blue", "green"};
ComparePrettyPrintToChars(colors,
R"(std::list with 3 elements = {"red", "blue", "green"})");
}

void deque_test() {
std::deque<int> i_am_empty{};
ComparePrettyPrintToChars(i_am_empty, "std::deque is empty");

std::deque<int> one_two_three {1, 2, 3};
ComparePrettyPrintToChars(one_two_three,
"std::deque with 3 elements = {1, 2, 3}");

std::deque<example::example_struct> bfg;
for (int i = 0; i < 10; ++i) {
example::example_struct current;
current.a = i;
bfg.push_back(current);
}
for (int i = 0; i < 3; ++i) {
bfg.pop_front();
}
for (int i = 0; i < 3; ++i) {
bfg.pop_back();
}
ComparePrettyPrintToRegex(bfg,
"std::deque with 4 elements = {"
"{a = 3, arr = {[^}]+}}, "
"{a = 4, arr = {[^}]+}}, "
"{a = 5, arr = {[^}]+}}, "
"{a = 6, arr = {[^}]+}}}");
}

void map_test() {
std::map<int, int> i_am_empty{};
ComparePrettyPrintToChars(i_am_empty, "std::map is empty");

std::map<int, std::string> one_two_three;
one_two_three.insert({1, "one"});
one_two_three.insert({2, "two"});
one_two_three.insert({3, "three"});
ComparePrettyPrintToChars(one_two_three,
"std::map with 3 elements = "
R"({[1] = "one", [2] = "two", [3] = "three"})");

std::map<int, example::example_struct> bfg;
for (int i = 0; i < 4; ++i) {
example::example_struct current;
current.a = 17 * i;
bfg.insert({i, current});
}
ComparePrettyPrintToRegex(bfg,
R"(std::map with 4 elements = {)"
R"(\[0\] = {a = 0, arr = {[^}]+}}, )"
R"(\[1\] = {a = 17, arr = {[^}]+}}, )"
R"(\[2\] = {a = 34, arr = {[^}]+}}, )"
R"(\[3\] = {a = 51, arr = {[^}]+}}})");
}

void multimap_test() {
std::multimap<int, int> i_am_empty{};
ComparePrettyPrintToChars(i_am_empty, "std::multimap is empty");

std::multimap<int, std::string> one_two_three;
one_two_three.insert({1, "one"});
one_two_three.insert({3, "three"});
one_two_three.insert({1, "ein"});
one_two_three.insert({2, "two"});
one_two_three.insert({2, "zwei"});
one_two_three.insert({1, "bir"});

ComparePrettyPrintToChars(one_two_three,
"std::multimap with 6 elements = "
R"({[1] = "one", [1] = "ein", [1] = "bir", )"
R"([2] = "two", [2] = "zwei", [3] = "three"})");
}

void queue_test() {
std::queue<int> i_am_empty;
ComparePrettyPrintToChars(i_am_empty,
"std::queue wrapping = {std::deque is empty}");

std::queue<int> one_two_three(std::deque<int>{1, 2, 3});
ComparePrettyPrintToChars(one_two_three,
"std::queue wrapping = {"
"std::deque with 3 elements = {1, 2, 3}}");
}

void priority_queue_test() {
std::priority_queue<int> i_am_empty;
ComparePrettyPrintToChars(i_am_empty,
"std::priority_queue wrapping = {std::vector of length 0, capacity 0}");

std::priority_queue<int> one_two_three;
one_two_three.push(11111);
one_two_three.push(22222);
one_two_three.push(33333);

ComparePrettyPrintToRegex(one_two_three,
R"(std::priority_queue wrapping = )"
R"({std::vector of length 3, capacity 3 = {33333)");

ComparePrettyPrintToRegex(one_two_three, ".*11111.*");
ComparePrettyPrintToRegex(one_two_three, ".*22222.*");
}

void set_test() {
std::set<int> i_am_empty;
ComparePrettyPrintToChars(i_am_empty, "std::set is empty");

std::set<int> one_two_three {3, 1, 2};
ComparePrettyPrintToChars(one_two_three,
"std::set with 3 elements = {1, 2, 3}");

std::set<std::pair<int, int>> prime_pairs {
std::make_pair(3, 5), std::make_pair(5, 7), std::make_pair(3, 5)};

ComparePrettyPrintToChars(prime_pairs,
"std::set with 2 elements = {"
"{first = 3, second = 5}, {first = 5, second = 7}}");
}

void stack_test() {
std::stack<int> test0;
ComparePrettyPrintToChars(test0,
"std::stack wrapping = {std::deque is empty}");
test0.push(5);
test0.push(6);
ComparePrettyPrintToChars(
test0, "std::stack wrapping = {std::deque with 2 elements = {5, 6}}");
std::stack<bool> test1;
test1.push(true);
test1.push(false);
ComparePrettyPrintToChars(
test1,
"std::stack wrapping = {std::deque with 2 elements = {true, false}}");

std::stack<std::string> test2;
test2.push("Hello");
test2.push("World");
ComparePrettyPrintToChars(test2,
"std::stack wrapping = {std::deque with 2 elements "
"= {\"Hello\", \"World\"}}");
}

void multiset_test() {
std::multiset<int> i_am_empty;
ComparePrettyPrintToChars(i_am_empty, "std::multiset is empty");

std::multiset<std::string> one_two_three {"1:one", "2:two", "3:three", "1:one"};
ComparePrettyPrintToChars(one_two_three,
"std::multiset with 4 elements = {"
R"("1:one", "1:one", "2:two", "3:three"})");
}

void vector_test() {
std::vector<bool> test0 = {true, false};
ComparePrettyPrintToChars(test0,
"std::vector<bool> of "
"length 2, capacity 64 = {1, 0}");
for (int i = 0; i < 31; ++i) {
test0.push_back(true);
test0.push_back(false);
}
ComparePrettyPrintToRegex(
test0,
"std::vector<bool> of length 64, "
"capacity 64 = {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, "
"0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, "
"0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}");
test0.push_back(true);
ComparePrettyPrintToRegex(
test0,
"std::vector<bool> of length 65, "
"capacity 128 = {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, "
"1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, "
"1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}");

std::vector<int> test1;
ComparePrettyPrintToChars(test1, "std::vector of length 0, capacity 0");

std::vector<int> test2 = {5, 6, 7};
ComparePrettyPrintToChars(test2,
"std::vector of length "
"3, capacity 3 = {5, 6, 7}");

std::vector<int, UncompressibleAllocator<int>> test3({7, 8});
ComparePrettyPrintToChars(std::move(test3),
"std::vector of length "
"2, capacity 2 = {7, 8}");
}

void set_iterator_test() {
std::set<int> one_two_three {1111, 2222, 3333};
auto it = one_two_three.find(2222);
MarkAsLive(it);
CompareExpressionPrettyPrintToRegex("it",
R"(std::__tree_const_iterator = {\[0x[a-f0-9]+\] = 2222})");

auto not_found = one_two_three.find(1234);
MarkAsLive(not_found);
// Because the end_node is not easily detected, just be sure it doesn't crash.
CompareExpressionPrettyPrintToRegex("not_found",
R"(std::__tree_const_iterator = {\[0x[a-f0-9]+\] = .*})");
}

void map_iterator_test() {
std::map<int, std::string> one_two_three;
one_two_three.insert({1, "one"});
one_two_three.insert({2, "two"});
one_two_three.insert({3, "three"});
auto it = one_two_three.begin();
MarkAsLive(it);
CompareExpressionPrettyPrintToRegex("it",
R"(std::__map_iterator = )"
R"({\[0x[a-f0-9]+\] = {first = 1, second = "one"}})");

auto not_found = one_two_three.find(7);
MarkAsLive(not_found);
CompareExpressionPrettyPrintToRegex("not_found",
R"(std::__map_iterator = {\[0x[a-f0-9]+\] = end\(\)})");
}

void unordered_set_test() {
std::unordered_set<int> i_am_empty;
ComparePrettyPrintToChars(i_am_empty, "std::unordered_set is empty");

std::unordered_set<int> numbers {12345, 67890, 222333, 12345};
numbers.erase(numbers.find(222333));
ComparePrettyPrintToRegex(numbers, "std::unordered_set with 2 elements = ");
ComparePrettyPrintToRegex(numbers, ".*12345.*");
ComparePrettyPrintToRegex(numbers, ".*67890.*");

std::unordered_set<std::string> colors {"red", "blue", "green"};
ComparePrettyPrintToRegex(colors, "std::unordered_set with 3 elements = ");
ComparePrettyPrintToRegex(colors, R"(.*"red".*)");
ComparePrettyPrintToRegex(colors, R"(.*"blue".*)");
ComparePrettyPrintToRegex(colors, R"(.*"green".*)");
}

void unordered_multiset_test() {
std::unordered_multiset<int> i_am_empty;
ComparePrettyPrintToChars(i_am_empty, "std::unordered_multiset is empty");

std::unordered_multiset<int> numbers {12345, 67890, 222333, 12345};
ComparePrettyPrintToRegex(numbers,
"std::unordered_multiset with 4 elements = ");
ComparePrettyPrintToRegex(numbers, ".*12345.*12345.*");
ComparePrettyPrintToRegex(numbers, ".*67890.*");
ComparePrettyPrintToRegex(numbers, ".*222333.*");

std::unordered_multiset<std::string> colors {"red", "blue", "green", "red"};
ComparePrettyPrintToRegex(colors,
"std::unordered_multiset with 4 elements = ");
ComparePrettyPrintToRegex(colors, R"(.*"red".*"red".*)");
ComparePrettyPrintToRegex(colors, R"(.*"blue".*)");
ComparePrettyPrintToRegex(colors, R"(.*"green".*)");
}

void unordered_map_test() {
std::unordered_map<int, int> i_am_empty;
ComparePrettyPrintToChars(i_am_empty, "std::unordered_map is empty");

std::unordered_map<int, std::string> one_two_three;
one_two_three.insert({1, "one"});
one_two_three.insert({2, "two"});
one_two_three.insert({3, "three"});
ComparePrettyPrintToRegex(one_two_three,
"std::unordered_map with 3 elements = ");
ComparePrettyPrintToRegex(one_two_three, R"(.*\[1\] = "one".*)");
ComparePrettyPrintToRegex(one_two_three, R"(.*\[2\] = "two".*)");
ComparePrettyPrintToRegex(one_two_three, R"(.*\[3\] = "three".*)");
}

void unordered_multimap_test() {
std::unordered_multimap<int, int> i_am_empty;
ComparePrettyPrintToChars(i_am_empty, "std::unordered_multimap is empty");

std::unordered_multimap<int, std::string> one_two_three;
one_two_three.insert({1, "one"});
one_two_three.insert({2, "two"});
one_two_three.insert({3, "three"});
one_two_three.insert({2, "two"});
ComparePrettyPrintToRegex(one_two_three,
"std::unordered_multimap with 4 elements = ");
ComparePrettyPrintToRegex(one_two_three, R"(.*\[1\] = "one".*)");
ComparePrettyPrintToRegex(one_two_three, R"(.*\[2\] = "two".*\[2\] = "two")");
ComparePrettyPrintToRegex(one_two_three, R"(.*\[3\] = "three".*)");
}

void unordered_map_iterator_test() {
std::unordered_map<int, int> ones_to_eights;
ones_to_eights.insert({1, 8});
ones_to_eights.insert({11, 88});
ones_to_eights.insert({111, 888});

auto ones_to_eights_begin = ones_to_eights.begin();
MarkAsLive(ones_to_eights_begin);
CompareExpressionPrettyPrintToRegex("ones_to_eights_begin",
R"(std::__hash_map_iterator = {\[1+\] = 8+})");

auto not_found = ones_to_eights.find(5);
MarkAsLive(not_found);
CompareExpressionPrettyPrintToRegex("not_found",
R"(std::__hash_map_iterator = end\(\))");
}

void unordered_set_iterator_test() {
std::unordered_set<int> ones;
ones.insert(111);
ones.insert(1111);
ones.insert(11111);

auto ones_begin = ones.begin();
MarkAsLive(ones_begin);
CompareExpressionPrettyPrintToRegex("ones_begin",
R"(std::__hash_const_iterator = {1+})");

auto not_found = ones.find(5);
MarkAsLive(not_found);
CompareExpressionPrettyPrintToRegex("not_found",
R"(std::__hash_const_iterator = end\(\))");
}

// Check that libc++ pretty printers do not handle pointers.
void pointer_negative_test() {
int abc = 123;
int *int_ptr = &abc;
// Check that the result is equivalent to "p/r int_ptr" command.
ComparePrettyPrintToRegex(int_ptr, R"(\(int \*\) 0x[a-f0-9]+)");
}

void shared_ptr_test() {
// Shared ptr tests while using test framework call another function
// due to which there is one more count for the pointer. Hence, all the
// following tests are testing with expected count plus 1.
std::shared_ptr<const int> test0 = std::make_shared<const int>(5);
ComparePrettyPrintToRegex(
test0,
R"(std::shared_ptr<int> count 2, weak 0 containing = {__ptr_ = 0x[a-f0-9]+})");

std::shared_ptr<const int> test1(test0);
ComparePrettyPrintToRegex(
test1,
R"(std::shared_ptr<int> count 3, weak 0 containing = {__ptr_ = 0x[a-f0-9]+})");

{
std::weak_ptr<const int> test2 = test1;
ComparePrettyPrintToRegex(
test0,
R"(std::shared_ptr<int> count 3, weak 1 containing = {__ptr_ = 0x[a-f0-9]+})");
}

ComparePrettyPrintToRegex(
test0,
R"(std::shared_ptr<int> count 3, weak 0 containing = {__ptr_ = 0x[a-f0-9]+})");

std::shared_ptr<const int> test3;
ComparePrettyPrintToChars(test3, "std::shared_ptr is nullptr");
}

void streampos_test() {
std::streampos test0 = 67;
ComparePrettyPrintToChars(
test0, "std::fpos with stream offset:67 with state: {count:0 value:0}");
std::istringstream input("testing the input stream here");
std::streampos test1 = input.tellg();
ComparePrettyPrintToChars(
test1, "std::fpos with stream offset:0 with state: {count:0 value:0}");
std::unique_ptr<char[]> buffer(new char[5]);
input.read(buffer.get(), 5);
test1 = input.tellg();
ComparePrettyPrintToChars(
test1, "std::fpos with stream offset:5 with state: {count:0 value:0}");
}

int main(int argc, char* argv[]) {
framework_self_test();

string_test();

u32string_test();
tuple_test();
unique_ptr_test();
shared_ptr_test();
bitset_test();
list_test();
deque_test();
map_test();
multimap_test();
queue_test();
priority_queue_test();
stack_test();
set_test();
multiset_test();
vector_test();
set_iterator_test();
map_iterator_test();
unordered_set_test();
unordered_multiset_test();
unordered_map_test();
unordered_multimap_test();
unordered_map_iterator_test();
unordered_set_iterator_test();
pointer_negative_test();
streampos_test();
return 0;
}
992 changes: 992 additions & 0 deletions libcxx/utils/gdb/libcxx/printers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,992 @@
#===----------------------------------------------------------------------===##
#
# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
# See https://llvm.org/LICENSE.txt for license information.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#
#===----------------------------------------------------------------------===##
"""GDB pretty-printers for libc++.
These should work for objects compiled when _LIBCPP_ABI_UNSTABLE is defined
and when it is undefined.
"""

from __future__ import print_function

import re
import gdb

# One under-documented feature of the gdb pretty-printer API
# is that clients can call any other member of the API
# before they call to_string.
# Therefore all self.FIELDs must be set in the pretty-printer's
# __init__ function.

_void_pointer_type = gdb.lookup_type("void").pointer()


_long_int_type = gdb.lookup_type("unsigned long long")


def addr_as_long(addr):
return int(addr.cast(_long_int_type))


# The size of a pointer in bytes.
_pointer_size = _void_pointer_type.sizeof


def _remove_cxx_namespace(typename):
"""Removed libc++ specific namespace from the type.
Arguments:
typename(string): A type, such as std::__u::something.
Returns:
A string without the libc++ specific part, such as std::something.
"""

return re.sub("std::__.*?::", "std::", typename)


def _remove_generics(typename):
"""Remove generics part of the type. Assumes typename is not empty.
Arguments:
typename(string): A type such as std::my_collection<element>.
Returns:
The prefix up to the generic part, such as std::my_collection.
"""

match = re.match("^([^<]+)", typename)
return match.group(1)


# Some common substitutions on the types to reduce visual clutter (A user who
# wants to see the actual details can always use print/r).
_common_substitutions = [
("std::basic_string<char, std::char_traits<char>, std::allocator<char> >",
"std::string"),
]


def _prettify_typename(gdb_type):
"""Returns a pretty name for the type, or None if no name can be found.
Arguments:
gdb_type(gdb.Type): A type object.
Returns:
A string, without type_defs, libc++ namespaces, and common substitutions
applied.
"""

type_without_typedefs = gdb_type.strip_typedefs()
typename = type_without_typedefs.name or type_without_typedefs.tag or \
str(type_without_typedefs)
result = _remove_cxx_namespace(typename)
for find_str, subst_str in _common_substitutions:
result = re.sub(find_str, subst_str, result)
return result


def _typename_for_nth_generic_argument(gdb_type, n):
"""Returns a pretty string for the nth argument of the given type.
Arguments:
gdb_type(gdb.Type): A type object, such as the one for std::map<int, int>
n: The (zero indexed) index of the argument to return.
Returns:
A string for the nth argument, such a "std::string"
"""
element_type = gdb_type.template_argument(n)
return _prettify_typename(element_type)


def _typename_with_n_generic_arguments(gdb_type, n):
"""Return a string for the type with the first n (1, ...) generic args."""

base_type = _remove_generics(_prettify_typename(gdb_type))
arg_list = [base_type]
template = "%s<"
for i in range(n):
arg_list.append(_typename_for_nth_generic_argument(gdb_type, i))
template += "%s, "
result = (template[:-2] + ">") % tuple(arg_list)
return result


def _typename_with_first_generic_argument(gdb_type):
return _typename_with_n_generic_arguments(gdb_type, 1)


class StdTuplePrinter(object):
"""Print a std::tuple."""

class _Children(object):
"""Class to iterate over the tuple's children."""

def __init__(self, val):
self.val = val
self.child_iter = iter(self.val["__base_"].type.fields())
self.count = 0

def __iter__(self):
return self

def next(self):
# child_iter raises StopIteration when appropriate.
field_name = self.child_iter.next()
child = self.val["__base_"][field_name]["__value_"]
self.count += 1
return ("[%d]" % self.count, child)

def __init__(self, val):
self.val = val

def to_string(self):
typename = _remove_generics(_prettify_typename(self.val.type))
if not self.val.type.fields():
return "empty %s" % typename
return "%s containing" % typename

def children(self):
if not self.val.type.fields():
return iter(())
return self._Children(self.val)


def _get_base_subobject(child_class_value, index=0):
"""Returns the object's value in the form of the parent class at index.
This function effectively casts the child_class_value to the base_class's
type, but the type-to-cast to is stored in the field at index, and once
we know the field, we can just return the data.
Args:
child_class_value: the value to cast
index: the parent class index
Raises:
Exception: field at index was not a base-class field.
"""

field = child_class_value.type.fields()[index]
if not field.is_base_class:
raise Exception("Not a base-class field.")
return child_class_value[field]


def _value_of_pair_first(value):
"""Convenience for _get_base_subobject, for the common case."""
return _get_base_subobject(value, 0)["__value_"]


class StdStringPrinter(object):
"""Print a std::string."""

def _get_short_size(self, short_field, short_size):
"""Short size depends on both endianness and a compile-time define."""

# If the padding field is present after all this indirection, then string
# was compiled with _LIBCPP_ABI_ALTERNATE_STRING_LAYOUT defined.
field = short_field.type.fields()[1].type.fields()[0]
libcpp_abi_alternate_string_layout = field.name and "__padding" in field.name

# Strictly, this only tells us the current mode, not how libcxx was
# compiled.
libcpp_big_endian = "big endian" in gdb.execute("show endian",
to_string=True)

# This logical structure closely follows the original code (which is clearer
# in C++). Keep them parallel to make them easier to compare.
if libcpp_abi_alternate_string_layout:
if libcpp_big_endian:
return short_size >> 1
else:
return short_size
elif libcpp_big_endian:
return short_size
else:
return short_size >> 1

def __init__(self, val):
self.val = val

def to_string(self):
"""Build a python string from the data whether stored inline or separately."""

value_field = _value_of_pair_first(self.val["__r_"])
short_field = value_field["__s"]
short_size = short_field["__size_"]
if short_size == 0:
return ""
short_mask = self.val["__short_mask"]
# Counter intuitive to compare the size and short_mask to see if the string
# is long, but that's the way the implementation does it. Note that
# __is_long() doesn't use get_short_size in C++.
is_long = short_size & short_mask
if is_long:
long_field = value_field["__l"]
data = long_field["__data_"]
size = long_field["__size_"]
else:
data = short_field["__data_"]
size = self._get_short_size(short_field, short_size)
if hasattr(data, "lazy_string"):
return data.lazy_string(length=size)
return data.string(length=size)

def display_hint(self):
return "string"


class StdUniquePtrPrinter(object):
"""Print a std::unique_ptr."""

def __init__(self, val):
self.val = val
self.addr = _value_of_pair_first(self.val["__ptr_"])
self.pointee_type = self.val.type.template_argument(0)

def to_string(self):
typename = _remove_generics(_prettify_typename(self.val.type))
if not self.addr:
return "%s is nullptr" % typename
return ("%s<%s> containing" %
(typename,
_remove_generics(_prettify_typename(self.pointee_type))))

def __iter__(self):
if self.addr:
yield "__ptr_", self.addr.cast(self.pointee_type.pointer())

def children(self):
return self


class StdSharedPointerPrinter(object):
"""Print a std::shared_ptr."""

def __init__(self, val):
self.val = val
self.addr = self.val["__ptr_"]

def to_string(self):
"""Returns self as a string."""
typename = _remove_generics(_prettify_typename(self.val.type))
pointee_type = _remove_generics(
_prettify_typename(self.val.type.template_argument(0)))
if not self.addr:
return "%s is nullptr" % typename
refcount = self.val["__cntrl_"]
if refcount != 0:
usecount = refcount["__shared_owners_"] + 1
weakcount = refcount["__shared_weak_owners_"]
if usecount == 0:
state = "expired, weak %d" % weakcount
else:
state = "count %d, weak %d" % (usecount, weakcount)
return "%s<%s> %s containing" % (typename, pointee_type, state)

def __iter__(self):
if self.addr:
yield "__ptr_", self.addr

def children(self):
return self


class StdVectorPrinter(object):
"""Print a std::vector."""

class _VectorBoolIterator(object):
"""Class to iterate over the bool vector's children."""

def __init__(self, begin, size, bits_per_word):
self.item = begin
self.size = size
self.bits_per_word = bits_per_word
self.count = 0
self.offset = 0

def __iter__(self):
return self

def next(self):
"""Retrieve the next element."""

self.count += 1
if self.count > self.size:
raise StopIteration
entry = self.item.dereference()
if entry & (1 << self.offset):
outbit = 1
else:
outbit = 0
self.offset += 1
if self.offset >= self.bits_per_word:
self.item += 1
self.offset = 0
return ("[%d]" % self.count, outbit)

class _VectorIterator(object):
"""Class to iterate over the non-bool vector's children."""

def __init__(self, begin, end):
self.item = begin
self.end = end
self.count = 0

def __iter__(self):
return self

def next(self):
self.count += 1
if self.item == self.end:
raise StopIteration
entry = self.item.dereference()
self.item += 1
return ("[%d]" % self.count, entry)

def __init__(self, val):
"""Set val, length, capacity, and iterator for bool and normal vectors."""
self.val = val
self.typename = _remove_generics(_prettify_typename(val.type))
begin = self.val["__begin_"]
if self.val.type.template_argument(0).code == gdb.TYPE_CODE_BOOL:
self.typename += "<bool>"
self.length = self.val["__size_"]
bits_per_word = self.val["__bits_per_word"]
self.capacity = _value_of_pair_first(
self.val["__cap_alloc_"]) * bits_per_word
self.iterator = self._VectorBoolIterator(
begin, self.length, bits_per_word)
else:
end = self.val["__end_"]
self.length = end - begin
self.capacity = _get_base_subobject(
self.val["__end_cap_"])["__value_"] - begin
self.iterator = self._VectorIterator(begin, end)

def to_string(self):
return ("%s of length %d, capacity %d" %
(self.typename, self.length, self.capacity))

def children(self):
return self.iterator

def display_hint(self):
return "array"


class StdBitsetPrinter(object):
"""Print a std::bitset."""

def __init__(self, val):
self.val = val
self.n_words = int(self.val["__n_words"])
self.bits_per_word = int(self.val["__bits_per_word"])
if self.n_words == 1:
self.values = [int(self.val["__first_"])]
else:
self.values = [int(self.val["__first_"][index])
for index in range(self.n_words)]

def to_string(self):
typename = _prettify_typename(self.val.type)
return "%s" % typename

def _byte_it(self, value):
index = -1
while value:
index += 1
will_yield = value % 2
value /= 2
if will_yield:
yield index

def _list_it(self):
for word_index in range(self.n_words):
current = self.values[word_index]
if current:
for n in self._byte_it(current):
yield ("[%d]" % (word_index * self.bits_per_word + n), 1)

def __iter__(self):
return self._list_it()

def children(self):
return self


class StdDequePrinter(object):
"""Print a std::deque."""

def __init__(self, val):
self.val = val
self.size = int(_value_of_pair_first(val["__size_"]))
self.start_ptr = self.val["__map_"]["__begin_"]
self.first_block_start_index = int(self.val["__start_"])
self.node_type = self.start_ptr.type
self.block_size = self._calculate_block_size(
val.type.template_argument(0))

def _calculate_block_size(self, element_type):
"""Calculates the number of elements in a full block."""
size = element_type.sizeof
# Copied from struct __deque_block_size implementation of libcxx.
return 4096 / size if size < 256 else 16

def _bucket_it(self, start_addr, start_index, end_index):
for i in range(start_index, end_index):
yield i, (start_addr.dereference() + i).dereference()

def _list_it(self):
"""Primary iteration worker."""
num_emitted = 0
current_addr = self.start_ptr
start_index = self.first_block_start_index
while num_emitted < self.size:
end_index = min(start_index + self.size -
num_emitted, self.block_size)
for _, elem in self._bucket_it(current_addr, start_index, end_index):
yield "", elem
num_emitted += end_index - start_index
current_addr = gdb.Value(addr_as_long(current_addr) + _pointer_size) \
.cast(self.node_type)
start_index = 0

def to_string(self):
typename = _remove_generics(_prettify_typename(self.val.type))
if self.size:
return "%s with %d elements" % (typename, self.size)
return "%s is empty" % typename

def __iter__(self):
return self._list_it()

def children(self):
return self

def display_hint(self):
return "array"


class StdListPrinter(object):
"""Print a std::list."""

def __init__(self, val):
self.val = val
size_alloc_field = self.val["__size_alloc_"]
self.size = int(_value_of_pair_first(size_alloc_field))
dummy_node = self.val["__end_"]
self.nodetype = gdb.lookup_type(
re.sub("__list_node_base", "__list_node",
str(dummy_node.type.strip_typedefs()))).pointer()
self.first_node = dummy_node["__next_"]

def to_string(self):
typename = _remove_generics(_prettify_typename(self.val.type))
if self.size:
return "%s with %d elements" % (typename, self.size)
return "%s is empty" % typename

def _list_iter(self):
current_node = self.first_node
for _ in range(self.size):
yield "", current_node.cast(self.nodetype).dereference()["__value_"]
current_node = current_node.dereference()["__next_"]

def __iter__(self):
return self._list_iter()

def children(self):
return self if self.nodetype else iter(())

def display_hint(self):
return "array"


class StdQueueOrStackPrinter(object):
"""Print a std::queue or std::stack."""

def __init__(self, val):
self.val = val
self.underlying = val["c"]

def to_string(self):
typename = _remove_generics(_prettify_typename(self.val.type))
return "%s wrapping" % typename

def children(self):
return iter([("", self.underlying)])

def display_hint(self):
return "array"


class StdPriorityQueuePrinter(object):
"""Print a std::priority_queue."""

def __init__(self, val):
self.val = val
self.underlying = val["c"]

def to_string(self):
# TODO(tamur): It would be nice to print the top element. The technical
# difficulty is that, the implementation refers to the underlying
# container, which is a generic class. libstdcxx pretty printers do not
# print the top element.
typename = _remove_generics(_prettify_typename(self.val.type))
return "%s wrapping" % typename

def children(self):
return iter([("", self.underlying)])

def display_hint(self):
return "array"


class RBTreeUtils(object):
"""Utility class for std::(multi)map, and std::(multi)set and iterators."""

def __init__(self, cast_type, root):
self.cast_type = cast_type
self.root = root

def left_child(self, node):
result = node.cast(self.cast_type).dereference()["__left_"]
return result

def right_child(self, node):
result = node.cast(self.cast_type).dereference()["__right_"]
return result

def parent(self, node):
"""Return the parent of node, if it exists."""
# If this is the root, then from the algorithm's point of view, it has no
# parent.
if node == self.root:
return None

# We don't have enough information to tell if this is the end_node (which
# doesn't have a __parent_ field), or the root (which doesn't have a parent
# from the algorithm's point of view), so cast_type may not be correct for
# this particular node. Use heuristics.

# The end_node's left child is the root. Note that when printing interators
# in isolation, the root is unknown.
if self.left_child(node) == self.root:
return None

parent = node.cast(self.cast_type).dereference()["__parent_"]
# If the value at the offset of __parent_ doesn't look like a valid pointer,
# then assume that node is the end_node (and therefore has no parent).
# End_node type has a pointer embedded, so should have pointer alignment.
if addr_as_long(parent) % _void_pointer_type.alignof:
return None
# This is ugly, but the only other option is to dereference an invalid
# pointer. 0x8000 is fairly arbitrary, but has had good results in
# practice. If there was a way to tell if a pointer is invalid without
# actually dereferencing it and spewing error messages, that would be ideal.
if parent < 0x8000:
return None
return parent

def is_left_child(self, node):
parent = self.parent(node)
return parent is not None and self.left_child(parent) == node

def is_right_child(self, node):
parent = self.parent(node)
return parent is not None and self.right_child(parent) == node


class AbstractRBTreePrinter(object):
"""Abstract super class for std::(multi)map, and std::(multi)set."""

def __init__(self, val):
self.val = val
tree = self.val["__tree_"]
self.size = int(_value_of_pair_first(tree["__pair3_"]))
dummy_root = tree["__pair1_"]
root = _value_of_pair_first(dummy_root)["__left_"]
cast_type = self._init_cast_type(val.type)
self.util = RBTreeUtils(cast_type, root)

def _get_key_value(self, node):
"""Subclasses should override to return a list of values to yield."""
raise NotImplementedError

def _traverse(self):
"""Traverses the binary search tree in order."""
current = self.util.root
skip_left_child = False
while True:
if not skip_left_child and self.util.left_child(current):
current = self.util.left_child(current)
continue
skip_left_child = False
for key_value in self._get_key_value(current):
yield "", key_value
right_child = self.util.right_child(current)
if right_child:
current = right_child
continue
while self.util.is_right_child(current):
current = self.util.parent(current)
if self.util.is_left_child(current):
current = self.util.parent(current)
skip_left_child = True
continue
break

def __iter__(self):
return self._traverse()

def children(self):
return self if self.util.cast_type and self.size > 0 else iter(())

def to_string(self):
typename = _remove_generics(_prettify_typename(self.val.type))
if self.size:
return "%s with %d elements" % (typename, self.size)
return "%s is empty" % typename


class StdMapPrinter(AbstractRBTreePrinter):
"""Print a std::map or std::multimap."""

def _init_cast_type(self, val_type):
map_it_type = gdb.lookup_type(
str(val_type) + "::iterator").strip_typedefs()
tree_it_type = map_it_type.template_argument(0)
node_ptr_type = tree_it_type.template_argument(1)
return node_ptr_type

def display_hint(self):
return "map"

def _get_key_value(self, node):
key_value = node.cast(self.util.cast_type).dereference()[
"__value_"]["__cc"]
return [key_value["first"], key_value["second"]]


class StdSetPrinter(AbstractRBTreePrinter):
"""Print a std::set."""

def _init_cast_type(self, val_type):
set_it_type = gdb.lookup_type(
str(val_type) + "::iterator").strip_typedefs()
node_ptr_type = set_it_type.template_argument(1)
return node_ptr_type

def display_hint(self):
return "array"

def _get_key_value(self, node):
key_value = node.cast(self.util.cast_type).dereference()["__value_"]
return [key_value]


class AbstractRBTreeIteratorPrinter(object):
"""Abstract super class for std::(multi)map, and std::(multi)set iterator."""

def _initialize(self, val, typename):
self.typename = typename
self.val = val
self.addr = self.val["__ptr_"]
cast_type = self.val.type.template_argument(1)
self.util = RBTreeUtils(cast_type, None)
if self.addr:
self.node = self.addr.cast(cast_type).dereference()

def _is_valid_node(self):
if not self.util.parent(self.addr):
return False
return self.util.is_left_child(self.addr) or \
self.util.is_right_child(self.addr)

def to_string(self):
if not self.addr:
return "%s is nullptr" % self.typename
return "%s " % self.typename

def _get_node_value(self, node):
raise NotImplementedError

def __iter__(self):
addr_str = "[%s]" % str(self.addr)
if not self._is_valid_node():
yield addr_str, " end()"
else:
yield addr_str, self._get_node_value(self.node)

def children(self):
return self if self.addr else iter(())


class MapIteratorPrinter(AbstractRBTreeIteratorPrinter):
"""Print a std::(multi)map iterator."""

def __init__(self, val):
self._initialize(val["__i_"],
_remove_generics(_prettify_typename(val.type)))

def _get_node_value(self, node):
return node["__value_"]["__cc"]


class SetIteratorPrinter(AbstractRBTreeIteratorPrinter):
"""Print a std::(multi)set iterator."""

def __init__(self, val):
self._initialize(val, _remove_generics(_prettify_typename(val.type)))

def _get_node_value(self, node):
return node["__value_"]


class StdFposPrinter(object):
"""Print a std::fpos or std::streampos."""

def __init__(self, val):
self.val = val

def to_string(self):
typename = _remove_generics(_prettify_typename(self.val.type))
offset = self.val["__off_"]
state = self.val["__st_"]
count = state["__count"]
value = state["__value"]["__wch"]
return "%s with stream offset:%s with state: {count:%s value:%s}" % (
typename, offset, count, value)


class AbstractUnorderedCollectionPrinter(object):
"""Abstract super class for std::unordered_(multi)[set|map]."""

def __init__(self, val):
self.val = val
self.table = val["__table_"]
self.sentinel = self.table["__p1_"]
self.size = int(_value_of_pair_first(self.table["__p2_"]))
node_base_type = self.sentinel.type.template_argument(0)
self.cast_type = node_base_type.template_argument(0)

def _list_it(self, sentinel_ptr):
next_ptr = _value_of_pair_first(sentinel_ptr)["__next_"]
while str(next_ptr.cast(_void_pointer_type)) != "0x0":
next_val = next_ptr.cast(self.cast_type).dereference()
for key_value in self._get_key_value(next_val):
yield "", key_value
next_ptr = next_val["__next_"]

def to_string(self):
typename = _remove_generics(_prettify_typename(self.val.type))
if self.size:
return "%s with %d elements" % (typename, self.size)
return "%s is empty" % typename

def _get_key_value(self, node):
"""Subclasses should override to return a list of values to yield."""
raise NotImplementedError

def children(self):
return self if self.cast_type and self.size > 0 else iter(())

def __iter__(self):
return self._list_it(self.sentinel)


class StdUnorderedSetPrinter(AbstractUnorderedCollectionPrinter):
"""Print a std::unordered_(multi)set."""

def _get_key_value(self, node):
return [node["__value_"]]

def display_hint(self):
return "array"


class StdUnorderedMapPrinter(AbstractUnorderedCollectionPrinter):
"""Print a std::unordered_(multi)map."""

def _get_key_value(self, node):
key_value = node["__value_"]["__cc"]
return [key_value["first"], key_value["second"]]

def display_hint(self):
return "map"


class AbstractHashMapIteratorPrinter(object):
"""Abstract class for unordered collection iterators."""

def _initialize(self, val, addr):
self.val = val
self.typename = _remove_generics(_prettify_typename(self.val.type))
self.addr = addr
if self.addr:
self.node = self.addr.cast(self.cast_type).dereference()

def _get_key_value(self):
"""Subclasses should override to return a list of values to yield."""
raise NotImplementedError

def to_string(self):
if not self.addr:
return "%s = end()" % self.typename
return "%s " % self.typename

def children(self):
return self if self.addr else iter(())

def __iter__(self):
for key_value in self._get_key_value():
yield "", key_value


class StdUnorderedSetIteratorPrinter(AbstractHashMapIteratorPrinter):
"""Print a std::(multi)set iterator."""

def __init__(self, val):
self.cast_type = val.type.template_argument(0)
self._initialize(val, val["__node_"])

def _get_key_value(self):
return [self.node["__value_"]]

def display_hint(self):
return "array"


class StdUnorderedMapIteratorPrinter(AbstractHashMapIteratorPrinter):
"""Print a std::(multi)map iterator."""

def __init__(self, val):
self.cast_type = val.type.template_argument(0).template_argument(0)
self._initialize(val, val["__i_"]["__node_"])

def _get_key_value(self):
key_value = self.node["__value_"]["__cc"]
return [key_value["first"], key_value["second"]]

def display_hint(self):
return "map"


def _remove_std_prefix(typename):
match = re.match("^std::(.+)", typename)
return match.group(1) if match is not None else ""


class LibcxxPrettyPrinter(object):
"""PrettyPrinter object so gdb-commands like 'info pretty-printers' work."""

def __init__(self, name):
super(LibcxxPrettyPrinter, self).__init__()
self.name = name
self.enabled = True

self.lookup = {
"basic_string": StdStringPrinter,
"string": StdStringPrinter,
"tuple": StdTuplePrinter,
"unique_ptr": StdUniquePtrPrinter,
"shared_ptr": StdSharedPointerPrinter,
"weak_ptr": StdSharedPointerPrinter,
"bitset": StdBitsetPrinter,
"deque": StdDequePrinter,
"list": StdListPrinter,
"queue": StdQueueOrStackPrinter,
"stack": StdQueueOrStackPrinter,
"priority_queue": StdPriorityQueuePrinter,
"map": StdMapPrinter,
"multimap": StdMapPrinter,
"set": StdSetPrinter,
"multiset": StdSetPrinter,
"vector": StdVectorPrinter,
"__map_iterator": MapIteratorPrinter,
"__map_const_iterator": MapIteratorPrinter,
"__tree_iterator": SetIteratorPrinter,
"__tree_const_iterator": SetIteratorPrinter,
"fpos": StdFposPrinter,
"unordered_set": StdUnorderedSetPrinter,
"unordered_multiset": StdUnorderedSetPrinter,
"unordered_map": StdUnorderedMapPrinter,
"unordered_multimap": StdUnorderedMapPrinter,
"__hash_map_iterator": StdUnorderedMapIteratorPrinter,
"__hash_map_const_iterator": StdUnorderedMapIteratorPrinter,
"__hash_iterator": StdUnorderedSetIteratorPrinter,
"__hash_const_iterator": StdUnorderedSetIteratorPrinter,
}

self.subprinters = []
for name, subprinter in self.lookup.items():
# Subprinters and names are used only for the rarely used command "info
# pretty" (and related), so the name of the first data structure it prints
# is a reasonable choice.
if subprinter not in self.subprinters:
subprinter.name = name
self.subprinters.append(subprinter)

def __call__(self, val):
"""Return the pretty printer for a val, if the type is supported."""

# Do not handle any type that is not a struct/class.
if val.type.strip_typedefs().code != gdb.TYPE_CODE_STRUCT:
return None

# Don't attempt types known to be inside libstdcxx.
typename = val.type.name or val.type.tag or str(val.type)
match = re.match("^std::(__.*?)::", typename)
if match is None or match.group(1) in ["__cxx1998",
"__debug",
"__7",
"__g"]:
return None

# Handle any using declarations or other typedefs.
typename = _prettify_typename(val.type)
if not typename:
return None
without_generics = _remove_generics(typename)
lookup_name = _remove_std_prefix(without_generics)
if lookup_name in self.lookup:
return self.lookup[lookup_name](val)
return None


_libcxx_printer_name = "libcxx_pretty_printer"


# These are called for every binary object file, which could be thousands in
# certain pathological cases. Limit our pretty printers to the progspace.
def _register_libcxx_printers(event):
progspace = event.new_objfile.progspace
if not getattr(progspace, _libcxx_printer_name, False):
print("Loading libc++ pretty-printers.")
gdb.printing.register_pretty_printer(
progspace, LibcxxPrettyPrinter(_libcxx_printer_name))
setattr(progspace, _libcxx_printer_name, True)


def _unregister_libcxx_printers(event):
progspace = event.progspace
if getattr(progspace, _libcxx_printer_name, False):
for printer in progspace.pretty_printers:
if getattr(printer, "name", "none") == _libcxx_printer_name:
progspace.pretty_printers.remove(printer)
setattr(progspace, _libcxx_printer_name, False)
break


def register_libcxx_printer_loader():
"""Register event handlers to load libc++ pretty-printers."""
gdb.events.new_objfile.connect(_register_libcxx_printers)
gdb.events.clear_objfiles.connect(_unregister_libcxx_printers)