[libcxx-commits] [libcxx] r370551 - Add gdb pretty printers for a wide variety of libc++ data structures.

Sterling Augustine via libcxx-commits libcxx-commits at lists.llvm.org
Fri Aug 30 16:43:35 PDT 2019


Author: saugustine
Date: Fri Aug 30 16:43:34 2019
New Revision: 370551

URL: http://llvm.org/viewvc/llvm-project?rev=370551&view=rev
Log:
Add gdb pretty printers for a wide variety of libc++ data structures.

Summary: Also add a test suite.

Reviewers: EricWF

Subscribers: christof, llvm-commits

Tags: #llvm

Differential Revision: https://reviews.llvm.org/D65609

Run a pep8 formatter.

Run pep8 formatter.

Convert to PEP8, address other comments from code review.

Added:
    libcxx/trunk/test/pretty_printers/
    libcxx/trunk/test/pretty_printers/gdb_pretty_printer_test.py
    libcxx/trunk/test/pretty_printers/gdb_pretty_printer_test.sh.cpp
    libcxx/trunk/utils/gdb/
    libcxx/trunk/utils/gdb/libcxx/
    libcxx/trunk/utils/gdb/libcxx/printers.py

Added: libcxx/trunk/test/pretty_printers/gdb_pretty_printer_test.py
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/pretty_printers/gdb_pretty_printer_test.py?rev=370551&view=auto
==============================================================================
--- libcxx/trunk/test/pretty_printers/gdb_pretty_printer_test.py (added)
+++ libcxx/trunk/test/pretty_printers/gdb_pretty_printer_test.py Fri Aug 30 16:43:34 2019
@@ -0,0 +1,112 @@
+#===----------------------------------------------------------------------===##
+#
+# 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)

Added: libcxx/trunk/test/pretty_printers/gdb_pretty_printer_test.sh.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/pretty_printers/gdb_pretty_printer_test.sh.cpp?rev=370551&view=auto
==============================================================================
--- libcxx/trunk/test/pretty_printers/gdb_pretty_printer_test.sh.cpp (added)
+++ libcxx/trunk/test/pretty_printers/gdb_pretty_printer_test.sh.cpp Fri Aug 30 16:43:34 2019
@@ -0,0 +1,631 @@
+// -*- 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;
+}

Added: libcxx/trunk/utils/gdb/libcxx/printers.py
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/utils/gdb/libcxx/printers.py?rev=370551&view=auto
==============================================================================
--- libcxx/trunk/utils/gdb/libcxx/printers.py (added)
+++ libcxx/trunk/utils/gdb/libcxx/printers.py Fri Aug 30 16:43:34 2019
@@ -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)




More information about the libcxx-commits mailing list