// -*- C++ -*- //===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // UNSUPPORTED: system-windows // REQUIRES: libcxx_gdb // // RUN: %cxx %flags %s -o %t.exe %compile_flags -g %link_flags // Ensure locale-independence for unicode tests. // RUN: %libcxx_gdb -nx -batch -iex "set autoload off" -ex "source %libcxx_src_root/utils/gdb/libcxx/printers.py" -ex "python register_libcxx_printer_loader()" -ex "source %libcxx_src_root/test/pretty_printers/gdb_pretty_printer_test.py" %t.exe #include #include #include #include #include #include #include #include #include #include #include #include #include #include "test_macros.h" // 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. #ifdef TEST_COMPILER_GCC #define OPT_NONE __attribute__((noinline)) #else #define OPT_NONE __attribute__((optnone)) #endif void StopForDebugger(void *value, void *check) OPT_NONE; void StopForDebugger(void *value, void *check) {} // Prevents the compiler optimizing away the parameter in the caller function. template void MarkAsLive(Type &&t) OPT_NONE; template void MarkAsLive(Type &&t) {} // 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 void ComparePrettyPrintToChars( TypeToPrint value, const char *expectation) { StopForDebugger(&value, &expectation); } template 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 class UncompressibleAllocator : public std::allocator { 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, UncompressibleAllocator> 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 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 matilda(new std::string("Matilda")); ComparePrettyPrintToRegex( std::move(matilda), R"(std::unique_ptr containing = {__ptr_ = 0x[a-f0-9]+})"); std::unique_ptr forty_two(new int(42)); ComparePrettyPrintToRegex(std::move(forty_two), R"(std::unique_ptr containing = {__ptr_ = 0x[a-f0-9]+})"); std::unique_ptr 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 i_am_empty{}; ComparePrettyPrintToChars(i_am_empty, "std::list is empty"); std::list one_two_three {1, 2, 3}; ComparePrettyPrintToChars(one_two_three, "std::list with 3 elements = {1, 2, 3}"); std::list colors {"red", "blue", "green"}; ComparePrettyPrintToChars(colors, R"(std::list with 3 elements = {"red", "blue", "green"})"); } void deque_test() { std::deque i_am_empty{}; ComparePrettyPrintToChars(i_am_empty, "std::deque is empty"); std::deque one_two_three {1, 2, 3}; ComparePrettyPrintToChars(one_two_three, "std::deque with 3 elements = {1, 2, 3}"); std::deque 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 i_am_empty{}; ComparePrettyPrintToChars(i_am_empty, "std::map is empty"); std::map 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 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 i_am_empty{}; ComparePrettyPrintToChars(i_am_empty, "std::multimap is empty"); std::multimap 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 i_am_empty; ComparePrettyPrintToChars(i_am_empty, "std::queue wrapping = {std::deque is empty}"); std::queue one_two_three(std::deque{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 i_am_empty; ComparePrettyPrintToChars(i_am_empty, "std::priority_queue wrapping = {std::vector of length 0, capacity 0}"); std::priority_queue 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 i_am_empty; ComparePrettyPrintToChars(i_am_empty, "std::set is empty"); std::set one_two_three {3, 1, 2}; ComparePrettyPrintToChars(one_two_three, "std::set with 3 elements = {1, 2, 3}"); std::set> 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 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 test1; test1.push(true); test1.push(false); ComparePrettyPrintToChars( test1, "std::stack wrapping = {std::deque with 2 elements = {true, false}}"); std::stack test2; test2.push("Hello"); test2.push("World"); ComparePrettyPrintToChars(test2, "std::stack wrapping = {std::deque with 2 elements " "= {\"Hello\", \"World\"}}"); } void multiset_test() { std::multiset i_am_empty; ComparePrettyPrintToChars(i_am_empty, "std::multiset is empty"); std::multiset 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 test0 = {true, false}; ComparePrettyPrintToChars(test0, "std::vector 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 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 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 test1; ComparePrettyPrintToChars(test1, "std::vector of length 0, capacity 0"); std::vector test2 = {5, 6, 7}; ComparePrettyPrintToChars(test2, "std::vector of length " "3, capacity 3 = {5, 6, 7}"); std::vector> test3({7, 8}); ComparePrettyPrintToChars(std::move(test3), "std::vector of length " "2, capacity 2 = {7, 8}"); } void set_iterator_test() { std::set 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 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 i_am_empty; ComparePrettyPrintToChars(i_am_empty, "std::unordered_set is empty"); std::unordered_set 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 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 i_am_empty; ComparePrettyPrintToChars(i_am_empty, "std::unordered_multiset is empty"); std::unordered_multiset 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 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 i_am_empty; ComparePrettyPrintToChars(i_am_empty, "std::unordered_map is empty"); std::unordered_map 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 i_am_empty; ComparePrettyPrintToChars(i_am_empty, "std::unordered_multimap is empty"); std::unordered_multimap 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 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 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 test0 = std::make_shared(5); ComparePrettyPrintToRegex( test0, R"(std::shared_ptr count 2, weak 0 containing = {__ptr_ = 0x[a-f0-9]+})"); std::shared_ptr test1(test0); ComparePrettyPrintToRegex( test1, R"(std::shared_ptr count 3, weak 0 containing = {__ptr_ = 0x[a-f0-9]+})"); { std::weak_ptr test2 = test1; ComparePrettyPrintToRegex( test0, R"(std::shared_ptr count 3, weak 1 containing = {__ptr_ = 0x[a-f0-9]+})"); } ComparePrettyPrintToRegex( test0, R"(std::shared_ptr count 3, weak 0 containing = {__ptr_ = 0x[a-f0-9]+})"); std::shared_ptr 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 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; }