diff options
-rw-r--r-- | libcxx/test/pretty_printers/gdb_pretty_printer_test.py | 112 | ||||
-rw-r--r-- | libcxx/test/pretty_printers/gdb_pretty_printer_test.sh.cpp | 631 | ||||
-rw-r--r-- | libcxx/utils/gdb/libcxx/printers.py | 992 |
3 files changed, 0 insertions, 1735 deletions
diff --git a/libcxx/test/pretty_printers/gdb_pretty_printer_test.py b/libcxx/test/pretty_printers/gdb_pretty_printer_test.py deleted file mode 100644 index 5e425688734..00000000000 --- a/libcxx/test/pretty_printers/gdb_pretty_printer_test.py +++ /dev/null @@ -1,112 +0,0 @@ -#===----------------------------------------------------------------------===## -# -# 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 -# -#===----------------------------------------------------------------------===## -"""Commands used to automate testing gdb pretty printers. - -This script is part of a larger framework to test gdb pretty printers. It -runs the program, detects test cases, checks them, and prints results. - -See gdb_pretty_printer_test.sh.cpp on how to write a test case. - -""" - -from __future__ import print_function -import re -import gdb - -test_failures = 0 - - -class CheckResult(gdb.Command): - - def __init__(self): - super(CheckResult, self).__init__( - "print_and_compare", gdb.COMMAND_DATA) - - def invoke(self, arg, from_tty): - try: - # Stack frame is: - # 0. StopForDebugger - # 1. ComparePrettyPrintToChars or ComparePrettyPrintToRegex - # 2. TestCase - compare_frame = gdb.newest_frame().older() - testcase_frame = compare_frame.older() - test_loc = testcase_frame.find_sal() - # Use interactive commands in the correct context to get the pretty - # printed version - - value_str = self._get_value_string(compare_frame, testcase_frame) - - # Ignore the convenience variable name and newline - value = value_str[value_str.find("= ") + 2:-1] - gdb.newest_frame().select() - - expectation_val = compare_frame.read_var("expectation") - if "PrettyPrintToRegex" in compare_frame.name(): - check_literal = expectation_val.string() - test_fails = not re.match(check_literal, value) - else: - check_literal_string = expectation_val.string(encoding="utf-8") - check_literal = check_literal_string.encode("utf-8") - test_fails = value != check_literal - - if test_fails: - global test_failures - print("FAIL: " + test_loc.symtab.filename + - ":" + str(test_loc.line)) - print("GDB printed:") - print(" " + value) - print("Value should match:") - print(" " + check_literal) - test_failures += 1 - else: - print("PASS: " + test_loc.symtab.filename + - ":" + str(test_loc.line)) - - except RuntimeError as e: - # At this point, lots of different things could be wrong, so don't try to - # recover or figure it out. Don't exit either, because then it's - # impossible debug the framework itself. - print("FAIL: Something is wrong in the test framework.") - print(str(e)) - test_failures += 1 - - def _get_value_string(self, compare_frame, testcase_frame): - compare_frame.select() - if "ComparePrettyPrint" in compare_frame.name(): - return gdb.execute("p value", to_string=True) - value_str = str(compare_frame.read_var("value")) - clean_expression_str = value_str.strip("'\"") - testcase_frame.select() - return gdb.execute("p " + clean_expression_str, to_string=True) - - -def exit_handler(event=None): - global test_failures - if test_failures: - print("FAILED %d cases" % test_failures) - exit(test_failures) - - -# Start code executed at load time - -# Disable terminal paging -gdb.execute("set height 0") -gdb.execute("set python print-stack full") -test_failures = 0 -CheckResult() -test_bp = gdb.Breakpoint("StopForDebugger") -test_bp.enabled = True -test_bp.silent = True -test_bp.commands = "print_and_compare\ncontinue" -# "run" won't return if the program exits; ensure the script regains control. -gdb.events.exited.connect(exit_handler) -gdb.execute("run") -# If the program didn't exit, something went wrong, but we don't -# know what. Fail on exit. -test_failures += 1 -exit_handler(None) diff --git a/libcxx/test/pretty_printers/gdb_pretty_printer_test.sh.cpp b/libcxx/test/pretty_printers/gdb_pretty_printer_test.sh.cpp deleted file mode 100644 index 27c747bb76f..00000000000 --- a/libcxx/test/pretty_printers/gdb_pretty_printer_test.sh.cpp +++ /dev/null @@ -1,631 +0,0 @@ -// -*- 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 - -// RUN: %cxx %flags %s -o %t.exe %compile_flags -g %link_flags -// Ensure locale-independence for unicode tests. -// RUN: LC_CTYPE="en_US.UTF-8" gdb -batch -nx -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; -} diff --git a/libcxx/utils/gdb/libcxx/printers.py b/libcxx/utils/gdb/libcxx/printers.py deleted file mode 100644 index dc2f2e97aec..00000000000 --- a/libcxx/utils/gdb/libcxx/printers.py +++ /dev/null @@ -1,992 +0,0 @@ -#===----------------------------------------------------------------------===## -# -# 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) |